示例#1
0
        public CatRotationCalculation CalculateRotation(int roarCP, BiteUsage biteUsage)
        {
            float totalEnergy =
                100f +                                                                                                                            //Starting Energy
                FightDuration * MeleeStats.EnergyRegenPerSecond +                                                                                 //Passive Regen
                (FightDuration / MeleeStats.AttackSpeed) * MeleeStats.ClearcastChance * 40f +                                                     //Clearcast Energy
                ((FightDuration - 10f) / TigersFuryStats.Cooldown) * (TigersFuryStats.EnergyGenerated + TigersFuryStats.MaxEnergyIncrease / 4f) + //TF Energy
                ((FightDuration - 10f) / BerserkStats.Cooldown) * (TigersFuryStats.MaxEnergyIncrease / 4f);                                       //Berserk Energy
            float tfUptime      = ((((FightDuration - 10f) / TigersFuryStats.Cooldown) * TigersFuryStats.Duration) / FightDuration);
            float berserkUptime = 0f;

            if (Abilities.Stats.Tier_12_4pc)
            {
                berserkUptime = (((FightDuration - 10f) / BerserkStats.Cooldown) * BerserkStats.Duration + derive4t12BerserkExt(roarCP)) / FightDuration;
            }
            else
            {
                berserkUptime = (((FightDuration - 10f) / BerserkStats.Cooldown) * BerserkStats.Duration) / FightDuration;
            }
            float energyCostMultiplier = 1f - (1f - BerserkStats.EnergyCostMultiplier) * berserkUptime;             //Average energy cost reduction due to Berserk
            float damageMultiplier     = 1f + TigersFuryStats.DamageMultiplier * tfUptime;

            //Usage Counts
            MangleCount               = 0f;
            RakeInitCount             = 0f;
            RakeTickCount             = 0f;
            RipTickCount              = 0f;
            ShredCount                = 0f;
            BiteCount                 = 0f;
            RavageCount               = 0f;
            RoarCount                 = 0f;
            RavageAbove80PercentCount = 0f;
            EnergyCostMultiplier      = energyCostMultiplier;
            EnergyRemaining           = totalEnergy / energyCostMultiplier;
            CPRemaining               = 0f;

            //Lead up time
            float currentLeadUpTime = 0f;
            float ripLeadUpTime     = 0f;
            float rakeLeadUpTime    = 0f;
            float roarLeadUpTime    = 0f;
            {
                float cpToRip = 5f + _chanceExtraCP[4];

                //Ravage Twice
                Ravage(1, false, true);
                if (RavageStats.FeralChargeCooldown > 0f)
                {
                    Ravage(1, true, true);
                }
                currentLeadUpTime += 3f;

                //Mangle, if necessary
                if (MangleUsage != MangleUsage.None)
                {
                    Mangle(1);
                    currentLeadUpTime++;
                }

                //Rake
                if (CPRemaining < cpToRip)
                {
                    float rakesUsed = Math.Min(1f, (cpToRip - CPRemaining) / RakeStats.ComboPointsGenerated);
                    Rake(rakesUsed);
                    RakeTick(rakesUsed * RakeStats.Duration / 3f);
                    rakeLeadUpTime     = currentLeadUpTime + rakesUsed * RakeStats.Duration;                 //Raked [rakesUsed] times to start with, future rakes will start after this one
                    currentLeadUpTime += rakesUsed;
                }

                //Shred, if you still need cp
                if (CPRemaining < cpToRip)
                {
                    float shredsUsed = (cpToRip - CPRemaining) / ShredStats.ComboPointsGenerated;
                    Shred(shredsUsed);
                    currentLeadUpTime += shredsUsed;
                }

                ripLeadUpTime = currentLeadUpTime;                 //Rips will start from here
                if (RakeInitCount == 0)
                {
                    rakeLeadUpTime = currentLeadUpTime + 1;              //if we haven't Raked already, Rakes will start here
                }
                roarLeadUpTime = currentLeadUpTime + 2f;                 //Roar will start after the next CPG
            }

            //1. Rip
            float ripUptimeSec = FightDuration - ripLeadUpTime;

            RipCount = (ripUptimeSec - FightDuration * BiteStats.RipRefreshChanceOnTargetsBelow25Percent * (Abilities.Stats.Tier_13_2_piece ? PercentOfTimeBelow60Percent : PercentOfTimeBelow25Percent)) / RipStats.Duration;
            Rip(RipCount);
            RipTick(ripUptimeSec / 2f);

            //2. Roar
            float roarCPAverage = roarCP + _chanceExtraCP[roarCP - 1];

            float[] roarDurationsByCP = new float[] { RoarStats.Duration1CP, RoarStats.Duration2CP,
                                                      RoarStats.Duration3CP, RoarStats.Duration4CP, RoarStats.Duration5CP };
            float roarDurationAverage =
                roarDurationsByCP[roarCP - 1] * (1f - _chanceExtraCP[roarCP - 1]) +
                roarDurationsByCP[Math.Min(4, roarCP)] * _chanceExtraCP[roarCP - 1];

            float roarUptimeSec         = FightDuration - roarLeadUpTime;
            float meleeDamageMultiplier = 1f + RoarStats.MeleeDamageMultiplier * roarUptimeSec / FightDuration;

            RoarCount = roarUptimeSec / roarDurationAverage;
            Roar(RoarCount, roarCPAverage);

            //3. Rake
            float rakeUptimeSec = FightDuration - rakeLeadUpTime;
            float rakeCount     = rakeUptimeSec / RakeStats.Duration;

            Rake(rakeCount);
            RakeTick(rakeUptimeSec / 3f);

            //4. Ravage
            if (RavageStats.FeralChargeCooldown > 0f)
            {
                float ravageCount = (FightDuration / RavageStats.FeralChargeCooldown) + (Abilities.Stats.Tier_13_4_piece ? (FightDuration / TigersFuryStats.Cooldown) : 0f);
                Ravage(ravageCount * PercentOfTimeAbove80Percent, true, true);
                Ravage(ravageCount * (1f - PercentOfTimeAbove80Percent), true, false);
            }

            //5. Mangle
            float mangleCount = 0f;

            switch (MangleUsage)
            {
            case MangleUsage.MaintainMangle:
                mangleCount = FightDuration / MangleStats.Duration - 1f;                         //Minus the one from the lead up
                break;

            case MangleUsage.Maintain4T11:
                mangleCount = FightDuration / 27f - 1f;                         //Minus the one from the lead up
                break;

            case MangleUsage.MangleDPS:
                if (CPRemaining < 0)
                {
                    mangleCount = -CPRemaining / MangleStats.ComboPointsGenerated;
                }
                break;
            }
            Mangle(mangleCount);

            //6. Shred
            if (CPRemaining < 0)
            {
                float shredCount = 0;
                if (MangleUsage == MangleUsage.MangleDPS)
                {
                    shredCount = 0;
                }
                else
                {
                    shredCount = -CPRemaining / ShredStats.ComboPointsGenerated;
                }
                Shred(shredCount);
            }

            //7. Bite
            float biteCPAverage          = 5f + _chanceExtraCP[4];                                                                //TODO: Need to try 4CP Bites
            float biteExtraEnergyPercent = BiteStats.MaxExtraEnergy == 0f ? 0f : (biteUsage == BiteUsage.HighEnergy ? 1f : 0.1f); //Assume unglyphed that you use an average of 3.5 extra energy per bite

            if (biteUsage != BiteUsage.None)
            {
                if (CPRemaining > 0)
                {
                    float biteCount = CPRemaining / biteCPAverage;
                    Bite(biteCount, biteCPAverage, biteExtraEnergyPercent);
                }
            }

            //8. Convert Extra Energy to Shreds+Bite
            if (biteUsage == BiteUsage.None)
            {
                float shredCount = EnergyRemaining / ShredStats.EnergyCost;
                Shred(shredCount);
            }
            else
            {
                float shredsPerBite = biteCPAverage / ShredStats.ComboPointsGenerated;
                float setEnergy     = shredsPerBite * ShredStats.EnergyCost + BiteStats.EnergyCost + BiteStats.MaxExtraEnergy * biteExtraEnergyPercent;
                float setCount      = EnergyRemaining / setEnergy;
                Shred(setCount * shredsPerBite);
                Bite(setCount, biteCPAverage, biteExtraEnergyPercent);
            }

            //Calculate Damage Done
            float totalDamage = 0f;

            totalDamage += MeleeStats.DPSAverage * FightDuration * meleeDamageMultiplier;
            totalDamage += MeleeStats.DPSFurySwipesAverage * FightDuration;
            totalDamage += MangleCount * MangleStats.DamageAverage;
            totalDamage += RakeInitCount * RakeStats.DamageAverage;
            totalDamage += RakeTickCount * RakeStats.DamageTickAverage;
            totalDamage += RipTickCount * RipStats.DamageTickAverage;
            totalDamage += ShredCount * ShredStats.DamageAverage;
            totalDamage += BiteCount * BiteStats.DamageAverage;
            totalDamage += RavageCount * RavageStats.DamageAverage;
            totalDamage += RavageAbove80PercentCount * RavageStats.DamageAbove80PercentAverage;


            return(new CatRotationCalculation()
            {
                TotalDamage = totalDamage,
                DPS = totalDamage / FightDuration,
                BiteUsage = biteUsage,
                RoarCP = roarCP,

                MeleeDPS = MeleeStats.DPSAverage * meleeDamageMultiplier + MeleeStats.DPSFurySwipesAverage,
                MangleCount = MangleCount,
                RakeInitCount = RakeInitCount,
                RakeTickCount = RakeTickCount,
                RipTickCount = RipTickCount,
                ShredCount = ShredCount,
                BiteCount = BiteCount,
                RavageCount = RavageCount,
                RavageAbove80PercentCount = RavageAbove80PercentCount,
            });
        }
		public CatRotationCalculation CalculateRotation(int roarCP, BiteUsage biteUsage)
		{
			float totalEnergy =
				100f + //Starting Energy
				FightDuration * MeleeStats.EnergyRegenPerSecond + //Passive Regen
				(FightDuration / MeleeStats.AttackSpeed) * MeleeStats.ClearcastChance * 40f + //Clearcast Energy
				((FightDuration - 10f) / TigersFuryStats.Cooldown) * (TigersFuryStats.EnergyGenerated + TigersFuryStats.MaxEnergyIncrease / 4f) + //TF Energy
				((FightDuration - 10f) / BerserkStats.Cooldown) * (TigersFuryStats.MaxEnergyIncrease / 4f); //Berserk Energy
			float tfUptime = ((((FightDuration - 10f) / TigersFuryStats.Cooldown) * TigersFuryStats.Duration) / FightDuration);
            float berserkUptime = 0f;
            if (Abilities.Stats.Tier_12_4pc)
                berserkUptime = (((FightDuration - 10f) / BerserkStats.Cooldown) * BerserkStats.Duration + derive4t12BerserkExt(roarCP)) / FightDuration;
            else
                berserkUptime = (((FightDuration - 10f) / BerserkStats.Cooldown) * BerserkStats.Duration) / FightDuration;
			float energyCostMultiplier = 1f - (1f - BerserkStats.EnergyCostMultiplier) * berserkUptime; //Average energy cost reduction due to Berserk
			float damageMultiplier = 1f + TigersFuryStats.DamageMultiplier * tfUptime;

			//Usage Counts
			MangleCount = 0f;
			RakeInitCount = 0f;
			RakeTickCount = 0f;
			RipTickCount = 0f;
			ShredCount = 0f;
			BiteCount = 0f;
			RavageCount = 0f;
            RoarCount = 0f;
			RavageAbove80PercentCount = 0f;
			EnergyCostMultiplier = energyCostMultiplier;
			EnergyRemaining = totalEnergy / energyCostMultiplier;
			CPRemaining = 0f;

			//Lead up time
			float currentLeadUpTime = 0f;
			float ripLeadUpTime = 0f;
			float rakeLeadUpTime = 0f;
			float roarLeadUpTime = 0f;
			{
				float cpToRip = 5f + _chanceExtraCP[4];

				//Ravage Twice
				Ravage(1, false, true);
				if (RavageStats.FeralChargeCooldown > 0f)
					Ravage(1, true, true);
				currentLeadUpTime += 3f;

				//Mangle, if necessary
				if (MangleUsage != MangleUsage.None)
				{
					Mangle(1);
					currentLeadUpTime++;
				}

				//Rake
				if (CPRemaining < cpToRip)
				{
					float rakesUsed = Math.Min(1f, (cpToRip - CPRemaining) / RakeStats.ComboPointsGenerated);
					Rake(rakesUsed);
					RakeTick(rakesUsed * RakeStats.Duration / 3f);
					rakeLeadUpTime = currentLeadUpTime + rakesUsed * RakeStats.Duration; //Raked [rakesUsed] times to start with, future rakes will start after this one
					currentLeadUpTime += rakesUsed;
				}

				//Shred, if you still need cp
				if (CPRemaining < cpToRip)
				{
					float shredsUsed = (cpToRip - CPRemaining) / ShredStats.ComboPointsGenerated;
					Shred(shredsUsed);
					currentLeadUpTime += shredsUsed;
				}

				ripLeadUpTime = currentLeadUpTime; //Rips will start from here
				if (RakeInitCount == 0)
					rakeLeadUpTime = currentLeadUpTime + 1; //if we haven't Raked already, Rakes will start here
				roarLeadUpTime = currentLeadUpTime + 2f; //Roar will start after the next CPG
			}

			//1. Rip
			float ripUptimeSec = FightDuration - ripLeadUpTime;
            RipCount = (ripUptimeSec - FightDuration * BiteStats.RipRefreshChanceOnTargetsBelow25Percent * (Abilities.Stats.Tier_13_2_piece ? PercentOfTimeBelow60Percent : PercentOfTimeBelow25Percent)) / RipStats.Duration;
            Rip(RipCount);
			RipTick(ripUptimeSec / 2f);

			//2. Roar
			float roarCPAverage = roarCP + _chanceExtraCP[roarCP - 1];
			float[] roarDurationsByCP = new float[]{RoarStats.Duration1CP, RoarStats.Duration2CP, 
				RoarStats.Duration3CP, RoarStats.Duration4CP, RoarStats.Duration5CP};
			float roarDurationAverage =
				roarDurationsByCP[roarCP - 1] * (1f - _chanceExtraCP[roarCP - 1]) +
				roarDurationsByCP[Math.Min(4, roarCP)] * _chanceExtraCP[roarCP - 1];

			float roarUptimeSec = FightDuration - roarLeadUpTime;
			float meleeDamageMultiplier = 1f + RoarStats.MeleeDamageMultiplier * roarUptimeSec / FightDuration;
			RoarCount = roarUptimeSec / roarDurationAverage;
            Roar(RoarCount, roarCPAverage);

			//3. Rake
			float rakeUptimeSec = FightDuration - rakeLeadUpTime;
			float rakeCount = rakeUptimeSec / RakeStats.Duration;
			Rake(rakeCount);
			RakeTick(rakeUptimeSec / 3f);

			//4. Ravage
			if (RavageStats.FeralChargeCooldown > 0f)
			{
				float ravageCount = (FightDuration / RavageStats.FeralChargeCooldown) + (Abilities.Stats.Tier_13_4_piece ? (FightDuration / TigersFuryStats.Cooldown) : 0f);
				Ravage(ravageCount * PercentOfTimeAbove80Percent, true, true);
				Ravage(ravageCount * (1f - PercentOfTimeAbove80Percent), true, false);
			}

			//5. Mangle
			float mangleCount = 0f;
			switch (MangleUsage)
			{
				case MangleUsage.MaintainMangle:
					mangleCount = FightDuration / MangleStats.Duration - 1f; //Minus the one from the lead up
					break;
				case MangleUsage.Maintain4T11:
					mangleCount = FightDuration / 27f - 1f; //Minus the one from the lead up
					break;
                case MangleUsage.MangleDPS:
                    if (CPRemaining < 0)
                        mangleCount = -CPRemaining / MangleStats.ComboPointsGenerated;
                    break;
			}
			Mangle(mangleCount);

			//6. Shred
			if (CPRemaining < 0)
			{
                float shredCount = 0;
                if (MangleUsage == MangleUsage.MangleDPS)
                    shredCount = 0;
                else
				    shredCount = -CPRemaining / ShredStats.ComboPointsGenerated;
				Shred(shredCount);
			}

			//7. Bite
			float biteCPAverage = 5f + _chanceExtraCP[4]; //TODO: Need to try 4CP Bites
			float biteExtraEnergyPercent = BiteStats.MaxExtraEnergy == 0f ? 0f : (biteUsage == BiteUsage.HighEnergy ? 1f : 0.1f); //Assume unglyphed that you use an average of 3.5 extra energy per bite
			if (biteUsage != BiteUsage.None)
			{
				if (CPRemaining > 0)
				{
					float biteCount = CPRemaining / biteCPAverage;
					Bite(biteCount, biteCPAverage, biteExtraEnergyPercent);
				}
			}

			//8. Convert Extra Energy to Shreds+Bite
			if (biteUsage == BiteUsage.None)
			{
				float shredCount = EnergyRemaining / ShredStats.EnergyCost;
				Shred(shredCount);
			}
			else
			{
				float shredsPerBite = biteCPAverage / ShredStats.ComboPointsGenerated;
				float setEnergy = shredsPerBite * ShredStats.EnergyCost + BiteStats.EnergyCost + BiteStats.MaxExtraEnergy * biteExtraEnergyPercent;
				float setCount = EnergyRemaining / setEnergy;
				Shred(setCount * shredsPerBite);
				Bite(setCount, biteCPAverage, biteExtraEnergyPercent);
			}

			//Calculate Damage Done
			float totalDamage = 0f;
			totalDamage += MeleeStats.DPSAverage * FightDuration * meleeDamageMultiplier;
			totalDamage += MeleeStats.DPSFurySwipesAverage * FightDuration;
			totalDamage += MangleCount * MangleStats.DamageAverage;
			totalDamage += RakeInitCount * RakeStats.DamageAverage;
			totalDamage += RakeTickCount * RakeStats.DamageTickAverage;
			totalDamage += RipTickCount * RipStats.DamageTickAverage;
			totalDamage += ShredCount * ShredStats.DamageAverage;
			totalDamage += BiteCount * BiteStats.DamageAverage;
			totalDamage += RavageCount * RavageStats.DamageAverage;
			totalDamage += RavageAbove80PercentCount * RavageStats.DamageAbove80PercentAverage;


			return new CatRotationCalculation()
			{
				TotalDamage = totalDamage,
				DPS = totalDamage / FightDuration,
				BiteUsage = biteUsage,
				RoarCP = roarCP,

				MeleeDPS = MeleeStats.DPSAverage * meleeDamageMultiplier + MeleeStats.DPSFurySwipesAverage,
				MangleCount = MangleCount,
				RakeInitCount = RakeInitCount,
				RakeTickCount = RakeTickCount,
				RipTickCount = RipTickCount,
				ShredCount = ShredCount,
				BiteCount = BiteCount,
				RavageCount = RavageCount,
				RavageAbove80PercentCount = RavageAbove80PercentCount,
			};
		}