Пример #1
0
 public virtual Spell GetSpell(CastingState castingState)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     spell.CalculateDerivedStats(castingState);
     return spell;
 }
Пример #2
0
        public static Spell NewFromReference(Spell reference, CastingState castingState)
        {
            Spell s = New(reference.template, castingState.Solver);

            s.castingState = castingState;

            s.BasePeriodicDamage    = reference.BasePeriodicDamage;
            s.BaseCastTime          = reference.BaseCastTime;
            s.CostModifier          = reference.CostModifier;
            s.CostAmplifier         = reference.CostAmplifier;
            s.SpellModifier         = reference.SpellModifier;
            s.AdditiveSpellModifier = reference.AdditiveSpellModifier;
            s.DirectDamageModifier  = reference.DirectDamageModifier;
            s.DotDamageModifier     = reference.DotDamageModifier;
            s.CritRate            = reference.CritRate;
            s.CritBonus           = reference.CritBonus;
            s.RawSpellDamage      = reference.RawSpellDamage;
            s.InterruptProtection = reference.InterruptProtection;
            s.AreaEffect          = reference.AreaEffect;
            s.AreaEffectDot       = reference.AreaEffectDot;

            s.SpammedDot          = reference.SpammedDot;
            s.Pom                 = reference.Pom;
            s.Ticks               = reference.Ticks;
            s.CastProcs           = reference.CastProcs;
            s.HitProcs            = reference.HitProcs;
            s.CritProcs           = reference.CritProcs;
            s.IgniteProcs         = reference.IgniteProcs;
            s.TargetProcs         = reference.TargetProcs;
            s.DotProcs            = reference.DotProcs;
            s.ChannelReduction    = reference.ChannelReduction;
            s.CastTime            = reference.CastTime;
            s.OO5SR               = reference.OO5SR;
            s.AverageDamage       = reference.AverageDamage;
            s.AverageThreat       = reference.AverageThreat;
            s.AverageCost         = reference.AverageCost;
            s.DamagePerSpellPower = reference.DamagePerSpellPower;
            s.DamagePerMastery    = reference.DamagePerMastery;
            s.DamagePerCrit       = reference.DamagePerCrit;
            // ignite tracking for separation in spell distribution
            s.IgniteDamage = reference.IgniteDamage;
            s.IgniteDamagePerSpellPower = reference.IgniteDamagePerSpellPower;
            s.IgniteDamagePerMastery    = reference.IgniteDamagePerMastery;
            s.IgniteDamagePerCrit       = reference.IgniteDamagePerCrit;
            // absorb spells
            s.Absorb      = reference.Absorb;
            s.TotalAbsorb = reference.TotalAbsorb;
            // dot spells
            s.DotAverageDamage       = reference.DotAverageDamage;
            s.DotAverageThreat       = reference.DotAverageThreat;
            s.DotDamagePerSpellPower = reference.DotDamagePerSpellPower;
            s.DotDamagePerMastery    = reference.DotDamagePerMastery;
            s.DotDamagePerCrit       = reference.DotDamagePerCrit;
            s.DotFullDuration        = reference.DotFullDuration;
            s.DotExtraTicks          = reference.DotExtraTicks;

            s.RecalculateCastTime(castingState);

            return(s);
        }
Пример #3
0
 public virtual void RecalculateCastTime(CastingState castingState)
 {
     if (ChannelReduction != 0)
     {
         Ticks               /= (1 - ChannelReduction);
         HitProcs            /= (1 - ChannelReduction);
         CritProcs           /= (1 - ChannelReduction);
         TargetProcs         /= (1 - ChannelReduction);
         CastProcs            = template.CastProcs;
         AverageDamage       /= (1 - ChannelReduction);
         AverageThreat       /= (1 - ChannelReduction);
         DamagePerSpellPower /= (1 - ChannelReduction);
         DamagePerMastery    /= (1 - ChannelReduction);
         DamagePerCrit       /= (1 - ChannelReduction);
     }
     CastTime = template.CalculateCastTime(castingState, InterruptProtection, CritRate, Pom, BaseCastTime, out ChannelReduction);
     if (ChannelReduction != 0)
     {
         Ticks               *= (1 - ChannelReduction);
         HitProcs            *= (1 - ChannelReduction);
         CritProcs           *= (1 - ChannelReduction);
         TargetProcs         *= (1 - ChannelReduction);
         CastProcs            = template.CastProcs2 + (template.CastProcs - template.CastProcs2) * (1 - ChannelReduction);
         AverageDamage       *= (1 - ChannelReduction);
         AverageThreat       *= (1 - ChannelReduction);
         DamagePerSpellPower *= (1 - ChannelReduction);
         DamagePerMastery    *= (1 - ChannelReduction);
         DamagePerCrit       *= (1 - ChannelReduction);
     }
 }
Пример #4
0
        public float GetEffectAverageDamage(CastingState castingState)
        {
            Spell spell = Spell.New(this, castingState.Solver);

            spell.Calculate(castingState);
            spell.CalculateAverageDamage(castingState.Solver, 0, false, false);
            return(spell.AverageDamage);
        }
Пример #5
0
        public virtual Spell GetSpell(CastingState castingState)
        {
            Spell spell = Spell.New(this, castingState.Solver);

            spell.Calculate(castingState);
            spell.CalculateDerivedStats(castingState);
            return(spell);
        }
Пример #6
0
        public float GetEffectAverageDamage(CastingState castingState)
        {
            Spell spell = Spell.New(this, castingState.Solver);

            spell.Calculate(castingState);
            float damagePerSpellPower;
            float igniteDamage;
            float igniteDamagePerSpellPower;

            return(spell.CalculateAverageDamage(castingState.Solver, 0, false, false, out damagePerSpellPower, out igniteDamage, out igniteDamagePerSpellPower));
        }
Пример #7
0
        public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState, Cycle baseCycle)
        {
            Cycle cycle = Cycle.New(needsDisplayCalculations, castingState);
            cycle.Name = baseCycle.Name;
            cycle.AreaEffect = baseCycle.AreaEffect;

            // uptime
            float fightDuration = castingState.CalculationOptions.FightDuration;
            float effectDuration = Solver.MirrorImageDuration;
            float effectCooldown = Solver.MirrorImageCooldown;
            int activations = 0;
            float total;
            if (fightDuration < effectDuration)
            {
                total = fightDuration;
                activations = 1;
            }
            else
            {
                total = effectDuration;
                activations = 1;
                fightDuration -= effectDuration;
                int count = (int)(fightDuration / effectCooldown);
                total += effectDuration * count;
                activations += count;
                fightDuration -= effectCooldown * count;
                fightDuration -= effectCooldown - effectDuration;
                if (fightDuration > 0) 
                {
                    total += fightDuration;
                    activations++;
                }
            }          

            Spell mirrorImage = castingState.GetSpell(SpellId.MirrorImage);
           
            // activations * gcd in fightDuration
            float gcd = castingState.Solver.BaseGlobalCooldown + castingState.CalculationOptions.LatencyGCD;

            cycle.AddCycle(needsDisplayCalculations, baseCycle, (castingState.CalculationOptions.FightDuration - activations * gcd) / baseCycle.CastTime);
            cycle.CastTime += activations * gcd;
            cycle.costPerSecond += activations * (int)(0.10 * SpellTemplate.BaseMana[castingState.CalculationOptions.PlayerLevel]);
            //effectDamagePerSecond += (mirrorImage.AverageDamage + spellPower * mirrorImage.DamagePerSpellPower) / mirrorImage.CastTime;
            cycle.damagePerSecond += total * mirrorImage.AverageDamage / mirrorImage.CastTime;
            cycle.DpsPerSpellPower += total * mirrorImage.DamagePerSpellPower / mirrorImage.CastTime;
            cycle.Calculate();

            cycle.Note = baseCycle.Note;

            return cycle;
        }
Пример #8
0
 public override void Calculate(CastingState castingState)
 {
     base.Calculate(castingState);
     // 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.06f * castingState.MageTalents.MoltenFury);
     }
     if (castingState.MageTalents.MoltenFury > 0)
     {
         SpellModifier *= (1 + 0.06f * castingState.MageTalents.MoltenFury * castingState.CalculationOptions.MoltenFuryPercentage);
     }
 }
Пример #9
0
        public CastingState NewCastingState()
        {
            if (stateIndex < statePool.Length)
            {
                CastingState state = statePool[stateIndex];
                if (state == null)
                {
                    goto COLD;
                }
                stateIndex++;
                return(state);
            }
COLD:
            return(NewCastingStateCold());
        }
Пример #10
0
 public virtual Spell GetSpell(CastingState castingState)
 {
     if (AreaEffect)
     {
         AoeSpell spell = new AoeSpell(this);
         spell.Calculate(castingState);
         spell.CalculateDerivedStats(castingState);
         return(spell);
     }
     else
     {
         Spell spell = Spell.New(this, castingState.Solver);
         spell.Calculate(castingState);
         spell.CalculateDerivedStats(castingState);
         return(spell);
     }
 }
Пример #11
0
 public bool CooldownRestrictionsValid(Segment segment, CastingState state)
 {
     if (CooldownRestrictionList == null)
     {
         return(true);
     }
     foreach (CooldownRestriction restriction in CooldownRestrictionList)
     {
         if (segment.TimeStart >= restriction.TimeStart && segment.TimeEnd <= restriction.TimeEnd)
         {
             if (!restriction.IsMatch(state.Effects))
             {
                 return(false);
             }
         }
     }
     return(true);
 }
Пример #12
0
        public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState, Cycle baseCycle)
        {
            Cycle cycle = Cycle.New(needsDisplayCalculations, castingState);
            cycle.Name = "Mage Ward+" + baseCycle.Name;

            Spell MageWard = castingState.GetSpell(SpellId.MageWard);

            // 1 ward every 30 seconds

            cycle.AreaEffect = baseCycle.AreaEffect;
            cycle.AddSpell(needsDisplayCalculations, MageWard, 1);
            cycle.AddCycle(needsDisplayCalculations, baseCycle, (30 - MageWard.CastTime) / baseCycle.CastTime);
            cycle.Calculate();

            cycle.Note = baseCycle.Note;

            return cycle;
        }
Пример #13
0
        public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState, Cycle baseCycle, bool averaged)
        {
            Cycle cycle = Cycle.New(needsDisplayCalculations, castingState);
            cycle.Name = baseCycle.Name;
            cycle.AreaEffect = baseCycle.AreaEffect;

            Spell FlameOrb = castingState.GetSpell(SpellId.FlameOrb);

            // 1 flame orb in 15 seconds
            // 1 flame orb in 60 seconds (averaged)

            cycle.AddSpell(needsDisplayCalculations, FlameOrb, 1);
            cycle.AddCycle(needsDisplayCalculations, baseCycle, ((averaged ? 60 : 15) - FlameOrb.CastTime) / baseCycle.CastTime);
            cycle.Calculate();

            cycle.Note = baseCycle.Note;

            return cycle;
        }
