/// <summary>Clones the stats object for duplication with separation</summary> public new StatsWarrior Clone() { StatsWarrior clone = (StatsWarrior)this.MemberwiseClone(); StatsWarrior retVal = new StatsWarrior(); // retVal.Accumulate(base.Clone()); // retVal._rawAdditiveWarriorData = (float[])clone._rawAdditiveWarriorData.Clone(); retVal._rawMultiplicativeWarriorData = (float[])clone._rawMultiplicativeWarriorData.Clone(); retVal._rawInverseMultiplicativeWarriorData = (float[])clone._rawInverseMultiplicativeWarriorData.Clone(); retVal._rawNoStackWarriorData = (float[])clone._rawNoStackWarriorData.Clone(); // return(retVal); }
public void Accumulate(StatsWarrior data, float weight) { #region Base if (data.sparseIndices != null) { int i = 0; for (int a = 0; a < data.sparseAdditiveCount; a++, i++) { int index = data.sparseIndices[i]; rawAdditiveData[index] += weight * data.rawAdditiveData[index]; } for (int a = 0; a < data.sparseMultiplicativeCount; a++, i++) { int index = data.sparseIndices[i]; rawMultiplicativeData[index] = (1 + rawMultiplicativeData[index]) * (1 + weight * data.rawMultiplicativeData[index]) - 1; } for (int a = 0; a < data.sparseInverseMultiplicativeCount; a++, i++) { int index = data.sparseIndices[i]; rawInverseMultiplicativeData[index] = 1 - (1 - rawInverseMultiplicativeData[index]) * (1 - weight * data.rawInverseMultiplicativeData[index]); } for (int a = 0; a < data.sparseNoStackCount; a++, i++) { int index = data.sparseIndices[i]; float value = weight * data.rawNoStackData[index]; if (value > rawNoStackData[index]) rawNoStackData[index] = value; } } else { float[] add = data.rawAdditiveData; for (int i = 0; i < rawAdditiveData.Length; i++) { rawAdditiveData[i] += weight * add[i]; } add = data.rawMultiplicativeData; for (int i = 0; i < rawMultiplicativeData.Length; i++) { rawMultiplicativeData[i] = (1 + rawMultiplicativeData[i]) * (1 + weight * add[i]) - 1; } add = data.rawInverseMultiplicativeData; for (int i = 0; i < rawInverseMultiplicativeData.Length; i++) { rawInverseMultiplicativeData[i] = 1 - (1 - rawInverseMultiplicativeData[i]) * (1 - weight * add[i]); } add = data.rawNoStackData; for (int i = 0; i < rawNoStackData.Length; i++) { if (weight * add[i] > rawNoStackData[i]) rawNoStackData[i] = weight * add[i]; } } #endregion #region Warrior if (data._sparseIndicesWarrior != null) { int i = 0; for (int a = 0; a < data._sparseAdditiveWarriorCount; a++, i++) { int index = data._sparseIndicesWarrior[i]; _rawAdditiveWarriorData[index] += weight * data._rawAdditiveWarriorData[index]; } for (int a = 0; a < data._sparseMultiplicativeWarriorCount; a++, i++) { int index = data._sparseIndicesWarrior[i]; _rawMultiplicativeWarriorData[index] = (1 + _rawMultiplicativeWarriorData[index]) * (1 + weight * data._rawMultiplicativeWarriorData[index]) - 1; } for (int a = 0; a < data._sparseInverseMultiplicativeWarriorCount; a++, i++) { int index = data._sparseIndicesWarrior[i]; _rawInverseMultiplicativeWarriorData[index] = 1 - (1 - _rawInverseMultiplicativeWarriorData[index]) * (1 - weight * data._rawInverseMultiplicativeWarriorData[index]); } for (int a = 0; a < data._sparseNoStackWarriorCount; a++, i++) { int index = data._sparseIndicesWarrior[i]; float value = weight * data._rawNoStackWarriorData[index]; if (value > _rawNoStackWarriorData[index]) _rawNoStackWarriorData[index] = value; } } else { float[] add = data._rawAdditiveWarriorData; for (int i = 0; i < _rawAdditiveWarriorData.Length; i++) { _rawAdditiveWarriorData[i] += weight * add[i]; } add = data._rawMultiplicativeWarriorData; for (int i = 0; i < _rawMultiplicativeWarriorData.Length; i++) { _rawMultiplicativeWarriorData[i] = (1 + _rawMultiplicativeWarriorData[i]) * (1 + weight * add[i]) - 1; } add = data._rawInverseMultiplicativeWarriorData; for (int i = 0; i < _rawInverseMultiplicativeWarriorData.Length; i++) { _rawInverseMultiplicativeWarriorData[i] = 1 - (1 - _rawInverseMultiplicativeWarriorData[i]) * (1 - weight * add[i]); } add = data._rawNoStackWarriorData; for (int i = 0; i < _rawNoStackWarriorData.Length; i++) { if (weight * add[i] > _rawNoStackWarriorData[i]) _rawNoStackWarriorData[i] = weight * add[i]; } } #endregion if (data._rawSpecialEffectDataSize > 0) { EnsureSpecialEffectCapacity(_rawSpecialEffectDataSize + data._rawSpecialEffectDataSize); Array.Copy(data._rawSpecialEffectData, 0, _rawSpecialEffectData, _rawSpecialEffectDataSize, data._rawSpecialEffectDataSize); _rawSpecialEffectDataSize += data._rawSpecialEffectDataSize; } }
/// <summary>Clones the stats object for duplication with separation</summary> public new StatsWarrior Clone() { StatsWarrior clone = (StatsWarrior)this.MemberwiseClone(); StatsWarrior retVal = new StatsWarrior(); // retVal.Accumulate(base.Clone()); // retVal._rawAdditiveWarriorData = (float[])clone._rawAdditiveWarriorData.Clone(); retVal._rawMultiplicativeWarriorData = (float[])clone._rawMultiplicativeWarriorData.Clone(); retVal._rawInverseMultiplicativeWarriorData = (float[])clone._rawInverseMultiplicativeWarriorData.Clone(); retVal._rawNoStackWarriorData = (float[])clone._rawNoStackWarriorData.Clone(); // return retVal; }
private StatsWarrior GetSpecialEffectStats(Player player) { StatsWarrior statsSpecialEffects = new StatsWarrior(); Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>(); Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>(); player.DefendModel = new DefendModel(player); player.AttackModel = new AttackModel(player, AttackModelMode.Optimal); float effectiveMasteryRating = Lookup.MaxEffectiveMasteryRating(player); float effectiveBuffedMasteryRating = effectiveMasteryRating * (1.0f - 10.0f / player.CalcOpts.ShieldBlockInterval) + Lookup.MaxEffectiveBuffedMasteryRating(player) * (10.0f / player.CalcOpts.ShieldBlockInterval); triggerIntervals[Trigger.Use] = 0.0f; triggerIntervals[Trigger.MeleeAttack] = player.AttackModel.WeaponAttacksPerSecond; triggerIntervals[Trigger.MeleeHit] = triggerIntervals[Trigger.MeleeAttack]; triggerIntervals[Trigger.MeleeCrit] = triggerIntervals[Trigger.MeleeAttack]; triggerIntervals[Trigger.PhysicalHit] = triggerIntervals[Trigger.MeleeAttack]; triggerIntervals[Trigger.PhysicalAttack] = triggerIntervals[Trigger.MeleeAttack]; triggerIntervals[Trigger.PhysicalCrit] = triggerIntervals[Trigger.MeleeAttack]; triggerIntervals[Trigger.ExecuteHit] = triggerIntervals[Trigger.MeleeAttack]; triggerIntervals[Trigger.DoTTick] = (player.Talents.DeepWounds > 0) ? 2.0f : 0.0f; triggerIntervals[Trigger.DamageDone] = triggerIntervals[Trigger.MeleeAttack] + triggerIntervals[Trigger.DoTTick]; triggerIntervals[Trigger.DamageOrHealingDone] = triggerIntervals[Trigger.DamageDone]; triggerIntervals[Trigger.DamageTaken] = 1.0f / player.DefendModel.AttackerSwingsPerSecond; triggerIntervals[Trigger.DamageAvoided] = triggerIntervals[Trigger.DamageTaken]; triggerIntervals[Trigger.DamageParried] = triggerIntervals[Trigger.DamageTaken]; triggerIntervals[Trigger.DamageTakenPutsMeBelow35PercHealth] = triggerIntervals[Trigger.DamageTaken]; triggerIntervals[Trigger.ShieldBlock] = 60f - (player.Talents.ShieldMastery * 10f); triggerChances[Trigger.Use] = 1.0f; triggerChances[Trigger.MeleeAttack] = 1.0f; triggerChances[Trigger.MeleeHit] = player.AttackModel.HitsPerSecond / player.AttackModel.WeaponAttacksPerSecond; triggerChances[Trigger.MeleeCrit] = player.AttackModel.CritsPerSecond / player.AttackModel.WeaponAttacksPerSecond; triggerChances[Trigger.PhysicalAttack] = 1.0f; triggerChances[Trigger.PhysicalHit] = triggerChances[Trigger.MeleeHit]; triggerChances[Trigger.PhysicalCrit] = triggerChances[Trigger.MeleeCrit]; triggerChances[Trigger.ExecuteHit] = triggerChances[Trigger.MeleeHit]; triggerChances[Trigger.DoTTick] = (player.Talents.DeepWounds > 0) ? 1.0f : 0.0f; triggerChances[Trigger.DamageDone] = (player.AttackModel.HitsPerSecond + ((player.Talents.DeepWounds > 0) ? 2.0f : 0.0f)) / (player.AttackModel.WeaponAttacksPerSecond + ((player.Talents.DeepWounds > 0) ? 1.0f : 0.0f)); triggerChances[Trigger.DamageOrHealingDone] = triggerChances[Trigger.DamageDone]; triggerChances[Trigger.DamageTaken] = player.DefendModel.AttackerHitsPerSecond / player.DefendModel.AttackerSwingsPerSecond; triggerChances[Trigger.DamageAvoided] = player.DefendModel.DefendTable.AnyAvoid; triggerChances[Trigger.DamageParried] = player.DefendModel.DefendTable.Parry; triggerChances[Trigger.DamageTakenPutsMeBelow35PercHealth] = triggerChances[Trigger.DamageTaken] * 0.35f; triggerChances[Trigger.ShieldBlock] = 1.0f; foreach (SpecialEffect effect in player.Stats.SpecialEffects()) { if (RelevantTriggers.Contains(effect.Trigger)) { // Effective Mastery Capping on Large Proc Effects if ((effect.Trigger == Trigger.Use && effect.Stats.MasteryRating > effectiveMasteryRating) || effect.Stats.MasteryRating > effectiveBuffedMasteryRating) { Stats cappedStats = new Stats(); cappedStats.Accumulate(effect.Stats); // Assume Use Effects Bypass Shield Block Collision if (effect.Trigger == Trigger.Use) cappedStats.MasteryRating = effectiveMasteryRating; else cappedStats.MasteryRating = effectiveBuffedMasteryRating; // calculate average up-time of this trinket float averageUpTime = 0.0f; if (effect.Trigger == Trigger.ExecuteHit) averageUpTime = effect.GetAverageFactor(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], player.AttackModel.WeaponSpeed, player.BossOpts.BerserkTimer * (float)player.BossOpts.Under20Perc); else averageUpTime = effect.GetAverageFactor(triggerIntervals[effect.Trigger], triggerChances[effect.Trigger], player.AttackModel.WeaponSpeed, player.BossOpts.BerserkTimer); // accumulate the capped stats from the trinket into our final stats statsSpecialEffects.Accumulate(cappedStats, averageUpTime); } else { if (effect.Trigger == Trigger.ExecuteHit) effect.AccumulateAverageStats(statsSpecialEffects, triggerIntervals, triggerChances, player.AttackModel.WeaponSpeed, player.BossOpts.BerserkTimer * (float)player.BossOpts.Under20Perc); else effect.AccumulateAverageStats(statsSpecialEffects, triggerIntervals, triggerChances, player.AttackModel.WeaponSpeed, player.BossOpts.BerserkTimer); } } } // Base Stats statsSpecialEffects.Stamina = (float)Math.Floor(statsSpecialEffects.Stamina * (1.0f + player.Stats.BonusStaminaMultiplier)); statsSpecialEffects.Strength = (float)Math.Floor(statsSpecialEffects.Strength * (1.0f + player.Stats.BonusStrengthMultiplier)); statsSpecialEffects.Agility = (float)Math.Floor(statsSpecialEffects.Agility * (1.0f + player.Stats.BonusAgilityMultiplier)); return statsSpecialEffects; }
private void AccumulateCharacterStats(Player player, Item additionalItem) { float baseStrength, baseBonusMasteryBlockPercentage, baseParryRatingFromStrength; // Items and Buffs StatsWarrior statsItemsBuffs = new StatsWarrior(); AccumulateItemStats(statsItemsBuffs, player.Character, additionalItem); statsItemsBuffs.Accumulate(GetBuffsStats(player)); // AccumulateBuffsStats(statsItemsBuffs, player.Character.ActiveBuffs); player.Stats.Accumulate(statsItemsBuffs); // Race Stats StatsWarrior statsRace = new StatsWarrior(); statsRace.Accumulate(BaseStats.GetBaseStats(player.Character.Level, CharacterClass.Warrior, player.Character.Race)); statsRace.Expertise += BaseStats.GetRacialExpertise(player.Character, ItemSlot.MainHand); player.Stats.Accumulate(statsRace); // Talents StatsWarrior statsTalents = new StatsWarrior() { Block = 0.15f, // Sentinel BonusStaminaMultiplier = (1.0f + 0.15f) * (1.0f + (Character.ValidateArmorSpecialization(player.Character, ItemType.Plate) ? 0.05f : 0.0f)) - 1.0f, // Sentinel & Plate Specialization BaseArmorMultiplier = player.Talents.Toughness * 0.03f, }; if (player.Talents.HoldTheLine > 0) statsTalents.AddSpecialEffect(_SE_HoldTheLine[player.Talents.HoldTheLine]); if (player.Talents.BastionOfDefense > 0) statsTalents.AddSpecialEffect(_SE_BastionOfDefense[player.Talents.BastionOfDefense]); player.Stats.Accumulate(statsTalents); // Base Stats player.Stats.BaseAgility = statsRace.Agility; player.Stats.Stamina = (float)Math.Floor((statsRace.Stamina + statsTalents.Stamina) * (1.0f + player.Stats.BonusStaminaMultiplier)); player.Stats.Stamina += (float)Math.Floor(statsItemsBuffs.Stamina * (1.0f + player.Stats.BonusStaminaMultiplier)); player.Stats.Strength = baseStrength = (float)Math.Floor((statsRace.Strength + statsTalents.Strength) * (1.0f + player.Stats.BonusStrengthMultiplier)); player.Stats.Strength += (float)Math.Floor(statsItemsBuffs.Strength * (1.0f + player.Stats.BonusStrengthMultiplier)); player.Stats.Agility = (float)Math.Floor((statsRace.Agility + statsTalents.Agility) * (1.0f + player.Stats.BonusAgilityMultiplier)); player.Stats.Agility += (float)Math.Floor(statsItemsBuffs.Agility * (1.0f + player.Stats.BonusAgilityMultiplier)); // First-Pass Avoidance for Accurate Special Effect Trigger Chances baseBonusMasteryBlockPercentage = Lookup.BonusMasteryBlockPercentage(player); baseParryRatingFromStrength = (player.Stats.Strength - baseStrength) * 0.27f; // Parry Rating conversion ignores base Strength player.Stats.Block += baseBonusMasteryBlockPercentage; player.Stats.ParryRating += baseParryRatingFromStrength; // Calculate Procs and Special Effects player.Stats.Accumulate(GetSpecialEffectStats(player)); // Highest Stat Effects if (player.Stats.Strength > player.Stats.Agility) player.Stats.Strength += (float)Math.Floor((player.Stats.HighestStat + player.Stats.Paragon) * (1.0f + player.Stats.BonusStrengthMultiplier)); else player.Stats.Agility += (float)Math.Floor((player.Stats.HighestStat + player.Stats.Paragon) * (1.0f + player.Stats.BonusAgilityMultiplier)); if (player.Stats.HighestSecondaryStat > 0) { float paragonValue = player.Stats.HighestSecondaryStat; // how much paragon to add player.Stats.HighestSecondaryStat = 0f; // remove Paragon stat, since it's not needed if (player.Stats.CritRating > player.Stats.HasteRating && player.Stats.CritRating > player.Stats.MasteryRating) player.Stats.CritRating += paragonValue; else if (player.Stats.HasteRating > player.Stats.CritRating && player.Stats.HasteRating > player.Stats.MasteryRating) player.Stats.HasteRating += paragonValue; else player.Stats.MasteryRating += paragonValue; } // Defensive Stats, Add Difference of First-Pass and Second-Pass Block/Parry player.Stats.Armor = (float)Math.Ceiling(player.Stats.Armor * (1.0f + player.Stats.BaseArmorMultiplier)); player.Stats.Armor += (float)Math.Ceiling(player.Stats.BonusArmor * (1.0f + player.Stats.BonusArmorMultiplier)); player.Stats.Block += (Lookup.BonusMasteryBlockPercentage(player) - baseBonusMasteryBlockPercentage); player.Stats.ParryRating += (((player.Stats.Strength - baseStrength) * 0.27f) - baseParryRatingFromStrength); // Parry Rating conversion ignores base Strength player.Stats.NatureResistance += player.Stats.NatureResistanceBuff; player.Stats.FireResistance += player.Stats.FireResistanceBuff; player.Stats.FrostResistance += player.Stats.FrostResistanceBuff; player.Stats.ShadowResistance += player.Stats.ShadowResistanceBuff; player.Stats.ArcaneResistance += player.Stats.ArcaneResistanceBuff; // Final Derived Stats player.Stats.Health += StatConversion.GetHealthFromStamina(player.Stats.Stamina, CharacterClass.Warrior) + player.Stats.BattlemasterHealthProc; player.Stats.Health = (float)Math.Floor(player.Stats.Health * (1.0f + player.Stats.BonusHealthMultiplier)); player.Stats.AttackPower += player.Stats.Strength * 2.0f + (player.Stats.Health * 0.1f * player.CalcOpts.AverageVengeance); player.Stats.AttackPower = (float)Math.Floor(player.Stats.AttackPower * (1.0f + player.Stats.BonusAttackPowerMultiplier)); }
public void Accumulate(StatsWarrior data, float weight) { #region Base if (data.sparseIndices != null) { int i = 0; for (int a = 0; a < data.sparseAdditiveCount; a++, i++) { int index = data.sparseIndices[i]; rawAdditiveData[index] += weight * data.rawAdditiveData[index]; } for (int a = 0; a < data.sparseMultiplicativeCount; a++, i++) { int index = data.sparseIndices[i]; rawMultiplicativeData[index] = (1 + rawMultiplicativeData[index]) * (1 + weight * data.rawMultiplicativeData[index]) - 1; } for (int a = 0; a < data.sparseInverseMultiplicativeCount; a++, i++) { int index = data.sparseIndices[i]; rawInverseMultiplicativeData[index] = 1 - (1 - rawInverseMultiplicativeData[index]) * (1 - weight * data.rawInverseMultiplicativeData[index]); } for (int a = 0; a < data.sparseNoStackCount; a++, i++) { int index = data.sparseIndices[i]; float value = weight * data.rawNoStackData[index]; if (value > rawNoStackData[index]) { rawNoStackData[index] = value; } } } else { float[] add = data.rawAdditiveData; for (int i = 0; i < rawAdditiveData.Length; i++) { rawAdditiveData[i] += weight * add[i]; } add = data.rawMultiplicativeData; for (int i = 0; i < rawMultiplicativeData.Length; i++) { rawMultiplicativeData[i] = (1 + rawMultiplicativeData[i]) * (1 + weight * add[i]) - 1; } add = data.rawInverseMultiplicativeData; for (int i = 0; i < rawInverseMultiplicativeData.Length; i++) { rawInverseMultiplicativeData[i] = 1 - (1 - rawInverseMultiplicativeData[i]) * (1 - weight * add[i]); } add = data.rawNoStackData; for (int i = 0; i < rawNoStackData.Length; i++) { if (weight * add[i] > rawNoStackData[i]) { rawNoStackData[i] = weight * add[i]; } } } #endregion #region Warrior if (data._sparseIndicesWarrior != null) { int i = 0; for (int a = 0; a < data._sparseAdditiveWarriorCount; a++, i++) { int index = data._sparseIndicesWarrior[i]; _rawAdditiveWarriorData[index] += weight * data._rawAdditiveWarriorData[index]; } for (int a = 0; a < data._sparseMultiplicativeWarriorCount; a++, i++) { int index = data._sparseIndicesWarrior[i]; _rawMultiplicativeWarriorData[index] = (1 + _rawMultiplicativeWarriorData[index]) * (1 + weight * data._rawMultiplicativeWarriorData[index]) - 1; } for (int a = 0; a < data._sparseInverseMultiplicativeWarriorCount; a++, i++) { int index = data._sparseIndicesWarrior[i]; _rawInverseMultiplicativeWarriorData[index] = 1 - (1 - _rawInverseMultiplicativeWarriorData[index]) * (1 - weight * data._rawInverseMultiplicativeWarriorData[index]); } for (int a = 0; a < data._sparseNoStackWarriorCount; a++, i++) { int index = data._sparseIndicesWarrior[i]; float value = weight * data._rawNoStackWarriorData[index]; if (value > _rawNoStackWarriorData[index]) { _rawNoStackWarriorData[index] = value; } } } else { float[] add = data._rawAdditiveWarriorData; for (int i = 0; i < _rawAdditiveWarriorData.Length; i++) { _rawAdditiveWarriorData[i] += weight * add[i]; } add = data._rawMultiplicativeWarriorData; for (int i = 0; i < _rawMultiplicativeWarriorData.Length; i++) { _rawMultiplicativeWarriorData[i] = (1 + _rawMultiplicativeWarriorData[i]) * (1 + weight * add[i]) - 1; } add = data._rawInverseMultiplicativeWarriorData; for (int i = 0; i < _rawInverseMultiplicativeWarriorData.Length; i++) { _rawInverseMultiplicativeWarriorData[i] = 1 - (1 - _rawInverseMultiplicativeWarriorData[i]) * (1 - weight * add[i]); } add = data._rawNoStackWarriorData; for (int i = 0; i < _rawNoStackWarriorData.Length; i++) { if (weight * add[i] > _rawNoStackWarriorData[i]) { _rawNoStackWarriorData[i] = weight * add[i]; } } } #endregion if (data._rawSpecialEffectDataSize > 0) { EnsureSpecialEffectCapacity(_rawSpecialEffectDataSize + data._rawSpecialEffectDataSize); Array.Copy(data._rawSpecialEffectData, 0, _rawSpecialEffectData, _rawSpecialEffectDataSize, data._rawSpecialEffectDataSize); _rawSpecialEffectDataSize += data._rawSpecialEffectDataSize; } }