//HunterRatings ratings; public ShotRotationCalculator(Character character, CharacterCalculationsHunter calculatedStats, CalculationOptionsHunter options, double totalStaticHaste, double effectiveRAPAgainstMob, double abilitiesCritDmgModifier, double yellowCritDmgModifier, double weaponDamageAverage, double ammoDamage, double talentModifiers) { //ratings = new HunterRatings(); this.character = character; this.calculatedStats = calculatedStats; this.options = options; //this.hawkRAPBonus = ratings.HAWK_BONUS_AP * (1.0 + 0.5 * character.HunterTalents.AspectMastery); this.totalStaticHaste = totalStaticHaste; this.effectiveRAPAgainstMob = effectiveRAPAgainstMob; this.abilitiesCritDmgModifier = abilitiesCritDmgModifier; this.yellowCritDmgModifier = yellowCritDmgModifier; this.weaponDamageAverage = weaponDamageAverage; this.ammoDamage = ammoDamage; this.talentModifiers = talentModifiers; //int targetArmor = options.TargetArmor; //this.armorReduction = 1f - StatConversion.GetArmorDamageReduction(character.Level, targetArmor, // calculatedStats.BasicStats.ArmorPenetration, 0f, calculatedStats.BasicStats.ArmorPenetrationRating); //double targetArmor = (options.TargetArmor - calculatedStats.BasicStats.ArmorPenetration) * (1.0 - calculatedStats.BasicStats.ArmorPenetrationRating / (ratings.ARP_RATING_PER_PERCENT * 100.0)); //this.armorReduction = 1.0 - (targetArmor / (467.5 * options.TargetLevel + targetArmor - 22167.5)); //reducedArmor *= (1f - character.HunterTalents.PiercingShots * 0.02f); //this.talentedArmorReduction = 1f - StatConversion.GetArmorDamageReduction(character.Level, targetArmor, // calculatedStats.BasicStats.ArmorPenetration, character.HunterTalents.PiercingShots * 0.02f, // calculatedStats.BasicStats.ArmorPenetrationRating); //this.talentedArmorReduction = 1.0 - (targetArmor / (467.5 * options.TargetLevel + targetArmor - 22167.5)); }
public RotationTest(Character character, CharacterCalculationsHunter calculatedStats, CalculationOptionsHunter calcOpts, BossOptions bossOpts) { this.character = character; this.CalcOpts = calcOpts; this.BossOpts = bossOpts; this.calculatedStats = calculatedStats; // 091109 Drizz: Added //this.calculatedStats.sequence = "Time :Shot :CastTime:Done :CD Until" + Environment.NewLine; }
public PetCalculations(Character character, CharacterCalculationsHunter calculatedStats, CalculationOptionsHunter calcopts, BossOptions bossOpts, StatsHunter hunterStats) { this.character = character; this.calculatedStats = calculatedStats; this.CalcOpts = calcopts; this.BossOpts = bossOpts; this.PetTalents = calcopts.PetTalents; this.Talents = character.HunterTalents; this.HunterStats = hunterStats; PetStats = new StatsHunter(); }
public virtual void Initialize(CharacterCalculationsHunter calcs) { this.calcs = calcs; StatS = calcs.Hunter.Stats; Char = calcs.character; BossOpts = calcs.BossOpts; Fight = new FightTime(BossOpts); CalcOpts = calcs.CalcOpts; // Get our Ability list with valid options. initAbilities(); // Generate the rotation doIterations(); float iActions = WhiteAtks.RwActivates; #if false iActions += AbilityList[typeof(SteadyShot)].numActivates; iActions += AbilityList[typeof(CobraShot)].numActivates; iActions += AbilityList[typeof(MultiShot)].numActivates; iActions += AbilityList[typeof(ArcaneShot)].numActivates; iActions += AbilityList[typeof(ExplosiveShot)].numActivates; iActions += AbilityList[typeof(AimedShot)].numActivates; iActions += AbilityList[typeof(ChimeraShot)].numActivates; iActions += AbilityList[typeof(KillShot)].numActivates; iActions += AbilityList[typeof(SerpentSting)].numActivates; #endif #region WildQuiver if (FightDuration > 0) { float numWQProcs = (iActions * calcs.Hunter.MasteryRatePercent); calcs.WildQuiverDPS = (numWQProcs * WhiteAtks.RwDamageOnUse) / FightDuration; } #endregion #region Piercing Shots if (Talents.PiercingShots > 0) { float PiercingShotsPerc = Talents.PiercingShots * .1f; float SScritRate = AbilityList[typeof(SteadyShot)].ability.GetXActs(AttackTableSelector.Crit, 1); calcs.PiercingShotsDPSSteadyShot = AbilityList[typeof(SteadyShot)].DPS * SScritRate * PiercingShotsPerc; float CScritRate = AbilityList[typeof(ChimeraShot)].ability.GetXActs(AttackTableSelector.Crit, 1); calcs.PiercingShotsDPSChimeraShot = AbilityList[typeof(ChimeraShot)].DPS * CScritRate * PiercingShotsPerc; float AScritRate = AbilityList[typeof(AimedShot)].ability.GetXActs(AttackTableSelector.Crit, 1); calcs.PiercingShotsDPSAimedShot = AbilityList[typeof(AimedShot)].DPS * AScritRate * PiercingShotsPerc; float MMMAScritRate = AbilityList[typeof(MMMAimedShot)].ability.GetXActs(AttackTableSelector.Crit, 1); calcs.PiercingShotsDPSAimedShot += AbilityList[typeof(MMMAimedShot)].DPS * MMMAScritRate * PiercingShotsPerc; float CAAScritRate = AbilityList[typeof(CAAimedShot)].ability.GetXActs(AttackTableSelector.Crit, 1); calcs.PiercingShotsDPSAimedShot += AbilityList[typeof(CAAimedShot)].DPS * CAAScritRate * PiercingShotsPerc; } #endregion float fDPS = WhiteAtks.RwDPS; fDPS += calcs.WildQuiverDPS; fDPS += AbilityList[typeof(SteadyShot)].DPS; fDPS += AbilityList[typeof(CobraShot)].DPS; fDPS += AbilityList[typeof(MultiShot)].DPS; fDPS += AbilityList[typeof(ArcaneShot)].DPS; fDPS += AbilityList[typeof(ExplosiveShot)].DPS; fDPS += AbilityList[typeof(MMMAimedShot)].DPS; fDPS += AbilityList[typeof(CAAimedShot)].DPS; fDPS += AbilityList[typeof(AimedShot)].DPS; fDPS += AbilityList[typeof(ChimeraShot)].DPS; fDPS += AbilityList[typeof(KillShot)].DPS; fDPS += AbilityList[typeof(SerpentSting)].DPS; fDPS += calcs.PiercingShotsDPS; calcs.CustomDPS = fDPS; #region Populate the display values. // Whites calcs.Whites = WhiteAtks; // Filler/ Focus regen. calcs.Steady = AbilityList[typeof(SteadyShot)]; calcs.Cobra = AbilityList[typeof(CobraShot)]; // SV or BM (Replaces Steady Shot) // Shared Instants calcs.Multi = AbilityList[typeof(MultiShot)]; calcs.Arcane = AbilityList[typeof(ArcaneShot)]; // Spec specific. calcs.Explosive = AbilityList[typeof(ExplosiveShot)]; // SV //calcs.BlackArrowD = BlackArrowD; // SV calcs.Aimed = this.AbilityList[typeof(AimedShot)]; // MM calcs.MMMAimed = this.AbilityList[typeof(MMMAimedShot)]; // MM calcs.CAAimed = this.AbilityList[typeof(CAAimedShot)]; // MM calcs.Chimera = this.AbilityList[typeof(ChimeraShot)]; // MM // calcs.Intimidate = Intimidate; // BM calcs.Bestial = Bestial; // BM // Generic calcs.Kill = AbilityList[typeof(KillShot)]; // Buffs calcs.Rapid = Rapid; calcs.Ready = Ready; // DOT calcs.Serpent = AbilityList[typeof(SerpentSting)]; // Traps calcs.Immolation = Immolation; calcs.ExplosiveT = ExplosiveT; calcs.Freezing = Freezing; calcs.Frost = Frost; #endregion }
public virtual void Initialize(CharacterCalculationsHunter calcs) { this.calcs = calcs; StatS = calcs.AverageStats; initAbilities(); //doIterations(); // Whites calcs.Whites = WhiteAtks; // Anti-Debuff calcs.Explosive = Explosive; /*calcs.HF = HF; * calcs.EM = EM; * calcs.CH = CH; * calcs.IN = IN; * calcs.IV = IV; * // Rage Generators * calcs.SndW = SndW; * calcs.BZ = BZ; * calcs.BR = BR; * // Maintenance * calcs.BTS = BTS; * calcs.CS = CS; * calcs.DS = DS; * calcs.SN = SN; * calcs.TH = TH; * calcs.HMS = HMS; * calcs.ER = ER; * // Periodics * calcs.ST = ST; * calcs.SW = SW; * calcs.Death = Death; * calcs.RK = RK;*/ // Shared Instants calcs.Multi = Multi; calcs.Steady = Steady; calcs.Aimed = Aimed; calcs.Multi = Multi; calcs.Arcane = Arcane; // Arms // Generic //calcs.CL = CL; calcs.Piercing = Piercing; //calcs.HS = HS; calcs.Kill = Kill; calcs.Silencing = Silencing; calcs.Volley = Volley; calcs.Bestial = Bestial; calcs.Rapid = Rapid; calcs.BlackArrowB = BlackArrowB; calcs.Ready = Ready; calcs.Piercing = Piercing; calcs.BlackArrowD = BlackArrowD; calcs.Serpent = Serpent; calcs.Scorpid = Scorpid; calcs.Viper = Viper; calcs.Chimera = Chimera; calcs.Immolation = Immolation; calcs.ExplosiveT = ExplosiveT; calcs.Freezing = Freezing; calcs.Frost = Frost; }
//private static readonly SpecialEffect FuriousHowl = new SpecialEffect(Trigger.Use, new StatsHunter() { AttackPower = 320f, PetAttackPower = 320f, }, 20f, 40f); private static void CalculateTriggers(Character character, CharacterCalculationsHunter calculatedStats, StatsHunter statsTotal, CalculationOptionsHunter calcOpts, BossOptions bossOpts, Dictionary<Trigger, float> triggerIntervals, Dictionary<Trigger, float> triggerChances) { const float GCD = 1f; const float GCDpSec = 1f / GCD; int levelDif = bossOpts.Level - character.Level; float critMOD = StatConversion.NPC_LEVEL_CRIT_MOD[levelDif]; HunterTalents talents = character.HunterTalents; float rangedWeaponSpeed = 2.4f; float rangedWeaponDamage = 1; float autoShotSpeed = rangedWeaponSpeed; // Should be the same as rangedWeaponSpeed hasted. float autoShotsPerSecond = 1 / autoShotSpeed; float specialShotsPerSecond = GCDpSec; // I know it doesn't start w/ a special every GCD, but something to start with more than 0. float totalShotsPerSecond = GCDpSec + autoShotsPerSecond; float shotsPerSecondWithoutHawk = 0; // RotationTest rotationTest; //GenRotation(character, statsTotal, calculatedStats, calcOpts, bossOpts, talents, // out rangedWeaponSpeed, out rangedWeaponDamage, out autoShotSpeed, // out autoShotsPerSecond, out specialShotsPerSecond, out totalShotsPerSecond, out shotsPerSecondWithoutHawk, // out rotationTest); float ChanceToMiss = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP[levelDif] - statsTotal.PhysicalHit); float ChanceToSpellMiss = Math.Max(0f, StatConversion.GetSpellMiss(levelDif, false) - statsTotal.SpellHit); // TODO: Ensure that we don't have any div by 0 issues here. #region Generic // Use triggerIntervals[Trigger.Use] = 0f; // Should be Effect cooldown. triggerChances[Trigger.Use] = 1f; // Physical Hit triggerIntervals[Trigger.PhysicalAttack] = triggerIntervals[Trigger.RangedHit] = triggerIntervals[Trigger.PhysicalHit] = triggerIntervals[Trigger.RangedCrit] = triggerIntervals[Trigger.PhysicalCrit] = 1f / totalShotsPerSecond; // Note with DOT crits, this may have to be adjusted. triggerChances[Trigger.PhysicalAttack] = triggerChances[Trigger.RangedHit] = triggerChances[Trigger.PhysicalHit] = (1f - ChanceToMiss); triggerChances[Trigger.RangedCrit] = triggerChances[Trigger.PhysicalCrit] = Math.Min(1f + critMOD, Math.Max(0f, statsTotal.PhysicalCrit)); // Dots & damage done. triggerIntervals[Trigger.DoTTick] = talents.PiercingShots > 0 ? 1f : 0f; // Also need to add other DOTs into this value. triggerIntervals[Trigger.DamageDone] = Math.Max(0f, 1f / (totalShotsPerSecond + ((talents.PiercingShots > 0 ? 1f : 0f) > 0 ? 1f / (talents.PiercingShots > 0 ? 1f : 0f) : 0f))); triggerIntervals[Trigger.DamageOrHealingDone] = Math.Max(0f, 1f / (totalShotsPerSecond + ((talents.PiercingShots > 0 ? 1f : 0f) > 0 ? 1f / (talents.PiercingShots > 0 ? 1f : 0f) : 0f))); // Need to add Self/pet-Heals triggerChances[Trigger.DoTTick] = 1f; // This should be up-time on DoTs & PS. triggerChances[Trigger.DamageDone] = 1f; triggerChances[Trigger.DamageOrHealingDone] = 1f; #endregion #region Pets only //triggerIntervals[Trigger.MeleeHit] = //triggerIntervals[Trigger.MeleeCrit] = Math.Max(0f, calculatedStats.PetCalc.PetCompInterval); //triggerIntervals[Trigger.PetClawBiteSmackCrit] = Math.Max(0f, calculatedStats.PetCalc.PetClawBiteSmackInterval); //triggerChances[Trigger.MeleeHit] = calculatedStats.PetCalc.WhAtkTable.AnyLand; //triggerChances[Trigger.MeleeCrit] = Math.Min(1f + critMOD, Math.Max(0f, calculatedStats.PetCalc.WhAtkTable.Crit)); //triggerChances[Trigger.PetClawBiteSmackCrit] = Math.Min(1f + critMOD, Math.Max(0f, calculatedStats.PetCalc.WhAtkTable.Crit)); #endregion #region Hunter Specific triggerIntervals[Trigger.HunterAutoShotHit] = 1f / autoShotsPerSecond; triggerIntervals[Trigger.SteadyShotHit] = 0; // calculatedStats.steadyShot.Cd; triggerIntervals[Trigger.CobraShotHit] = 0; // calculatedStats.cobraShot.Cd; triggerIntervals[Trigger.EnergyOrFocusDropsBelow20PercentOfMax] = 4f; // Approximating as 80% chance every 4 seconds. TODO: Put in some actual method of calculating this triggerChances[Trigger.HunterAutoShotHit] = (1f - ChanceToMiss); triggerChances[Trigger.SteadyShotHit] = (1f - ChanceToMiss); triggerChances[Trigger.CobraShotHit] = (1f - ChanceToMiss); triggerChances[Trigger.EnergyOrFocusDropsBelow20PercentOfMax] = 0.80f; // Approximating as 80% chance every 4 seconds. TODO: Put in some actual method of calculating this #endregion }
private StatsHunter GetCharacterStats(Character character, Item additionalItem, bool bGetMaxStats) { StatsHunter statsTotal = new StatsHunter(); try { #region NullChecks if (null == character) { #if DEBUG throw new NullReferenceException("Character is Null"); #else return statsTotal; #endif } CalculationOptionsHunter calcOpts = character.CalculationOptions as CalculationOptionsHunter; if (null == calcOpts) { calcOpts = new CalculationOptionsHunter(); } HunterTalents talents = character.HunterTalents; if (null == talents) { return statsTotal; } Specialization tree = GetSpecialization(talents); CharacterCalculationsHunter calculatedStats = new CharacterCalculationsHunter(); if (null == calculatedStats) { #if DEBUG throw new NullReferenceException("Character Calculations is Null"); #else return statsTotal; #endif } BossOptions bossOpts = new BossOptions(); bossOpts = character.BossOptions; if (null == bossOpts) { #if DEBUG throw new NullReferenceException("Boss Options is Null"); #else return statsTotal; #endif } #endregion statsTotal.Accumulate(BaseStats.GetBaseStats(character.Level, CharacterClass.Hunter, character.Race)); statsTotal.Accumulate(GetItemStats(character, additionalItem)); AccumulateBuffsStats(statsTotal, character.ActiveBuffs); // DO NOT MOVE GetRelevantStats any lower in this progression of functions. // It will erase stats specific to hunters. statsTotal = GetRelevantStats(statsTotal) as StatsHunter; statsTotal.Accumulate(GetTalentStats(character.HunterTalents)); statsTotal.Stamina = (float)Math.Floor(statsTotal.Stamina * (1f + statsTotal.BonusStaminaMultiplier)); statsTotal.Agility = (float)Math.Floor(statsTotal.Agility * (1f + statsTotal.BonusAgilityMultiplier)); calculatedStats.critFromAgi = statsTotal.Agility; // Agi bonus to Survival doesn't affect crit? if (tree == Specialization.Survival) calculatedStats.critFromAgi /= 1.1f; statsTotal.AttackPower += (statsTotal.Agility * 2f); statsTotal.AttackPower = statsTotal.RangedAttackPower = (float)Math.Floor(statsTotal.AttackPower * (1f + statsTotal.BonusAttackPowerMultiplier)); statsTotal.Health += (float)Math.Floor((statsTotal.Stamina - 20f) * 14f + 20f); statsTotal.Health = (float)Math.Floor(statsTotal.Health * (1f + statsTotal.BonusHealthMultiplier)); statsTotal.Armor = (float)Math.Floor(statsTotal.Armor * (1f + statsTotal.BonusArmorMultiplier)) + statsTotal.BonusArmor; statsTotal.NatureResistance += statsTotal.NatureResistanceBuff; statsTotal.FireResistance += statsTotal.FireResistanceBuff; statsTotal.FrostResistance += statsTotal.FrostResistanceBuff; statsTotal.ShadowResistance += statsTotal.ShadowResistanceBuff; statsTotal.ArcaneResistance += statsTotal.ArcaneResistanceBuff; int targetLevel = bossOpts.Level; float hasteBonus = StatConversion.GetPhysicalHasteFromRating(statsTotal.HasteRating, CharacterClass.Hunter); statsTotal.RangedHaste = (1f + hasteBonus) * (1f + statsTotal.BonusHasteMultiplier) * (1f + statsTotal.PhysicalHaste) - 1f; float hitBonus = StatConversion.GetPhysicalHitFromRating(statsTotal.HitRating) + statsTotal.PhysicalHit; float chanceMiss = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP[targetLevel - character.Level] - hitBonus); float chanceAvoided = chanceMiss; float rawChanceCrit = StatConversion.GetPhysicalCritFromRating(statsTotal.CritRating) + StatConversion.GetCritFromAgility(calculatedStats.critFromAgi, CharacterClass.Hunter) + statsTotal.PhysicalCrit + StatConversion.NPC_LEVEL_CRIT_MOD[targetLevel - character.Level]; calculatedStats.critRateOverall = rawChanceCrit * (1f - chanceAvoided); float chanceHit = 1f - chanceAvoided; if (bGetMaxStats) { #region Special Effects StatsHunter statsProcs = new StatsHunter(); Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>(); Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>(); CalculateTriggers(character, calculatedStats, statsTotal, calcOpts, bossOpts, triggerIntervals, triggerChances); /*if (calcOpts.PetFamily == PETFAMILY.Wolf && calculatedStats.pet.priorityRotation.getSkillFrequency(PetAttacks.FuriousHowl) > 0) { statsTotal.AddSpecialEffect(FuriousHowl); } */ foreach (SpecialEffect effect in statsTotal.SpecialEffects()) { statsProcs.Accumulate(getSpecialEffects(effect, triggerIntervals, triggerChances, character)); } #region Handle Results of Special Effects // Base Stats statsProcs.Stamina = (float)Math.Floor(statsProcs.Stamina * (1f + statsTotal.BonusStaminaMultiplier) * (1f + statsProcs.BonusStaminaMultiplier)); statsProcs.Agility = statsProcs.Agility * (1f + statsTotal.BonusAgilityMultiplier) * (1f + statsProcs.BonusAgilityMultiplier); statsProcs.Agility += statsProcs.HighestStat * (1f + statsTotal.BonusAgilityMultiplier) * (1f + statsProcs.BonusAgilityMultiplier); statsProcs.Agility += statsProcs.Paragon * (1f + statsTotal.BonusAgilityMultiplier) * (1f + statsProcs.BonusAgilityMultiplier); statsProcs.HighestStat = statsProcs.Paragon = 0f; // we've added them into agi so kill it statsProcs.Health += (float)Math.Floor(statsProcs.Stamina * 10f); float HighestSecondaryStatValue = statsProcs.HighestSecondaryStat; // how much HighestSecondaryStat to add statsProcs.HighestSecondaryStat = 0f; // remove HighestSecondaryStat stat, since it's not needed if (statsTotal.CritRating > statsTotal.HasteRating && statsTotal.CritRating > statsTotal.MasteryRating) { statsTotal.CritRating += HighestSecondaryStatValue; } else if (statsTotal.HasteRating > statsTotal.CritRating && statsTotal.HasteRating > statsTotal.MasteryRating) { statsTotal.HasteRating += HighestSecondaryStatValue; } else /*if (statsTotal.MasteryRating > statsTotal.CritRating && statsTotal.MasteryRating > statsTotal.HasteRating)*/ { statsTotal.MasteryRating += HighestSecondaryStatValue; } // Armor statsProcs.Armor = statsProcs.Armor * (1f + statsTotal.BaseArmorMultiplier + statsProcs.BaseArmorMultiplier); //statsProcs.BonusArmor += statsProcs.Agility * 2f; statsProcs.BonusArmor = statsProcs.BonusArmor * (1f + statsTotal.BonusArmorMultiplier + statsProcs.BonusArmorMultiplier); statsProcs.Armor += statsProcs.BonusArmor; statsProcs.BonusArmor = 0; //it's been added to Armor so kill it // Attack Power statsProcs.BonusAttackPowerMultiplier *= (1f + statsProcs.BonusRangedAttackPowerMultiplier); statsProcs.BonusRangedAttackPowerMultiplier = 0; // it's been added to Attack Power so kill it float totalBAPMProcs = (1f + statsTotal.BonusAttackPowerMultiplier) * (1f + statsProcs.BonusAttackPowerMultiplier) - 1f; float apFromAGIProcs = (1f + totalBAPMProcs) * (statsProcs.Agility) * 2f; //float apFromSTRProcs = (1f + totalBAPMProcs) * (statsProcs.Strength); float apBonusOtherProcs = (1f + totalBAPMProcs) * (statsProcs.AttackPower + statsProcs.RangedAttackPower); statsProcs.AttackPower = Math.Max(0f, apFromAGIProcs + /*apFromSTRProcs +*/ apBonusOtherProcs); statsProcs.RangedAttackPower = statsProcs.AttackPower; statsTotal.AttackPower *= (1f + statsProcs.BonusAttackPowerMultiplier); // Make sure the originals get your AP% procs // Crit statsProcs.PhysicalCrit += StatConversion.GetCritFromAgility(statsProcs.Agility, character.Class); statsProcs.PhysicalCrit += StatConversion.GetCritFromRating(statsProcs.CritRating + statsProcs.RangedCritRating, character.Class); // Haste statsProcs.PhysicalHaste = (1f + statsProcs.PhysicalHaste) * (1f + statsProcs.RangedHaste) * (1f + StatConversion.GetPhysicalHasteFromRating(statsProcs.HasteRating, character.Class)) - 1f; #endregion // Add it back into the fold statsTotal.Accumulate(statsProcs); #endregion } return statsTotal; } catch (Exception ex) { new Base.ErrorBox() { Title = "Error in getting Character Stats", Function = "GetCharacterStats()", TheException = ex, }.Show(); } return new StatsHunter(); }
/* private void GenPrioRotation(CharacterCalculationsHunter calculatedStats, CalculationOptionsHunter calcOpts, HunterTalents talents) { calculatedStats.priorityRotation = new ShotPriority(calcOpts); calculatedStats.priorityRotation.priorities[0] = getShotByIndex(calcOpts.PriorityIndex1, calculatedStats); calculatedStats.priorityRotation.priorities[1] = getShotByIndex(calcOpts.PriorityIndex2, calculatedStats); calculatedStats.priorityRotation.priorities[2] = getShotByIndex(calcOpts.PriorityIndex3, calculatedStats); calculatedStats.priorityRotation.priorities[3] = getShotByIndex(calcOpts.PriorityIndex4, calculatedStats); calculatedStats.priorityRotation.priorities[4] = getShotByIndex(calcOpts.PriorityIndex5, calculatedStats); calculatedStats.priorityRotation.priorities[5] = getShotByIndex(calcOpts.PriorityIndex6, calculatedStats); calculatedStats.priorityRotation.priorities[6] = getShotByIndex(calcOpts.PriorityIndex7, calculatedStats); calculatedStats.priorityRotation.priorities[7] = getShotByIndex(calcOpts.PriorityIndex8, calculatedStats); calculatedStats.priorityRotation.priorities[8] = getShotByIndex(calcOpts.PriorityIndex9, calculatedStats); calculatedStats.priorityRotation.priorities[9] = getShotByIndex(calcOpts.PriorityIndex10, calculatedStats); calculatedStats.priorityRotation.validateShots(talents); } private void GenAbilityCds(Character character, CharacterCalculationsHunter calculatedStats, CalculationOptionsHunter calcOpts, BossOptions bossOpts, HunterTalents talents) { calculatedStats.serpentSting.Cd = 1.5f; calculatedStats.serpentSting.Duration = talents.GlyphOfSerpentSting ? 21 : 15; calculatedStats.aimedShot.Cd = talents.GlyphOfAimedShot ? 8 : 10; calculatedStats.explosiveShot.Cd = 6; calculatedStats.chimeraShot.Cd = talents.GlyphOfChimeraShot ? 9 : 10; calculatedStats.arcaneShot.Cd = 6; calculatedStats.multiShot.Cd = /*talents.GlyphOfMultiShot ? 9 : 10; calculatedStats.blackArrow.Cd = 30 - (talents.Resourcefulness * 2); calculatedStats.blackArrow.Duration = 15; calculatedStats.killShot.Cd = talents.GlyphOfKillShot ? 9 : 15; calculatedStats.immolationTrap.Cd = 30 - talents.Resourcefulness * 2; calculatedStats.immolationTrap.Duration = talents.GlyphOfImmolationTrap ? 9 : 15; calculatedStats.explosiveTrap.Cd = 30 - talents.Resourcefulness * 2; calculatedStats.explosiveTrap.Duration = 20; calculatedStats.freezingTrap.Cd = 30 - talents.Resourcefulness * 2; calculatedStats.freezingTrap.Duration = 20; calculatedStats.frostTrap.Cd = 30 - talents.Resourcefulness * 2; calculatedStats.frostTrap.Duration = 30; // if (bossOpts.MultiTargs && bossOpts.MultiTargsTime > 0) { // // Good to go, now change the cooldown based on the multitargs uptime // calculatedStats.volley.Duration = 6f; // calculatedStats.volley.Cd = (1f / (bossOpts.MultiTargsTime / calculatedStats.volley.Duration)) * bossOpts.BerserkTimer; //#endif // } else { // // invalidate it // calculatedStats.volley.Cd = -1f; // //calculatedStats.volley.CastTime = -1f; // calculatedStats.volley.Duration = -1f; // } if (calculatedStats.priorityRotation.containsShot(Shots.Readiness)) { calculatedStats.rapidFire.Cd = 157.5f - (30f * talents.RapidKilling); } else { calculatedStats.rapidFire.Cd = (5 - talents.RapidKilling) * 60f; } calculatedStats.rapidFire.Duration = 15; // We will set the correct value for this later, after we've calculated haste calculatedStats.steadyShot.Cd = 2; // TODO Zhok: Same 4 cobra? calculatedStats.readiness.Cd = 180; calculatedStats.bestialWrath.Cd = (talents.GlyphOfBestialWrath ? 100f : 120f) * (1f - talents.Longevity * 0.10f); calculatedStats.bestialWrath.Duration = calcOpts.PetFamily == PETFAMILY.None ? 0 : 10; // We can calculate the rough frequencies now calculatedStats.priorityRotation.initializeTimings(); if (!calcOpts.UseRotationTest) { calculatedStats.priorityRotation.calculateFrequencies(); calculatedStats.priorityRotation.calculateLALProcs(character); calculatedStats.priorityRotation.calculateFrequencies(); calculatedStats.priorityRotation.calculateFrequencySums(); } } private static void GenRotation(Character character, Stats stats, CharacterCalculationsHunter calculatedStats, CalculationOptionsHunter calcOpts, BossOptions bossOpts, HunterTalents talents, out float rangedWeaponSpeed, out float rangedWeaponDamage, out float autoShotSpeed, out float autoShotsPerSecond, out float specialShotsPerSecond, out float totalShotsPerSecond, out float shotsPerSecondWithoutHawk, out RotationTest rotationTest) { #region Ranged Weapon Stats rangedWeaponDamage = 0; rangedWeaponSpeed = 0; if (character.Ranged != null) { rangedWeaponDamage = (character.Ranged.Item.MinDamage + character.Ranged.Item.MaxDamage) / 2f; rangedWeaponSpeed = character.Ranged.Item.Speed; } /* Projectiles/Ammo have been removed in Cata if (character.Projectile != null) { rangedAmmoDPS = (float)(character.Projectile.Item.MaxDamage + character.Projectile.Item.MinDamage) / 2f; } #endregion #region Static Haste Calcs // default quiver speed calculatedStats.hasteFromBase = 0.15f; // haste from haste rating calculatedStats.hasteFromRating = StatConversion.GetHasteFromRating(stats.HasteRating, character.Class); // haste buffs calculatedStats.hasteFromRangedBuffs = stats.RangedHaste; // total hastes calculatedStats.hasteStaticTotal = stats.PhysicalHaste; // Needed by the rotation test calculatedStats.autoShotStaticSpeed = rangedWeaponSpeed / (1f + calculatedStats.hasteStaticTotal); #endregion #region Rotation Test // Using the rotation test will get us better frequencies //RotationTest rotationTest = new RotationTest(character, calculatedStats, calcOpts, bossOpts); if (calcOpts.UseRotationTest) { // The following properties of CalculatedStats must be ready by this call: // * priorityRotation (shot order, durations, cooldowns) // * quickShotsEffect // * hasteStaticTotal // * autoShotStaticSpeed rotationTest.RunTest(); } #endregion #region Dynamic Haste Calcs /* http://elitistjerks.com/f74/t65904-hunter_dps_analyzer/p25/#post1887407 * 1) Base focus regen is 4.00. * 2) Pathing adds an additional 1% base focus regen per point (4.12 with 3/3 and no gear). * 3) WF/IT/HP and ISS don't modify base regen directly. * 4) Each 1% gear haste adds 2% base focus regen. * 5) Rapid Fire adds 40% base regen (4.00->5.60). * 6) Hero adds 30% base regen (4.00->5.20). * 7) Glyph of Rapid Fire adds 10% base regen (4.00->6.00). * 8) Focused Fire adds 15% base regen (4.00->4.60). // Now we have the haste, we can calculate steady shot cast time, // then rebuild other various stats. // (future enhancement: we only really need to rebuild steady shot) calculatedStats.steadyShot.Cd = 2f / (1f + calculatedStats.hasteStaticTotal); if (calcOpts.UseRotationTest) { calculatedStats.priorityRotation.initializeTimings(); calculatedStats.priorityRotation.recalculateRatios(); calculatedStats.priorityRotation.calculateFrequencySums(); } else { calculatedStats.priorityRotation.initializeTimings(); calculatedStats.priorityRotation.calculateFrequencies(); calculatedStats.priorityRotation.calculateFrequencySums(); } //float autoShotSpeed = rangedWeaponSpeed / (1f + calculatedStats.hasteStaticTotal); #endregion #region Shots Per Second float baseAutoShotsPerSecond = autoShotSpeed > 0 ? 1f / autoShotSpeed : 0; //float autoShotsPerSecond = baseAutoShotsPerSecond; //float specialShotsPerSecond = calculatedStats.priorityRotation.specialShotsPerSecond; //float totalShotsPerSecond = autoShotsPerSecond + specialShotsPerSecond; float crittingSpecialsPerSecond = calculatedStats.priorityRotation.critSpecialShotsPerSecond; float crittingShotsPerSecond = autoShotsPerSecond + crittingSpecialsPerSecond; //float shotsPerSecondWithoutHawk = specialShotsPerSecond + baseAutoShotsPerSecond; calculatedStats.BaseAttackSpeed = (float)autoShotSpeed; calculatedStats.shotsPerSecondCritting = crittingShotsPerSecond; #endregion } public float ConstrainCrit(float lvlDifMOD, float chance) { return Math.Min(1f + lvlDifMOD, Math.Max(0f, chance)); } */ 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 CharacterCalculationsHunter calc = new CharacterCalculationsHunter(); if (character == null) { return calc; } CalculationOptionsHunter calcOpts = character.CalculationOptions as CalculationOptionsHunter; if (calcOpts == null) { return calc; } // calc.character = character; calc.CalcOpts = calcOpts; BossOptions bossOpts = character.BossOptions; calc.BossOpts = bossOpts; HunterTalents talents = character.HunterTalents; Specialization hunterspec = GetSpecialization(talents); calc.Hunter = new HunterBase(character, this.GetCharacterStats(character, additionalItem, true), talents, hunterspec, bossOpts.Level); CombatFactors combatFactors = new CombatFactors(character, calc.Hunter.Stats, calcOpts, bossOpts); // WhiteAttacks whiteAttacks = new WhiteAttacks(character, calc.Hunter.Stats, combatFactors, calcOpts, bossOpts); Rotation Rot = new Rotation(combatFactors, talents); Rot.Initialize(calc); // Sets up the shots in the rotation and provides connection with the Calc object. calc.HunterDpsPoints = calc.CustomDPS; if (needsDisplayCalculations) { calc.HunterUnBuffed = new HunterBase(character, GetCharacterStats(character, additionalItem, false), talents, hunterspec, bossOpts.Level); } return calc; }
private ComparisonCalculationHunter comparisonFromStat(Character character, CharacterCalculationsHunter calcBase, Stats stats, string label) { ComparisonCalculationHunter comp = new ComparisonCalculationHunter(); CharacterCalculationsHunter calcStat = GetCharacterCalculations(character, new Item() { Stats = stats }) as CharacterCalculationsHunter; comp.Name = label; comp.HunterDpsPoints = calcStat.HunterDpsPoints - calcBase.HunterDpsPoints; comp.PetDpsPoints = calcStat.PetDpsPoints - calcBase.PetDpsPoints; comp.OverallPoints = calcStat.OverallPoints - calcBase.OverallPoints; return comp; }
/* public virtual List<ComparisonCalculationBase> GetPetBuffCalculations(Character character, CalculationOptionsHunter calcOpts, CharacterCalculationsBase currentCalcs, string filter) { //ClearCache(); List<ComparisonCalculationBase> buffCalcs = new List<ComparisonCalculationBase>(); CharacterCalculationsBase calcsOpposite = null; //CharacterCalculationsBase calcsEquipped = null; //CharacterCalculationsBase calcsUnequipped = null; Character charAutoActivated = character.Clone(); foreach (Buff autoBuff in currentCalcs.AutoActivatedBuffs) { if (!charAutoActivated.ActiveBuffs.Contains(autoBuff)) { charAutoActivated.ActiveBuffsAdd(autoBuff); RemoveConflictingBuffs(charAutoActivated.ActiveBuffs, autoBuff); } } charAutoActivated.DisableBuffAutoActivation = true; string[] multiFilter = filter.Split('|'); List<Buff> relevantBuffs = new List<Buff>(); foreach (Buff buff in RelevantPetBuffs) { bool isinMultiFilter = false; if (multiFilter.Length > 0) { foreach (string mFilter in multiFilter) { if (buff.Group.Equals(mFilter, StringComparison.CurrentCultureIgnoreCase)) { isinMultiFilter = true; break; } } } if (filter == null || filter == "All" || filter == "Current" || buff.Group.Equals(filter, StringComparison.CurrentCultureIgnoreCase) || isinMultiFilter) { relevantBuffs.Add(buff); relevantBuffs.AddRange(buff.Improvements); } } foreach (Buff buff in relevantBuffs) { if (!"Current".Equals(filter, StringComparison.CurrentCultureIgnoreCase) || (charAutoActivated.CalculationOptions as CalculationOptionsHunter).petActiveBuffs.Contains(buff)) { Character charOpposite = charAutoActivated.Clone(); //Character charUnequipped = charAutoActivated.Clone(); //Character charEquipped = charAutoActivated.Clone(); CalculationOptionsHunter calcOptsOpposite = charOpposite.CalculationOptions as CalculationOptionsHunter; //CalculationOptionsHunter calcOptsUnequipped = charUnequipped.CalculationOptions as CalculationOptionsHunter; //CalculationOptionsHunter calcOptsEquipped = charEquipped.CalculationOptions as CalculationOptionsHunter; bool which = (charAutoActivated.CalculationOptions as CalculationOptionsHunter).petActiveBuffs.Contains(buff); charOpposite.DisableBuffAutoActivation = true; //charUnequipped.DisableBuffAutoActivation = true; //charEquipped.DisableBuffAutoActivation = true; if (which) { calcOptsOpposite.petActiveBuffs.Remove(buff); } else { calcOptsOpposite.petActiveBuffs.Add(buff); } //if (calcOptsUnequipped.petActiveBuffs.Contains(buff)) { calcOptsUnequipped.petActiveBuffs.Remove(buff); } //if (!calcOptsEquipped.petActiveBuffs.Contains(buff)) { calcOptsEquipped.petActiveBuffs.Add(buff); } RemoveConflictingBuffs(calcOptsOpposite.petActiveBuffs, buff); //RemoveConflictingBuffs(calcOptsEquipped.petActiveBuffs, buff); //RemoveConflictingBuffs(calcOptsUnequipped.petActiveBuffs, buff); calcsOpposite = GetCharacterCalculations(charOpposite, null, false, false, false); //calcsUnequipped = GetCharacterCalculations(charUnequipped, null, false, false, false); //calcsEquipped = GetCharacterCalculations(charEquipped, null, false, false, false); ComparisonCalculationBase buffCalc = CreateNewComparisonCalculation(); buffCalc.Name = buff.Name; buffCalc.Item = new Item() { Name = buff.Name, Stats = buff.Stats, Quality = ItemQuality.Temp }; buffCalc.Equipped = which;//(charAutoActivated.CalculationOptions as CalculationOptionsHunter).petActiveBuffs.Contains(buff); buffCalc.OverallPoints = (which ? currentCalcs.OverallPoints - calcsOpposite.OverallPoints : calcsOpposite.OverallPoints - currentCalcs.OverallPoints); //buffCalc.OverallPoints = currentCalcs.OverallPoints - (buffCalc.Equipped ? calcsEquipped.OverallPoints : calcsUnequipped.OverallPoints); float[] subPoints = new float[calcsOpposite.SubPoints.Length]; //float[] subPoints = new float[calcsEquipped.SubPoints.Length]; for (int i = 0; i < calcsOpposite.SubPoints.Length; i++) { subPoints[i] = (which ? currentCalcs.SubPoints[i] - calcsOpposite.SubPoints[i] : calcsOpposite.SubPoints[i] - currentCalcs.SubPoints[i]); } //for (int i = 0; i < calcsEquipped.SubPoints.Length; i++) { subPoints[i] = calcsEquipped.SubPoints[i] - calcsUnequipped.SubPoints[i]; } buffCalc.SubPoints = subPoints; buffCalcs.Add(buffCalc); // Revert, cuz it's evil if (!which) { calcOptsOpposite.petActiveBuffs.Remove(buff); } else { calcOptsOpposite.petActiveBuffs.Add(buff); } } } return buffCalcs; }*/ private ComparisonCalculationHunter[] GetPetTalentChart(Character character, CharacterCalculationsHunter calcs) { List<ComparisonCalculationHunter> talentCalculations = new List<ComparisonCalculationHunter>(); Character baseChar = character.Clone(); CalculationOptionsHunter baseCalcOpts = baseChar.CalculationOptions as CalculationOptionsHunter; Character newChar = character.Clone(); CalculationOptionsHunter newCalcOpts = newChar.CalculationOptions as CalculationOptionsHunter; CharacterCalculationsHunter currentCalc; CharacterCalculationsHunter newCalc; ComparisonCalculationHunter compare; currentCalc = (CharacterCalculationsHunter)Calculations.GetCharacterCalculations(baseChar, null, false, true, false); foreach (PropertyInfo pi in baseCalcOpts.PetTalents.GetType().GetProperties()) { PetTalentDataAttribute[] petTalentDatas = pi.GetCustomAttributes(typeof(PetTalentDataAttribute), true) as PetTalentDataAttribute[]; int orig; if (petTalentDatas.Length > 0) { PetTalentDataAttribute petTalentData = petTalentDatas[0]; orig = baseCalcOpts.PetTalents.Data[petTalentData.Index]; if (petTalentData.MaxPoints == (int)pi.GetValue(baseCalcOpts.PetTalents, null)) { newCalcOpts.PetTalents.Data[petTalentData.Index]--; newCalc = (CharacterCalculationsHunter)GetCharacterCalculations(newChar, null, false, true, false); compare = (ComparisonCalculationHunter)GetCharacterComparisonCalculations(newCalc, currentCalc, petTalentData.Name, petTalentData.MaxPoints == orig, orig != 0 && orig != petTalentData.MaxPoints); } else { newCalcOpts.PetTalents.Data[petTalentData.Index]++; newCalc = (CharacterCalculationsHunter)GetCharacterCalculations(newChar, null, false, true, false); compare = (ComparisonCalculationHunter)GetCharacterComparisonCalculations(currentCalc, newCalc, petTalentData.Name, petTalentData.MaxPoints == orig, orig != 0 && orig != petTalentData.MaxPoints); } string text = string.Format("Current Rank {0}/{1}\r\n\r\n", orig, petTalentData.MaxPoints); if (orig == 0) { // We originally didn't have it, so first rank is next rank text += "Next Rank:\r\n"; text += petTalentData.Description[0]; } else if (orig >= petTalentData.MaxPoints) { // We originally were at max, so there isn't a next rank, just show the capped one text += petTalentData.Description[petTalentData.MaxPoints - 1]; } else { // We aren't at 0 or MaxPoints originally, so it's just a point in between text += petTalentData.Description[orig - 1]; text += "\r\n\r\nNext Rank:\r\n"; text += petTalentData.Description[orig]; } compare.Description = text; compare.Item = null; talentCalculations.Add(compare); newCalcOpts.PetTalents.Data[petTalentData.Index] = orig; } } return talentCalculations.ToArray(); }
public CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem, CompiledCalculationOptions calculationOptions, string armor, bool computeIncrementalSet) { CharacterCalculationsHunter calculatedStats = null; return(calculatedStats); }