Пример #14
0
        public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState, Cycle baseCycle)
        {
            Cycle cycle = Cycle.New(needsDisplayCalculations, castingState);
            cycle.Name = baseCycle.Name;
            cycle.AreaEffect = baseCycle.AreaEffect;

            Spell Combustion = castingState.GetSpell(SpellId.Combustion);

            // 1 combustion in 10 seconds
            // the dot duplication is currently calculated in individual spells
            // consider splitting that out for display purposes

            cycle.AddSpell(needsDisplayCalculations, Combustion, 1);
            cycle.AddCycle(needsDisplayCalculations, baseCycle, (10 - Combustion.CastTime) / baseCycle.CastTime);
            cycle.Calculate();

            cycle.Note = baseCycle.Note;

            return cycle;
        }
Пример #15
0
        public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState)
        {
            Cycle cycle = Cycle.New(needsDisplayCalculations, castingState);
            cycle.Name = "AE4AB";

            Spell AB4 = castingState.GetSpell(SpellId.ArcaneBlast4);
            Spell AE4 = castingState.GetSpell(SpellId.ArcaneExplosion4);

            // AEx4-AB

            // 6 seconds on AB debuff - time to refresh AB
            int aeCount = (int)((6.0f - AB4.CastTime) / AE4.CastTime);

            cycle.AddSpell(needsDisplayCalculations, AE4, aeCount);
            cycle.AddSpell(needsDisplayCalculations, AB4, 1);
            cycle.Calculate();

            cycle.AreaEffect = true;

            return cycle;
        }
Пример #16
0
        private CastingState NewCastingStateCold()
        {
            /*if (statePool == null)
             * {
             *  statePool = new CastingState[32];
             * }
             * else */if (stateIndex >= statePool.Length)
            {
                CastingState[] arr = new CastingState[statePool.Length * 2];
                Array.Copy(statePool, arr, statePool.Length);
                statePool = arr;
            }
            CastingState state = statePool[stateIndex];

            if (state == null)
            {
                state = new CastingState();
                statePool[stateIndex] = state;
            }
            stateIndex++;
            return(state);
        }
Пример #17
0
        public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState)
        {
            Cycle cycle = Cycle.New(needsDisplayCalculations, castingState);
            Spell FrB;
            float K;
            cycle.Name = "FrBFB";

            FrB = castingState.GetSpell(SpellId.Frostbolt);
            Spell FB = castingState.GetSpell(SpellId.FireballBF);

            // FrB      1 - brainFreeze
            // FrB-FB   brainFreeze

            float T8 = 0;

            K = 0.05f * castingState.MageTalents.BrainFreeze / (1 - T8);

            cycle.AddSpell(needsDisplayCalculations, FrB, 1);
            cycle.AddSpell(needsDisplayCalculations, FB, K);
            cycle.Calculate();
            return cycle;
        }
        private void HotStreakUtilization_Click(object sender, RoutedEventArgs e)
        {
            string armor = "Molten Armor";
            CalculationOptionsMage calculationOptions = Character.CalculationOptions as CalculationOptionsMage;
            CalculationsMage       calculations       = (CalculationsMage)Calculations.Instance;
            Solver solver = new Solver(Character, calculationOptions, false, false, false, 0, armor, false, false, true, false);

            solver.Initialize(null);
            CastingState baseState = new CastingState(solver, 0, false, 0);

            FireCycleGenerator generator = new FireCycleGenerator(baseState);

            GenericCycle c1 = new GenericCycle("test", baseState, generator.StateList, true);
            Cycle        c2 = baseState.GetCycle(CycleId.FBLBPyro);

            Dictionary <string, SpellContribution> dict1 = new Dictionary <string, SpellContribution>();
            Dictionary <string, SpellContribution> dict2 = new Dictionary <string, SpellContribution>();

            c1.AddDamageContribution(dict1, 1.0f);
            c2.AddDamageContribution(dict2, 1.0f);

            float predicted = dict2["Pyroblast"].Hits / dict2["Fireball"].Hits;
            float actual    = dict1["Pyroblast"].Hits / dict1["Fireball"].Hits;

            StringBuilder sb = new StringBuilder();

            sb.AppendLine("Pyro/Nuke Ratio:");
            sb.AppendLine();
            sb.AppendLine("Approximation Model: " + predicted);
            sb.AppendLine("Exact Model: " + actual);
            sb.AppendLine();
            // predicted = raw * (1 - wastedold)
            // actual = raw * (1 - wasted)
            // wasted = 1 - actual / predicted * (1 - wastedold)
            sb.AppendLine("Predicted Wasted Hot Streaks: " + (1 - actual / predicted));

            MessageBox.Show(sb.ToString());
        }
Пример #19
0
        public virtual void Calculate(CastingState castingState)
        {
            this.castingState = castingState;

            BaseCastTime         = template.BaseCastTime;
            CostModifier         = template.BaseCostModifier;
            CostAmplifier        = template.BaseCostAmplifier;
            DirectDamageModifier = template.BaseDirectDamageModifier;
            DotDamageModifier    = template.BaseDotDamageModifier;
            if (castingState.PowerInfusion)
            {
                CostModifier -= 0.2f;                             // don't have any information on this, going by best guess
            }
            if (castingState.ArcanePower)
            {
                CostModifier += 0.2f;
            }
            InterruptProtection = template.BaseInterruptProtection;

            SpellModifier         = template.BaseSpellModifier * castingState.StateSpellModifier;
            AdditiveSpellModifier = template.BaseAdditiveSpellModifier + castingState.StateAdditiveSpellModifier;
            CritBonus             = template.CritBonus;
            CritRate = template.BaseCritRate + castingState.StateCritRate;
            if (castingState.Combustion && (MagicSchool == MagicSchool.Fire || MagicSchool == MagicSchool.FrostFire))
            {
                CritRate = 3 / castingState.CombustionDuration;
                if (MagicSchool == MagicSchool.Fire)
                {
                    CritBonus = castingState.Solver.CombustionFireCritBonus;
                }
                else if (MagicSchool == MagicSchool.FrostFire)
                {
                    CritBonus = castingState.Solver.CombustionFrostFireCritBonus;
                }
            }

            switch (MagicSchool)
            {
            case MagicSchool.Arcane:
                RawSpellDamage = castingState.ArcaneSpellPower;
                break;

            case MagicSchool.Fire:
                RawSpellDamage = castingState.FireSpellPower;
                break;

            case MagicSchool.FrostFire:
                RawSpellDamage = castingState.FrostFireSpellPower;
                break;

            case MagicSchool.Frost:
                RawSpellDamage = castingState.FrostSpellPower;
                break;

            case MagicSchool.Nature:
                RawSpellDamage = castingState.NatureSpellPower;
                break;

            case MagicSchool.Shadow:
                RawSpellDamage = castingState.ShadowSpellPower;
                break;

            case MagicSchool.Holy:
                RawSpellDamage = castingState.HolySpellPower;
                break;
            }
        }
Пример #20
0
        public override Spell GetSpell(CastingState castingState)
        {
            Spell spell = Spell.New(this, castingState.Solver);
            spell.Calculate(castingState);
            spell.CastTime = speed;
            spell.CritRate = castingState.CritRate;

            if (spell.CritRate < 0.0f) spell.CritRate = 0.0f;
            if (spell.CritRate > 1.0f) spell.CritRate = 1.0f;

            spell.SpellModifier = BaseSpellModifier;

            spell.HitProcs = HitRate;
            spell.CritProcs = spell.HitProcs * spell.CritRate;
            spell.TargetProcs = spell.HitProcs;

            spell.CalculateAverageDamage(castingState.Solver, 0, false, false);
            spell.DamagePerSpellPower = 0;
            spell.DamagePerMastery = 0;
            spell.DamagePerCrit = 0;
            spell.AverageThreat = spell.AverageDamage * ThreatMultiplier;
            spell.IgniteDamage = 0;
            spell.IgniteDamagePerSpellPower = 0;
            spell.IgniteDamagePerMastery = 0;
            spell.IgniteDamagePerCrit = 0;
            spell.AverageCost = 0;
            spell.OO5SR = 1;
            return spell;
        }
Пример #21
0
 public Spell GetSpell(CastingState castingState, int debuff, int castDebuff)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     spell.BaseCastTime -= castDebuff * 0.1f * castTimeMultiplier;
     spell.AdditiveSpellModifier += arcaneBlastDamageMultiplier * debuff;
     spell.SpellModifier *= (1 + tormentTheWeak * castingState.SnaredTime);
     spell.CostModifier += arcaneBlastManaMultiplier * debuff;
     spell.CalculateDerivedStats(castingState, false, false, false, true, false, false);
     return spell;
 }
Пример #22
0
        public ABarAM(bool needsDisplayCalculations, CastingState castingState)
            : base(needsDisplayCalculations, castingState)
        {
            float MB;
            Name = "ABarAM";

            Spell ABar = castingState.GetSpell(SpellId.ArcaneBarrage);
            Spell AM = castingState.GetSpell(SpellId.ArcaneMissiles);
            Spell MBAM = castingState.GetSpell(SpellId.ArcaneMissilesMB);

            MB = 0.04f * castingState.MageTalents.MissileBarrage;

            if (MB == 0.0)
            {
                // if we don't have barrage then this degenerates to ABar-AM
                AddSpell(needsDisplayCalculations, ABar, 1);
                AddSpell(needsDisplayCalculations, AM, 1);

                Calculate();
            }
            else
            {
                //AB-AM 0.85
                AddSpell(needsDisplayCalculations, ABar, 1 - MB);
                AddSpell(needsDisplayCalculations, AM, 1 - MB);

                //AB-MBAM 0.15
                AddSpell(needsDisplayCalculations, ABar, MB);
                AddSpell(needsDisplayCalculations, MBAM, MB);
                if (ABar.CastTime + MBAM.CastTime < 3.0) AddPause(3.0f - ABar.CastTime - MBAM.CastTime, MB);

                Calculate();
            }
        }
