public static bool ValidateSMTBonus(DPSWarrCharacter dpswarchar) { // Null Check if (dpswarchar.Char == null) { return(false); } if (dpswarchar.Char.MainHand == null || dpswarchar.Char.OffHand == null) { return(false); } // Item Type Fails if (dpswarchar.Char.MainHand.Type != ItemType.OneHandAxe && dpswarchar.Char.MainHand.Type != ItemType.OneHandSword && dpswarchar.Char.MainHand.Type != ItemType.OneHandMace) { return(false); } if (dpswarchar.Char.OffHand.Type != ItemType.OneHandAxe && dpswarchar.Char.OffHand.Type != ItemType.OneHandSword && dpswarchar.Char.OffHand.Type != ItemType.OneHandMace) { return(false); } // If it hasn't failed by now, it must be good return(true); }
public bool Equals(DPSWarrCharacter other) { if (Char != other.Char) { return(false); } if (Rot != other.Rot) { return(false); } if (CombatFactors != other.CombatFactors) { return(false); } if (CalcOpts != other.CalcOpts) { return(false); } if (BossOpts != other.BossOpts) { return(false); } if (Talents != other.Talents) { return(false); } return(true); }
public static SpecialEffect GetDeathWishWithMastery(float masteryVal, DPSWarrCharacter dpswarrchar) { SpecialEffect retVal = new SpecialEffect(Trigger.Use, new Stats() { BonusDamageMultiplier = 0.20f * (1f + masteryVal), DamageTakenReductionMultiplier = (dpswarrchar.Talents.GlyphOfDeathWish ? 0f : -0.05f), }, 30f, 3f * 60f * (1f - 0.10f * dpswarrchar.Talents.IntensifyRage)); return(retVal); }
public ArmsRotation(DPSWarrCharacter dpswarrchar) { DPSWarrChar = dpswarrchar; _cachedLatentGCD = 1.5f + DPSWarrChar.CalcOpts.Latency + DPSWarrChar.CalcOpts.AllowedReact; AbilityWrapper.LatentGCD = _cachedLatentGCD; _cachedNumGCDsO20 = FightDurationO20 / LatentGCD; _cachedNumGCDsU20 = FightDurationU20 / LatentGCD; #if DEBUG //ConstructionCounts["Rotation Arms"]++; #endif }
/// <summary> /// Fury Implementation of the Rotation class, generates Fury rotation numbers /// </summary> public FuryRotation(DPSWarrCharacter dpswarrchar) { DPSWarrChar = dpswarrchar; _cachedLatentGCD = 1.5f + DPSWarrChar.CalcOpts.Latency + DPSWarrChar.CalcOpts.AllowedReact; AbilityWrapper.LatentGCD = _cachedLatentGCD; _cachedNumGCDsO20 = FightDurationO20 / LatentGCD; _cachedNumGCDsU20 = FightDurationU20 / LatentGCD; #if DEBUG //ConstructionCounts["Rotation Fury"]++; #endif }
public bool Equals(DPSWarrCharacter other) { if (Char != other.Char) return false; if (Rot != other.Rot) return false; if (CombatFactors != other.CombatFactors) return false; if (CalcOpts != other.CalcOpts) return false; if (BossOpts != other.BossOpts) return false; if (Talents != other.Talents) return false; return true; }
public static bool ValidatePlateSpec(DPSWarrCharacter dpswarchar) { // Null Check if (dpswarchar.Char == null) { return(false); } // Item Type Fails if (dpswarchar.Char.Head == null || dpswarchar.Char.Head.Type != ItemType.Plate) { return(false); } if (dpswarchar.Char.Shoulders == null || dpswarchar.Char.Shoulders.Type != ItemType.Plate) { return(false); } if (dpswarchar.Char.Chest == null || dpswarchar.Char.Chest.Type != ItemType.Plate) { return(false); } if (dpswarchar.Char.Wrist == null || dpswarchar.Char.Wrist.Type != ItemType.Plate) { return(false); } if (dpswarchar.Char.Hands == null || dpswarchar.Char.Hands.Type != ItemType.Plate) { return(false); } if (dpswarchar.Char.Waist == null || dpswarchar.Char.Waist.Type != ItemType.Plate) { return(false); } if (dpswarchar.Char.Legs == null || dpswarchar.Char.Legs.Type != ItemType.Plate) { return(false); } if (dpswarchar.Char.Feet == null || dpswarchar.Char.Feet.Type != ItemType.Plate) { return(false); } // If it hasn't failed by now, it must be good return(true); }
public static SpecialEffect GetDeathWishWithMastery(float masteryVal, DPSWarrCharacter dpswarrchar) { SpecialEffect retVal = new SpecialEffect(Trigger.Use, new Stats() { BonusDamageMultiplier = 0.20f * (1f + masteryVal), DamageTakenReductionMultiplier = (dpswarrchar.Talents.GlyphOfDeathWish ? 0f : -0.05f), }, 30f, 3f * 60f * (1f - 0.10f * dpswarrchar.Talents.IntensifyRage)); return retVal; }
public static bool ValidateSMTBonus(DPSWarrCharacter dpswarchar) { // Null Check if (dpswarchar.Char == null) { return false; } if (dpswarchar.Char.MainHand == null || dpswarchar.Char.OffHand == null) { return false; } // Item Type Fails if (dpswarchar.Char.MainHand.Type != ItemType.OneHandAxe && dpswarchar.Char.MainHand.Type != ItemType.OneHandSword && dpswarchar.Char.MainHand.Type != ItemType.OneHandMace) { return false; } if (dpswarchar.Char.OffHand.Type != ItemType.OneHandAxe && dpswarchar.Char.OffHand.Type != ItemType.OneHandSword && dpswarchar.Char.OffHand.Type != ItemType.OneHandMace) { return false; } // If it hasn't failed by now, it must be good return true; }
public static bool ValidatePlateSpec(DPSWarrCharacter dpswarchar) { // Null Check if (dpswarchar.Char == null) { return false; } // Item Type Fails if (dpswarchar.Char.Head == null || dpswarchar.Char.Head.Type != ItemType.Plate) { return false; } if (dpswarchar.Char.Shoulders == null || dpswarchar.Char.Shoulders.Type != ItemType.Plate) { return false; } if (dpswarchar.Char.Chest == null || dpswarchar.Char.Chest.Type != ItemType.Plate) { return false; } if (dpswarchar.Char.Wrist == null || dpswarchar.Char.Wrist.Type != ItemType.Plate) { return false; } if (dpswarchar.Char.Hands == null || dpswarchar.Char.Hands.Type != ItemType.Plate) { return false; } if (dpswarchar.Char.Waist == null || dpswarchar.Char.Waist.Type != ItemType.Plate) { return false; } if (dpswarchar.Char.Legs == null || dpswarchar.Char.Legs.Type != ItemType.Plate) { return false; } if (dpswarchar.Char.Feet == null || dpswarchar.Char.Feet.Type != ItemType.Plate) { return false; } // If it hasn't failed by now, it must be good return true; }
float getSurv(DPSWarrCharacter dpswarchar, int Iter, bool with) { bool orig = dpswarchar.CalcOpts.MaintenanceTree[Iter]; dpswarchar.CalcOpts.MaintenanceTree[Iter] = with; CharacterCalculationsDPSWarr calculations = GetCharacterCalculations(dpswarchar.Char.Clone()) as CharacterCalculationsDPSWarr; dpswarchar.CalcOpts.MaintenanceTree[Iter] = orig; return calculations.TotalHPS; }
public static SpecialEffect GetEnragedRegenerationWithMastery(float masteryVal, DPSWarrCharacter dpswarrchar) { SpecialEffect retVal = new SpecialEffect(Trigger.MeleeHit, new Stats() { BonusDamageMultiplier = 0.10f / 3f * dpswarrchar.Talents.Enrage * (1f + masteryVal), }, 9f, 0f, 0.03f * 3f); return(retVal); }
ComparisonCalculationDPSWarr getComp(DPSWarrCharacter dpswarchar, string Name, int Iter) { ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr(); comparison.Name = Name; comparison.Equipped = dpswarchar.CalcOpts.MaintenanceTree[Iter] == true; float with = getDPS(dpswarchar, Iter, true); float without = getDPS(dpswarchar, Iter, false); comparison.DPSPoints = with - without; with = getSurv(dpswarchar, Iter, true) * dpswarchar.CalcOpts.SurvScale; without = getSurv(dpswarchar, Iter, false) * dpswarchar.CalcOpts.SurvScale; comparison.SurvPoints = with - without; //comparison.ImageSource = aw.ability.Icon; // TODO //comparison.Description = aw.ability.Desc; // TODO return comparison; }
private Base.StatsWarrior GetCharacterStats_Buffed(DPSWarrCharacter dpswarchar, Item additionalItem, bool isBuffed, out Base.StatsWarrior statsRace) { #if DEBUG //ConstructionCounts["GetCharacterStats_Buffed"]++; #endif if (dpswarchar.CalcOpts == null) { dpswarchar.CalcOpts = dpswarchar.Char.CalculationOptions as CalculationOptionsDPSWarr; } if (dpswarchar.BossOpts == null) { dpswarchar.BossOpts = dpswarchar.Char.BossOptions; } if (dpswarchar.CombatFactors == null) { dpswarchar.CombatFactors = new CombatFactors(dpswarchar.Char, new Base.StatsWarrior(), dpswarchar.CalcOpts, dpswarchar.BossOpts); } WarriorTalents talents = dpswarchar.Char.WarriorTalents; #region From Race statsRace = new Base.StatsWarrior(); statsRace.Accumulate(BaseStats.GetBaseStats(dpswarchar.Char.Level, CharacterClass.Warrior, dpswarchar.Char.Race)); #endregion #region From Gear/Buffs Base.StatsWarrior statsBuffs = (isBuffed ? GetBuffsStats(dpswarchar) : new Base.StatsWarrior()); Base.StatsWarrior statsItems = new Base.StatsWarrior(); statsItems.Accumulate(GetItemStats(dpswarchar.Char, additionalItem)); #endregion #region From Options Base.StatsWarrior statsOptionsPanel = new Base.StatsWarrior() { //BonusStrengthMultiplier = (dpswarchar.combatFactors.FuryStance ? talents.ImprovedBerserkerStance * 0.04f : 0f), //PhysicalCrit = (dpswarchar.combatFactors.FuryStance ? 0.03f + statsBuffs.BonusWarrior_T9_2P_Crit : 0f), // Stance Related Damage Given/Taken mods /* Battle Stance A balanced combat stance. Increases damage done by 5%. Decreases damage taken by 5%. Berserker Stance An aggressive combat stance. Increases damage done by 10%. */ DamageTakenReductionMultiplier = (!dpswarchar.CombatFactors.FuryStance ? -0.05f : 0f), BonusDamageMultiplier = (!dpswarchar.CombatFactors.FuryStance ? 0.05f : 0.10f), // Battle Shout Strength = (dpswarchar.CalcOpts.M_BattleShout ? 549f : 0f), Agility = (dpswarchar.CalcOpts.M_BattleShout ? 549f : 0f), // Commanding Shout Stamina = (dpswarchar.CalcOpts.M_CommandingShout ? 585f : 0f), // Demo Shout BossPhysicalDamageDealtReductionMultiplier = (dpswarchar.CalcOpts.M_DemoralizingShout ? 0.10f : 0f), // Sunder Armor ArmorPenetration = (dpswarchar.CalcOpts.M_SunderArmor ? 0.04f * 3f : 0f), // Thunder Clap BossAttackSpeedReductionMultiplier = (dpswarchar.CalcOpts.M_ThunderClap ? 0.20f : 0f), }; if (dpswarchar.CalcOpts.M_ColossusSmash) { statsOptionsPanel.AddSpecialEffect(TalentsAsSpecialEffects.ColossusSmash); } #endregion #region From Talents Base.StatsWarrior statsTalents = new Base.StatsWarrior() { // Offensive BonusDamageMultiplier = ((!dpswarchar.CombatFactors.FuryStance && dpswarchar.Char.MainHand != null && dpswarchar.Char.MainHand.Slot == ItemSlot.TwoHand ? 1.12f : 1.00f) * (dpswarchar.CombatFactors.FuryStance && talents.SingleMindedFury > 0 && HelperFunctions.ValidateSMTBonus(dpswarchar) ? 1.20f : 1.00f)) - 1f, BonusPhysicalDamageMultiplier = ((dpswarchar.CalcOpts.M_Rend // Have Rend up || talents.DeepWounds > 0 // Have Deep Wounds ? (1.0f + talents.BloodFrenzy * 0.02f) : 1.0f) * (dpswarchar.CombatFactors.FuryStance ? 1.05f : 1.0f)) - 1f, BonusBleedDamageMultiplier = (dpswarchar.CalcOpts.M_Rend // Have Rend up || talents.DeepWounds > 0 // Have Deep Wounds ? talents.BloodFrenzy * 0.15f : 0f), PhysicalCrit = (talents.Rampage > 0 && dpswarchar.CombatFactors.FuryStance && isBuffed ? 0.05f + 0.02f : 0f), // Cata has a new +2% on self (group gets 5%, self gets total 7%) PhysicalHit = (dpswarchar.CombatFactors.FuryStance ? 0.03f : 0f), // Fury Spec has passive 3% Hit BonusWhiteDamageMultiplier = (dpswarchar.CombatFactors.FuryStance ? 0.40f : 0f), // Fury Spec has passive 40% Bonus White Damage Mult // Defensive BaseArmorMultiplier = talents.Toughness * 0.10f / 3f, BonusHealingReceived = talents.FieldDressing * 0.03f, BonusStrengthMultiplier = HelperFunctions.ValidatePlateSpec(dpswarchar) ? 0.05f : 0f, // Specific Abilities BonusMortalStrikeDamageMultiplier = (1f + (dpswarchar.Talents.GlyphOfMortalStrike ? 0.10f : 0f)) * (1f + dpswarchar.Talents.WarAcademy * 0.05f) - 1f, BonusRagingBlowDamageMultiplier = dpswarchar.Talents.WarAcademy * 0.05f, BonusOverpowerDamageMultiplier = (dpswarchar.Talents.GlyphOfOverpower ? 0.10f : 0f), BonusSlamDamageMultiplier = (1f + dpswarchar.Talents.ImprovedSlam * 0.20f) * (1f + dpswarchar.Talents.WarAcademy * 0.05f) - 1f, BonusVictoryRushDamageMultiplier = dpswarchar.Talents.WarAcademy * 0.05f, BonusBloodthirstDamageMultiplier = (dpswarchar.Talents.GlyphOfBloodthirst ? 0.10f : 0f), }; // Add Talents that give SpecialEffects if (talents.WreckingCrew > 0 && dpswarchar.Char.MainHand != null) { statsTalents.AddSpecialEffect(TalentsAsSpecialEffects.WreckingCrew[talents.WreckingCrew]); } if (talents.LambsToTheSlaughter > 0 && dpswarchar.CalcOpts.M_MortalStrike) { statsTalents.AddSpecialEffect(TalentsAsSpecialEffects.LambsToTheSlaughter[talents.LambsToTheSlaughter]); // TODO: This should also refresh rend in 4.1.0 } if (talents.BloodCraze > 0) { statsTalents.AddSpecialEffect(TalentsAsSpecialEffects.BloodCraze[talents.BloodCraze]); } if (talents.Executioner > 0 && dpswarchar.CalcOpts.M_ExecuteSpam) { statsTalents.AddSpecialEffect(TalentsAsSpecialEffects.Executioner[talents.Executioner]); } if (talents.BloodFrenzy > 0) { statsTalents.AddSpecialEffect(TalentsAsSpecialEffects.BloodFrenzy[talents.BloodFrenzy]); } if (talents.MeatCleaver > 0 && (dpswarchar.CalcOpts.M_Whirlwind || dpswarchar.CalcOpts.M_Cleave)) { statsTalents.AddSpecialEffect(TalentsAsSpecialEffects.MeatCleaver[talents.MeatCleaver]); } #endregion Base.StatsWarrior statsTotal = new Base.StatsWarrior(); statsTotal.Accumulate(statsRace); statsTotal.Accumulate(statsItems); statsTotal.Accumulate(statsBuffs); statsTotal.Accumulate(statsTalents); statsTotal.Accumulate(statsOptionsPanel); statsTotal = UpdateStatsAndAdd(statsTotal, null, dpswarchar.Char); float multiplier = 0.0560f; float masteryBonusVal = (2f*0.056f + multiplier * StatConversion.GetMasteryFromRating(statsTotal.MasteryRating, CharacterClass.Warrior)); if (talents.DeathWish > 0 && dpswarchar.CalcOpts.M_DeathWish && dpswarchar.CombatFactors.FuryStance) { statsTotal.AddSpecialEffect(TalentsAsSpecialEffects.GetDeathWishWithMastery(masteryBonusVal, dpswarchar)); } if (talents.Enrage > 0 && dpswarchar.CombatFactors.FuryStance) { statsTotal.AddSpecialEffect(TalentsAsSpecialEffects.GetEnragedRegenerationWithMastery(masteryBonusVal, dpswarchar)); } //Stats statsProcs = new Stats(); // Dodge (your dodging incoming attacks) // Warriors no longer gain dodge from Agility since patch 4.2 // statsTotal.Dodge += StatConversion.GetDodgeFromAgility(statsTotal.Agility, dpswarchar.Char.Class); statsTotal.Dodge += StatConversion.GetDodgeFromRating(statsTotal.DodgeRating, dpswarchar.Char.Class); // Parry (your parrying incoming attacks) statsTotal.Parry += StatConversion.GetParryFromRating(statsTotal.ParryRating, dpswarchar.Char.Class); return statsTotal; }
public Base.StatsWarrior GetBuffsStats(DPSWarrCharacter dpswarchar) { List<Buff> removedBuffs = new List<Buff>(); List<Buff> addedBuffs = new List<Buff>(); List<Buff> buffGroup = new List<Buff>(); #region Maintenance Auto-Fixing // Removes the Sunder Armor if you are maintaining it yourself // Also removes Acid Spit and Expose Armor // We are now calculating this internally for better accuracy and to provide value to relevant talents if (dpswarchar.CalcOpts.M_SunderArmor) { buffGroup.Clear(); buffGroup.Add(Buff.GetBuffBySpellId(7386));//Buff.GetBuffByName("Sunder Armor")); buffGroup.Add(Buff.GetBuffBySpellId(8647));//Buff.GetBuffByName("Expose Armor")); buffGroup.Add(Buff.GetBuffBySpellId(770));//Buff.GetBuffByName("Faerie Fire")); buffGroup.Add(Buff.GetBuffBySpellId(35387));//Buff.GetBuffByName("Corrosive Spit")); buffGroup.Add(Buff.GetBuffBySpellId(50498));//Buff.GetBuffByName("Tear Armor")); MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs); } // Removes the Shattering Throw Buff if you are maintaining it yourself // We are now calculating this internally for better accuracy and to provide value to relevant talents if (dpswarchar.CalcOpts.M_ShatteringThrow) { buffGroup.Clear(); buffGroup.Add(Buff.GetBuffBySpellId(64382));//Buff.GetBuffByName("Shattering Throw")); MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs); } // Removes the Thunder Clap & Improved Buffs if you are maintaining it yourself // Also removes Judgements of the Just, Infected Wounds, Frost Fever, Improved Icy Touch // We are now calculating this internally for better accuracy and to provide value to relevant talents if (dpswarchar.CalcOpts.M_ThunderClap) { buffGroup.Clear(); buffGroup.Add(Buff.GetBuffBySpellId(6343));//Buff.GetBuffByName("Thunder Clap")); buffGroup.Add(Buff.GetBuffBySpellId(59921));//Buff.GetBuffByName("Frost Fever")); buffGroup.Add(Buff.GetBuffBySpellId(53696));//Buff.GetBuffByName("Judgements of the Just")); buffGroup.Add(Buff.GetBuffBySpellId(48484));//Buff.GetBuffByName("Infected Wounds")); MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs); } // Removes the Demoralizing Shout & Improved Buffs if you are maintaining it yourself // We are now calculating this internally for better accuracy and to provide value to relevant talents if (dpswarchar.CalcOpts.M_DemoralizingShout) { buffGroup.Clear(); buffGroup.Add(Buff.GetBuffBySpellId(1160));//Buff.GetBuffByName("Demoralizing Shout")); buffGroup.Add(Buff.GetBuffBySpellId(99));//Buff.GetBuffByName("Demoralizing Roar")); buffGroup.Add(Buff.GetBuffBySpellId(26016));//Buff.GetBuffByName("Vindication")); buffGroup.Add(Buff.GetBuffBySpellId(702));//Buff.GetBuffByName("Curse of Weakness")); buffGroup.Add(Buff.GetBuffBySpellId(81132));//Buff.GetBuffByName("Scarlet Fever")); MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs); } // Removes the Battle Shout & Commanding Presence Buffs if you are maintaining it yourself // Also removes their equivalent of Blessing of Might (+Improved) // We are now calculating this internally for better accuracy and to provide value to relevant talents if (dpswarchar.CalcOpts.M_BattleShout) { buffGroup.Clear(); buffGroup.Add(Buff.GetBuffBySpellId(6673));//Buff.GetBuffByName("Battle Shout")); buffGroup.Add(Buff.GetBuffBySpellId(8075));//Buff.GetBuffByName("Strength of Earth Totem")); buffGroup.Add(Buff.GetBuffBySpellId(57730));//Buff.GetBuffByName("Horn of Winter")); buffGroup.Add(Buff.GetBuffBySpellId(93435));//Buff.GetBuffByName("Roar of Courage")); MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs); } // Removes the Commanding Shout & Commanding Presence Buffs if you are maintaining it yourself // Also removes their equivalent of Blood Pact (+Improved Imp) // We are now calculating this internally for better accuracy and to provide value to relevant talents if (dpswarchar.CalcOpts.M_CommandingShout) { buffGroup.Clear(); buffGroup.Add(Buff.GetBuffBySpellId(469));//Buff.GetBuffByName("Commanding Shout")); buffGroup.Add(Buff.GetBuffBySpellId(21562));//Buff.GetBuffByName("Power Word: Fortitude")); buffGroup.Add(Buff.GetBuffBySpellId(90364));//Buff.GetBuffByName("Quiraji Fortitude")); buffGroup.Add(Buff.GetBuffBySpellId(6307));//Buff.GetBuffByName("Blood Pact")); MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs); } #endregion #region Passive Ability Auto-Fixing // Removes the Blood Frenzy Buff and it's equivalent of Savage Combat if you are maintaining it yourself // Cata also has BF giving what Trauma used to // We are now calculating this internally for better accuracy and to provide value to relevant talents if (dpswarchar.Char.WarriorTalents.BloodFrenzy > 0) { buffGroup.Clear(); buffGroup.Add(Buff.GetBuffBySpellId(29859));//Buff.GetBuffByName("Blood Frenzy (Bleed)")); buffGroup.Add(Buff.GetBuffBySpellId(33876));//Buff.GetBuffByName("Mangle")); buffGroup.Add(Buff.GetBuffBySpellId(16511));//Buff.GetBuffByName("Hemorrhage")); buffGroup.Add(Buff.GetBuffBySpellId(50271));//Buff.GetBuffByName("Tendon Rip")); buffGroup.Add(Buff.GetBuffBySpellId(35290));//Buff.GetBuffByName("Gore")); buffGroup.Add(Buff.GetBuffBySpellId(57386));//Buff.GetBuffByName("Stampede")); // buffGroup.Add(Buff.GetBuffBySpellId(29859*10));//Buff.GetBuffByName("Blood Frenzy (Vuln)")); buffGroup.Add(Buff.GetBuffBySpellId(58413));//Buff.GetBuffByName("Savage Combat")); buffGroup.Add(Buff.GetBuffBySpellId(81328));//Buff.GetBuffByName("Brittle Bones")); buffGroup.Add(Buff.GetBuffBySpellId(50518));//Buff.GetBuffByName("Ravage")); buffGroup.Add(Buff.GetBuffBySpellId(55749));//Buff.GetBuffByName("Acid Spit")); MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs); } // Removes the Rampage Buff and it's equivalent of Leader of the Pack if you are maintaining it yourself // We are now calculating this internally for better accuracy and to provide value to relevant talents if (dpswarchar.Char.WarriorTalents.Rampage > 0 && dpswarchar.CombatFactors.FuryStance) { buffGroup.Clear(); buffGroup.Add(Buff.GetBuffBySpellId(29801));//Buff.GetBuffByName("Rampage")); buffGroup.Add(Buff.GetBuffBySpellId(17007));//Buff.GetBuffByName("Leader of the Pack")); buffGroup.Add(Buff.GetBuffBySpellId(51701));//Buff.GetBuffByName("Honor Among Thieves")); buffGroup.Add(Buff.GetBuffBySpellId(51470));//Buff.GetBuffByName("Elemental Oath")); buffGroup.Add(Buff.GetBuffBySpellId(24604));//Buff.GetBuffByName("Furious Howl")); buffGroup.Add(Buff.GetBuffBySpellId(90309));//Buff.GetBuffByName("Terrifying Roar")); MaintBuffHelper(buffGroup, dpswarchar.Char, removedBuffs); } #endregion Base.StatsWarrior statsBuffs = new Base.StatsWarrior(); statsBuffs.Accumulate(GetBuffsStats(dpswarchar.Char.ActiveBuffs)); AccumulateSetBonusStats(statsBuffs, dpswarchar.Char.SetBonusCount); if (dpswarchar.Char.ActiveBuffs.Find<Buff>(x => x.SpellId == 22738) != null) { statsBuffs.BonusWarrior_PvP_4P_InterceptCDReduc = 5f; } /*if (dpswarchar.Char.ActiveBuffs.Find<Buff>(x => x.SpellId == 70854) != null) { statsBuffs.BonusWarrior_PvP_4P_InterceptCDReduc = 5f; } if (dpswarchar.Char.ActiveBuffs.Find<Buff>(x => x.SpellId == 70847) != null) { statsBuffs.BonusWarrior_PvP_4P_InterceptCDReduc = 5f; }*/ int T11count; dpswarchar.Char.SetBonusCount.TryGetValue("Earthen Warplate", out T11count); if (T11count >= 2) {//dpswarchar.Char.ActiveBuffs.Find<Buff>(x => x.SpellId == 90293) != null) { statsBuffs.BonusMortalStrikeDamageMultiplier = 0.05f; statsBuffs.BonusBloodthirstDamageMultiplier = 0.05f; } if (T11count >= 4) {//dpswarchar.Char.ActiveBuffs.Find<Buff>(x => x.SpellId == 90295) != null) { statsBuffs.AddSpecialEffect(_SE_4T11); } int T12count; dpswarchar.Char.SetBonusCount.TryGetValue("Molten Giant Warplate", out T12count); if (T12count >= 2) { statsBuffs.AddSpecialEffect(_SE_2T12[dpswarchar.Talents.BoomingVoice]); } if (T12count >= 4) { statsBuffs.AddSpecialEffect(_SE_4T12); } foreach (Buff b in removedBuffs) { dpswarchar.Char.ActiveBuffsAdd(b); } foreach (Buff b in addedBuffs) { dpswarchar.Char.ActiveBuffs.Remove(b); } return statsBuffs; }
private Stats IterativeSpecialEffectsStats(DPSWarrCharacter charStruct, List<SpecialEffect> specialEffects, List<SpecialEffect> critEffects, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, float oldFlurryUptime, bool iterate, Base.StatsWarrior iterateOld, Base.StatsWarrior originalStats) { #if DEBUG //ConstructionCounts["IterativeSpecialEffectsStats"]++; #endif WarriorTalents talents = charStruct.Char.WarriorTalents; float fightDuration = charStruct.BossOpts.BerserkTimer; Base.StatsWarrior statsProcs = new Base.StatsWarrior(); try { float dmgTakenInterval = fightDuration / charStruct.BossOpts.AoETargsFreq; //float attempted = charStruct.Rot.AttemptedAtksOverDur; //float land = charStruct.Rot.LandedAtksOverDur; //float crit = charStruct.Rot.CriticalAtksOverDur; //int LevelDif = charStruct.bossOpts.Level - charStruct.Char.Level; List<Trigger> critTriggers = new List<Trigger>(); List<float> critWeights = new List<float>(); bool needsHitTableReset = false; foreach (SpecialEffect effect in critEffects) { needsHitTableReset = true; critTriggers.Add(effect.Trigger); critWeights.Add(1f); } foreach (SpecialEffect effect in specialEffects) { #region old arp code /*if (effect.Stats.ArmorPenetrationRating > 0) { float arpenBuffs = ((combatFactors.CmhItemType == ItemType.TwoHandMace) ? talents.MaceSpecialization * 0.03f : 0.00f) + (!calcOpts.FuryStance ? (0.10f + originalStats.BonusWarrior_T9_2P_ArP) : 0.0f); float OriginalArmorReduction = StatConversion.GetArmorDamageReduction(Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif], originalStats.ArmorPenetration, arpenBuffs, originalStats.ArmorPenetrationRating); float ProccedArmorReduction = StatConversion.GetArmorDamageReduction(Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif], originalStats.ArmorPenetration + effect.Stats.ArmorPenetration, arpenBuffs, originalStats.ArmorPenetrationRating + effect.Stats.ArmorPenetrationRating); Stats dummyStats = new Stats(); float procUptime = ApplySpecialEffect(effect, Char, Rot, combatFactors, calcOpts, originalStats.Dodge + originalStats.Parry, ref dummyStats); float targetReduction = ProccedArmorReduction * procUptime + OriginalArmorReduction * (1f - procUptime); //float arpDiff = OriginalArmorReduction - targetReduction; float procArp = StatConversion.GetRatingFromArmorReduction(Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif], originalStats.ArmorPenetration, arpenBuffs, targetReduction); statsProcs.ArmorPenetrationRating += (procArp - originalStats.ArmorPenetrationRating); } else */ #endregion float numProcs = 0; if (effect.Stats.ManaorEquivRestore > 0f && effect.Stats.HealthRestoreFromMaxHealth > 0f) { // effect.Duration = 0, so GetAverageStats won't work float value1 = effect.Stats.ManaorEquivRestore; float value2 = effect.Stats.HealthRestoreFromMaxHealth; SpecialEffect dummy = new SpecialEffect(effect.Trigger, new Stats() { ManaorEquivRestore = value1, HealthRestoreFromMaxHealth = value2 }, effect.Duration, effect.Cooldown, effect.Chance) { BypassCache = true }; numProcs = dummy.GetAverageProcsPerSecond(dmgTakenInterval, originalStats.Dodge + originalStats.Parry, 0f, 0f) * fightDuration; statsProcs.ManaorEquivRestore += dummy.Stats.ManaorEquivRestore * numProcs; dummy.Stats.ManaorEquivRestore = 0f; //numProcs = effect.GetAverageProcsPerSecond(triggerIntervals[Trigger.PhysicalCrit], triggerChances[Trigger.PhysicalCrit], 0f, 0f) * fightDuration; //statsProcs.HealthRestoreFromMaxHealth += effect.Stats.HealthRestoreFromMaxHealth * numProcs; ApplySpecialEffect(dummy, charStruct, triggerIntervals, triggerChances, ref statsProcs); } else if (effect.Stats.ManaorEquivRestore > 0f) { // effect.Duration = 0, so GetAverageStats won't work numProcs = effect.GetAverageProcsPerSecond(dmgTakenInterval, originalStats.Dodge + originalStats.Parry, 0f, 0f) * fightDuration; statsProcs.ManaorEquivRestore += effect.Stats.ManaorEquivRestore * numProcs; } else if (effect.Stats.HealthRestoreFromMaxHealth > 0f) { // effect.Duration = 0, so GetAverageStats won't work numProcs = effect.GetAverageProcsPerSecond(dmgTakenInterval, originalStats.Dodge + originalStats.Parry, 0f, 0f) * fightDuration; statsProcs.HealthRestoreFromMaxHealth += effect.Stats.HealthRestoreFromMaxHealth * numProcs; } else { ApplySpecialEffect(effect, charStruct, triggerIntervals, triggerChances, ref statsProcs); } } WeightedStat[] critProcs; if (critEffects.Count == 0) { critProcs = new WeightedStat[] { new WeightedStat() { Value = 0f, Chance = 1f } }; } else if (critEffects.Count == 1) { float interval = triggerIntervals[critEffects[0].Trigger]; float chance = triggerChances[critEffects[0].Trigger]; float upTime = critEffects[0].GetAverageStackSize(interval, chance, charStruct.CombatFactors.CMHItemSpeed, (charStruct.CalcOpts.SE_UseDur ? charStruct.BossOpts.BerserkTimer : 0f)); upTime *= critWeights[0]; critProcs = new WeightedStat[] { new WeightedStat() { Value = critEffects[0].Stats.CritRating, Chance = upTime }, new WeightedStat() { Value = 0f, Chance = 1f - upTime } }; } else { float[] intervals = new float[critEffects.Count]; float[] chances = new float[critEffects.Count]; float[] offset = new float[critEffects.Count]; for (int i = 0; i < critEffects.Count; i++) { intervals[i] = triggerIntervals[critEffects[i].Trigger]; chances[i] = triggerChances[critEffects[i].Trigger]; } critProcs = SpecialEffect.GetAverageCombinedUptimeCombinations(critEffects.ToArray(), intervals, chances, offset, critWeights.ToArray(), charStruct.CombatFactors.CMHItemSpeed, charStruct.BossOpts.BerserkTimer, AdditiveStat.CritRating); } charStruct.CombatFactors.CritProcs = critProcs; float flurryUptime = 0f; if (iterate && talents.Flurry > 0f && charStruct.CombatFactors.FuryStance && charStruct.Char.MainHand != null && charStruct.Char.MainHand.Item != null) { float numFlurryHits = 3f; // default float mhPerc = 1f; // 100% by default float flurryHaste = 0.25f / 3f * talents.Flurry; bool useOffHand = false; float flurryHitsPerSec = charStruct.CombatFactors.TotalHaste * (1f + flurryHaste) / (1f + flurryHaste * oldFlurryUptime); float temp = 1f / charStruct.Char.MainHand.Item.Speed; if (charStruct.Char.OffHand != null && charStruct.Char.OffHand.Item != null) { useOffHand = true; temp += 1f / charStruct.Char.OffHand.Item.Speed; mhPerc = (charStruct.Char.MainHand.Speed / charStruct.Char.OffHand.Speed) / (1f + charStruct.Char.MainHand.Speed / charStruct.Char.OffHand.Speed); if (charStruct.Char.OffHand.Speed == charStruct.Char.MainHand.Speed) numFlurryHits = 4f; } flurryHitsPerSec *= temp; float flurryDuration = numFlurryHits / flurryHitsPerSec; flurryUptime = 1f; foreach (AbilityWrapper aw in charStruct.Rot.DamagingAbilities) { if (aw.Ability.CanCrit && aw.AllNumActivates > 0f) { float tempFactor = (float) Math.Pow(1f - aw.Ability.MHAtkTable.Crit, flurryDuration* (aw.AllNumActivates*aw.Ability.SwingsPerActivate* aw.Ability.AvgTargets/fightDuration)); flurryUptime *= tempFactor; if (aw.Ability.SwingsOffHand && useOffHand) { flurryUptime *= (float) Math.Pow(1f - aw.Ability.OHAtkTable.Crit, flurryDuration* (aw.AllNumActivates*aw.Ability.SwingsPerActivate* aw.Ability.AvgTargets/fightDuration)); } } } flurryUptime *= (float)Math.Pow(1f - charStruct.Rot.DPSWarrChar.Whiteattacks.MHAtkTable.Crit, numFlurryHits * mhPerc); flurryUptime *= (float)Math.Pow(1f - charStruct.Rot.DPSWarrChar.Whiteattacks.OHAtkTable.Crit, numFlurryHits * (1f - mhPerc)); flurryUptime = 1f - flurryUptime; statsProcs.PhysicalHaste = (1f + statsProcs.PhysicalHaste) * (1f + flurryHaste * flurryUptime) - 1f; } charStruct.CombatFactors.StatS = UpdateStatsAndAdd(statsProcs, originalStats, charStruct.Char); charStruct.CombatFactors.InvalidateCache(); //Rot.InvalidateCache(); if (iterate) { const float precisionWhole = 0.01f; const float precisionDec = 0.0001f; if (statsProcs.Agility - iterateOld.Agility > precisionWhole || statsProcs.HasteRating - iterateOld.HasteRating > precisionWhole || statsProcs.HitRating - iterateOld.HitRating > precisionWhole || statsProcs.CritRating - iterateOld.CritRating > precisionWhole || statsProcs.PhysicalHaste - iterateOld.PhysicalHaste > precisionDec || statsProcs.PhysicalCrit - iterateOld.PhysicalCrit > precisionDec || statsProcs.PhysicalHit - iterateOld.PhysicalHit > precisionDec) { if (needsHitTableReset) charStruct.Rot.ResetHitTables(); charStruct.Rot.DoIterations(); CalculateTriggers(charStruct, triggerIntervals, triggerChances); return IterativeSpecialEffectsStats(charStruct, specialEffects, critEffects, triggerIntervals, triggerChances, flurryUptime, true, statsProcs, originalStats); } else { /*int j = 0;*/ } } return statsProcs; } catch (Exception ex) { new Base.ErrorBox() { Title = "Error in creating SpecialEffects Stats", Function = "GetSpecialEffectsStats()", TheException = ex, }.Show(); return new Stats(); } }
/*public static string GetConstructionCounts() { string retVal = ""; // foreach (string c in ConstructionCounts.Keys) { retVal += string.Format("{0:000000}/{1:00000.00}: {2}\n", ConstructionCounts[c] < 5 ? 0 : ConstructionCounts[c] - 5, (float)(ConstructionCounts[c] < 5 ? 0 : ConstructionCounts[c] - 5) / (float)(ConstructionCounts["GetCharacterCalculations"] < 5 ? 1 : ConstructionCounts["GetCharacterCalculations"] - 5), c); } // return retVal + "\n"; } public static Dictionary<string, int> ConstructionCounts = new Dictionary<string,int>() { { "GetCharacterCalculations", 0 }, { "GetCharacterStats", 0 }, { "GetCharacterStats_Inner", 0 }, { "GetCharacterStats_Buffed", 0 }, { "GetCharacterStats_Override", 0 }, { "CalculateTriggers", 0 }, { "IterativeSpecialEffectsStats", 0 }, { "ApplySpecialEffect", 0 }, { "UpdateStatsAndAdd", 0 }, { "DoSpecialEffects", 0 }, { "Rotation Base", 0 }, { "Rotation Arms", 0 }, { "Rotation Fury", 0 }, { "CombatFactors", 0 }, };*/ #endif public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem, bool referenceCalculation, bool significantChange, bool needsDisplayCalculations) { #if DEBUG //ConstructionCounts["GetCharacterCalculations"]++; #endif CharacterCalculationsDPSWarr calc = new CharacterCalculationsDPSWarr(); try { #region Object Creation // First things first, we need to ensure that we aren't using bad data if (character == null) { return calc; } CalculationOptionsDPSWarr calcOpts = character.CalculationOptions as CalculationOptionsDPSWarr; if (calcOpts == null) { return calc; } // BossOptions bossOpts = character.BossOptions; CombatFactors combatFactors; Skills.WhiteAttacks whiteAttacks; Rotation Rot; Base.StatsWarrior statsRace = null; Base.StatsWarrior stats = GetCharacterStats(character, additionalItem, StatType.Average, calcOpts, bossOpts, out statsRace, out combatFactors, out whiteAttacks, out Rot); DPSWarrCharacter charStruct = new DPSWarrCharacter() { CalcOpts = calcOpts, BossOpts = bossOpts, Rot = Rot, CombatFactors = combatFactors, Char = character, Talents = character.WarriorTalents }; /*if (calcOpts.UseMarkov) { if (combatFactors.FuryStance) { //Markov.StateSpaceGeneratorFuryTest b = new Markov.StateSpaceGeneratorFuryTest(); //Markov.StateSpaceGeneratorFuryTest.StateSpaceGeneratorFuryTest1(character, stats, combatFactors, whiteAttacks, calcOpts, bossOpts, needsDisplayCalculations); } else { //Markov.StateSpaceGeneratorArmsTest b = new Markov.StateSpaceGeneratorArmsTest(); //Markov.StateSpaceGeneratorArmsTest.StateSpaceGeneratorArmsTest1(character, stats, combatFactors, whiteAttacks, calcOpts, bossOpts, needsDisplayCalculations); } }*/ #endregion calc.Duration = bossOpts.BerserkTimer; calc.PTRMode = calcOpts.PtrMode; calc.AverageStats = stats; if (needsDisplayCalculations) { calc.UnbuffedStats = GetCharacterStats(character, additionalItem, StatType.Unbuffed, calcOpts, bossOpts, out statsRace); calc.BuffedStats = GetCharacterStats(character, additionalItem, StatType.Buffed, calcOpts, bossOpts, out statsRace); calc.BuffsStats = GetBuffsStats(charStruct); calc.MaximumStats = GetCharacterStats(character, additionalItem, StatType.Maximum, calcOpts, bossOpts, out statsRace); } calc.CombatFactors = combatFactors; calc.Rot = Rot; calc.TargetLevel = bossOpts.Level; calc.BaseHealth = statsRace.Health; {// == Attack Table == // Miss calc.Miss = stats.Miss; calc.HitRating = stats.HitRating; calc.ExpertiseRating = stats.ExpertiseRating; calc.Expertise = StatConversion.GetExpertiseFromRating(stats.ExpertiseRating, CharacterClass.Warrior) + stats.Expertise; calc.MHExpertise = combatFactors.CMHexpertise; calc.OHExpertise = combatFactors.COhexpertise; calc.AgilityCritBonus = StatConversion.GetCritFromAgility(stats.Agility, CharacterClass.Warrior); calc.CritRating = stats.CritRating; calc.CritPercent = stats.PhysicalCrit; calc.MHCrit = combatFactors.CMHycrit; calc.OHCrit = combatFactors.COhycrit; } // Offensive //calc.ArmorPenetrationRating = stats.ArmorPenetrationRating; //calc.ArmorPenetrationRating2Perc = StatConversion.GetArmorPenetrationFromRating(stats.ArmorPenetrationRating); //calc.ArmorPenetration = Math.Min(1f, calc.ArmorPenetrationRating2Perc); calc.HasteRating = stats.HasteRating; calc.HastePercent = stats.PhysicalHaste; calc.MasteryVal = StatConversion.GetMasteryFromRating(stats.MasteryRating, CharacterClass.Warrior); // DPS Rot.Initialize(calc); calc.PlateSpecValid = HelperFunctions.ValidatePlateSpec(charStruct); // Neutral // Defensive calc.Armor = stats.Armor; Rot.MakeRotationandDoDPS(true, needsDisplayCalculations); #region Special Damage Procs, like Bandit's Insignia or Hand-mounted Pyro Rockets calc.SpecProcDPS = calc.SpecProcDmgPerHit = calc.SpecProcActs = 0f; if (stats._rawSpecialEffectData != null && character.MainHand != null) { bool runIt = false; foreach (SpecialEffect s in stats.SpecialEffects()) { if (s.Stats == null) { continue; } if (s.Stats.ShadowDamage != 0) { runIt = true; break; } if (s.Stats.FireDamage != 0) { runIt = true; break; } if (s.Stats.HolyDamage != 0) { runIt = true; break; } if (s.Stats.FrostDamage != 0) { runIt = true; break; } if (s.Stats.NatureDamage != 0) { runIt = true; break; } if (s.Stats.ArcaneDamage != 0) { runIt = true; break; } } if (runIt) { DamageProcs.SpecialDamageProcs SDP; Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>(); Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>(); CalculateTriggers(charStruct, triggerIntervals, triggerChances); if (character.Race == CharacterRace.Goblin && statsRace._rawSpecialEffectData.Length > 0) { // Fix the damage for Goblin Rockets foreach (SpecialEffect s in stats.SpecialEffects()) { if (s.Stats != null && s.Stats.FireDamage == (1f + character.Level * 2)) { s.Stats.FireDamage += stats.AttackPower * 0.25f // AP Bonus + stats.Intellect * 0.50193f; // Int Bonus } } } SDP = new Rawr.DamageProcs.SpecialDamageProcs(character, stats, calc.TargetLevel - character.Level, new List<SpecialEffect>(stats.SpecialEffects()), triggerIntervals, triggerChances, bossOpts.BerserkTimer, combatFactors.DamageReduction); calc.SpecProcDPS = SDP.CalculateAll(); calc.SpecProcDmgPerHit = SDP.GetDamagePerHit; calc.SpecProcActs = SDP.GetTotalNumProcs; } } calc.TotalDPS += calc.SpecProcDPS; #endregion #region Survivability List<Attack> Attacks; if (calcOpts.SurvScale != 0f && (Attacks = bossOpts.Attacks.FindAll(a => a.AffectsRole[PLAYER_ROLES.MeleeDPS])).Count > 0) { Dictionary<ItemDamageType, float> countsDmg = new Dictionary<ItemDamageType, float>() { { ItemDamageType.Physical, 0f }, { ItemDamageType.Arcane, 0f }, { ItemDamageType.Fire, 0f }, { ItemDamageType.Frost, 0f }, { ItemDamageType.Holy, 0f }, { ItemDamageType.Nature, 0f }, { ItemDamageType.Shadow, 0f }, }; Dictionary<ItemDamageType, float> percDmg = new Dictionary<ItemDamageType, float>() { { ItemDamageType.Physical, 0f }, { ItemDamageType.Arcane, 0f }, { ItemDamageType.Fire, 0f }, { ItemDamageType.Frost, 0f }, { ItemDamageType.Holy, 0f }, { ItemDamageType.Nature, 0f }, { ItemDamageType.Shadow, 0f }, }; Dictionary<ItemDamageType, float> highestDmg = new Dictionary<ItemDamageType, float>() { { ItemDamageType.Physical, 0f }, { ItemDamageType.Arcane, 0f }, { ItemDamageType.Fire, 0f }, { ItemDamageType.Frost, 0f }, { ItemDamageType.Holy, 0f }, { ItemDamageType.Nature, 0f }, { ItemDamageType.Shadow, 0f }, }; int totalCount = 0; foreach (Attack a in Attacks) { countsDmg[a.DamageType] += 1; totalCount++; if ((a.DamagePerHit + a.DamagePerTick) > highestDmg[a.DamageType]) { highestDmg[a.DamageType] = (a.DamagePerHit + a.DamagePerTick); } } foreach (ItemDamageType t in countsDmg.Keys) { percDmg[t] = countsDmg[t] / (float)totalCount; } float TotalConstantDamageReduction = 1f - (1f - (stats.Armor > 0 ? StatConversion.GetArmorDamageReduction(bossOpts.Level, character.Level, stats.Armor, 0f, 0f) : 0)) * (1f - stats.DamageTakenReductionMultiplier) * (1f - stats.BossPhysicalDamageDealtReductionMultiplier); Dictionary<ItemDamageType, float> SurvivabilityPointsRaw = new Dictionary<ItemDamageType, float>() { { ItemDamageType.Physical, stats.Health / (1f - TotalConstantDamageReduction) }, { ItemDamageType.Arcane, stats.Health / ((1f - StatConversion.GetMinimumResistance(bossOpts.Level, character.Level, stats.ArcaneResistance, 0)) * (1f - stats.DamageTakenReductionMultiplier)) }, { ItemDamageType.Fire, stats.Health / ((1f - StatConversion.GetMinimumResistance(bossOpts.Level, character.Level, stats.FireResistance, 0)) * (1f - stats.DamageTakenReductionMultiplier)) }, { ItemDamageType.Frost, stats.Health / ((1f - StatConversion.GetMinimumResistance(bossOpts.Level, character.Level, stats.FrostResistance, 0)) * (1f - stats.DamageTakenReductionMultiplier)) }, { ItemDamageType.Holy, stats.Health / ((1f) * (1f - stats.DamageTakenReductionMultiplier)) }, { ItemDamageType.Nature, stats.Health / ((1f - StatConversion.GetMinimumResistance(bossOpts.Level, character.Level, stats.NatureResistance, 0)) * (1f - stats.DamageTakenReductionMultiplier)) }, { ItemDamageType.Shadow, stats.Health / ((1f - StatConversion.GetMinimumResistance(bossOpts.Level, character.Level, stats.ShadowResistance, 0)) * (1f - stats.DamageTakenReductionMultiplier)) }, }; Dictionary<ItemDamageType, float> SurvivabilityPoints = SoftCapSurvivals(charStruct, highestDmg, SurvivabilityPointsRaw); if (stats.HealthRestoreFromMaxHealth > 0) { stats.HealthRestore += stats.HealthRestoreFromMaxHealth * stats.Health * bossOpts.BerserkTimer; } float survs = 0f; foreach (ItemDamageType t in SurvivabilityPoints.Keys) { survs += SurvivabilityPoints[t] * percDmg[t]; } float HealthRest2Surv = survs / 100f + Rot._HPS_TTL + stats.HealthRestore / 100f; calc.TotalHPS = Rot._HPS_TTL; calc.Survivability = calcOpts.SurvScale * HealthRest2Surv; } else { // No point in running all those calcs just to zero them out after calc.TotalHPS = Rot._HPS_TTL; calc.Survivability = 0f; } #endregion calc.OverallPoints = calc.TotalDPS + calc.Survivability; #region Old ArP cap calc, keeping in case another cap has to be implemented //calculatedStats.UnbuffedStats = GetCharacterStats(character, additionalItem, StatType.Unbuffed, calcOpts, bossOpts); /*if (needsDisplayCalculations) { calc.BuffedStats = GetCharacterStats(character, additionalItem, StatType.Buffed, calcOpts, bossOpts, out statsRace); //calculatedStats.MaximumStats = GetCharacterStats(character, additionalItem, StatType.Maximum, calcOpts, bossOpts); float maxArp = calc.BuffedStats.ArmorPenetrationRating; foreach (SpecialEffect effect in calc.BuffedStats.SpecialEffects(s => s.Stats.ArmorPenetrationRating > 0f)) { maxArp += effect.Stats.ArmorPenetrationRating; } calc.MaxArmorPenetration = StatConversion.GetArmorPenetrationFromRating(maxArp); }*/ #endregion #if DEBUG /*foreach (string c in Rotation.ConstructionCounts.Keys) { if (!ConstructionCounts.ContainsKey(c)) { ConstructionCounts.Add(c, 0); } ConstructionCounts[c] = Rotation.ConstructionCounts[c]; } foreach (string c in CombatFactors.ConstructionCounts.Keys) { if (!ConstructionCounts.ContainsKey(c)) { ConstructionCounts.Add(c, 0); } ConstructionCounts[c] = CombatFactors.ConstructionCounts[c]; } string counts = GetConstructionCounts(); Clipboard.SetText(counts); //System.Windows.MessageBox.Show(counts, "DPSWarr Counts", System.Windows.MessageBoxButton.OK);*/ #endif } catch (Exception ex) { new Base.ErrorBox() { Title = "Error in creating Stat Pane Calculations", Function = "GetCharacterCalculations()", TheException = ex, }.Show(); } return calc; }
private static void CalculateTriggers(DPSWarrCharacter charStruct, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances) { #if DEBUG //ConstructionCounts["CalculateTriggers"]++; string addInfo = "No Additional Info"; #endif try { float fightDuration = charStruct.BossOpts.BerserkTimer; float fightDurationO20 = charStruct.Rot.FightDurationO20; float fightDurationU20 = charStruct.Rot.FightDurationU20; #if DEBUG addInfo = "FightDur Passed"; #endif //float fightDuration2Pass = charStruct.calcOpts.SE_UseDur ? fightDuration : 0; float attemptedMH = charStruct.Rot.AttemptedAtksOverDurMH; float attemptedOH = charStruct.Rot.AttemptedAtksOverDurOH; float attempted = attemptedMH + attemptedOH; float landMH = charStruct.Rot.LandedAtksOverDurMH; float landOH = charStruct.Rot.LandedAtksOverDurOH; float land = landMH + landOH; float crit = charStruct.Rot.CriticalAtksOverDur; float avoidedAttacks = charStruct.CombatFactors.StatS.Dodge + charStruct.CombatFactors.StatS.Parry; float dwbleed = 0; #if DEBUG addInfo += "\r\nBig if started"; #endif if (charStruct.Char.WarriorTalents.DeepWounds > 0 && crit != 0f) { float dwTicks = 1f; foreach (AbilityWrapper aw in charStruct.Rot.DamagingAbilities) { if (aw.AllNumActivates > 0f && aw.Ability.CanCrit) { if (aw.Ability.SwingsOffHand) { dwTicks *= (float)Math.Pow(1f - aw.Ability.MHAtkTable.Crit * (1f - aw.Ability.OHAtkTable.Crit), aw.AllNumActivates / fightDuration); dwTicks *= (float)Math.Pow(1f - aw.Ability.OHAtkTable.Crit, aw.AllNumActivates / fightDuration); } else { // to fix this breaking apart when you're close to yellow crit cap for these abilities, namely OP if (aw.Ability is Skills.Overpower || aw.Ability is Skills.TasteForBlood) dwTicks *= (1f - aw.AllNumActivates / fightDuration * aw.Ability.MHAtkTable.Crit); else dwTicks *= (float)Math.Pow(1f - aw.Ability.MHAtkTable.Crit, aw.AllNumActivates / fightDuration); } } } dwbleed = fightDuration * dwTicks; } #if DEBUG addInfo += "\r\nBuncha Floats started"; #endif float bleed = dwbleed + fightDuration * (charStruct.CombatFactors.FuryStance || !charStruct.CalcOpts.MaintenanceTree[(int)Maintenance.Rend] ? 0f : 1f / 3f); float bleedHitInterval = fightDuration / bleed; float dwbleedHitInterval = fightDuration / dwbleed; float attemptedAtkInterval = fightDuration / attempted; float attemptedAtksIntervalMH = fightDuration / attemptedMH; float attemptedAtksIntervalOH = fightDuration / attemptedOH; //float landedAtksInterval = fightDuration / land; float dmgDoneInterval = fightDuration / (land + bleed); float dmgTakenInterval = 0f; { List<Attack> attacks = charStruct.BossOpts.Attacks.FindAll(a => a.AffectsRole[PLAYER_ROLES.MeleeDPS] && a.Validate); float frequency = 0; foreach (Attack a in attacks) { frequency += 1f / a.AttackSpeed; } dmgTakenInterval = 1f / frequency; } #if DEBUG addInfo += "\r\nAoETargsFreq Passed"; #endif float hitRate = 1, hitRateMH = 1, hitRateOH = 1, critRate = 1; if (attempted != 0f) { hitRate = land / attempted; critRate = crit / attempted; } if (attemptedMH != 0f) hitRateMH = landMH / attemptedMH; if (attemptedOH != 0f) hitRateOH = landOH / attemptedOH; #if DEBUG addInfo += "\r\nTriggers started"; #endif triggerIntervals[Trigger.Use] = 0f; triggerChances[Trigger.Use] = 1f; triggerIntervals[Trigger.MeleeAttack] = attemptedAtkInterval; triggerChances[Trigger.MeleeAttack] = 1f; float mhWhites = charStruct.Rot.DPSWarrChar.Whiteattacks.MHActivatesAll; triggerIntervals[Trigger.WhiteAttack] = fightDuration / (mhWhites != 0 ? mhWhites : 1f); triggerChances[Trigger.WhiteAttack] = 1f; triggerIntervals[Trigger.WhiteCrit] = fightDuration / (mhWhites != 0 ? mhWhites : 1f); triggerChances[Trigger.WhiteCrit] = charStruct.Rot.DPSWarrChar.Whiteattacks.MHAtkTable.Crit; triggerIntervals[Trigger.WhiteHit] = fightDuration / (mhWhites != 0 ? mhWhites : 1f); triggerChances[Trigger.WhiteHit] = charStruct.Rot.DPSWarrChar.Whiteattacks.MHAtkTable.AnyLand; triggerIntervals[Trigger.MeleeHit] = triggerIntervals[Trigger.PhysicalHit] = triggerIntervals[Trigger.PhysicalAttack] = attemptedAtkInterval; triggerChances[Trigger.MeleeHit] = triggerChances[Trigger.PhysicalHit] = hitRate; triggerChances[Trigger.PhysicalAttack] = 1f; triggerIntervals[Trigger.PhysicalCrit] = triggerIntervals[Trigger.MeleeCrit] = attemptedAtkInterval; triggerChances[Trigger.PhysicalCrit] = triggerChances[Trigger.MeleeCrit] = critRate; triggerIntervals[Trigger.MainHandHit] = attemptedAtksIntervalMH; triggerChances[Trigger.MainHandHit] = hitRateMH; triggerIntervals[Trigger.OffHandHit] = attemptedAtksIntervalOH; triggerChances[Trigger.OffHandHit] = hitRateOH; triggerIntervals[Trigger.DoTTick] = bleedHitInterval; triggerChances[Trigger.DoTTick] = 1f; triggerIntervals[Trigger.DamageDone] = dmgDoneInterval; triggerChances[Trigger.DamageDone] = 1f; triggerIntervals[Trigger.DamageOrHealingDone] = dmgDoneInterval; // Need to add Self Heals triggerChances[Trigger.DamageOrHealingDone] = 1f; triggerIntervals[Trigger.DamageTaken] = dmgTakenInterval; triggerChances[Trigger.DamageTaken] = 1f; triggerIntervals[Trigger.DamageAvoided] = dmgTakenInterval; triggerChances[Trigger.DamageAvoided] = avoidedAttacks; triggerIntervals[Trigger.HSorSLHit] = fightDurationO20 / charStruct.Rot.CritHSSlamOverDur; triggerChances[Trigger.HSorSLHit] = 1f; triggerIntervals[Trigger.ExecuteHit] = fightDurationU20 / charStruct.Rot.GetWrapper<Skills.Execute>().NumActivatesU20; triggerChances[Trigger.ExecuteHit] = charStruct.Rot.GetWrapper<Skills.Execute>().Ability.MHAtkTable.AnyLand; triggerIntervals[Trigger.DeepWoundsTick] = dwbleedHitInterval; triggerChances[Trigger.DeepWoundsTick] = 1f; triggerIntervals[Trigger.DarkIntentCriticalPeriodicDamageOrHealing] = (float)charStruct.CalcOpts.DarkIntentDotTickInterval; triggerChances[Trigger.DarkIntentCriticalPeriodicDamageOrHealing] = (float)charStruct.CalcOpts.DarkIntentDotTickCritChance / 100f; triggerIntervals[Trigger.WWorCleaveHit] = fightDuration / (charStruct.Rot.GetWrapper<Skills.Whirlwind>().AllNumActivates + charStruct.Rot.GetWrapper<Skills.Cleave>().AllNumActivates); triggerChances[Trigger.WWorCleaveHit] = 1f; if (charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesO20 > 0 && charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesU20 > 0) { triggerIntervals[Trigger.MortalStrikeCrit] = (fightDurationO20 / charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesO20) * (fightDurationO20 / fightDuration) + (fightDurationU20 / charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesU20) * (fightDurationU20 / fightDuration); } else if (charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesU20 > 0) { triggerIntervals[Trigger.MortalStrikeCrit] = fightDurationU20 / charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesU20; } else if (charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesO20 > 0) { triggerIntervals[Trigger.MortalStrikeCrit] = fightDurationO20 / charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesO20; } else { triggerIntervals[Trigger.MortalStrikeCrit] = 0f; } triggerChances[Trigger.MortalStrikeCrit] = charStruct.Rot.GetWrapper<Skills.MortalStrike>().Ability.MHAtkTable.Crit; triggerIntervals[Trigger.MortalStrikeHit] = fightDurationO20 / charStruct.Rot.GetWrapper<Skills.MortalStrike>().NumActivatesO20; triggerChances[Trigger.MortalStrikeHit] = charStruct.Rot.GetWrapper<Skills.MortalStrike>().Ability.MHAtkTable.AnyLand; triggerIntervals[Trigger.ColossusSmashHit] = fightDuration / charStruct.Rot.GetWrapper<Skills.ColossusSmash>().AllNumActivates; triggerChances[Trigger.ColossusSmashHit] = charStruct.Rot.GetWrapper<Skills.ColossusSmash>().Ability.MHAtkTable.AnyLand; float opActs = charStruct.Rot.GetWrapper<Skills.Overpower>().AllNumActivates + charStruct.Rot.GetWrapper<Skills.TasteForBlood>().AllNumActivates; float rbActs = charStruct.Rot.GetWrapper<Skills.RagingBlow>().AllNumActivates; triggerIntervals[Trigger.OPorRBAttack] = (((opActs + rbActs) > 0f) ? (fightDuration / (opActs + rbActs)) : 0f); triggerChances[Trigger.OPorRBAttack] = 1f; triggerIntervals[Trigger.BattleorCommandingShout] = 60f - charStruct.Talents.BoomingVoice * 15f; triggerChances[Trigger.BattleorCommandingShout] = 1f; #if DEBUG addInfo += "\r\nFinished"; #endif } catch (Exception ex) { new Base.ErrorBox() { Title = "Error Calculating Triggers", Function = "CalculateTriggers()", #if DEBUG Info = addInfo, #endif TheException = ex, }.Show(); } }
private void DoSpecialEffects(DPSWarrCharacter charStruct, List<SpecialEffect> bersMainHand, List<SpecialEffect> bersOffHand, Base.StatsWarrior statsTotal) { #if DEBUG //ConstructionCounts["DoSpecialEffects"]++; #endif #region Initialize Triggers Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>(); Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>(); CalculateTriggers(charStruct, triggerIntervals, triggerChances); #endregion #region ArPen Lists /*List<float> tempArPenRatings = new List<float>(); List<float> tempArPenRatingsCapLimited = new List<float>(); List<float> tempArPenRatingUptimes = new List<float>(); List<SpecialEffect> tempArPenEffects = new List<SpecialEffect>(); List<float> tempArPenEffectIntervals = new List<float>(); List<float> tempArPenEffectChances = new List<float>(); List<float> tempArPenEffectScales = new List<float>();*/ #endregion // First Let's add InnerRage in, because that affects other calcs if (charStruct.CalcOpts.M_InnerRage) { AbilityWrapper ir = charStruct.Rot.GetWrapper<Skills.InnerRage>(); statsTotal.Accumulate(((ir.Ability as Skills.InnerRage).Effect.Stats as Base.StatsWarrior), (ir.Ability as Skills.InnerRage).Effect.GetAverageUptime(0f, 1.00f)); } List<SpecialEffect> critEffects = new List<SpecialEffect>(); List<SpecialEffect> firstPass = new List<SpecialEffect>(); List<SpecialEffect> secondPass = new List<SpecialEffect>(); List<SpecialEffect> thirdPass = new List<SpecialEffect>(); //bool doubleExecutioner = false; foreach (SpecialEffect effect in statsTotal.SpecialEffects()) { effect.Stats.GenerateSparseData(); if (!triggerIntervals.ContainsKey(effect.Trigger)) continue; else if (effect.Stats.CritRating > 0f) { critEffects.Add(effect); } #region ArP Proc (which don't exist anymore) /*else if (effect.Stats.ArmorPenetrationRating > 0f) { if (doubleExecutioner) continue; Trigger realTrigger; if (bersMainHand.Contains(effect)) { bersMainHand.Remove(effect); doubleExecutioner = true; if (bersOffHand.Contains(effect)) { bersOffHand.Remove(effect); realTrigger = Trigger.MeleeHit; } else realTrigger = Trigger.MainHandHit; } else if (bersOffHand.Contains(effect)) { bersOffHand.Remove(effect); realTrigger = Trigger.OffHandHit; } else realTrigger = effect.Trigger; tempArPenEffects.Add(effect); tempArPenEffectIntervals.Add(triggerIntervals[realTrigger]); tempArPenEffectChances.Add(triggerChances[realTrigger]); tempArPenEffectScales.Add(1f); }*/ #endregion else if (!bersMainHand.Contains(effect) && !bersOffHand.Contains(effect) && (effect.Stats.Agility > 0f || effect.Stats.HasteRating > 0f || effect.Stats.HitRating > 0f || effect.Stats.CritRating > 0f || effect.Stats.MasteryRating > 0f || effect.Stats.PhysicalHaste > 0f || effect.Stats.PhysicalCrit > 0f || effect.Stats.PhysicalHit > 0f)) { // These procs affect rotation firstPass.Add(effect); } else if (!bersMainHand.Contains(effect) && !bersOffHand.Contains(effect) && (effect.Stats.FireDamage > 0 || effect.Stats.FireDamage > 0f || effect.Stats.NatureDamage > 0f || effect.Stats.ShadowDamage > 0f || effect.Stats.HolyDamage > 0f || effect.Stats.ArcaneDamage > 0f)) { // It's a Special Damage Proc thirdPass.Add(effect); } else if (!bersMainHand.Contains(effect) && !bersOffHand.Contains(effect)) { // Any other stat proc secondPass.Add(effect); } } #region ArP Proc Cap handling /*if (tempArPenEffects.Count == 0) { //tempArPenRatings.Add(0.0f); //tempArPenRatingUptimes.Add(1.0f); } else if (tempArPenEffects.Count == 1) { //Only one, add it to SpecialEffect effect = tempArPenEffects[0]; float uptime = effect.GetAverageStackSize(tempArPenEffectIntervals[0], tempArPenEffectChances[0], charStruct.combatFactors.CmhItemSpeed, (charStruct.calcOpts.SE_UseDur ? charStruct.bossOpts.BerserkTimer : 0f)) * tempArPenEffectScales[0]; tempArPenRatings.Add(effect.Stats.ArmorPenetrationRating); tempArPenRatingUptimes.Add(uptime); tempArPenRatings.Add(0.0f); tempArPenRatingUptimes.Add(1.0f - uptime); } else if (tempArPenEffects.Count > 1) { //if (tempArPenEffects.Count >= 2) //{ // offset[0] = calcOpts.TrinketOffset; //} WeightedStat[] arPenWeights = SpecialEffect.GetAverageCombinedUptimeCombinations(tempArPenEffects.ToArray(), tempArPenEffectIntervals.ToArray(), tempArPenEffectChances.ToArray(), new float[tempArPenEffectChances.Count], tempArPenEffectScales.ToArray(), charStruct.combatFactors.CmhItemSpeed, charStruct.bossOpts.BerserkTimer, AdditiveStat.ArmorPenetrationRating); for (int i = 0; i < arPenWeights.Length; i++) { tempArPenRatings.Add(arPenWeights[i].Value); tempArPenRatingUptimes.Add(arPenWeights[i].Chance); } }*/ // Get the average Armor Pen Rating across all procs /*if (tempArPenRatings.Count > 0f) { Stats originalStats = charStruct.combatFactors.StatS; int LevelDif = charStruct.bossOpts.Level- charStruct.Char.Level; float arpenBuffs = 0.0f; //float OriginalArmorReduction = StatConversion.GetArmorDamageReduction(charStruct.Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif], originalStats.TargetArmorReduction, arpenBuffs, Math.Max(0f, originalStats.ArmorPenetrationRating)); float AverageArmorPenRatingsFromProcs = 0f; float tempCap = StatConversion.RATING_PER_ARMORPENETRATION - StatConversion.GetRatingFromArmorPenetration(arpenBuffs); for (int i = 0; i < tempArPenRatings.Count; i++) { tempArPenRatingsCapLimited.Add(Math.Max(0f, Math.Min(tempCap - originalStats.ArmorPenetrationRating, tempArPenRatings[i]))); //float bah = StatConversion.GetArmorDamageReduction(charStruct.Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif], originalStats.TargetArmorReduction, arpenBuffs, Math.Max(0f, originalStats.ArmorPenetrationRating + tempArPenRatingsCapLimited[i])); AverageArmorPenRatingsFromProcs += tempArPenRatingUptimes[i] * tempArPenRatingsCapLimited[i]; } Stats dummyStats = new Stats(); //float procArp = StatConversion.GetRatingFromArmorReduction(charStruct.Char.Level, (int)StatConversion.NPC_ARMOR[LevelDif], originalStats.TargetArmorReduction, arpenBuffs, ProccedArmorReduction); originalStats.ArmorPenetrationRating += AverageArmorPenRatingsFromProcs;//(procArp - originalStats.ArmorPenetrationRating); }*/ #endregion IterativeSpecialEffectsStats(charStruct, firstPass, critEffects, triggerIntervals, triggerChances, 0f, true, new Base.StatsWarrior(), charStruct.CombatFactors.StatS); IterativeSpecialEffectsStats(charStruct, secondPass, critEffects, triggerIntervals, triggerChances, 0f, false, null, charStruct.CombatFactors.StatS); IterativeSpecialEffectsStats(charStruct, thirdPass, critEffects, triggerIntervals, triggerChances, 0f, false, null, charStruct.CombatFactors.StatS); }
private Base.StatsWarrior GetCharacterStats(Character character, Item additionalItem, StatType statType, CalculationOptionsDPSWarr calcOpts, BossOptions bossOpts, out Base.StatsWarrior statsRace, out CombatFactors combatFactors, out Skills.WhiteAttacks whiteAttacks, out Rotation Rot) { #if DEBUG //ConstructionCounts["GetCharacterStats_Inner"]++; #endif DPSWarrCharacter dpswarchar = new DPSWarrCharacter { Char = character, CalcOpts = calcOpts, BossOpts = bossOpts, Talents = character.WarriorTalents, CombatFactors = null, Rot = null }; Base.StatsWarrior statsTotal = GetCharacterStats_Buffed(dpswarchar, additionalItem, statType != StatType.Unbuffed, out statsRace); dpswarchar.StatS = statsTotal; combatFactors = new CombatFactors(character, statsTotal, calcOpts, bossOpts); // we have to regenerate it here dpswarchar.CombatFactors = combatFactors; whiteAttacks = new Skills.WhiteAttacks(dpswarchar); dpswarchar.Whiteattacks = whiteAttacks; if (combatFactors.FuryStance) Rot = new FuryRotation(dpswarchar); else Rot = new ArmsRotation(dpswarchar); dpswarchar.Rot = Rot; if (statType == (StatType.Buffed | StatType.Unbuffed)) { return statsTotal; } // SpecialEffects: Supposed to handle all procs such as Berserking, Mirror of Truth, Grim Toll, etc. Rot.Initialize(); Rot.MakeRotationandDoDPS(false, false); Rot.AddValidatedSpecialEffects(statsTotal, character.WarriorTalents); DPSWarrCharacter charStruct = new DPSWarrCharacter(){ CalcOpts = calcOpts, BossOpts = bossOpts, Char = character, CombatFactors = combatFactors, Rot = Rot, Talents = character.WarriorTalents, StatS = statsTotal, Whiteattacks = whiteAttacks, }; float fightDuration = bossOpts.BerserkTimer; List<SpecialEffect> bersMainHand = new List<SpecialEffect>(); List<SpecialEffect> bersOffHand = new List<SpecialEffect>(); if (character.MainHandEnchant != null/* && character.MainHandEnchant.Id == 3789*/) { // 3789 = Berserker Enchant ID, but now supporting other proc effects as well Stats.SpecialEffectEnumerator mhEffects = character.MainHandEnchant.Stats.SpecialEffects(); if (mhEffects.MoveNext()) { bersMainHand.Add(mhEffects.Current); } } if (character.MainHand != null && character.MainHand.Item.Stats._rawSpecialEffectData != null) { Stats.SpecialEffectEnumerator mhEffects = character.MainHand.Item.Stats.SpecialEffects(); if (mhEffects.MoveNext()) { bersMainHand.Add(mhEffects.Current); } } if (combatFactors.useOH && character.OffHandEnchant != null /*&& character.OffHandEnchant.Id == 3789*/) { Stats.SpecialEffectEnumerator ohEffects = character.OffHandEnchant.Stats.SpecialEffects(); if (ohEffects.MoveNext()) { bersOffHand.Add(ohEffects.Current); } } if (character.OffHand != null && character.OffHand.Item.Stats._rawSpecialEffectData != null) { Stats.SpecialEffectEnumerator ohEffects = character.OffHand.Item.Stats.SpecialEffects(); if (ohEffects.MoveNext()) { bersOffHand.Add(ohEffects.Current); } } if (statType == StatType.Average) { DoSpecialEffects(charStruct, bersMainHand, bersOffHand, statsTotal); } else // if (statType == StatType.Maximum) { Base.StatsWarrior maxSpecEffects = new Base.StatsWarrior(); foreach (SpecialEffect effect in statsTotal.SpecialEffects()) maxSpecEffects.Accumulate(effect.Stats); return UpdateStatsAndAdd(maxSpecEffects as Base.StatsWarrior, combatFactors.StatS, character); } //UpdateStatsAndAdd(statsProcs, statsTotal, character); // Already done in GetSpecialEffectStats // special case for dual wielding w/ berserker enchant on one/both weapons, as they act independently //combatFactors.StatS = statsTotal; Base.StatsWarrior bersStats = new Base.StatsWarrior(); foreach (SpecialEffect e in bersMainHand) { if (e.Duration == 0) { bersStats.ShadowDamage = e.GetAverageProcsPerSecond(fightDuration / Rot.AttemptedAtksOverDurMH, Rot.LandedAtksOverDurMH / Rot.AttemptedAtksOverDurMH, combatFactors.CMHItemSpeed, calcOpts.SE_UseDur ? fightDuration : 0); } else { // berserker enchant id float f = e.GetAverageUptime(fightDuration / Rot.AttemptedAtksOverDurMH, Rot.LandedAtksOverDurMH / Rot.AttemptedAtksOverDurMH, combatFactors.CMHItemSpeed, calcOpts.SE_UseDur ? fightDuration : 0); bersStats.Accumulate(e.Stats, f); } } foreach (SpecialEffect e in bersOffHand) { if (e.Duration == 0) { bersStats.ShadowDamage += e.GetAverageProcsPerSecond(fightDuration / Rot.AttemptedAtksOverDurOH, Rot.LandedAtksOverDurOH / Rot.AttemptedAtksOverDurOH, combatFactors.COHItemSpeed, calcOpts.SE_UseDur ? fightDuration : 0); } else { float f = e.GetAverageUptime(fightDuration / Rot.AttemptedAtksOverDurOH, Rot.LandedAtksOverDurOH / Rot.AttemptedAtksOverDurOH, combatFactors.COHItemSpeed, calcOpts.SE_UseDur ? fightDuration : 0); bersStats.Accumulate(e.Stats, f); } } combatFactors.StatS = UpdateStatsAndAdd(bersStats, combatFactors.StatS, character); combatFactors.InvalidateCache(); return combatFactors.StatS; }
public static SpecialEffect GetEnragedRegenerationWithMastery(float masteryVal, DPSWarrCharacter dpswarrchar) { SpecialEffect retVal = new SpecialEffect(Trigger.MeleeHit, new Stats() { BonusDamageMultiplier = 0.10f / 3f * dpswarrchar.Talents.Enrage * (1f + masteryVal), }, 9f, 0f, 0.03f * 3f); return retVal; }
public override ComparisonCalculationBase[] GetCustomChartData(Character character, string chartName) { if (character == null) { return null;} Character zeOriginal = character.Clone(); Character zeClone = character.Clone(); CharacterCalculationsDPSWarr calculations = GetCharacterCalculations(zeOriginal) as CharacterCalculationsDPSWarr; CalculationOptionsDPSWarr calcOpts = zeOriginal.CalculationOptions as CalculationOptionsDPSWarr; ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = true; bool[] origMaints = (bool[])calcOpts.MaintenanceTree.Clone(); DPSWarrCharacter dpswarchar = new DPSWarrCharacter() { Char = zeOriginal, CalcOpts = (CalculationOptionsDPSWarr)zeOriginal.CalculationOptions, BossOpts = zeOriginal.BossOptions, CombatFactors = null, Rot = null, }; switch (chartName) { #region Ability DPS case "Ability DPS": { List<ComparisonCalculationBase> comparisons = new List<ComparisonCalculationBase>(); foreach (AbilityWrapper aw in calculations.Rot.TheAbilityList) { if (aw.Ability.DamageOnUse == 0) { continue; } ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr(); comparison.Name = aw.Ability.Name; comparison.Description = aw.Ability.Desc; if (aw.Ability is Skills.Rend) { comparison.DPSPoints = (aw.Ability as Skills.Rend).GetDPS(aw.NumActivatesO20, calculations.Rot.GetWrapper<Skills.ThunderClap>().NumActivatesO20, calculations.Rot.TimeOver20Perc) * calculations.Rot.TimeOver20Perc + (aw.Ability as Skills.Rend).GetDPS(aw.NumActivatesU20, calculations.Rot.GetWrapper<Skills.ThunderClap>().NumActivatesU20, calculations.Rot.TimeUndr20Perc) * calculations.Rot.TimeUndr20Perc; } else { comparison.DPSPoints = aw.AllDPS; } comparison.SurvPoints = aw.AllHPS; comparison.ImageSource = aw.Ability.Icon; comparisons.Add(comparison); } foreach (ComparisonCalculationDPSWarr comp in comparisons) { comp.OverallPoints = comp.DPSPoints + comp.SurvPoints; } calcOpts.MaintenanceTree = origMaints; ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = false; return comparisons.ToArray(); } #endregion #region Ability Damage per GCD case "Ability Damage per GCD": { List<ComparisonCalculationBase> comparisons = new List<ComparisonCalculationBase>(); foreach (AbilityWrapper aw in calculations.Rot.TheAbilityList) { if (aw.Ability.DamageOnUse == 0) { continue; } ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr(); comparison.Name = aw.Ability.Name; comparison.Description = aw.Ability.Desc; comparison.DPSPoints = ( ((aw.Ability is Skills.Rend) ? ((aw.Ability as Skills.Rend).TickSize * (aw.Ability as Skills.Rend).NumTicks) : aw.Ability.DamageOnUse)) / (aw.Ability.GCDTime == 0 ? 1f : (aw.Ability.GCDTime / calculations.Rot.LatentGCD)); comparison.SurvPoints = aw.Ability.GetAvgHealingOnUse(aw.AllNumActivates); comparison.ImageSource = aw.Ability.Icon; comparisons.Add(comparison); } foreach (ComparisonCalculationDPSWarr comp in comparisons) { comp.OverallPoints = comp.DPSPoints + comp.SurvPoints; } calcOpts.MaintenanceTree = origMaints; ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = false; return comparisons.ToArray(); } #endregion #region Ability Maintenance Changes case "Ability Maintenance Changes": { List<ComparisonCalculationBase> comparisons = new List<ComparisonCalculationBase>(); #region Rage Generators comparisons.Add(getComp(dpswarchar, "Berserker Rage", (int)Maintenance.BerserkerRage)); comparisons.Add(getComp(dpswarchar, "Deadly Calm", (int)Maintenance.DeadlyCalm)); #endregion #region Maintenance comparisons.Add(getComp(dpswarchar, "Battle Shout", (int)Maintenance.BattleShout)); comparisons.Add(getComp(dpswarchar, "Commanding Shout", (int)Maintenance.CommandingShout)); comparisons.Add(getComp(dpswarchar, "Demoralizing Shout", (int)Maintenance.DemoralizingShout)); comparisons.Add(getComp(dpswarchar, "Sunder Armor", (int)Maintenance.SunderArmor)); comparisons.Add(getComp(dpswarchar, "Thunder Clap", (int)Maintenance.ThunderClap)); comparisons.Add(getComp(dpswarchar, "Hamstring", (int)Maintenance.Hamstring)); #endregion #region Periodics comparisons.Add(getComp(dpswarchar, "Shattering Throw", (int)Maintenance.ShatteringThrow)); comparisons.Add(getComp(dpswarchar, "Sweeping Strikes", (int)Maintenance.SweepingStrikes)); comparisons.Add(getComp(dpswarchar, "Death Wish", (int)Maintenance.DeathWish)); comparisons.Add(getComp(dpswarchar, "Recklessness", (int)Maintenance.Recklessness)); comparisons.Add(getComp(dpswarchar, "Enraged Regeneration", (int)Maintenance.EnragedRegeneration)); #endregion #region Damage Dealers if (calculations.Rot.GetType() == typeof(FuryRotation)) { #region Fury comparisons.Add(getComp(dpswarchar, "Bloodsurge", (int)Maintenance.Bloodsurge)); comparisons.Add(getComp(dpswarchar, "Bloodthirst", (int)Maintenance.Bloodthirst)); // comparisons.Add(getComp(dpswarchar, "Whirlwind", (int)Maintenance.Whirlwind)); comparisons.Add(getComp(dpswarchar, "Raging Blow", (int)Maintenance.RagingBlow)); #endregion } else if (calculations.Rot.GetType() == typeof(ArmsRotation)) { #region Arms comparisons.Add(getComp(dpswarchar, "Bladestorm", (int)Maintenance.Bladestorm)); comparisons.Add(getComp(dpswarchar, "Mortal Strike", (int)Maintenance.MortalStrike)); comparisons.Add(getComp(dpswarchar, "Rend", (int)Maintenance.Rend)); comparisons.Add(getComp(dpswarchar, "Overpower", (int)Maintenance.Overpower)); comparisons.Add(getComp(dpswarchar, "Taste for Blood", (int)Maintenance.TasteForBlood)); comparisons.Add(getComp(dpswarchar, "Colossus Smash", (int)Maintenance.ColossusSmash)); comparisons.Add(getComp(dpswarchar, "Slam", (int)Maintenance.Slam)); #endregion } comparisons.Add(getComp(dpswarchar, "<20% Execute Spamming", (int)Maintenance.ExecuteSpam)); #endregion #region Rage Dumps comparisons.Add(getComp(dpswarchar, "Heroic Strike", (int)Maintenance.HeroicStrike)); comparisons.Add(getComp(dpswarchar, "Cleave", (int)Maintenance.Cleave)); comparisons.Add(getComp(dpswarchar, "Inner Rage", (int)Maintenance.InnerRage)); #endregion foreach (ComparisonCalculationDPSWarr comp in comparisons) { comp.OverallPoints = comp.DPSPoints + comp.SurvPoints; } calcOpts.MaintenanceTree = origMaints; ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = false; return comparisons.ToArray(); } #endregion #region Rage Cost per Damage case "Rage Cost per Damage": { List<ComparisonCalculationBase> comparisons = new List<ComparisonCalculationBase>(); float DeepWoundsDamage = calculations.Rot.DW.TickSize * 6f; foreach (AbilityWrapper aw in calculations.Rot.TheAbilityList) { if (aw.Ability.DamageOnUse == 0 || aw.Ability.RageCost == -1f) { continue; } ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr(); comparison.Name = aw.Ability.Name; comparison.Description = string.Format("Costs {0} Rage\r\n{1}", aw.Ability.RageCost, aw.Ability.Desc); float extraRage = (aw.Ability is Skills.Execute) ? (aw.Ability as Skills.Execute).UsedExtraRage : 0f; float val = (((aw.Ability.RageCost + extraRage) != 0) ? (aw.Ability.RageCost < -1f ? aw.Ability.RageCost * -1f : aw.Ability.RageCost) + extraRage : 1f); if (aw.Ability is Skills.Rend) { comparison.SubPoints[0] = ((aw.Ability as Skills.Rend).TickSize * (aw.Ability as Skills.Rend).NumTicks) / val; } else { comparison.SubPoints[0] = aw.Ability.DamageOnUse / val; } comparison.SubPoints[1] = (aw.Ability.MHAtkTable.Crit * DeepWoundsDamage) / val; comparison.ImageSource = aw.Ability.Icon; comparisons.Add(comparison); } foreach (ComparisonCalculationDPSWarr comp in comparisons) { comp.OverallPoints = comp.SubPoints[0] + comp.SubPoints[1]; } calcOpts.MaintenanceTree = origMaints; ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = false; return comparisons.ToArray(); } #endregion #region Execute Spam case "Execute Spam": { List<ComparisonCalculationBase> comparisons = new List<ComparisonCalculationBase>(); { bool orig = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam; bool orig2 = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2; ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam = false; ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2 = false; //CharacterCalculationsDPSWarr bah = GetCharacterCalculations(zeClone) as CharacterCalculationsDPSWarr; ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr(); comparison.Name = "Without Execute Spam"; comparison.Description = "Turning <20% Execute Spam off on the options pane will change your DPS to this"; comparison.SubPoints[0] = GetCharacterCalculations(zeClone).SubPoints[0]; comparison.SubPoints[1] = GetCharacterCalculations(zeClone).SubPoints[1]; comparison.Equipped = orig == false; comparison.ImageSource = Skills.MortalStrike.SIcon; comparisons.Add(comparison); ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam = orig; ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2 = orig2; } { bool orig = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam; bool orig2 = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2; ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam = true; ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2 = false; //CharacterCalculationsDPSWarr bah = GetCharacterCalculations(zeClone) as CharacterCalculationsDPSWarr; ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr(); comparison.Name = "With Execute Spam"; comparison.Description = "Turning <20% Execute Spam on on the options pane will change your DPS to this"; comparison.SubPoints[0] = GetCharacterCalculations(zeClone).SubPoints[0]; comparison.SubPoints[1] = GetCharacterCalculations(zeClone).SubPoints[1]; comparison.Equipped = orig == true && orig2 == false; comparison.ImageSource = Skills.Execute.SIcon; comparisons.Add(comparison); ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam = orig; ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2 = orig2; } { bool orig = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam; bool orig2 = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2; ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam = true; ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2 = true; CharacterCalculationsDPSWarr bah = GetCharacterCalculations(zeClone) as CharacterCalculationsDPSWarr; ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr(); comparison.Name = "With Execute Spam Stage 2"; comparison.Description = "Turning <20% Execute Spam on on the options pane AND Enforcing Taste for Blood will change your DPS to this"; comparison.SubPoints[0] = GetCharacterCalculations(zeClone).SubPoints[0]; comparison.SubPoints[1] = GetCharacterCalculations(zeClone).SubPoints[1]; comparison.Equipped = orig == true && orig2 == true; comparison.ImageSource = Skills.TasteForBlood.SIcon; comparisons.Add(comparison); ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpam = orig; ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).M_ExecuteSpamStage2 = orig2; } foreach (ComparisonCalculationDPSWarr comp in comparisons) { comp.OverallPoints = comp.SubPoints[0] + comp.SubPoints[1]; } calcOpts.MaintenanceTree = origMaints; ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = false; return comparisons.ToArray(); } #endregion #region PTR Testing case "PTR Testing": { List<ComparisonCalculationBase> comparisons = new List<ComparisonCalculationBase>(); { bool orig = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).PtrMode; ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).PtrMode = false; CharacterCalculationsDPSWarr bah = GetCharacterCalculations(zeClone) as CharacterCalculationsDPSWarr; ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr(); comparison.Name = "Live Mode"; comparison.Description = "This makes Thunderclap apply Rend's Initial Damage along with its own."; comparison.SubPoints[0] = GetCharacterCalculations(zeClone).SubPoints[0]; comparison.SubPoints[1] = GetCharacterCalculations(zeClone).SubPoints[1]; comparison.Equipped = orig == false; comparison.ImageSource = "spell_nature_callstorm"; comparisons.Add(comparison); ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).PtrMode = orig; } { bool orig = ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).PtrMode; ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).PtrMode = true; CharacterCalculationsDPSWarr bah = GetCharacterCalculations(zeClone) as CharacterCalculationsDPSWarr; ComparisonCalculationDPSWarr comparison = new ComparisonCalculationDPSWarr(); comparison.Name = "PTR Mode"; comparison.Description = "This makes Thunderclap NOT apply Rend's Initial Damage along with its own."; comparison.SubPoints[0] = GetCharacterCalculations(zeClone).SubPoints[0]; comparison.SubPoints[1] = GetCharacterCalculations(zeClone).SubPoints[1]; comparison.Equipped = orig == true; comparison.ImageSource = Skills.Rend.SIcon; comparisons.Add(comparison); ((CalculationOptionsDPSWarr)zeClone.CalculationOptions).PtrMode = orig; } foreach (ComparisonCalculationDPSWarr comp in comparisons) { comp.OverallPoints = comp.SubPoints[0] + comp.SubPoints[1]; } calcOpts.MaintenanceTree = origMaints; ((CalculationOptionsPanelDPSWarr)CalculationOptionsPanel).IsLoadingCalculationOptions = false; return comparisons.ToArray(); } #endregion default: { calcOpts.MaintenanceTree = origMaints; return new ComparisonCalculationBase[0]; } } }
private float ApplySpecialEffect(SpecialEffect effect, DPSWarrCharacter charStruct, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances, ref Base.StatsWarrior applyTo) { #if DEBUG //ConstructionCounts["ApplySpecialEffect"]++; #endif float fightDuration = charStruct.BossOpts.BerserkTimer; float fightDuration2Pass = charStruct.CalcOpts.SE_UseDur ? fightDuration : 0; Stats effectStats = effect.Stats; float upTime = 0f; //float avgStack = 1f; /*if (effect.Stats.TargetArmorReduction > 0f || effect.Stats.ArmorPenetrationRating > 0f) { //int j = 0; }*/ if (effect.Trigger == Trigger.Use) { if (effect.Stats._rawSpecialEffectDataSize == 1) { upTime = effect.GetAverageUptime(0f, 1f, charStruct.CombatFactors.CMHItemSpeed, fightDuration2Pass); //float uptime = (effect.Cooldown / fightDuration); List<SpecialEffect> nestedEffect = new List<SpecialEffect>(); nestedEffect.Add(effect.Stats._rawSpecialEffectData[0]); Base.StatsWarrior _stats2 = new Base.StatsWarrior(); ApplySpecialEffect(effect.Stats._rawSpecialEffectData[0], charStruct, triggerIntervals, triggerChances, ref _stats2); effectStats = _stats2; } else { upTime = effect.GetAverageStackSize(0f, 1f, charStruct.CombatFactors.CMHItemSpeed, fightDuration2Pass); } } else if (effect.Duration == 0f && triggerIntervals.ContainsKey(effect.Trigger) && !float.IsInfinity(triggerIntervals[effect.Trigger])) { upTime = effect.GetAverageProcsPerSecond(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], charStruct.CombatFactors.CMHItemSpeed, fightDuration2Pass); } else if (effect.Trigger == Trigger.ExecuteHit) { upTime = effect.GetAverageStackSize(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], charStruct.CombatFactors.CMHItemSpeed, fightDuration2Pass * (float)charStruct.BossOpts.Under20Perc); } else if (triggerIntervals.ContainsKey(effect.Trigger) && !float.IsInfinity(triggerIntervals[effect.Trigger])) { upTime = effect.GetAverageStackSize(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], charStruct.CombatFactors.CMHItemSpeed, fightDuration2Pass); } if (upTime > 0f) { if (effect.Duration == 0f) { applyTo.Accumulate(effectStats, upTime * fightDuration); } else if (upTime <= effect.MaxStack) { applyTo.Accumulate(effectStats, upTime); } return upTime; } return 0f; }
public Dictionary<ItemDamageType, float> SoftCapSurvivals(DPSWarrCharacter dpswarchar, Dictionary<ItemDamageType, float> highestDmg, Dictionary<ItemDamageType, float> survivalRaws) { Dictionary<ItemDamageType, float> cappedValues = new Dictionary<ItemDamageType, float>() { }; // foreach (ItemDamageType t in highestDmg.Keys) { cappedValues[t] = SoftCapSurv(survivalRaws[t], highestDmg[t]); } // return cappedValues; }