Example #1
0
        private void Calculate()
        {
            float globalCooldownSpeed = Lookup.GlobalCooldownSpeed(Character, true);
            float baseBossAttackSpeed = Options.BossAttackSpeed * (1.0f - Stats.BossAttackSpeedMultiplier);
            float baseWeaponSpeed     = Lookup.WeaponSpeed(Character, Stats);
            float bossAttackHaste     = 0.0f;
            float weaponHaste         = 0.0f;

            BossAttackSpeed = baseBossAttackSpeed;
            WeaponSpeed     = baseWeaponSpeed;

            if (Options.UseParryHaste)
            {
                // Iterate on this a few times to get a 'stable' result
                for (int j = 0; j < 4; j++)
                {
                    weaponHaste     = DefendTable.Parry * 0.24f * (WeaponSpeed / BossAttackSpeed);
                    bossAttackHaste = AttackTable.Parry * 0.24f * ((BossAttackSpeed / WeaponSpeed) + (BossAttackSpeed / globalCooldownSpeed));

                    WeaponSpeed     = baseWeaponSpeed / (1.0f + weaponHaste);
                    BossAttackSpeed = baseBossAttackSpeed / (1.0f + bossAttackHaste);
                }
            }
            else
            {
                // Simple adjust to the defender's speed if the attacker isn't parry hasted
                WeaponSpeed /= (1.0f + (DefendTable.Parry * 0.24f * (WeaponSpeed / BossAttackSpeed)));
            }
        }