Пример #23
0
        public float CalculateCastTime(CastingState castingState, float interruptProtection, float critRate, bool pom, float baseCastTime, out float channelReduction)
        {
            CalculationOptionsMage calculationOptions = castingState.CalculationOptions;
            float castingSpeed = castingState.CastingSpeed;
            float spellHasteRating = castingState.SpellHasteRating;
            float levelScalingFactor = calculationOptions.LevelScalingFactor;
            float hasteFactor = levelScalingFactor / 1000f;
            float rootCastingSpeed = castingSpeed / (1 + spellHasteRating * hasteFactor);

            float InterruptFactor = 0f;
            float maxPushback = 0f;
            if (calculationOptions.InterruptFrequency > 0f)
            {
                // interrupt factors of more than once per spell are not supported, so put a limit on it (up to twice is probably approximately correct)
                InterruptFactor = Math.Min(calculationOptions.InterruptFrequency, 2 * castingSpeed / baseCastTime);
                if (castingState.IcyVeins) interruptProtection = 1;
                maxPushback = 0.5f * Math.Max(0, 1 - interruptProtection);
                if (Channeled) maxPushback = 0.0f;
            }

            if (pom) baseCastTime = 0.0f;

            float globalCooldown = Math.Max(Spell.GlobalCooldownLimit, GlobalCooldown / castingSpeed);
            float latency;
            if (baseCastTime <= GlobalCooldown || Instant)
            {
                latency = calculationOptions.LatencyGCD;
            }
            else if (Channeled)
            {
                latency = calculationOptions.LatencyChannel;
            }
            else
            {
                latency = calculationOptions.LatencyCast;
            }
            float averageTicks = Ticks;
            float castTime = baseCastTime / castingSpeed;
            /*if (calculationOptions.Beta && Channeled)
            {
                float tickCastTime = castTime / Ticks;
                averageTicks = (float)Math.Floor(baseCastTime / tickCastTime);
                castTime = averageTicks * tickCastTime;
            }*/
            if (InterruptFactor > 0)
            {
                castTime = castTime * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f) * maxPushback * InterruptFactor;
            }
            castTime += latency;
            float gcdcap = globalCooldown + calculationOptions.LatencyGCD;
            if (castTime < gcdcap) castTime = gcdcap;
            channelReduction = 0.0f;

            if (!calculationOptions.AdvancedHasteProcs)
            {
                if (castingState.Solver.Mage2T13)
                {
                    // very rough and very ugly
                    SpecialEffect effect = Solver.SpecialEffect2T13;
                    float chance = (castingState.Solver.Specialization == Specialization.Arcane) ? 1.0f : 0.5f;
                    float triggers = 0f;
                    int resets = 1;
                    switch (castingState.Solver.Specialization) // guesstimates
                    {
                        case Specialization.Arcane:
                            triggers = 0.47f;
                            resets = 1 + (int)(castingState.CalculationOptions.FightDuration / castingState.Solver.ArcanePowerCooldown);
                            break;
                        case Specialization.Fire:
                            triggers = 0.45f;
                            resets = 1 + (int)(castingState.CalculationOptions.FightDuration / castingState.Solver.CombustionCooldown);
                            break;
                        case Specialization.Frost:
                            triggers = 0.52f;
                            resets = 1 + (int)(castingState.CalculationOptions.FightDuration / castingState.Solver.IcyVeinsCooldown);
                            break;
                    }

                    spellHasteRating += 50 * effect.GetAverageStackSize(1 / triggers, chance, 3.0f, calculationOptions.FightDuration, resets);
                    castingSpeed = rootCastingSpeed * (1 + spellHasteRating * hasteFactor);

                    globalCooldown = Math.Max(Spell.GlobalCooldownLimit, GlobalCooldown / castingSpeed);
                    castTime = baseCastTime / castingSpeed;
                    if (InterruptFactor > 0)
                    {
                        castTime = castTime * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f) * maxPushback * InterruptFactor;
                    }
                    castTime += latency;
                    gcdcap = globalCooldown + calculationOptions.LatencyGCD;
                    if (castTime < gcdcap) castTime = gcdcap;
                }
                for (int i = 0; i < castingState.Solver.HasteRatingEffectsCount; i++)
                {
                    SpecialEffect effect = castingState.Solver.HasteRatingEffects[i];
                    float procs = 0.0f;
                    int triggers = 0;
                    switch (effect.Trigger)
                    {
                        case Trigger.DamageSpellCast:
                        case Trigger.SpellCast:
                            procs = CastProcs;
                            triggers = (int)procs;
                            break;
                        case Trigger.DamageSpellCrit:
                        case Trigger.SpellCrit:
                            procs = critRate * averageTicks;
                            triggers = (int)averageTicks;
                            break;
                        case Trigger.DamageSpellHit:
                        case Trigger.SpellHit:
                            procs = HitRate * averageTicks;
                            triggers = (int)averageTicks;
                            break;
                    }
                    if (procs == 0.0f) continue;
                    float procHaste = effect.Stats.HasteRating;
                    if (effect.Cooldown >= effect.Duration)
                    {
                        // hasted casttime
                        float speed = rootCastingSpeed * (1 + (spellHasteRating + procHaste) * hasteFactor);
                        float gcd = Math.Max(Spell.GlobalCooldownLimit, GlobalCooldown / speed);
                        float cast = baseCastTime / speed;
                        /*if (calculationOptions.Beta && Channeled)
                        {
                            float tickCastTime = cast / Ticks;
                            cast = (float)Math.Floor(baseCastTime / tickCastTime) * tickCastTime;
                        }*/
                        if (InterruptFactor > 0)
                        {
                            cast = cast * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f) * maxPushback * InterruptFactor;
                        }
                        cast += latency;
                        gcdcap = gcd + calculationOptions.LatencyGCD;
                        if (cast < gcdcap) cast = gcdcap;

                        float castsAffected = 0;
                        if (triggers > 1)
                        {
                            // multi tick spell (i.e. AM)
                            for (int c = 0; c < triggers; c++) castsAffected += (float)Math.Ceiling((effect.Duration - c * castTime / triggers) / cast);
                            castsAffected /= triggers;
                        }
                        else
                        {
                            // single tick spell
                            castsAffected = (float)Math.Ceiling(effect.Duration / cast); // should the first proc be already hasted?
                        }
                        float effectiveDuration = castsAffected * cast;
                        // this isn't completely accurate, we should have made a separate SpecialEffect and change the actual duration
                        // but performance would hurt so this'll have to do
                        spellHasteRating += procHaste * (effectiveDuration / effect.Duration) * effect.GetAverageUptime(castTime / triggers, procs / triggers, 3.0f, calculationOptions.FightDuration);
                        //spellHasteRating += procHaste * castsAffected * cast / (effect.Cooldown + castTime / procs / effect.Chance);
                        //Haste += castingState.BasicStats.SpellHasteFor6SecOnCast_15_45 * 6f / (45f + CastTime / CastProcs / 0.15f);
                        castingSpeed = rootCastingSpeed * (1 + spellHasteRating * hasteFactor);

                        globalCooldown = Math.Max(Spell.GlobalCooldownLimit, GlobalCooldown / castingSpeed);
                        castTime = baseCastTime / castingSpeed;
                        /*if (calculationOptions.Beta && Channeled)
                        {
                            float tickCastTime = castTime / Ticks;
                            averageTicks = (float)Math.Floor(baseCastTime / tickCastTime);
                            castTime = averageTicks * tickCastTime;
                        }*/
                        if (InterruptFactor > 0)
                        {
                            castTime = castTime * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f) * maxPushback * InterruptFactor;
                        }
                        castTime += latency;
                        gcdcap = globalCooldown + calculationOptions.LatencyGCD;
                        if (castTime < gcdcap) castTime = gcdcap;
                    }
                    else if (effect.Cooldown == 0 && (effect.Trigger == Trigger.SpellCrit || effect.Trigger == Trigger.DamageSpellCrit))
                    {
                        float rawHaste = spellHasteRating;

                        castingSpeed /= (1 + spellHasteRating / 1000f * levelScalingFactor);
                        float proccedSpeed = castingSpeed * (1 + (rawHaste + procHaste) / 1000f * levelScalingFactor);
                        float proccedGcd = Math.Max(Spell.GlobalCooldownLimit, GlobalCooldown / proccedSpeed);
                        float proccedCastTime = baseCastTime / proccedSpeed;
                        float proccedTicks = averageTicks;
                        /*if (calculationOptions.Beta && Channeled)
                        {
                            float tickCastTime = proccedCastTime / Ticks;
                            proccedTicks = (float)Math.Floor(baseCastTime / tickCastTime);
                            castTime = proccedTicks * tickCastTime;
                        }*/
                        if (InterruptFactor > 0)
                        {
                            proccedCastTime = proccedCastTime * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f) * maxPushback * InterruptFactor;
                        }
                        proccedCastTime += latency;
                        if (proccedCastTime < proccedGcd + calculationOptions.LatencyGCD) proccedCastTime = proccedGcd + calculationOptions.LatencyGCD;
                        int chancesToProc = (int)(((int)Math.Floor(effect.Duration / proccedCastTime) + 1) * proccedTicks);
                        if (!(Instant || pom)) chancesToProc -= 1;
                        if (AreaEffect) chancesToProc *= calculationOptions.AoeTargets;
                        spellHasteRating = rawHaste + procHaste * (1 - (float)Math.Pow(1 - effect.Chance * critRate, chancesToProc));
                        //Haste = rawHaste + castingState.BasicStats.SpellHasteFor5SecOnCrit_50 * ProcBuffUp(1 - (float)Math.Pow(1 - 0.5f * CritRate, HitProcs), 5, CastTime);
                        castingSpeed *= (1 + spellHasteRating / 1000f * levelScalingFactor);
                        globalCooldown = Math.Max(Spell.GlobalCooldownLimit, GlobalCooldown / castingSpeed);
                        castTime = baseCastTime / castingSpeed;
                        /*if (calculationOptions.Beta && Channeled)
                        {
                            float tickCastTime = castTime / Ticks;
                            averageTicks = (float)Math.Floor(baseCastTime / tickCastTime);
                            castTime = averageTicks * tickCastTime;
                        }*/
                        if (InterruptFactor > 0)
                        {
                            castTime = castTime * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f + latency) * maxPushback * InterruptFactor;
                        }
                        castTime += latency;
                        if (castTime < globalCooldown + calculationOptions.LatencyGCD) castTime = globalCooldown + calculationOptions.LatencyGCD;
                    }
                }
                // on use stacking items
                for (int i = 0; i < castingState.Solver.StackingHasteEffectCooldownsCount; i++)
                {
                    EffectCooldown effectCooldown = castingState.Solver.StackingHasteEffectCooldowns[i];
                    if (castingState.EffectsActive(effectCooldown.Mask))
                    {
                        Stats stats = effectCooldown.SpecialEffect.Stats;
                        for (int j = 0; j < stats._rawSpecialEffectDataSize; j++)
                        {
                            SpecialEffect effect = stats._rawSpecialEffectData[j];
                            float procHaste = effect.Stats.HasteRating;
                            if (procHaste > 0)
                            {                                
                                float procs = 0.0f;
                                switch (effect.Trigger)
                                {
                                    case Trigger.DamageSpellCast:
                                    case Trigger.SpellCast:
                                        procs = CastProcs;
                                        break;
                                    case Trigger.DamageSpellCrit:
                                    case Trigger.SpellCrit:
                                        procs = critRate * averageTicks;
                                        break;
                                    case Trigger.DamageSpellHit:
                                    case Trigger.SpellHit:
                                        procs = HitRate * averageTicks;
                                        break;
                                    case Trigger.MageNukeCast:
                                        procs = NukeProcs;
                                        break;
                                    case Trigger.MageNukeCast2:
                                        procs = NukeProcs2;
                                        break;
                                }
                                if (procs == 0.0f) continue;
                                // until they put in some good trinkets with such effects just do a quick dirty calculation
                                float effectHasteRating;
                                if (procs > averageTicks)
                                {
                                    // some 100% on cast procs, happens because AM has 6 cast procs and only 5 ticks
                                    effectHasteRating = effect.GetAverageStackSize(castTime / procs, 1.0f, 3.0f, effectCooldown.SpecialEffect.Duration) * procHaste;
                                }
                                else
                                {
                                    effectHasteRating = effect.GetAverageStackSize(castTime / averageTicks, procs / averageTicks, 3.0f, effectCooldown.SpecialEffect.Duration) * procHaste;
                                }

                                castingSpeed /= (1 + spellHasteRating / 1000f * levelScalingFactor);
                                spellHasteRating += effectHasteRating;
                                castingSpeed *= (1 + spellHasteRating / 1000f * levelScalingFactor);

                                globalCooldown = Math.Max(Spell.GlobalCooldownLimit, GlobalCooldown / castingSpeed);
                                castTime = baseCastTime / castingSpeed;
                                /*if (calculationOptions.Beta && Channeled)
                                {
                                    float tickCastTime = castTime / Ticks;
                                    averageTicks = (float)Math.Floor(baseCastTime / tickCastTime);
                                    castTime = averageTicks * tickCastTime;
                                }*/
                                if (InterruptFactor > 0)
                                {
                                    castTime = castTime * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f) * maxPushback * InterruptFactor;
                                }
                                castTime += latency;
                                if (castTime < globalCooldown + calculationOptions.LatencyGCD) castTime = globalCooldown + calculationOptions.LatencyGCD;
                            }
                        }
                    }
                }
            }

            /*if (Channeled && calculationOptions.Beta)
            {
                channelReduction = 1 - averageTicks / Ticks;
            }*/

            // channeled pushback
            if (Channeled && InterruptFactor > 0)
            {
                int maxLostTicks = (int)Math.Ceiling(averageTicks * 0.25f * Math.Max(0, 1 - interruptProtection));
                // pushbacks that happen up to pushbackCastTime cut the cast time to pushbackCastTime
                // pushbacks that happen after just terminate the channel
                // [---|---X---|---|---]
                castTime -= latency;
                float tickFactor = 0;
                for (int i = 0; i < maxLostTicks; i++)
                {
                    tickFactor += InterruptFactor * castTime / averageTicks * (i + 1) / averageTicks;
                }
                tickFactor += InterruptFactor * (averageTicks - maxLostTicks) * castTime / averageTicks * maxLostTicks / averageTicks;
                /*if (calculationOptions.Beta)
                {
                    channelReduction = 1 - averageTicks * (1 - tickFactor) / Ticks;
                }
                else*/
                {
                    channelReduction = tickFactor;
                }
                castTime = castTime * (1 - tickFactor) + latency;
            }

            return castTime;
        }
