public TalentsBase TalentSpec() { if (Spec == null) { return(null); } TalentsBase spec; if (Class == CharacterClass.DeathKnight) { spec = new DeathKnightTalents(Spec); } else if (Class == CharacterClass.Warrior) { spec = new WarriorTalents(Spec); } else if (Class == CharacterClass.Paladin) { spec = new PaladinTalents(Spec); } else if (Class == CharacterClass.Shaman) { spec = new ShamanTalents(Spec); } else if (Class == CharacterClass.Hunter) { spec = new HunterTalents(Spec); } else if (Class == CharacterClass.Rogue) { spec = new RogueTalents(Spec); } else if (Class == CharacterClass.Druid) { spec = new DruidTalents(Spec); } else if (Class == CharacterClass.Warlock) { spec = new WarlockTalents(Spec); } else if (Class == CharacterClass.Priest) { spec = new PriestTalents(Spec); } else { spec = new MageTalents(Spec); } return(spec); }
public static bool HasTalent( MageTalents tal) { return TalentManager.IsSelected((int)tal); }
public static bool HasTalent(MageTalents tal) { return(TalentManager.IsSelected((int)tal)); }
public virtual void CalculateDerivedStats(CastingState castingState, bool outOfFiveSecondRule, bool pom, bool spammedDot, bool round, bool forceHit, bool forceMiss, bool dotUptime) { MageTalents mageTalents = castingState.MageTalents; Stats baseStats = castingState.BaseStats; CalculationOptionsMage calculationOptions = castingState.CalculationOptions; if (AreaEffect) { // do not count debuffs for aoe effects, can't assume it will be up on all // do not include molten fury (molten fury relates to boss), instead amplify all by average if (castingState.MoltenFury) { SpellModifier /= (1 + 0.04f * castingState.MageTalents.MoltenFury); } if (castingState.MageTalents.MoltenFury > 0) { SpellModifier *= (1 + 0.04f * castingState.MageTalents.MoltenFury * castingState.CalculationOptions.MoltenFuryPercentage); } } SpellModifier *= AdditiveSpellModifier; if (CritRate < 0.0f) { CritRate = 0.0f; } if (CritRate > 1.0f) { CritRate = 1.0f; } Ticks = template.Ticks; CastProcs = template.CastProcs; HitProcs = Ticks * HitRate; if (AreaEffect) { TargetProcs = HitProcs * castingState.CalculationOptions.AoeTargets; } else { TargetProcs = HitProcs; } Pom = pom; if (Instant) { InterruptProtection = 1; } CastTime = template.CalculateCastTime(castingState, InterruptProtection, CritRate, pom, BaseCastTime, out ChannelReduction); // add crit rate for on use stacking crit effects (would be better if it was computed // on cycle level, but right now the architecture doesn't allow that too well) // we'd actually need some iterations of this as cast time can depend on crit etc, just ignore that for now for (int i = 0; i < castingState.Solver.StackingNonHasteEffectCooldownsCount; i++) { EffectCooldown effectCooldown = castingState.Solver.StackingNonHasteEffectCooldowns[i]; if (castingState.EffectsActive(effectCooldown.Mask)) { Stats stats = effectCooldown.SpecialEffect.Stats; for (int j = 0; j < stats._rawSpecialEffectDataSize; j++) { SpecialEffect effect = stats._rawSpecialEffectData[j]; if (effect.Chance == 1f && effect.Cooldown == 0f && (effect.Trigger == Trigger.DamageSpellCrit || effect.Trigger == Trigger.SpellCrit)) { if (effect.Stats.CritRating < 0 && effectCooldown.SpecialEffect.Stats.CritRating > 0) { float critScale = castingState.CalculationOptions.LevelScalingFactor / 1400f; CritRate += SpecialEffect.GetAverageStackingCritRate(CastTime, effectCooldown.SpecialEffect.Duration, HitRate, CritRate, effectCooldown.SpecialEffect.Stats.CritRating * critScale, effect.Stats.CritRating * critScale, effect.MaxStack); if (CritRate > 1.0f) { CritRate = 1.0f; } } } } } } if (castingState.Frozen) { CritRate *= (1 + castingState.MageTalents.Shatter); if (CritRate > 1.0f) { CritRate = 1.0f; } } CritProcs = HitProcs * CritRate; if ((MagicSchool == MagicSchool.Fire || MagicSchool == MagicSchool.FrostFire) && mageTalents.Ignite > 0) { IgniteProcs = CritProcs; } else { IgniteProcs = 0; } if (DotTickInterval > 0) { // non-spammed we have to take into account haste on dot duration and increase in number of ticks // probably don't want to take into account haste procs as that might skew optimization thresholds as we're averaging cast time over procs // reevaluate this if needed float x = DotTickInterval / DotDuration; DotExtraTicks = (float)Math.Floor((castingState.CastingSpeed - 1) / x + 0.5); DotFullDuration = (DotDuration + DotTickInterval * DotExtraTicks) / castingState.CastingSpeed; if (spammedDot) { // spammed dots no longer clip on reapplication DotProcs = Math.Min(CastTime, DotDuration) / DotTickInterval; } else { DotProcs = DotDuration / DotTickInterval + DotExtraTicks; } } else { DotProcs = 0; DotFullDuration = 0; DotExtraTicks = 0; } SpammedDot = spammedDot; if ((BaseMinDamage > 0 || BasePeriodicDamage > 0) && !forceMiss) { if (dotUptime) { CalculateDirectAverageDamage(castingState.Solver, RawSpellDamage, forceHit); AverageThreat = AverageDamage * ThreatMultiplier; CalculateDotAverageDamage(castingState.Solver, RawSpellDamage, forceHit); DotAverageThreat = DotAverageDamage * ThreatMultiplier; } else { CalculateAverageDamage(castingState.Solver, RawSpellDamage, spammedDot, forceHit); AverageThreat = AverageDamage * ThreatMultiplier; } } else { AverageDamage = 0; AverageThreat = 0; DamagePerSpellPower = 0; DamagePerMastery = 0; DamagePerCrit = 0; IgniteDamage = 0; IgniteDamagePerSpellPower = 0; IgniteDamagePerMastery = 0; IgniteDamagePerCrit = 0; if (dotUptime) { DotAverageDamage = 0; DotAverageThreat = 0; DotDamagePerSpellPower = 0; DotDamagePerMastery = 0; DotDamagePerCrit = 0; } } if (ChannelReduction != 0) { Ticks *= (1 - ChannelReduction); HitProcs *= (1 - ChannelReduction); CritProcs *= (1 - ChannelReduction); TargetProcs *= (1 - ChannelReduction); CastProcs = CastProcs2 + (CastProcs - CastProcs2) * (1 - ChannelReduction); AverageDamage *= (1 - ChannelReduction); AverageThreat *= (1 - ChannelReduction); DamagePerSpellPower *= (1 - ChannelReduction); DamagePerMastery *= (1 - ChannelReduction); DamagePerCrit *= (1 - ChannelReduction); } AverageCost = CalculateCost(castingState.Solver, round); Absorb = 0; TotalAbsorb = 0; if (outOfFiveSecondRule) { OO5SR = 1; } else { OO5SR = 0; } }
public virtual void CalculateDerivedStats(CastingState castingState, bool outOfFiveSecondRule, bool pom, bool spammedDot, bool round, bool forceHit, bool forceMiss, bool dotUptime) { MageTalents mageTalents = castingState.MageTalents; Stats baseStats = castingState.BaseStats; CalculationOptionsMage calculationOptions = castingState.CalculationOptions; SpellModifier *= AdditiveSpellModifier; if (CritRate < 0.0f) { CritRate = 0.0f; } if (CritRate > 1.0f) { CritRate = 1.0f; } Ticks = template.Ticks; CastProcs = template.CastProcs; HitProcs = Ticks * HitRate; CritProcs = HitProcs * CritRate; if ((MagicSchool == MagicSchool.Fire || MagicSchool == MagicSchool.FrostFire) && mageTalents.Ignite > 0) { IgniteProcs = CritProcs; } else { IgniteProcs = 0; } TargetProcs = HitProcs; Pom = pom; if (Instant) { InterruptProtection = 1; } CastTime = template.CalculateCastTime(castingState, InterruptProtection, CritRate, pom, BaseCastTime, out ChannelReduction); // add crit rate for on use stacking crit effects (would be better if it was computed // on cycle level, but right now the architecture doesn't allow that too well) // we'd actually need some iterations of this as cast time can depend on crit etc, just ignore that for now for (int i = 0; i < castingState.Solver.StackingNonHasteEffectCooldownsCount; i++) { EffectCooldown effectCooldown = castingState.Solver.StackingNonHasteEffectCooldowns[i]; if (castingState.EffectsActive(effectCooldown.Mask)) { Stats stats = effectCooldown.SpecialEffect.Stats; for (int j = 0; j < stats._rawSpecialEffectDataSize; j++) { SpecialEffect effect = stats._rawSpecialEffectData[j]; if (effect.Chance == 1f && effect.Cooldown == 0f && (effect.Trigger == Trigger.DamageSpellCrit || effect.Trigger == Trigger.SpellCrit)) { if (effect.Stats.CritRating < 0 && effectCooldown.SpecialEffect.Stats.CritRating > 0) { float critScale = castingState.CalculationOptions.LevelScalingFactor / 1400f; CritRate += SpecialEffect.GetAverageStackingCritRate(CastTime, effectCooldown.SpecialEffect.Duration, HitRate, CritRate, effectCooldown.SpecialEffect.Stats.CritRating * critScale, effect.Stats.CritRating * critScale, effect.MaxStack); if (CritRate > 1.0f) { CritRate = 1.0f; } } } } } } if (DotTickInterval > 0) { if (spammedDot) { DotProcs = (float)Math.Floor(Math.Min(CastTime, DotDuration) / DotTickInterval); } else { DotProcs = DotDuration / DotTickInterval; } } else { DotProcs = 0; } SpammedDot = spammedDot; if (Ticks > 0 && !forceMiss) { if (dotUptime) { AverageDamage = CalculateDirectAverageDamage(castingState.Solver, RawSpellDamage, forceHit, out DamagePerSpellPower, out IgniteDamage, out IgniteDamagePerSpellPower); AverageThreat = AverageDamage * ThreatMultiplier; DotAverageDamage = CalculateDotAverageDamage(baseStats, calculationOptions, RawSpellDamage, forceHit, out DotDamagePerSpellPower); DotAverageThreat = DotAverageDamage * ThreatMultiplier; } else { AverageDamage = CalculateAverageDamage(castingState.Solver, RawSpellDamage, spammedDot, forceHit, out DamagePerSpellPower, out IgniteDamage, out IgniteDamagePerSpellPower); AverageThreat = AverageDamage * ThreatMultiplier; } } else { AverageDamage = 0; AverageThreat = 0; DamagePerSpellPower = 0; IgniteDamage = 0; IgniteDamagePerSpellPower = 0; if (dotUptime) { DotAverageDamage = 0; DotAverageThreat = 0; DotDamagePerSpellPower = 0; } } if (ChannelReduction != 0) { Ticks *= (1 - ChannelReduction); HitProcs *= (1 - ChannelReduction); CritProcs *= (1 - ChannelReduction); TargetProcs *= (1 - ChannelReduction); CastProcs = CastProcs2 + (CastProcs - CastProcs2) * (1 - ChannelReduction); AverageDamage *= (1 - ChannelReduction); AverageThreat *= (1 - ChannelReduction); DamagePerSpellPower *= (1 - ChannelReduction); } AverageCost = CalculateCost(castingState.Solver, round); Absorb = 0; TotalAbsorb = 0; if (outOfFiveSecondRule) { OO5SR = 1; } else { OO5SR = 0; } }
TalentsBase parse_talents_wowhead(CharacterClass characterclass, string talent_string) { // wowhead format: [tree_1]Z[tree_2]Z[tree_3] where the trees are character encodings // each character expands to a pair of numbers [0-5][0-5] // unused deeper talents are simply left blank instead of filling up the string with zero-zero encodings bool hasGlyphs = talent_string.Contains(":"); int[] talent_trees = new int[] { 0, 0, 0 }; int[] glyph_trees = new int[] { 3, 3, 3 }; switch (characterclass) { case CharacterClass.Warrior: { WarriorTalents talents = new WarriorTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Paladin: { PaladinTalents talents = new PaladinTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Hunter: { HunterTalents talents = new HunterTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Rogue: { RogueTalents talents = new RogueTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Priest: { PriestTalents talents = new PriestTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.DeathKnight: { DeathKnightTalents talents = new DeathKnightTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Shaman: { ShamanTalents talents = new ShamanTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Mage: { MageTalents talents = new MageTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Warlock: { WarlockTalents talents = new WarlockTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Druid: { DruidTalents talents = new DruidTalents(); talent_trees = talents.TreeLengths; break; } } int[] encoding = new int[talent_trees[0] + talent_trees[1] + talent_trees[2]]; int[][] glyphEncoding = new int[][] { new int[3], new int[3], new int[3], }; int[] tree_count = new int[] { 0, 0, 0 }; int[] glyph_count = new int[] { 0, 0, 0 }; int tree = 0; int count = 0; #region Talents parsing for (int i = 1; i < talent_string.Length; i++) { if (tree >= 3) { //sim -> errorf( "Player %s has malformed wowhead talent string. Too many talent trees specified.\n", name() ); return(null); } char c = talent_string[i]; if (c == ':') { break; // glyph encoding follows } if (c == 'Z') { count = 0; for (int j = 0; j <= tree; j++) { count += talent_trees[tree]; } tree++; continue; } decode_t decode = null; for (int j = 0; decoding[j].key != '\0' && decode == null; j++) { if (decoding[j].key == c) { decode = decoding[j]; } } if (decode == null) { //sim -> errorf( "Player %s has malformed wowhead talent string. Translation for '%c' unknown.\n", name(), c ); return(null); } encoding[count++] += decode.first - '0'; tree_count[tree] += 1; if (tree_count[tree] < talent_trees[tree]) { encoding[count++] += decode.second - '0'; tree_count[tree] += 1; } if (tree_count[tree] >= talent_trees[tree]) { tree++; } } #endregion #region Glyphs Parsing #region Notes /* This is totally crappy.... * Glyphs do not follow the same parsing rules. If you apply what was there for talents directly * to glyphs you get 1202032213120011050000000000000000. Which should only have 1's and 0's * * * Warriors: As I'm checking glyphs, here's what I get: * == PRIMES == * Link decode id Name * * http://www.wowhead.com/talent#L:0 00 43415 58388 Devastate * * http://www.wowhead.com/talent#L:z 01 43416 58367 Bloodthirst * * http://www.wowhead.com/talent#L:M 02 43421 58368 Mortal Strike * * http://www.wowhead.com/talent#L:c 03 43422 58386 Overpower * * http://www.wowhead.com/talent#L:m 04 43423 58385 Slam * * http://www.wowhead.com/talent#L:V 05 43424 58364 Revenge * * http://www.wowhead.com/talent#L:o 10 43425 58375 Shield Slam * * http://www.wowhead.com/talent#L:k 11 43432 58370 Raging Blow * * http://www.wowhead.com/talent#L:R 12 45790 63324 Bladestorm * == MAJORS == * * http://www.wowhead.com/talent#L:0 00 43397 Long Charge * * http://www.wowhead.com/talent#L:z 01 43399 Thunder Clap * * http://www.wowhead.com/talent#L:M 02 43413 Rapid Charge * * http://www.wowhead.com/talent#L:c 03 43414 Cleaving * * http://www.wowhead.com/talent#L:m 04 43417 Piercing Howl * * http://www.wowhead.com/talent#L:V 05 43418 Heroic Throw * * http://www.wowhead.com/talent#L:o 10 43419 Intervene * * http://www.wowhead.com/talent#L:k 11 43427 Sunder Armor * * http://www.wowhead.com/talent#L:R 12 43428 Sweeping Strikes * * http://www.wowhead.com/talent#L:s 13 43430 Resonating Power * * http://www.wowhead.com/talent#L:a 14 43431 Victory Rush * * http://www.wowhead.com/talent#L:q 15 45792 Shockwave * * http://www.wowhead.com/talent#L:b 20 45795 Spell Reflection * * http://www.wowhead.com/talent#L:d 21 45797 Shield Wall * * http://www.wowhead.com/talent#L:r 22 63481 Colossus Smash * * http://www.wowhead.com/talent#L:f 23 67482 Intercept * * http://www.wowhead.com/talent#L:w 24 67483 Death Wish * == MINORS == * * http://www.wowhead.com/talent#L:0 00 43395 Battle * * http://www.wowhead.com/talent#L:z 01 43396 Berserker Rage * * http://www.wowhead.com/talent#L:M 02 43398 Demoralizing Shout * * http://www.wowhead.com/talent#L:c 03 43400 Enduring Victory * * http://www.wowhead.com/talent#L:m 04 43412 Bloody Healing * * http://www.wowhead.com/talent#L:V 05 45793 Furious Sundering * * http://www.wowhead.com/talent#L:o 10 45794 Intimidating Shout * * http://www.wowhead.com/talent#L:k 11 49084 Command * * So http://www.wowhead.com/talent#LubcfRMRurkcrZ0b:RMcrsR0kV would mean: * Prime: Bladestorm, Mortal Strike, Overpower * Major: Colossus Smash, Resonating Power, Sweeping Strikes * Minor: Battle, Command, Furious Sundering * Which is correct, that's what we come out to */ #endregion tree = 0; count = 0; if (hasGlyphs) { for (int i = talent_string.IndexOf(":") + 1; i < talent_string.Length; i++) { if (tree >= 3) { //sim -> errorf( "Player %s has malformed wowhead talent string. Too many talent trees specified.\n", name() ); return(null); } char c = talent_string[i]; if (c == 'Z') { count = 0; /*for (int j = 0; j <= tree; j++) { * count += glyph_trees[tree]; * }*/ tree++; continue; } decode_t decode = null; for (int j = 0; decoding[j].key != '\0' && decode == null; j++) { if (decoding[j].key == c) { decode = decoding[j]; } } if (decode == null) { //sim -> errorf( "Player %s has malformed wowhead talent string. Translation for '%c' unknown.\n", name(), c ); return(null); } glyphEncoding[tree][count++] += (decode.first - '0') * 10 + decode.second - '0'; glyph_count[tree] += 1; if (glyph_count[tree] >= (glyph_trees[tree])) { tree++; count = 0; } } } #endregion string newtalentstring = ""; foreach (int i in encoding) { newtalentstring += i.ToString(); } if (hasGlyphs) { //newtalentstring += "."; //for (int t = 0; t < 3; t++) { // foreach (int i in glyphEncoding[t]) { newtalentstring += i.ToString(); } //} } switch (characterclass) { case CharacterClass.Warrior: { return(new WarriorTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); } case CharacterClass.Paladin: { return(new PaladinTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); } case CharacterClass.Hunter: { return(new HunterTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); } case CharacterClass.Rogue: { return(new RogueTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); } case CharacterClass.Priest: { return(new PriestTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); } case CharacterClass.DeathKnight: { return(new DeathKnightTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); } case CharacterClass.Shaman: { return(new ShamanTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); } case CharacterClass.Mage: { return(new MageTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); } case CharacterClass.Warlock: { return(new WarlockTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); } case CharacterClass.Druid: { return(new DruidTalents(newtalentstring, hasGlyphs ? glyphEncoding : null)); } } return(null); }
public void InitializeEffectDamage(Solver solver, MagicSchool magicSchool, float minDamage, float maxDamage) { Stats baseStats = solver.BaseStats; MageTalents mageTalents = solver.MageTalents; CalculationOptionsMage calculationOptions = solver.CalculationOptions; //AreaEffect = areaEffect; //BaseCost = cost - (int)baseStats.SpellsManaReduction; MagicSchool = magicSchool; BaseMinDamage = minDamage; BaseMaxDamage = maxDamage; //BasePeriodicDamage = periodicDamage; //SpellDamageCoefficient = spellDamageCoefficient; //Ticks = 1; //CastProcs = 0; //CastProcs2 = 0; //DotDamageCoefficient = dotDamageCoefficient; //DotDuration = dotDuration; BaseDirectDamageModifier = 1.0f; BaseDotDamageModifier = 1.0f; BaseCostModifier = 1.0f; //Range = range; /*float baseCostAmplifier = calculationOptions.EffectCostMultiplier; * baseCostAmplifier *= (1.0f - 0.01f * mageTalents.Precision); * if (mageTalents.FrostChanneling > 0) baseCostAmplifier *= (1.0f - 0.01f - 0.03f * mageTalents.FrostChanneling); * if (MagicSchool == MagicSchool.Arcane) baseCostAmplifier *= (1.0f - 0.01f * mageTalents.ArcaneFocus); * BaseCostAmplifier = baseCostAmplifier;*/ /*float baseInterruptProtection = baseStats.InterruptProtection; * if (MagicSchool == MagicSchool.Fire || MagicSchool == MagicSchool.FrostFire) * { * baseInterruptProtection += 0.35f * mageTalents.BurningSoul; * AffectedByFlameCap = true; * } * BaseInterruptProtection = baseInterruptProtection;*/ float realResistance; switch (MagicSchool) { case MagicSchool.Arcane: BaseSpellModifier = solver.BaseArcaneSpellModifier; BaseAdditiveSpellModifier = solver.BaseArcaneAdditiveSpellModifier; BaseCritRate = solver.BaseArcaneCritRate; CritBonus = solver.BaseArcaneCritBonus; HitRate = solver.BaseArcaneHitRate; ThreatMultiplier = solver.ArcaneThreatMultiplier; realResistance = calculationOptions.ArcaneResist; break; case MagicSchool.Fire: BaseSpellModifier = solver.BaseFireSpellModifier; BaseAdditiveSpellModifier = solver.BaseFireAdditiveSpellModifier; BaseCritRate = solver.BaseFireCritRate; CritBonus = solver.BaseFireCritBonus; HitRate = solver.BaseFireHitRate; ThreatMultiplier = solver.FireThreatMultiplier; realResistance = calculationOptions.FireResist; break; case MagicSchool.FrostFire: BaseSpellModifier = solver.BaseFrostFireSpellModifier; BaseAdditiveSpellModifier = solver.BaseFrostFireAdditiveSpellModifier; BaseCritRate = solver.BaseFrostFireCritRate; CritBonus = solver.BaseFrostFireCritBonus; HitRate = solver.BaseFrostFireHitRate; ThreatMultiplier = solver.FrostFireThreatMultiplier; if (calculationOptions.FireResist == -1) { realResistance = calculationOptions.FrostResist; } else if (calculationOptions.FrostResist == -1) { realResistance = calculationOptions.FireResist; } else { realResistance = Math.Min(calculationOptions.FireResist, calculationOptions.FrostResist); } break; case MagicSchool.Frost: BaseSpellModifier = solver.BaseFrostSpellModifier; BaseAdditiveSpellModifier = solver.BaseFrostAdditiveSpellModifier; BaseCritRate = solver.BaseFrostCritRate; CritBonus = solver.BaseFrostCritBonus; HitRate = solver.BaseFrostHitRate; ThreatMultiplier = solver.FrostThreatMultiplier; realResistance = calculationOptions.FrostResist; break; case MagicSchool.Nature: BaseSpellModifier = solver.BaseNatureSpellModifier; BaseAdditiveSpellModifier = solver.BaseNatureAdditiveSpellModifier; BaseCritRate = solver.BaseNatureCritRate; CritBonus = solver.BaseNatureCritBonus; HitRate = solver.BaseNatureHitRate; ThreatMultiplier = solver.NatureThreatMultiplier; realResistance = calculationOptions.NatureResist; break; case MagicSchool.Shadow: BaseSpellModifier = solver.BaseShadowSpellModifier; BaseAdditiveSpellModifier = solver.BaseShadowAdditiveSpellModifier; BaseCritRate = solver.BaseShadowCritRate; CritBonus = solver.BaseShadowCritBonus; HitRate = solver.BaseShadowHitRate; ThreatMultiplier = solver.ShadowThreatMultiplier; realResistance = calculationOptions.ShadowResist; break; case MagicSchool.Holy: default: BaseSpellModifier = solver.BaseHolySpellModifier; BaseAdditiveSpellModifier = solver.BaseHolyAdditiveSpellModifier; BaseCritRate = solver.BaseHolyCritRate; CritBonus = solver.BaseHolyCritBonus; HitRate = solver.BaseHolyHitRate; ThreatMultiplier = solver.HolyThreatMultiplier; realResistance = calculationOptions.HolyResist; break; } int playerLevel = calculationOptions.PlayerLevel; int targetLevel = calculationOptions.TargetLevel; /*if (areaEffect) * { * targetLevel = calculationOptions.AoeTargetLevel; * float hitRate = ((targetLevel <= playerLevel + 2) ? (0.96f - (targetLevel - playerLevel) * 0.01f) : (0.94f - (targetLevel - playerLevel - 2) * 0.11f)) + calculations.BaseSpellHit; * if (MagicSchool == MagicSchool.Arcane) hitRate += 0.01f * mageTalents.ArcaneFocus; * if (hitRate > Spell.MaxHitRate) hitRate = Spell.MaxHitRate; * HitRate = hitRate; * } * else * { * targetLevel = calculationOptions.TargetLevel; * }*/ RealResistance = realResistance; PartialResistFactor = (realResistance == -1) ? 0 : (1 - StatConversion.GetAverageResistance(playerLevel, targetLevel, realResistance, baseStats.SpellPenetration)); }
public void InitializeDamage(Solver solver, bool areaEffect, int range, MagicSchool magicSchool, SpellData spellData, float hitProcs, float castProcs, float dotDuration) { Stats baseStats = solver.BaseStats; MageTalents mageTalents = solver.MageTalents; CalculationOptionsMage calculationOptions = solver.CalculationOptions; AreaEffect = areaEffect; BaseCost = spellData.Cost - (int)baseStats.SpellsManaReduction; MagicSchool = magicSchool; Ticks = hitProcs; CastProcs = castProcs; CastProcs2 = castProcs; BaseMinDamage = spellData.MinDamage; BaseMaxDamage = spellData.MaxDamage; SpellDamageCoefficient = spellData.SpellDamageCoefficient; BasePeriodicDamage = spellData.PeriodicDamage; DotDamageCoefficient = spellData.DotDamageCoefficient; DotDuration = dotDuration; BaseDirectDamageModifier = 1.0f; BaseDotDamageModifier = 1.0f; BaseCostModifier = 1.0f; float baseCostAmplifier = calculationOptions.EffectCostMultiplier; baseCostAmplifier *= (1.0f - 0.01f * mageTalents.Precision); if (mageTalents.FrostChanneling > 0) { baseCostAmplifier *= (1.0f - 0.01f - 0.03f * mageTalents.FrostChanneling); } if (MagicSchool == MagicSchool.Arcane) { baseCostAmplifier *= (1.0f - 0.01f * mageTalents.ArcaneFocus); } BaseCostAmplifier = baseCostAmplifier; float baseInterruptProtection = baseStats.InterruptProtection; if (MagicSchool == MagicSchool.Fire || MagicSchool == MagicSchool.FrostFire) { baseInterruptProtection += 0.35f * mageTalents.BurningSoul; } BaseInterruptProtection = baseInterruptProtection; float realResistance; switch (MagicSchool) { case MagicSchool.Arcane: BaseSpellModifier = solver.BaseArcaneSpellModifier; BaseAdditiveSpellModifier = solver.BaseArcaneAdditiveSpellModifier; BaseCritRate = solver.BaseArcaneCritRate; CritBonus = solver.BaseArcaneCritBonus; HitRate = solver.BaseArcaneHitRate; ThreatMultiplier = solver.ArcaneThreatMultiplier; realResistance = calculationOptions.ArcaneResist; if (range != 0) { Range = range + mageTalents.MagicAttunement * 3; } break; case MagicSchool.Fire: BaseSpellModifier = solver.BaseFireSpellModifier; BaseAdditiveSpellModifier = solver.BaseFireAdditiveSpellModifier; BaseCritRate = solver.BaseFireCritRate; CritBonus = solver.BaseFireCritBonus; HitRate = solver.BaseFireHitRate; ThreatMultiplier = solver.FireThreatMultiplier; realResistance = calculationOptions.FireResist; if (range != 0) { Range = range + mageTalents.FlameThrowing * 3; } break; case MagicSchool.FrostFire: BaseSpellModifier = solver.BaseFrostFireSpellModifier; BaseAdditiveSpellModifier = solver.BaseFrostFireAdditiveSpellModifier; BaseCritRate = solver.BaseFrostFireCritRate; CritBonus = solver.BaseFrostFireCritBonus; HitRate = solver.BaseFrostFireHitRate; ThreatMultiplier = solver.FrostFireThreatMultiplier; if (calculationOptions.FireResist == -1) { realResistance = calculationOptions.FrostResist; } else if (calculationOptions.FrostResist == -1) { realResistance = calculationOptions.FireResist; } else { realResistance = Math.Min(calculationOptions.FireResist, calculationOptions.FrostResist); } Range = range; break; case MagicSchool.Frost: BaseSpellModifier = solver.BaseFrostSpellModifier; BaseAdditiveSpellModifier = solver.BaseFrostAdditiveSpellModifier; BaseCritRate = solver.BaseFrostCritRate; CritBonus = solver.BaseFrostCritBonus; HitRate = solver.BaseFrostHitRate; ThreatMultiplier = solver.FrostThreatMultiplier; realResistance = calculationOptions.FrostResist; Range = range * (1 + mageTalents.ArcticReach * 0.1f); break; case MagicSchool.Nature: BaseSpellModifier = solver.BaseNatureSpellModifier; BaseAdditiveSpellModifier = solver.BaseNatureAdditiveSpellModifier; BaseCritRate = solver.BaseNatureCritRate; CritBonus = solver.BaseNatureCritBonus; HitRate = solver.BaseNatureHitRate; ThreatMultiplier = solver.NatureThreatMultiplier; realResistance = calculationOptions.NatureResist; Range = range; break; case MagicSchool.Shadow: BaseSpellModifier = solver.BaseShadowSpellModifier; BaseAdditiveSpellModifier = solver.BaseShadowAdditiveSpellModifier; BaseCritRate = solver.BaseShadowCritRate; CritBonus = solver.BaseShadowCritBonus; HitRate = solver.BaseShadowHitRate; ThreatMultiplier = solver.ShadowThreatMultiplier; realResistance = calculationOptions.ShadowResist; Range = range; break; case MagicSchool.Holy: default: BaseSpellModifier = solver.BaseHolySpellModifier; BaseAdditiveSpellModifier = solver.BaseHolyAdditiveSpellModifier; BaseCritRate = solver.BaseHolyCritRate; CritBonus = solver.BaseHolyCritBonus; HitRate = solver.BaseHolyHitRate; ThreatMultiplier = solver.HolyThreatMultiplier; realResistance = calculationOptions.HolyResist; Range = range; break; } int playerLevel = calculationOptions.PlayerLevel; int targetLevel; if (areaEffect) { targetLevel = calculationOptions.AoeTargetLevel; float hitRate = ((targetLevel <= playerLevel + 2) ? (0.96f - (targetLevel - playerLevel) * 0.01f) : (0.94f - (targetLevel - playerLevel - 2) * 0.11f)) + solver.BaseSpellHit; if (MagicSchool == MagicSchool.Arcane) { hitRate += 0.01f * mageTalents.ArcaneFocus; } if (hitRate > Spell.MaxHitRate) { hitRate = Spell.MaxHitRate; } HitRate = hitRate; } else { targetLevel = calculationOptions.TargetLevel; } RealResistance = realResistance; PartialResistFactor = (realResistance == -1) ? 0 : (1 - StatConversion.GetAverageResistance(playerLevel, targetLevel, realResistance, baseStats.SpellPenetration)); }
public void InitializeDamage(Solver solver, bool areaEffect, int range, MagicSchool magicSchool, int cost, float minDamage, float maxDamage, float spellDamageCoefficient, float periodicDamage, float dotDamageCoefficient, float hitProcs, float castProcs, float dotDuration) { Stats baseStats = solver.BaseStats; MageTalents mageTalents = solver.MageTalents; CalculationOptionsMage calculationOptions = solver.CalculationOptions; AreaEffect = areaEffect; AreaEffectDot = areaEffect; MaximumAOETargets = 10; int manaReduction = (int)baseStats.SpellsManaCostReduction; if (manaReduction == 405) { // Shard of Woe hax manaReduction = 205; } BaseCost = Math.Max(cost - manaReduction, 0); MagicSchool = magicSchool; Ticks = hitProcs; CastProcs = castProcs; CastProcs2 = castProcs; BaseMinDamage = minDamage; BaseMaxDamage = maxDamage; SpellDamageCoefficient = spellDamageCoefficient; BasePeriodicDamage = periodicDamage; DotDamageCoefficient = dotDamageCoefficient; DotDuration = dotDuration; BaseDirectDamageModifier = 1.0f; BaseDotDamageModifier = 1.0f; BaseCostModifier = 1.0f; float baseCostAmplifier = calculationOptions.EffectCostMultiplier; if (mageTalents.EnduringWinter > 0) { BaseCostModifier -= 0.03f * mageTalents.EnduringWinter + (mageTalents.EnduringWinter == 3 ? 0.01f : 0.00f); } BaseCostAmplifier = baseCostAmplifier; float baseInterruptProtection = baseStats.InterruptProtection; baseInterruptProtection += 0.23f * mageTalents.BurningSoul + (mageTalents.BurningSoul == 3 ? 0.01f : 0.0f); BaseInterruptProtection = baseInterruptProtection; float realResistance; switch (MagicSchool) { case MagicSchool.Arcane: BaseSpellModifier = solver.BaseArcaneSpellModifier; BaseAdditiveSpellModifier = solver.BaseArcaneAdditiveSpellModifier; BaseCritRate = solver.BaseArcaneCritRate; CritBonus = solver.BaseArcaneCritBonus; HitRate = solver.BaseArcaneHitRate; ThreatMultiplier = solver.ArcaneThreatMultiplier; realResistance = calculationOptions.ArcaneResist; IgniteFactor = 0; break; case MagicSchool.Fire: BaseSpellModifier = solver.BaseFireSpellModifier; BaseAdditiveSpellModifier = solver.BaseFireAdditiveSpellModifier; BaseCritRate = solver.BaseFireCritRate; CritBonus = solver.BaseFireCritBonus; HitRate = solver.BaseFireHitRate; ThreatMultiplier = solver.FireThreatMultiplier; realResistance = calculationOptions.FireResist; IgniteFactor = solver.IgniteFactor; break; case MagicSchool.FrostFire: BaseSpellModifier = solver.BaseFrostFireSpellModifier; BaseAdditiveSpellModifier = solver.BaseFrostFireAdditiveSpellModifier; BaseCritRate = solver.BaseFrostFireCritRate; CritBonus = solver.BaseFrostFireCritBonus; HitRate = solver.BaseFrostFireHitRate; ThreatMultiplier = solver.FrostFireThreatMultiplier; if (calculationOptions.FireResist == -1) { realResistance = calculationOptions.FrostResist; } else if (calculationOptions.FrostResist == -1) { realResistance = calculationOptions.FireResist; } else { realResistance = Math.Min(calculationOptions.FireResist, calculationOptions.FrostResist); } Range = range; IgniteFactor = solver.IgniteFactor; break; case MagicSchool.Frost: BaseSpellModifier = solver.BaseFrostSpellModifier; BaseAdditiveSpellModifier = solver.BaseFrostAdditiveSpellModifier; BaseCritRate = solver.BaseFrostCritRate; CritBonus = solver.BaseFrostCritBonus; HitRate = solver.BaseFrostHitRate; ThreatMultiplier = solver.FrostThreatMultiplier; realResistance = calculationOptions.FrostResist; IgniteFactor = 0; break; case MagicSchool.Nature: BaseSpellModifier = solver.BaseNatureSpellModifier; BaseAdditiveSpellModifier = solver.BaseNatureAdditiveSpellModifier; BaseCritRate = solver.BaseNatureCritRate; CritBonus = solver.BaseNatureCritBonus; HitRate = solver.BaseNatureHitRate; ThreatMultiplier = solver.NatureThreatMultiplier; realResistance = calculationOptions.NatureResist; Range = range; IgniteFactor = 0; break; case MagicSchool.Shadow: BaseSpellModifier = solver.BaseShadowSpellModifier; BaseAdditiveSpellModifier = solver.BaseShadowAdditiveSpellModifier; BaseCritRate = solver.BaseShadowCritRate; CritBonus = solver.BaseShadowCritBonus; HitRate = solver.BaseShadowHitRate; ThreatMultiplier = solver.ShadowThreatMultiplier; realResistance = calculationOptions.ShadowResist; Range = range; IgniteFactor = 0; break; case MagicSchool.Holy: default: BaseSpellModifier = solver.BaseHolySpellModifier; BaseAdditiveSpellModifier = solver.BaseHolyAdditiveSpellModifier; BaseCritRate = solver.BaseHolyCritRate; CritBonus = solver.BaseHolyCritBonus; HitRate = solver.BaseHolyHitRate; ThreatMultiplier = solver.HolyThreatMultiplier; realResistance = calculationOptions.HolyResist; Range = range; IgniteFactor = 0; break; } NonHSCritRate = baseStats.SpellCritOnTarget; int playerLevel = calculationOptions.PlayerLevel; int targetLevel; if (areaEffect) { targetLevel = calculationOptions.AoeTargetLevel; float hitRate = ((targetLevel <= playerLevel + 2) ? (0.96f - (targetLevel - playerLevel) * 0.01f) : (0.94f - (targetLevel - playerLevel - 2) * 0.11f)) + solver.BaseSpellHit; if (hitRate > Spell.MaxHitRate) { hitRate = Spell.MaxHitRate; } HitRate = hitRate; } else { targetLevel = calculationOptions.TargetLevel; } RealResistance = realResistance; PartialResistFactor = (realResistance == -1) ? 0 : (1 - StatConversion.GetAverageResistance(playerLevel, targetLevel, realResistance, baseStats.SpellPenetration)); }
TalentsBase parse_talents_wowhead(CharacterClass characterclass, string talent_string) { // wowhead format: [tree_1]Z[tree_2]Z[tree_3] where the trees are character encodings // each character expands to a pair of numbers [0-5][0-5] // unused deeper talents are simply left blank instead of filling up the string with zero-zero encodings bool hasGlyphs = talent_string.Contains(":"); int[] talent_trees = new int[] { 0, 0, 0 }; int[] glyph_trees = new int[] { 3, 3, 3 }; switch (characterclass) { case CharacterClass.Warrior: { WarriorTalents talents = new WarriorTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Paladin: { PaladinTalents talents = new PaladinTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Hunter: { HunterTalents talents = new HunterTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Rogue: { RogueTalents talents = new RogueTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Priest: { PriestTalents talents = new PriestTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.DeathKnight: { DeathKnightTalents talents = new DeathKnightTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Shaman: { ShamanTalents talents = new ShamanTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Mage: { MageTalents talents = new MageTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Warlock: { WarlockTalents talents = new WarlockTalents(); talent_trees = talents.TreeLengths; break; } case CharacterClass.Druid: { DruidTalents talents = new DruidTalents(); talent_trees = talents.TreeLengths; break; } } int[] encoding = new int[talent_trees[0] + talent_trees[1] + talent_trees[2]]; int[][] glyphEncoding = new int[][] { new int[3], new int[3], new int[3], }; int[] tree_count = new int[] { 0, 0, 0 }; int[] glyph_count = new int[] { 0, 0, 0 }; int tree = 0; int count = 0; #region Talents parsing for (int i=1; i < talent_string.Length; i++) { if (tree >= 3) { //sim -> errorf( "Player %s has malformed wowhead talent string. Too many talent trees specified.\n", name() ); return null; } char c = talent_string[i]; if (c == ':') break; // glyph encoding follows if (c == 'Z') { count = 0; for (int j = 0; j <= tree; j++) { count += talent_trees[tree]; } tree++; continue; } decode_t decode = null; for (int j=0; decoding[j].key != '\0' && decode==null; j++) { if (decoding[j].key == c) { decode = decoding[j]; } } if (decode == null) { //sim -> errorf( "Player %s has malformed wowhead talent string. Translation for '%c' unknown.\n", name(), c ); return null; } encoding[count++] += decode.first - '0'; tree_count[tree] += 1; if (tree_count[tree] < talent_trees[tree]) { encoding[count++] += decode.second - '0'; tree_count[tree] += 1; } if (tree_count[tree] >= talent_trees[tree]) { tree++; } } #endregion #region Glyphs Parsing #region Notes /* This is totally crappy.... * Glyphs do not follow the same parsing rules. If you apply what was there for talents directly * to glyphs you get 1202032213120011050000000000000000. Which should only have 1's and 0's * * * Warriors: As I'm checking glyphs, here's what I get: * == PRIMES == * Link decode id Name * * http://www.wowhead.com/talent#L:0 00 43415 58388 Devastate * * http://www.wowhead.com/talent#L:z 01 43416 58367 Bloodthirst * * http://www.wowhead.com/talent#L:M 02 43421 58368 Mortal Strike * * http://www.wowhead.com/talent#L:c 03 43422 58386 Overpower * * http://www.wowhead.com/talent#L:m 04 43423 58385 Slam * * http://www.wowhead.com/talent#L:V 05 43424 58364 Revenge * * http://www.wowhead.com/talent#L:o 10 43425 58375 Shield Slam * * http://www.wowhead.com/talent#L:k 11 43432 58370 Raging Blow * * http://www.wowhead.com/talent#L:R 12 45790 63324 Bladestorm * == MAJORS == * * http://www.wowhead.com/talent#L:0 00 43397 Long Charge * * http://www.wowhead.com/talent#L:z 01 43399 Thunder Clap * * http://www.wowhead.com/talent#L:M 02 43413 Rapid Charge * * http://www.wowhead.com/talent#L:c 03 43414 Cleaving * * http://www.wowhead.com/talent#L:m 04 43417 Piercing Howl * * http://www.wowhead.com/talent#L:V 05 43418 Heroic Throw * * http://www.wowhead.com/talent#L:o 10 43419 Intervene * * http://www.wowhead.com/talent#L:k 11 43427 Sunder Armor * * http://www.wowhead.com/talent#L:R 12 43428 Sweeping Strikes * * http://www.wowhead.com/talent#L:s 13 43430 Resonating Power * * http://www.wowhead.com/talent#L:a 14 43431 Victory Rush * * http://www.wowhead.com/talent#L:q 15 45792 Shockwave * * http://www.wowhead.com/talent#L:b 20 45795 Spell Reflection * * http://www.wowhead.com/talent#L:d 21 45797 Shield Wall * * http://www.wowhead.com/talent#L:r 22 63481 Colossus Smash * * http://www.wowhead.com/talent#L:f 23 67482 Intercept * * http://www.wowhead.com/talent#L:w 24 67483 Death Wish * == MINORS == * * http://www.wowhead.com/talent#L:0 00 43395 Battle * * http://www.wowhead.com/talent#L:z 01 43396 Berserker Rage * * http://www.wowhead.com/talent#L:M 02 43398 Demoralizing Shout * * http://www.wowhead.com/talent#L:c 03 43400 Enduring Victory * * http://www.wowhead.com/talent#L:m 04 43412 Bloody Healing * * http://www.wowhead.com/talent#L:V 05 45793 Furious Sundering * * http://www.wowhead.com/talent#L:o 10 45794 Intimidating Shout * * http://www.wowhead.com/talent#L:k 11 49084 Command * * So http://www.wowhead.com/talent#LubcfRMRurkcrZ0b:RMcrsR0kV would mean: * Prime: Bladestorm, Mortal Strike, Overpower * Major: Colossus Smash, Resonating Power, Sweeping Strikes * Minor: Battle, Command, Furious Sundering * Which is correct, that's what we come out to */ #endregion tree = 0; count = 0; if (hasGlyphs) { for (int i=talent_string.IndexOf(":")+1; i < talent_string.Length; i++) { if (tree >= 3) { //sim -> errorf( "Player %s has malformed wowhead talent string. Too many talent trees specified.\n", name() ); return null; } char c = talent_string[i]; if (c == 'Z') { count = 0; /*for (int j = 0; j <= tree; j++) { count += glyph_trees[tree]; }*/ tree++; continue; } decode_t decode = null; for (int j=0; decoding[j].key != '\0' && decode==null; j++) { if (decoding[j].key == c) { decode = decoding[j]; } } if (decode == null) { //sim -> errorf( "Player %s has malformed wowhead talent string. Translation for '%c' unknown.\n", name(), c ); return null; } glyphEncoding[tree][count++] += (decode.first - '0') * 10 + decode.second - '0'; glyph_count[tree] += 1; if (glyph_count[tree] >= (glyph_trees[tree])) { tree++; count = 0; } } } #endregion string newtalentstring = ""; foreach (int i in encoding) { newtalentstring += i.ToString(); } if (hasGlyphs) { //newtalentstring += "."; //for (int t = 0; t < 3; t++) { // foreach (int i in glyphEncoding[t]) { newtalentstring += i.ToString(); } //} } switch (characterclass) { case CharacterClass.Warrior: { return new WarriorTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); } case CharacterClass.Paladin: { return new PaladinTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); } case CharacterClass.Hunter: { return new HunterTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); } case CharacterClass.Rogue: { return new RogueTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); } case CharacterClass.Priest: { return new PriestTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); } case CharacterClass.DeathKnight: { return new DeathKnightTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); } case CharacterClass.Shaman: { return new ShamanTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); } case CharacterClass.Mage: { return new MageTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); } case CharacterClass.Warlock: { return new WarlockTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); } case CharacterClass.Druid: { return new DruidTalents(newtalentstring, hasGlyphs ? glyphEncoding : null); } } return null; }