Example #2
0
        private void Calculate()
        {
            Dictionary <Ability, float> modelAbilities = new Dictionary <Ability, float>();
            float modelLength        = 0.0f;
            float modelThreat        = 0.0f;
            float modelDamage        = 0.0f;
            float modelCrits         = 0.0f;
            float modelHits          = 0.0f;
            float modelWeaponAttacks = 0.0f;

            // Rotation Auto-Detection
            if (AttackModelMode == AttackModelMode.Optimal)
            {
                if (Talents.Shockwave == 1 && Talents.SwordAndBoard == 3)
                {
                    if (Talents.ImprovedRevenge > 0)
                    {
                        AttackModelMode = AttackModelMode.FullProtectionRevenge;
                    }
                    else
                    {
                        AttackModelMode = AttackModelMode.FullProtection;
                    }
                }
                else if (Talents.SwordAndBoard == 3)
                {
                    if (Talents.ImprovedRevenge > 0)
                    {
                        AttackModelMode = AttackModelMode.SwordAndBoardRevenge;
                    }
                    else
                    {
                        AttackModelMode = AttackModelMode.SwordAndBoard;
                    }
                }
                else if (Talents.Devastate == 1)
                {
                    if (Talents.ImprovedRevenge > 0)
                    {
                        AttackModelMode = AttackModelMode.DevastateRevenge;
                    }
                    else
                    {
                        AttackModelMode = AttackModelMode.Devastate;
                    }
                }
                else if (Talents.UnrelentingAssault == 2)
                {
                    AttackModelMode = AttackModelMode.UnrelentingAssault;
                }
                else
                {
                    AttackModelMode = AttackModelMode.Basic;
                }
            }

            switch (AttackModelMode)
            {
            case AttackModelMode.Basic:
            {
                // Basic Rotation
                // Shield Slam -> Revenge -> Sunder Armor -> Sunder Armor
                Name        = "Basic Cycle";
                Description = "Shield Slam -> Revenge -> Sunder Armor -> Sunder Armor";
                modelLength = 6.0f;
                modelAbilities.Add(Ability.ShieldSlam, 1.0f);
                modelAbilities.Add(Ability.Revenge, 1.0f);
                modelAbilities.Add(Ability.SunderArmor, 2.0f);
                break;
            }

            case AttackModelMode.Devastate:
            {
                // Devastate Rotation
                // Shield Slam -> Devastate -> Devastate -> Devastate
                Name        = "Devastate";
                Description = "Shield Slam -> Devastate -> Devastate -> Devastate";
                modelLength = 6.0f;
                modelAbilities.Add(Ability.ShieldSlam, 1.0f);
                modelAbilities.Add(Ability.Devastate, 3.0f);
                break;
            }

            case AttackModelMode.DevastateRevenge:
            {
                // Devastate + Revenge Rotation
                // Shield Slam -> Revenge -> Devastate -> Devastate
                Name        = "Devastate + Revenge";
                Description = "Shield Slam -> Revenge -> Devastate -> Devastate";
                modelLength = 6.0f;
                modelAbilities.Add(Ability.ShieldSlam, 1.0f);
                modelAbilities.Add(Ability.Revenge, 1.0f);
                modelAbilities.Add(Ability.Devastate, 2.0f);
                break;
            }

            case AttackModelMode.SwordAndBoard:
            case AttackModelMode.SwordAndBoardRevenge:
            case AttackModelMode.FullProtection:
            case AttackModelMode.FullProtectionRevenge:
            {
                // Sword And Board Rotation
                // Shield Slam > Revenge > Devastate
                // The distribution of abilities in the model is as follows:
                // 1.0 * Shield Slam + 0.73 * Revenge + 1.4596 * Devastate
                // -or-
                // Shield Slam > Revenge > Devastate @ 3s Shield Slam Cooldown > Shockwave > Devastate
                // The distribution of abilities in the model is as follows:
                // 1.0 * Shield Slam + 0.73 * Revenge + 1.133 * Devastate + 0.3266 * (Concussion Blow/Shockwave/Devastate)
                // The cycle length is 4.7844s, abilities per cycle is 3.1896
                Name        = "Sword and Board";
                Description = "Shield Slam > ";
                if (AttackModelMode == AttackModelMode.SwordAndBoardRevenge || AttackModelMode == AttackModelMode.FullProtectionRevenge)
                {
                    Name        += " + Revenge";
                    Description += "Revenge > Devastate";
                }
                else
                {
                    Description += "Devastate";
                }
                if (AttackModelMode == AttackModelMode.FullProtection || AttackModelMode == AttackModelMode.FullProtectionRevenge)
                {
                    Name        += " + Shockwave";
                    Description += "\n@ 1.5s Shield Slam Cooldown: Shockwave > Devastate";
                }

                modelLength = 4.7844f;
                modelAbilities.Add(Ability.ShieldSlam, 1.0f);
                modelAbilities.Add(Ability.Devastate, 1.133f);

                // Add Revenge, if applicable
                if (AttackModelMode == AttackModelMode.SwordAndBoardRevenge || AttackModelMode == AttackModelMode.FullProtectionRevenge)
                {
                    modelAbilities.Add(Ability.Revenge, 0.73f);
                }
                else
                {
                    modelAbilities[Ability.Devastate] += 0.73f;
                }

                // Add Shockwave, if applicable
                if (AttackModelMode == AttackModelMode.FullProtection || AttackModelMode == AttackModelMode.FullProtectionRevenge)
                {
                    modelAbilities.Add(Ability.Shockwave, (0.3266f / 3.0f));
                    modelAbilities[Ability.Devastate] += (0.3266f / 3.0f) * 2.0f;
                }
                else
                {
                    modelAbilities[Ability.Devastate] += 0.3266f;
                }

                break;
            }

            case AttackModelMode.UnrelentingAssault:
            {
                Name        = "Unrelenting Assault";
                Description = "Revenge Only";
                modelLength = 1.0f;
                modelAbilities.Add(Ability.Revenge, 1.0f);
                break;
            }
            }

            // Accumulate base model statistics
            foreach (KeyValuePair <Ability, float> modelAbility in modelAbilities)
            {
                AbilityModel ability = Abilities[modelAbility.Key];
                modelThreat += ability.Threat * modelAbility.Value;
                modelDamage += ability.Damage * modelAbility.Value;
                if (ability.Damage > 0.0f)
                {
                    modelHits  += ability.HitPercentage * modelAbility.Value;
                    modelCrits += ability.CritPercentage * modelAbility.Value;
                }
                if (ability.IsWeaponAttack)
                {
                    modelWeaponAttacks += modelAbility.Value;
                }
            }

            // Simple GCD-Based Latency Adjustment
            modelLength *= Lookup.GlobalCooldownSpeed(Character, true) / Lookup.GlobalCooldownSpeed(Character, false);

            // Weapon Swings/Heroic Strike
            float attackerSwings         = (modelLength / ParryModel.BossAttackSpeed);
            float attackerHits           = DefendTable.AnyHit * attackerSwings;
            float weaponSwings           = modelLength / ParryModel.WeaponSpeed;
            float heroicStrikePercentage = Math.Max(0.0f, Math.Min(1.0f, Options.HeroicStrikeFrequency));

            AbilityModel heroicStrike = Abilities[Ability.HeroicStrike];

            modelThreat += heroicStrike.Threat * weaponSwings * heroicStrikePercentage;
            modelDamage += heroicStrike.Damage * weaponSwings * heroicStrikePercentage;
            modelHits   += heroicStrike.HitPercentage * weaponSwings * heroicStrikePercentage;
            modelCrits  += heroicStrike.CritPercentage * weaponSwings * heroicStrikePercentage;

            AbilityModel whiteSwing = Abilities[Ability.None];

            modelThreat += whiteSwing.Threat * weaponSwings * (1.0f - heroicStrikePercentage);
            modelDamage += whiteSwing.Damage * weaponSwings * (1.0f - heroicStrikePercentage);
            modelCrits  += whiteSwing.CritPercentage * weaponSwings * (1.0f - heroicStrikePercentage);
            modelHits   += whiteSwing.HitPercentage * weaponSwings * (1.0f - heroicStrikePercentage);

            modelWeaponAttacks += weaponSwings;

            // Damage Shield
            AbilityModel damageShield = Abilities[Ability.DamageShield];

            modelThreat += damageShield.Threat * attackerHits;
            modelDamage += damageShield.Damage * attackerHits;

            // Deep Wounds
            AbilityModel deepWounds = Abilities[Ability.DeepWounds];

            modelThreat += deepWounds.Threat * modelCrits;
            modelDamage += deepWounds.Damage * modelCrits;

            // Misc. Power Gains
            modelThreat += DefendTable.DodgeParryBlock * (modelLength / ParryModel.BossAttackSpeed) * 25.0f *
                           (Talents.ShieldSpecialization * 0.2f);
            modelThreat += DefendTable.DodgeParryBlock * (modelLength / ParryModel.BossAttackSpeed) * 1.0f *
                           Lookup.StanceThreatMultipler(Character, Stats) * (Talents.ImprovedDefensiveStance * 0.5f);

            // Vigilance, is already calculated as TPS
            if (Options.UseVigilance)
            {
                modelThreat += Abilities[Ability.Vigilance].Threat * modelLength;
            }

            // Final Per-Second Calculations
            ThreatPerSecond         = modelThreat / modelLength;
            DamagePerSecond         = modelDamage / modelLength;
            WeaponAttacksPerSecond  = modelWeaponAttacks / modelLength;
            HitsPerSecond           = modelHits / modelLength;
            CritsPerSecond          = modelCrits / modelLength;
            AttackerSwingsPerSecond = attackerSwings / modelLength;
            AttackerHitsPerSecond   = attackerHits / modelLength;
        }