Пример #24
0
 public override void CalculateDerivedStats(CastingState castingState, bool outOfFiveSecondRule, bool pom, bool spammedDot, bool round, bool forceHit, bool forceMiss)
 {
     base.CalculateDerivedStats(castingState, outOfFiveSecondRule, pom, spammedDot, round, forceHit, forceMiss);
     TargetProcs *= castingState.CalculationOptions.AoeTargets;
 }
Пример #25
0
        public ABSpamMBAM(bool needsDisplayCalculations, CastingState castingState)
            : base(needsDisplayCalculations, castingState)
        {
            Spell AB3;
            float MB, MB3, MB4, MB5, hit, miss;
            Name = "ABSpamMBAM";

            // main cycle is AB3 spam
            // on MB we change into ramp up mode

            // RAMP =
            // AB0-AB1-AB2           0.85*0.85*0.85 = k1
            // AB0-AB1-AB2-(AB3-)MBAM-RAMP 0.85*0.85*0.15 = k2
            // AB0-AB1-(AB2-)MBAM-RAMP     0.85*0.15      = k3
            // AB0-(AB1-)MBAM-RAMP         0.15           = k4

            // RAMP = k1 * (AB0+AB1+AB2) + k2 * (AB0+AB1+AB2+MBAM + RAMP) + k3 * (AB0+AB1+MBAM + RAMP) + k4 * (AB0+MBAM + RAMP)
            // RAMP * (1 - k2 - k3 - k4) = k1 * (AB0+AB1+AB2) + k2 * (AB0+AB1+AB2+MBAM) + k3 * (AB0+AB1+MBAM) + k4 * (AB0+MBAM)
            // RAMP = (AB0+AB1+AB2) + k2 / k1 * (AB0+AB1+AB2+MBAM) + k3 / k1 * (AB0+AB1+MBAM) + k4 / k1 * (AB0+MBAM)

            // RAMP =
            // AB0H-AB1H-AB2H                 0.85*hit*0.85*hit*0.85*hit = k1
            // AB0H-AB1H-AB2H-(AB3-)MBAM-RAMP 0.85*hit*0.85*hit*0.15*hit = k2
            // AB0H-AB1H-(AB2-)MBAM-RAMP      0.85*hit*0.15*hit          = k3
            // AB0H-(AB1-)MBAM-RAMP           0.15*hit                   = k4
            // AB0H-AB1H-AB2M-RAMP            0.85*hit*0.85*hit*miss     = k5
            // AB0H-AB1M-RAMP                 0.85*hit*miss              = k6
            // AB0M-RAMP                      miss                       = k7

            // RAMP = k1 * (AB0H+AB1H+AB2H) + k2 * (AB0H+AB1H+AB2H+AB3+MBAM + RAMP) + k3 * (AB0H+AB1H+AB2+MBAM + RAMP) + k4 * (AB0H+AB1+MBAM + RAMP) + k5 * (AB0H+AB1H+AB2M + RAMP) + k6 * (AB0H+AB1M + RAMP) + k7 * (AB0M + RAMP)
            // RAMP = (AB0H+AB1H+AB2H) + k2 / k1 * (AB0H+AB1H+AB2H+AB3+MBAM) + k3 / k1 * (AB0H+AB1H+AB2+MBAM) + k4 / k1 * (AB0H+AB1+MBAM) + k5 / k1 * (AB0H+AB1H+AB2M) + k6 / k1 * (AB0H+AB1M) + k7 / k1 * (AB0M)

            // AB3H                 0.85*hit
            // AB3H-(AB3-)MBAM-RAMP 0.15*hit
            // AB3M-RAMP            (1-hit)

            Spell AB0 = castingState.GetSpell(SpellId.ArcaneBlast0);
            Spell AB1 = castingState.GetSpell(SpellId.ArcaneBlast1);
            Spell AB2 = castingState.GetSpell(SpellId.ArcaneBlast2);
            AB3 = castingState.GetSpell(SpellId.ArcaneBlast3);
            Spell MBAM = castingState.GetSpell(SpellId.ArcaneMissilesMB);
            Spell MBAM2 = castingState.GetSpell(SpellId.ArcaneMissilesMB2);
            Spell MBAM3 = castingState.GetSpell(SpellId.ArcaneMissilesMB3);

            MB = 0.04f * castingState.MageTalents.MissileBarrage;
            hit = AB3.HitRate;
            miss = 1 - hit;

            if (MB == 0.0)
            {
                // TODO take hit rate into account
                // if we don't have barrage then this degenerates to AB

                AddSpell(needsDisplayCalculations, AB3, 1);
                Calculate();
            }
            else
            {
                MB3 = MB / (1 - MB);
                MB4 = MB / (1 - MB) / (1 - MB);
                MB5 = MB / (1 - MB) / (1 - MB) / (1 - MB);

                //AB3 0.85

                //AB3-MBAM-RAMP 0.15
                AddSpell(needsDisplayCalculations, AB3, MB);
                AddSpell(needsDisplayCalculations, AB3, MB); // account for latency
                AddSpell(needsDisplayCalculations, MBAM3, MB);
                AddSpell(needsDisplayCalculations, AB0, MB);
                AddSpell(needsDisplayCalculations, AB1, MB);
                AddSpell(needsDisplayCalculations, AB2, MB);

                AddSpell(needsDisplayCalculations, AB0, MB * MB3);
                AddSpell(needsDisplayCalculations, AB1, MB * MB3);
                AddSpell(needsDisplayCalculations, AB2, MB * MB3);
                AddSpell(needsDisplayCalculations, AB3, MB * MB3); // account for latency
                AddSpell(needsDisplayCalculations, MBAM3, MB * MB3);

                AddSpell(needsDisplayCalculations, AB0, MB * MB4);
                AddSpell(needsDisplayCalculations, AB1, MB * MB4);
                AddSpell(needsDisplayCalculations, AB2, MB * MB4); // account for latency
                AddSpell(needsDisplayCalculations, MBAM3, MB * MB4);

                AddSpell(needsDisplayCalculations, AB0, MB * MB5);
                AddSpell(needsDisplayCalculations, AB1, MB * MB5); // account for latency
                AddSpell(needsDisplayCalculations, MBAM2, MB * MB5);

                AddSpell(needsDisplayCalculations, AB3, 1 - MB);

                Calculate();
            }
        }
Пример #26
0
 public Spell GetSpell(CastingState castingState, bool pom, bool brainFreeze)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     if (brainFreeze)
     {
         spell.CostAmplifier = 0;
     }
     spell.CalculateDerivedStats(castingState, false, pom || brainFreeze, true);
     return spell;
 }
