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, }; }