Пример #27
0
        public virtual void Calculate(CastingState castingState)
        {
            this.castingState = castingState;

            BasePeriodicDamage   = template.BasePeriodicDamage;
            BaseCastTime         = template.BaseCastTime;
            CostModifier         = template.BaseCostModifier;
            CostAmplifier        = template.BaseCostAmplifier;
            DirectDamageModifier = template.BaseDirectDamageModifier;
            DotDamageModifier    = template.BaseDotDamageModifier * castingState.Solver.DarkIntentDotDamageAmplifier;
            if (MagicSchool == MagicSchool.Fire || MagicSchool == MagicSchool.FrostFire)
            {
                DotDamageModifier *= (1 + castingState.FlashburnBonus);
            }
            if (castingState.PowerInfusion)
            {
                CostModifier -= 0.2f;                             // don't have any information on this, going by best guess
            }
            if (castingState.ArcanePower)
            {
                if (castingState.Solver.Mage4T12)
                {
                    CostAmplifier *= 0.9f;
                }
                else
                {
                    CostModifier += 0.1f;
                }
            }
            InterruptProtection = template.BaseInterruptProtection;

            SpellModifier         = template.BaseSpellModifier * castingState.StateSpellModifier;
            AdditiveSpellModifier = template.BaseAdditiveSpellModifier + castingState.StateAdditiveSpellModifier;
            CritBonus             = template.CritBonus;
            CritRate = template.BaseCritRate + castingState.StateCritRate;

            switch (MagicSchool)
            {
            case MagicSchool.Arcane:
                RawSpellDamage = castingState.ArcaneSpellPower;
                break;

            case MagicSchool.Fire:
                RawSpellDamage = castingState.FireSpellPower;
                break;

            case MagicSchool.FrostFire:
                RawSpellDamage = castingState.FrostFireSpellPower;
                break;

            case MagicSchool.Frost:
                RawSpellDamage = castingState.FrostSpellPower;
                break;

            case MagicSchool.Nature:
                RawSpellDamage = castingState.NatureSpellPower;
                break;

            case MagicSchool.Shadow:
                RawSpellDamage = castingState.ShadowSpellPower;
                break;

            case MagicSchool.Holy:
                RawSpellDamage = castingState.HolySpellPower;
                break;
            }
            RawSpellDamage *= (1 + castingState.BaseStats.BonusSpellPowerMultiplier);

            AreaEffect    = template.AreaEffect;
            AreaEffectDot = template.AreaEffectDot;
        }
Пример #28
0
 public Spell GetSpell(CastingState castingState, bool manualClearcasting, bool clearcastingActive, bool pom)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     if (manualClearcasting) spell.CalculateManualClearcasting(true, false, clearcastingActive);
     if (castingState.Frozen)
     {
         spell.SpellModifier *= (1f + 0.1f * castingState.MageTalents.Shatter);
     }
     spell.CalculateDerivedStats(castingState, false, pom, false);
     if (manualClearcasting) spell.CalculateManualClearcastingCost(castingState.Solver, false, true, false, clearcastingActive);
     return spell;
 }
Пример #29
0
 public override Spell GetSpell(CastingState castingState)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     if (castingState.Frozen)
     {
         spell.SpellModifier *= (1f + 0.1f * castingState.MageTalents.Shatter);
     }
     spell.CalculateDerivedStats(castingState);
     return spell;
 }
Пример #30
0
 public override Spell GetSpell(CastingState castingState)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     spell.CalculateDerivedStats(castingState);
     // 70% absorbed, 30% negated
     // number of negates until absorb is distributed negative binomial
     // mean number of negated is then (1-p)/p = 0.3 / 0.7 times the absorb value
     // however on average it can't be more than (1-p) * incoming damage
     float q = 0f;
     float absorb = castingState.CalculationOptions.GetSpellValue(2.32399988174438f) + spellPowerCoefficient * castingState.ArcaneSpellPower;
     spell.Absorb = absorb;
     // in 3.3.3 warding doesn't count as absorb for IA, assume that we'll get to normal absorb at least once in 30 sec (i.e. we're not lucky enough to continue proccing warding for the whole 30 sec)
     float dps = (float)(castingState.Solver.IncomingDamageDpsFire + castingState.Solver.IncomingDamageDpsArcane + castingState.Solver.IncomingDamageDpsFrost);
     spell.TotalAbsorb = Math.Min(absorb, 30f * dps);
     //spell.TotalAbsorb = Math.Min((1 + q / (1 - q)) * absorb, 30f * dps);
     spell.AverageCost -= Math.Min(q / (1 - q) * absorb, q * 30f * dps);
     return spell;
 }
Пример #31
0
 public virtual Spell GetSpell(CastingState castingState, bool spammedDot)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     spell.CalculateDerivedStats(castingState, false, false, spammedDot);
     return spell;
 }
Пример #32
0
 public virtual Spell GetSpell(CastingState castingState, bool clearcastingActive)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     spell.CalculateManualClearcasting(true, false, clearcastingActive);
     spell.CalculateDerivedStats(castingState);
     spell.CalculateManualClearcastingCost(castingState.Solver, false, true, false, clearcastingActive);
     return spell;
 }
Пример #33
0
 public Spell GetSpell(CastingState castingState, float arcaneBlastDebuff)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     //if (castingState.CalculationOptions.ModePTR)
     //{
     //    spell.CostAmplifier = 1f - 0.25f * castingState.MageTalents.ImprovedArcaneExplosion;
     //}
     spell.AdditiveSpellModifier += arcaneBlastDamageMultiplier * arcaneBlastDebuff;
     spell.CalculateDerivedStats(castingState);
     return spell;
 }
Пример #34
0
 public void CalculateDerivedStats(CastingState castingState, bool outOfFiveSecondRule, bool pom, bool spammedDot)
 {
     CalculateDerivedStats(castingState, outOfFiveSecondRule, pom, spammedDot, false, false, false, false);
 }
Пример #35
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;
            }
        }
Пример #36
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;

            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;
            }
        }
Пример #37
0
        public ABABarSlow(bool needsDisplayCalculations, CastingState castingState)
            : base(needsDisplayCalculations, castingState)
        {
            float MB;
            float X;
            float S0;
            float S1;
            // TODO not updated for 3.0.8 mode, consider deprecated?
            Name = "ABABarSlow";

            Spell AB = castingState.GetSpell(SpellId.ArcaneBlast0);
            Spell ABar = castingState.MaintainSnareState.GetSpell(SpellId.ArcaneBarrage);
            Spell MBAM = castingState.MaintainSnareState.GetSpell(SpellId.ArcaneMissilesMB);
            Spell Slow = castingState.GetSpell(SpellId.Slow);

            MB = 0.04f * castingState.MageTalents.MissileBarrage;

            //S0
            //AB-Pause-ABar     1 - X  (1 - MB) * (1 - MB) => S0, 1 - (1 - MB) * (1 - MB) => S1
            //AB-Slow-ABar      X

            //S1
            //MBAM-Pause-ABar   1 - X  (1 - MB) => S0, MB => S1
            //MBAM-Slow-ABar    X

            //S0 + S1 = 1
            //S0a = (1 - X) * ((1 - MB) * (1 - MB) * S0 + (1 - MB) * S1)
            //S0b = X * ((1 - MB) * (1 - MB) * S0 + (1 - MB) * S1)
            //S1a = (1 - X) * ((1 - (1 - MB) * (1 - MB)) * S0 + MB * S1)
            //S1b = X * ((1 - (1 - MB) * (1 - MB)) * S0 + MB * S1)

            //S0 = (1 - MB) * (1 - MB) * S0 + (1 - MB) * S1
            //S1 = (1 - (1 - MB) * (1 - MB)) * S0 + MB * S1

            //S0 * (1 - (1 - MB) * (1 - MB) + (1 - MB)) = (1 - MB)

            //S0 = (1 - MB) / (1 + (1 - MB) * MB)
            //S1 = (2 - MB) * MB / (1 + (1 - MB) * MB)

            //time casting slow / all time casting = time(Slow) / 15

            //value = (1 - X) * S0 * (value(AB) + value(ABar) + value(Pause)) + X * S0 * (value(AB) + value(ABar) + value(Slow)) + (1 - X) * S1 * (value(MBAM) + value(ABar) + value(Pause)) * X * S1 * (value(MBAM) + value(ABar) + value(Slow))
            //value = S0 * value(AB-ABar) + X * S0 * value(Slow) + S1 * value(MBAM-ABar) + X * S1 * value(Slow) + (1 - X) * value(Pause)
            //value = S0 * value(AB-ABar) + S1 * value(MBAM-ABar) + X * value(Slow) + (1 - X) * value(Pause)

            // X = (S0 * time(AB-ABar) + S1 * time(MBAM-ABar) + time(Pause)) / (15 - time(Slow) + time(Pause))

            S0 = (1 - MB) / (1 + (1 - MB) * MB);
            S1 = (2 - MB) * MB / (1 + (1 - MB) * MB);
            float Pause = 0.0f;
            if (AB.CastTime + ABar.CastTime < 3.0) Pause = 3.0f + castingState.CalculationOptions.LatencyGCD - AB.CastTime - ABar.CastTime;
            X = (S0 * (AB.CastTime + ABar.CastTime) + S1 * (MBAM.CastTime + ABar.CastTime) + Pause) / (15.0f - Slow.CastTime + Pause);

            //AB-ABar
            AddSpell(needsDisplayCalculations, ABar, (1 - X) * S0);
            AddSpell(needsDisplayCalculations, AB, (1 - X) * S0);
            if (AB.CastTime + ABar.CastTime < 3.0) AddPause(3.0f + castingState.CalculationOptions.LatencyGCD - AB.CastTime - ABar.CastTime, (1 - X) * S0);

            //ABar-MBAM
            AddSpell(needsDisplayCalculations, ABar, (1 - X) * S1);
            AddSpell(needsDisplayCalculations, MBAM, (1 - X) * S1);
            if (MBAM.CastTime + ABar.CastTime < 3.0) AddPause(3.0f + castingState.CalculationOptions.LatencyGCD - MBAM.CastTime - ABar.CastTime, (1 - X) * S1);

            //AB-ABar-Slow
            AddSpell(needsDisplayCalculations, ABar, X * S0);
            AddSpell(needsDisplayCalculations, AB, X * S0);
            AddSpell(needsDisplayCalculations, Slow, X * S0);
            if (AB.CastTime + ABar.CastTime + Slow.CastTime < 3.0) AddPause(3.0f + castingState.CalculationOptions.LatencyGCD - AB.CastTime - ABar.CastTime - Slow.CastTime, X * S0);

            //ABar-MBAM-Slow
            AddSpell(needsDisplayCalculations, ABar, X * S1);
            AddSpell(needsDisplayCalculations, MBAM, X * S1);
            AddSpell(needsDisplayCalculations, Slow, X * S1);
            if (MBAM.CastTime + ABar.CastTime + Slow.CastTime < 3.0) AddPause(3.0f + castingState.CalculationOptions.LatencyGCD - MBAM.CastTime - ABar.CastTime - Slow.CastTime, X * S1);

            Calculate();
        }
Пример #38
0
 public float GetEffectAverageDamage(CastingState castingState)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     spell.CalculateAverageDamage(castingState.Solver, 0, false, false);
     return spell.AverageDamage;
 }
Пример #39
0
        public AB2ABarMBAM(bool needsDisplayCalculations, CastingState castingState)
            : base(needsDisplayCalculations, castingState)
        {
            float K1, K2, K3, K4;
            Name = "AB2ABarMBAM";

            Spell AB0 = castingState.GetSpell(SpellId.ArcaneBlast0);
            Spell AB1 = castingState.GetSpell(SpellId.ArcaneBlast1);
            Spell ABar = castingState.GetSpell(SpellId.ArcaneBarrage);
            Spell ABar2 = castingState.GetSpell(SpellId.ArcaneBarrage2);
            Spell MBAM = castingState.GetSpell(SpellId.ArcaneMissilesMB);
            Spell MBAM2 = castingState.GetSpell(SpellId.ArcaneMissilesMB2);

            float MB = 0.04f * castingState.MageTalents.MissileBarrage;

            //S0:
            //AB0-AB1-ABar2            (1 - MB) * (1 - MB) * (1 - MB)
            //AB0-AB1-ABar2-MBAM       (1 - MB) * (1 - (1 - MB) * (1 - MB))
            //AB0-AB1-MBAM2-ABar       MB * (1 - MB)
            //AB0-AB1-MBAM2-ABar-MBAM  MB * MB


            K1 = (1 - MB) * (1 - MB) * (1 - MB);
            K2 = (1 - MB) * (1 - (1 - MB) * (1 - MB));
            K3 = MB * (1 - MB);
            K4 = MB * MB;

            //AB0-AB1-ABar2
            AddSpell(needsDisplayCalculations, AB0, K1);
            AddSpell(needsDisplayCalculations, AB1, K1);
            if (AB0.CastTime + AB1.CastTime + ABar2.CastTime < 3.0) AddPause(3.0f + castingState.CalculationOptions.LatencyGCD - AB0.CastTime - AB1.CastTime - ABar2.CastTime, K1);
            AddSpell(needsDisplayCalculations, ABar2, K1);

            //AB0-AB1-ABar2-MBAM
            AddSpell(needsDisplayCalculations, AB0, K2);
            AddSpell(needsDisplayCalculations, AB1, K2);
            if (AB0.CastTime + AB1.CastTime + ABar2.CastTime < 3.0) AddPause(3.0f + castingState.CalculationOptions.LatencyGCD - AB0.CastTime - AB1.CastTime - ABar2.CastTime, K2);
            AddSpell(needsDisplayCalculations, ABar2, K2);
            AddSpell(needsDisplayCalculations, MBAM, K2);

            //AB0-AB1-MBAM2-ABar
            AddSpell(needsDisplayCalculations, AB0, K3);
            AddSpell(needsDisplayCalculations, AB1, K3);
            AddSpell(needsDisplayCalculations, MBAM2, K3);
            if (AB0.CastTime + AB1.CastTime + MBAM2.CastTime + ABar.CastTime < 3.0) AddPause(3.0f + castingState.CalculationOptions.LatencyGCD - AB0.CastTime - AB1.CastTime - ABar.CastTime - MBAM2.CastTime, K3);
            AddSpell(needsDisplayCalculations, ABar, K3);

            //AB0-AB1-MBAM2-ABar
            AddSpell(needsDisplayCalculations, AB0, K4);
            AddSpell(needsDisplayCalculations, AB1, K4);
            AddSpell(needsDisplayCalculations, MBAM2, K4);
            if (AB0.CastTime + AB1.CastTime + MBAM2.CastTime + ABar.CastTime < 3.0) AddPause(3.0f + castingState.CalculationOptions.LatencyGCD - AB0.CastTime - AB1.CastTime - ABar.CastTime - MBAM2.CastTime, K4);
            AddSpell(needsDisplayCalculations, ABar, K4);
            AddSpell(needsDisplayCalculations, MBAM, K4);

            Calculate();
        }
Пример #40
0
        /*public Spell GetSpell(CastingState castingState, bool barrage, int arcaneBlastDebuff)
        {
            return GetSpell(castingState, barrage, arcaneBlastDebuff, 5);
        }*/

        public Spell GetSpell(CastingState castingState, bool barrage, int arcaneBlastDebuff)
        {
            Spell spell = Spell.New(this, castingState.Solver);
            spell.Calculate(castingState);
            //spell.BaseCastTime = ticks;
            if (barrage)
            {
                spell.BaseCastTime *= 0.5f;
                spell.CostModifier = Math.Max(spell.CostModifier - 1, 0);
            }
            spell.SpellModifier *= (1 + tormentTheWeak * castingState.SnaredTime);
            spell.AdditiveSpellModifier += arcaneBlastDamageMultiplier * arcaneBlastDebuff;
            //spell.SpellModifier *= ticks / 5.0f;
            spell.CalculateDerivedStats(castingState);
            return spell;
        }
Пример #41
0
        private void CycleGeneratorComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            string armor = "Molten Armor";
            CalculationOptionsMage calculationOptions = character.CalculationOptions as CalculationOptionsMage;
            CalculationsMage       calculations       = (CalculationsMage)Calculations.Instance;
            Solver solver = new Solver(character, calculationOptions, false, false, false, 0, armor, false, false, false, false);

            solver.Initialize(null);

            switch ((string)((ComboBoxItem)CycleGeneratorComboBox.SelectedItem).Content)
            {
            case "Arcane (MB/2T10 duration collapsed)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new ArcaneCycleGenerator(castingState, true, false, true, false, true, true);
                break;

            case "Arcane (Arcane Power, MB/2T10 duration collapsed)":
                castingState = new CastingState(solver, (int)StandardEffect.ArcanePower, false, 0);
                generator    = new ArcaneCycleGenerator(castingState, true, false, true, false, true, true);
                break;

            case "Arcane (ABar on cooldown only, MB/2T10 duration/ABar cooldown collapsed)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new ArcaneCycleGenerator(castingState, true, true, true, true, true, true);
                break;

            case "Arcane (no ABar, MB duration collapsed)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new ArcaneCycleGenerator(castingState, false, true, true, true, false, true);
                break;

            case "Arcane Movement (average 1 per 10 sec for 1 sec)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new ArcaneMovementCycleGenerator(castingState, 0.1f, 1.0f, true, false, true, true, true, true);
                break;

            case "Frost":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator(castingState, true, false);
                break;

            case "Frost (no latency combos)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator(castingState, false, false);
                break;

            case "Frost+Deep Freeze":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, true, true, 30.0f, false, false, false);
                break;

            case "Frost+Deep Freeze (no latency combos)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, false, true, 30.0f, false, false, false);
                break;

            case "Frost+Deep Freeze (2T10 duration collapsed)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, true, true, 30.0f, true, false, false);
                break;

            case "Frost+Deep Freeze (2T10 duration collapsed, no latency combos)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, false, true, 30.0f, true, false, false);
                break;

            case "Frost+Deep Freeze (FOF instants on last charge only, 2T10 duration collapsed)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, true, true, 30.0f, true, true, false);
                break;

            case "Frost+Deep Freeze (FOF instants on last charge only, 2T10 duration collapsed, no latency combos)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, false, true, 30.0f, true, true, false);
                break;

            case "Frost+Deep Freeze (FOF instants on last charge only, 2T10 duration collapsed, 10 sec Deep Freeze cooldown)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, true, true, 10.0f, true, true, false);
                break;

            case "Frost+Deep Freeze (FOF instants on last charge only, 2T10 duration collapsed, 10 sec Deep Freeze cooldown, no latency combos)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, false, true, 10.0f, true, true, false);
                break;

            case "FrostFFB+Deep Freeze":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, true, true, 30.0f, false, false, true);
                break;

            case "FrostFFB+Deep Freeze (no latency combos)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, false, true, 30.0f, false, false, true);
                break;

            case "FrostFFB+Deep Freeze (2T10 duration collapsed)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, true, true, 30.0f, true, false, true);
                break;

            case "FrostFFB+Deep Freeze (2T10 duration collapsed, no latency combos)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, false, true, 30.0f, true, false, true);
                break;

            case "FrostFFB+Deep Freeze (FOF instants on last charge only, 2T10 duration collapsed)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, true, true, 30.0f, true, true, true);
                break;

            case "FrostFFB+Deep Freeze (FOF instants on last charge only, 2T10 duration collapsed, no latency combos)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, false, true, 30.0f, true, true, true);
                break;

            case "FrostFFB+Deep Freeze (FOF instants on last charge only, 2T10 duration collapsed, 10 sec Deep Freeze cooldown)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, true, true, 10.0f, true, true, true);
                break;

            case "FrostFFB+Deep Freeze (FOF instants on last charge only, 2T10 duration collapsed, 10 sec Deep Freeze cooldown, no latency combos)":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGenerator2(castingState, false, true, 10.0f, true, true, true);
                break;

            case "Fire":
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FireCycleGenerator(castingState);
                break;
            }

            if (castingState == null || generator == null)
            {
                return;
            }

            if (character.Ranged != null)
            {
                wand = new WandTemplate(solver, (MagicSchool)character.Ranged.Item.DamageType, character.Ranged.Item.MinDamage, character.Ranged.Item.MaxDamage, character.Ranged.Item.Speed).GetSpell(castingState);
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendLine(generator.StateDescription);

            sb.AppendLine("");
            for (int i = 0; i < generator.ControlOptions.Length; i++)
            {
                sb.Append(i);
                sb.Append(": ");
                sb.Append(generator.StateList[Array.IndexOf(generator.ControlIndex, i)]);
                sb.Append(": ");
                foreach (var kvp in generator.SpellMap[i])
                {
                    sb.Append(kvp.Value);
                    sb.Append("=");
                    sb.Append(kvp.Key);
                    sb.Append("  ");
                }
                sb.AppendLine();
            }

            Description.Text   = sb.ToString();
            ControlString.Text = new string('0', generator.ControlOptions.Length);

            //ControlString.SelectAll();
            ControlString.Focus();

            Calculate_Click(null, null);
        }
Пример #42
0
 public Spell GetSpell(CastingState castingState, int debuff, bool manualClearcasting, bool clearcastingActive, bool pom)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     spell.BaseCastTime -= debuff * 0.1f * castTimeMultiplier;
     if (manualClearcasting) spell.CalculateManualClearcasting(true, false, clearcastingActive);
     spell.AdditiveSpellModifier += arcaneBlastDamageMultiplier * debuff;
     spell.SpellModifier *= (1 + tormentTheWeak * castingState.SnaredTime);
     spell.CostModifier += arcaneBlastManaMultiplier * debuff;
     spell.CalculateDerivedStats(castingState, false, pom, false, true, false, false);
     if (manualClearcasting) spell.CalculateManualClearcastingCost(castingState.Solver, false, true, false, clearcastingActive);
     return spell;
 }
Пример #43
0
        public float CalculateCastTime(CastingState castingState, float interruptProtection, float critRate, bool pom, float baseCastTime, out float channelReduction)
        {
            CalculationOptionsMage calculationOptions = castingState.CalculationOptions;
            float castingSpeed       = castingState.CastingSpeed;
            float spellHasteRating   = castingState.SpellHasteRating;
            float levelScalingFactor = calculationOptions.LevelScalingFactor;
            float hasteFactor        = levelScalingFactor / 1000f;
            float rootCastingSpeed   = castingSpeed / (1 + spellHasteRating * hasteFactor);

            float InterruptFactor = 0f;
            float maxPushback     = 0f;

            if (calculationOptions.InterruptFrequency > 0f)
            {
                // interrupt factors of more than once per spell are not supported, so put a limit on it (up to twice is probably approximately correct)
                InterruptFactor = Math.Min(calculationOptions.InterruptFrequency, 2 * castingSpeed / baseCastTime);
                if (castingState.IcyVeins)
                {
                    interruptProtection = 1;
                }
                maxPushback = 0.5f * Math.Max(0, 1 - interruptProtection);
                if (Channeled)
                {
                    maxPushback = 0.0f;
                }
            }

            if (pom)
            {
                baseCastTime = 0.0f;
            }

            float globalCooldown = Math.Max(Spell.GlobalCooldownLimit, 1.5f / castingSpeed);
            float latency;

            if (baseCastTime <= 1.5f || Instant)
            {
                latency = calculationOptions.LatencyGCD;
            }
            else if (Channeled)
            {
                latency = calculationOptions.LatencyChannel;
            }
            else
            {
                latency = calculationOptions.LatencyCast;
            }
            float averageTicks = Ticks;
            float castTime     = baseCastTime / castingSpeed;

            if (calculationOptions.Beta && Channeled)
            {
                float tickCastTime = castTime / Ticks;
                averageTicks = (float)Math.Floor(baseCastTime / tickCastTime);
                castTime     = averageTicks * tickCastTime;
            }
            if (InterruptFactor > 0)
            {
                castTime = castTime * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f) * maxPushback * InterruptFactor;
            }
            castTime += latency;
            float gcdcap = globalCooldown + calculationOptions.LatencyGCD;

            if (castTime < gcdcap)
            {
                castTime = gcdcap;
            }
            channelReduction = 0.0f;

            if (!calculationOptions.AdvancedHasteProcs)
            {
                for (int i = 0; i < castingState.Solver.HasteRatingEffectsCount; i++)
                {
                    SpecialEffect effect   = castingState.Solver.HasteRatingEffects[i];
                    float         procs    = 0.0f;
                    int           triggers = 0;
                    switch (effect.Trigger)
                    {
                    case Trigger.DamageSpellCast:
                    case Trigger.SpellCast:
                        procs    = CastProcs;
                        triggers = (int)procs;
                        break;

                    case Trigger.DamageSpellCrit:
                    case Trigger.SpellCrit:
                        procs    = critRate * averageTicks;
                        triggers = (int)averageTicks;
                        break;

                    case Trigger.DamageSpellHit:
                    case Trigger.SpellHit:
                        procs    = HitRate * averageTicks;
                        triggers = (int)averageTicks;
                        break;
                    }
                    if (procs == 0.0f)
                    {
                        continue;
                    }
                    float procHaste = effect.Stats.HasteRating;
                    if (effect.Cooldown >= effect.Duration)
                    {
                        // hasted casttime
                        float speed = rootCastingSpeed * (1 + (spellHasteRating + procHaste) * hasteFactor);
                        float gcd   = Math.Max(Spell.GlobalCooldownLimit, 1.5f / speed);
                        float cast  = baseCastTime / speed;
                        if (calculationOptions.Beta && Channeled)
                        {
                            float tickCastTime = cast / Ticks;
                            cast = (float)Math.Floor(baseCastTime / tickCastTime) * tickCastTime;
                        }
                        if (InterruptFactor > 0)
                        {
                            cast = cast * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f) * maxPushback * InterruptFactor;
                        }
                        cast  += latency;
                        gcdcap = gcd + calculationOptions.LatencyGCD;
                        if (cast < gcdcap)
                        {
                            cast = gcdcap;
                        }

                        float castsAffected = 0;
                        if (triggers > 1)
                        {
                            // multi tick spell (i.e. AM)
                            for (int c = 0; c < triggers; c++)
                            {
                                castsAffected += (float)Math.Ceiling((effect.Duration - c * castTime / triggers) / cast);
                            }
                            castsAffected /= triggers;
                        }
                        else
                        {
                            // single tick spell
                            castsAffected = (float)Math.Ceiling(effect.Duration / cast); // should the first proc be already hasted?
                        }
                        float effectiveDuration = castsAffected * cast;
                        // this isn't completely accurate, we should have made a separate SpecialEffect and change the actual duration
                        // but performance would hurt so this'll have to do
                        spellHasteRating += procHaste * (effectiveDuration / effect.Duration) * effect.GetAverageUptime(castTime / triggers, procs / triggers, 3.0f, calculationOptions.FightDuration);
                        //spellHasteRating += procHaste * castsAffected * cast / (effect.Cooldown + castTime / procs / effect.Chance);
                        //Haste += castingState.BasicStats.SpellHasteFor6SecOnCast_15_45 * 6f / (45f + CastTime / CastProcs / 0.15f);
                        castingSpeed = rootCastingSpeed * (1 + spellHasteRating * hasteFactor);

                        globalCooldown = Math.Max(Spell.GlobalCooldownLimit, 1.5f / castingSpeed);
                        castTime       = baseCastTime / castingSpeed;
                        if (calculationOptions.Beta && Channeled)
                        {
                            float tickCastTime = castTime / Ticks;
                            averageTicks = (float)Math.Floor(baseCastTime / tickCastTime);
                            castTime     = averageTicks * tickCastTime;
                        }
                        if (InterruptFactor > 0)
                        {
                            castTime = castTime * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f) * maxPushback * InterruptFactor;
                        }
                        castTime += latency;
                        gcdcap    = globalCooldown + calculationOptions.LatencyGCD;
                        if (castTime < gcdcap)
                        {
                            castTime = gcdcap;
                        }
                    }
                    else if (effect.Cooldown == 0 && (effect.Trigger == Trigger.SpellCrit || effect.Trigger == Trigger.DamageSpellCrit))
                    {
                        float rawHaste = spellHasteRating;

                        castingSpeed /= (1 + spellHasteRating / 1000f * levelScalingFactor);
                        float proccedSpeed    = castingSpeed * (1 + (rawHaste + procHaste) / 1000f * levelScalingFactor);
                        float proccedGcd      = Math.Max(Spell.GlobalCooldownLimit, 1.5f / proccedSpeed);
                        float proccedCastTime = baseCastTime / proccedSpeed;
                        float proccedTicks    = averageTicks;
                        if (calculationOptions.Beta && Channeled)
                        {
                            float tickCastTime = proccedCastTime / Ticks;
                            proccedTicks = (float)Math.Floor(baseCastTime / tickCastTime);
                            castTime     = proccedTicks * tickCastTime;
                        }
                        if (InterruptFactor > 0)
                        {
                            proccedCastTime = proccedCastTime * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f) * maxPushback * InterruptFactor;
                        }
                        proccedCastTime += latency;
                        if (proccedCastTime < proccedGcd + calculationOptions.LatencyGCD)
                        {
                            proccedCastTime = proccedGcd + calculationOptions.LatencyGCD;
                        }
                        int chancesToProc = (int)(((int)Math.Floor(effect.Duration / proccedCastTime) + 1) * proccedTicks);
                        if (!(Instant || pom))
                        {
                            chancesToProc -= 1;
                        }
                        if (AreaEffect)
                        {
                            chancesToProc *= calculationOptions.AoeTargets;
                        }
                        spellHasteRating = rawHaste + procHaste * (1 - (float)Math.Pow(1 - effect.Chance * critRate, chancesToProc));
                        //Haste = rawHaste + castingState.BasicStats.SpellHasteFor5SecOnCrit_50 * ProcBuffUp(1 - (float)Math.Pow(1 - 0.5f * CritRate, HitProcs), 5, CastTime);
                        castingSpeed  *= (1 + spellHasteRating / 1000f * levelScalingFactor);
                        globalCooldown = Math.Max(Spell.GlobalCooldownLimit, 1.5f / castingSpeed);
                        castTime       = baseCastTime / castingSpeed;
                        if (calculationOptions.Beta && Channeled)
                        {
                            float tickCastTime = castTime / Ticks;
                            averageTicks = (float)Math.Floor(baseCastTime / tickCastTime);
                            castTime     = averageTicks * tickCastTime;
                        }
                        if (InterruptFactor > 0)
                        {
                            castTime = castTime * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f + latency) * maxPushback * InterruptFactor;
                        }
                        castTime += latency;
                        if (castTime < globalCooldown + calculationOptions.LatencyGCD)
                        {
                            castTime = globalCooldown + calculationOptions.LatencyGCD;
                        }
                    }
                }
                // on use stacking items
                for (int i = 0; i < castingState.Solver.StackingHasteEffectCooldownsCount; i++)
                {
                    EffectCooldown effectCooldown = castingState.Solver.StackingHasteEffectCooldowns[i];
                    if (castingState.EffectsActive(effectCooldown.Mask))
                    {
                        Stats stats = effectCooldown.SpecialEffect.Stats;
                        for (int j = 0; j < stats._rawSpecialEffectDataSize; j++)
                        {
                            SpecialEffect effect    = stats._rawSpecialEffectData[j];
                            float         procHaste = effect.Stats.HasteRating;
                            if (procHaste > 0)
                            {
                                float procs = 0.0f;
                                switch (effect.Trigger)
                                {
                                case Trigger.DamageSpellCast:
                                case Trigger.SpellCast:
                                    procs = CastProcs;
                                    break;

                                case Trigger.DamageSpellCrit:
                                case Trigger.SpellCrit:
                                    procs = critRate * averageTicks;
                                    break;

                                case Trigger.DamageSpellHit:
                                case Trigger.SpellHit:
                                    procs = HitRate * averageTicks;
                                    break;
                                }
                                if (procs == 0.0f)
                                {
                                    continue;
                                }
                                // until they put in some good trinkets with such effects just do a quick dirty calculation
                                float effectHasteRating;
                                if (procs > averageTicks)
                                {
                                    // some 100% on cast procs, happens because AM has 6 cast procs and only 5 ticks
                                    effectHasteRating = effect.GetAverageStackSize(castTime / procs, 1.0f, 3.0f, effectCooldown.SpecialEffect.Duration) * procHaste;
                                }
                                else
                                {
                                    effectHasteRating = effect.GetAverageStackSize(castTime / averageTicks, procs / averageTicks, 3.0f, effectCooldown.SpecialEffect.Duration) * procHaste;
                                }

                                castingSpeed     /= (1 + spellHasteRating / 1000f * levelScalingFactor);
                                spellHasteRating += effectHasteRating;
                                castingSpeed     *= (1 + spellHasteRating / 1000f * levelScalingFactor);

                                globalCooldown = Math.Max(Spell.GlobalCooldownLimit, 1.5f / castingSpeed);
                                castTime       = baseCastTime / castingSpeed;
                                if (calculationOptions.Beta && Channeled)
                                {
                                    float tickCastTime = castTime / Ticks;
                                    averageTicks = (float)Math.Floor(baseCastTime / tickCastTime);
                                    castTime     = averageTicks * tickCastTime;
                                }
                                if (InterruptFactor > 0)
                                {
                                    castTime = castTime * (1 + InterruptFactor * maxPushback) - (maxPushback * 0.5f) * maxPushback * InterruptFactor;
                                }
                                castTime += latency;
                                if (castTime < globalCooldown + calculationOptions.LatencyGCD)
                                {
                                    castTime = globalCooldown + calculationOptions.LatencyGCD;
                                }
                            }
                        }
                    }
                }
            }

            if (Channeled && calculationOptions.Beta)
            {
                channelReduction = 1 - averageTicks / Ticks;
            }

            // channeled pushback
            if (Channeled && InterruptFactor > 0)
            {
                int maxLostTicks = (int)Math.Ceiling(averageTicks * 0.25f * Math.Max(0, 1 - interruptProtection));
                // pushbacks that happen up to pushbackCastTime cut the cast time to pushbackCastTime
                // pushbacks that happen after just terminate the channel
                // [---|---X---|---|---]
                castTime -= latency;
                float tickFactor = 0;
                for (int i = 0; i < maxLostTicks; i++)
                {
                    tickFactor += InterruptFactor * castTime / averageTicks * (i + 1) / averageTicks;
                }
                tickFactor += InterruptFactor * (averageTicks - maxLostTicks) * castTime / averageTicks * maxLostTicks / averageTicks;
                if (calculationOptions.Beta)
                {
                    channelReduction = 1 - averageTicks * (1 - tickFactor) / Ticks;
                }
                else
                {
                    channelReduction = tickFactor;
                }
                castTime = castTime * (1 - tickFactor) + latency;
            }

            return(castTime);
        }
Пример #44
0
 public override Spell GetSpell(CastingState castingState)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     spell.RawSpellDamage *= 0.4f;
     spell.CalculateDerivedStats(castingState);
     spell.DamagePerSpellPower *= 0.4f;
     return spell;
 }
Пример #45
0
 public Spell GetSpell(CastingState castingState, bool aoe)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     if (aoe)
     {
         spell.AreaEffect = true; // but leave aoe dot at false
     }
     /*if (castingState.MageTalents.GlyphOfLivingBomb)
     {
         spell.DotDamageModifier = (1 + Math.Max(0.0f, Math.Min(1.0f, castingState.FireCritRate)) * (castingState.FireCritBonus - 1));
     }*/
     spell.CalculateDerivedStats(castingState, false, false, false);
     /*if (castingState.MageTalents.GlyphOfLivingBomb)
     {
         spell.IgniteProcs *= 5; // 4 ticks can proc ignite in addition to the explosion
         // add ignite contribution from dot
         if (castingState.Solver.NeedsDisplayCalculations)
         {
             float igniteFactor = spell.SpellModifier * spell.HitRate * spell.PartialResistFactor * Math.Max(0.0f, Math.Min(1.0f, castingState.FireCritRate)) * castingState.FireCritBonus * castingState.Solver.IgniteFactor / (1 + castingState.Solver.IgniteFactor);
             spell.IgniteDamage += spell.BasePeriodicDamage * igniteFactor;
             spell.IgniteDamagePerSpellPower += spell.DotDamageCoefficient * igniteFactor;
         }
     }*/
     return spell;
 }
Пример #46
0
 public Spell GetSpell(CastingState castingState, float arcaneBlastDebuff)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     spell.AdditiveSpellModifier += arcaneBlastDamageMultiplier * arcaneBlastDebuff;
     spell.SpellModifier *= (1 + tormentTheWeak * castingState.SnaredTime);
     spell.CalculateDerivedStats(castingState);
     return spell;
 }
Пример #47
0
 public void CalculateDerivedStats(CastingState castingState)
 {
     CalculateDerivedStats(castingState, false, false, true, false, false, false, false);
 }
Пример #48
0
 public override Spell GetSpell(CastingState castingState)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     // remove overestimated part of ignite contribution from on use spell power effects
     // let's try a fixed percentage reduction, meaning it's very unlikely that if on use effect
     // is active during combustion that it had any effect on building ignite
     float extraSpellPower = 0.8f * castingState.StateSpellPower;
     spell.BasePeriodicDamage -= IgniteContributionCoefficient * extraSpellPower;
     spell.CalculateDerivedStats(castingState, false, false, false);
     spell.CastTime = 0;
     return spell;
 }
Пример #49
0
 public virtual void CalculateDerivedStats(CastingState castingState, bool outOfFiveSecondRule, bool pom, bool spammedDot, bool round, bool forceHit, bool forceMiss)
 {
     CalculateDerivedStats(castingState, outOfFiveSecondRule, pom, spammedDot, round, forceHit, forceMiss, false);
 }
Пример #50
0
 public override Spell GetSpell(CastingState castingState)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     if (castingState.Frozen)
     {
         spell.SpellModifier *= 2;
         spell.AdditiveSpellModifier += 0.25f;
     }
     spell.CalculateDerivedStats(castingState);
     return spell;
 }
Пример #51
0
 public Spell GetSpell(CastingState castingState, bool pom)
 {
     Spell spell = Spell.New(this, castingState.Solver);
     spell.Calculate(castingState);
     spell.CalculateDerivedStats(castingState, false, pom, false, false, false, false, true);
     return spell;
 }
Пример #52
0
        private void CycleGeneratorComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            string armor;
            Solver solver;
            CalculationOptionsMage calculationOptions = character.CalculationOptions as CalculationOptionsMage;
            CalculationsMage       calculations       = (CalculationsMage)Calculations.Instance;

            switch ((string)((ComboBoxItem)CycleGeneratorComboBox.SelectedItem).Content)
            {
            case "Arcane":
            default:
                armor  = "Mage Armor";
                solver = new Solver(character, calculationOptions, false, false, false, 0, armor, false, false, false, false, true, false, false);
                solver.Initialize(null);
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new ArcaneCycleGeneratorBeta(castingState, true, false, false, false);
                break;

            case "Arcane Dragonwrath":
                armor  = "Mage Armor";
                solver = new Solver(character, calculationOptions, false, false, false, 0, armor, false, false, false, false, true, false, false);
                solver.Initialize(null);
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new ArcaneCycleGeneratorLegendary(castingState, true, false, false, false);
                break;

            case "Arcane Hyper Regen":
                armor  = "Mage Armor";
                solver = new Solver(character, calculationOptions, false, false, false, 0, armor, false, false, false, false, true, false, false);
                solver.Initialize(null);
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new ArcaneCycleGeneratorBeta(castingState, true, false, false, true);
                break;

            case "Arcane AOE":
                armor  = "Mage Armor";
                solver = new Solver(character, calculationOptions, false, false, false, 0, armor, false, false, false, false, true, false, false);
                solver.Initialize(null);
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new ArcaneAOECycleGenerator(castingState, true, false, false);
                break;

            case "Frost":
                armor  = "Molten Armor";
                solver = new Solver(character, calculationOptions, false, false, false, 0, armor, false, false, false, false, true, false, false);
                solver.Initialize(null);
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGeneratorBeta(castingState, false, 0.0f, false, 0.0f);
                break;

            case "Frost+Deep Freeze":
                armor  = "Molten Armor";
                solver = new Solver(character, calculationOptions, false, false, false, 0, armor, false, false, false, false, true, false, false);
                solver.Initialize(null);
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGeneratorBeta(castingState, true, 30.0f, false, 0.0f);
                break;

            case "Frost+Freeze":
                armor  = "Molten Armor";
                solver = new Solver(character, calculationOptions, false, false, false, 0, armor, false, false, false, false, true, false, false);
                solver.Initialize(null);
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGeneratorBeta(castingState, false, 0.0f, true, 25.0f);
                break;

            case "Frost+Freeze+Deep Freeze":
                armor  = "Molten Armor";
                solver = new Solver(character, calculationOptions, false, false, false, 0, armor, false, false, false, false, true, false, false);
                solver.Initialize(null);
                castingState = new CastingState(solver, 0, false, 0);
                generator    = new FrostCycleGeneratorBeta(castingState, true, 30.0f, true, 25.0f);
                break;
            }

            if (castingState == null || generator == null)
            {
                return;
            }

            if (character.Ranged != null)
            {
                wand = new WandTemplate(solver, (MagicSchool)character.Ranged.Item.DamageType, character.Ranged.Item.MinDamage, character.Ranged.Item.MaxDamage, character.Ranged.Item.Speed).GetSpell(castingState);
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendLine(generator.StateDescription);

            sb.AppendLine();
            for (int i = 0; i < generator.ControlOptions.Length; i++)
            {
                sb.Append(i);
                sb.Append(": ");
                sb.Append(generator.StateList[Array.IndexOf(generator.ControlIndex, i)]);
                sb.Append(": ");
                List <int> keys = new List <int>();
                foreach (var kvp in generator.SpellMap[i])
                {
                    keys.Add(generator.SpellList.IndexOf(kvp.Key));
                }
                keys.Sort();
                foreach (var key in keys)
                {
                    sb.Append(key);
                    sb.Append("=");
                    sb.Append(generator.SpellList[key]);
                    sb.Append("  ");
                }
                sb.AppendLine();
            }

            sb.AppendLine();
            sb.AppendLine("Transitions:");
            sb.AppendLine();

            for (int i = 0; i < generator.ControlOptions.Length; i++)
            {
                foreach (var kvp in generator.SpellMap[i])
                {
                    sb.Append(i);
                    sb.Append(": ");
                    sb.Append(kvp.Key);
                    sb.Append(" => ");

                    List <int> list = new List <int>();
                    for (int s = 0; s < generator.ControlIndex.Length; s++)
                    {
                        if (generator.ControlIndex[s] == i)
                        {
                            foreach (CycleControlledStateTransition transition in generator.StateList[s].Transitions)
                            {
                                string n;
                                if (transition.Spell != null)
                                {
                                    n = transition.Spell.Name;
                                }
                                else
                                {
                                    n = "Pause";
                                }
                                if (n == kvp.Key)
                                {
                                    int target = generator.ControlIndex[transition.TargetState.Index];
                                    if (!list.Contains(target))
                                    {
                                        list.Add(target);
                                    }
                                }
                            }
                        }
                    }

                    list.Sort();
                    sb.Append(string.Join(",", list));

                    sb.AppendLine();
                }
            }


            Description.Text   = sb.ToString();
            ControlString.Text = generator.ConvertCycleNameInternalToEasy(new string('0', generator.ControlOptions.Length));

            //ControlString.SelectAll();
            ControlString.Focus();

            Calculate_Click(null, null);
        }