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; }
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; }
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; }
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; }
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; }
public FrostCycleGenerator(CastingState castingState, bool useLatencyCombos, bool useDeepFreeze) { this.useLatencyCombos = useLatencyCombos; FrB = castingState.GetSpell(SpellId.Frostbolt); FrBS = castingState.FrozenState.GetSpell(SpellId.Frostbolt); FB = castingState.GetSpell(SpellId.FireballBF); FBS = castingState.FrozenState.GetSpell(SpellId.FireballBF); IL = castingState.GetSpell(SpellId.IceLance); ILS = castingState.FrozenState.GetSpell(SpellId.IceLance); DFS = castingState.FrozenState.GetSpell(SpellId.DeepFreeze); BF = 0.05f * castingState.MageTalents.BrainFreeze; FOF = (castingState.MageTalents.FingersOfFrost == 2 ? 0.15f : 0.07f * castingState.MageTalents.FingersOfFrost); T8 = 0; deepFreeze = useDeepFreeze; GenerateStateDescription(); }
public FrostCycleGeneratorBeta(CastingState castingState, bool useDeepFreeze, float deepFreezeCooldown, bool useFreeze, float freezeCooldown) { this.deepFreezeCooldown = deepFreezeCooldown; this.deepFreeze = useDeepFreeze; this.freezeCooldown = freezeCooldown; this.freeze = useFreeze; FrB = castingState.GetSpell(SpellId.Frostbolt); FrB.Label = "Frostbolt"; FFB = castingState.GetSpell(SpellId.FrostfireBoltBF); FFB.Label = "FrostfireBolt"; FFBF = castingState.FrozenState.GetSpell(SpellId.FrostfireBoltBF); FFBF.Label = "Frozen+FrostfireBolt"; IL = castingState.FrozenState.GetSpell(SpellId.IceLance); IL.Label = "Frozen+IceLance"; DF = castingState.FrozenState.GetSpell(SpellId.DeepFreeze); DF.Label = "Frozen+DeepFreeze"; BF = 0.05f * castingState.MageTalents.BrainFreeze; FOF = (castingState.MageTalents.FingersOfFrost == 3 ? 0.2f : 0.07f * castingState.MageTalents.FingersOfFrost); GenerateStateDescription(); }
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(); }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); cycle.Name = "AB4ABar34AM"; // S0: // AB0-AB1-AB2-AB3-ABar => S0 (1 - MB) * (1 - MB) * (1 - MB) * (1 - MB) * (1 - MB) // AB0-AB1-AB2-AB3-ABar-AB0-AB1-AB2-AM => S0 (1 - MB) * (1 - MB) * (1 - MB) * (1 - (1 - MB) * (1 - MB)) // AB0-AB1-AB2-AB3-AM => S0 (1 - MB) * (1 - MB) * MB // AB0-AB1-AB2-AM => S0 (1 - (1 - MB) * (1 - MB)) Spell AM = castingState.GetSpell(SpellId.ArcaneMissiles); Spell ABar = castingState.GetSpell(SpellId.ArcaneBarrage); float MB = 0.4f; float K2 = (1 - MB) * (1 - MB); float K3 = K2 * (1 - MB); float K4 = K2 * K2; float K0 = K3 * (1 - K2); float K5 = K3 * K2; float ABcast; Spell AB0 = castingState.GetSpell(SpellId.ArcaneBlast0); Spell AB1 = castingState.GetSpell(SpellId.ArcaneBlast1); Spell AB2 = castingState.GetSpell(SpellId.ArcaneBlast2); Spell AB3 = castingState.GetSpell(SpellId.ArcaneBlast3); cycle.AddSpell(needsDisplayCalculations, AB0, 1 + K0); cycle.AddSpell(needsDisplayCalculations, AB1, 1 + K0); cycle.AddSpell(needsDisplayCalculations, AB2, 1 + K0); cycle.AddSpell(needsDisplayCalculations, AB3, K2); ABcast = AB0.CastTime; cycle.AddSpell(needsDisplayCalculations, AM, 1 - K5); cycle.AddSpell(needsDisplayCalculations, ABar, K3); // a bit overkill on pause, but make sure we respect the ABar cooldown if (4 * ABcast + ABar.CastTime < ABar.Cooldown) { cycle.AddPause(ABar.Cooldown - 4 * ABcast - ABar.CastTime, K3); } cycle.Calculate(); return cycle; }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); cycle.Name = "AB2ABar02AMABABar"; if (castingState.Solver.Mage2T10) { // doesn't support haste transferring over several loops // S0: // AB0-AB1-ABar => S0 (1-MB)*(1-MB)*(1-MB) // AB0-AB1-AM-AB0-ABar => S0 (1-MB)*(1-MB)*MB // AM-AB0-ABar => S0 (1 - (1-MB)*(1-MB)) Spell ABar = castingState.GetSpell(SpellId.ArcaneBarrage); Spell AM = castingState.GetSpell(SpellId.ArcaneMissiles); Spell AB0T = castingState.Tier10TwoPieceState.GetSpell(SpellId.ArcaneBlast0); Spell ABarT = castingState.Tier10TwoPieceState.GetSpell(SpellId.ArcaneBarrage); Spell AB0 = castingState.GetSpell(SpellId.ArcaneBlast0); Spell AB1 = castingState.GetSpell(SpellId.ArcaneBlast1); float MB = 0.4f; float K2 = (1 - MB) * (1 - MB); float K3 = K2 * (1 - MB); float K4 = K2 * K2; cycle.AddSpell(needsDisplayCalculations, AB0, K2); cycle.AddSpell(needsDisplayCalculations, AB1, K2); cycle.AddSpell(needsDisplayCalculations, ABar, K3); cycle.AddSpell(needsDisplayCalculations, AM, 1 - K3); cycle.AddSpell(needsDisplayCalculations, AB0T, 1 - K3); cycle.AddSpell(needsDisplayCalculations, ABarT, 1 - K3); if (AB0.CastTime + AB1.CastTime + ABar.CastTime < ABar.Cooldown) { cycle.AddPause(ABar.Cooldown - AB0.CastTime - AB1.CastTime - ABar.CastTime, K3); } if (AM.CastTime + AB0T.CastTime + ABarT.CastTime < ABar.Cooldown) { cycle.AddPause(ABar.Cooldown - AM.CastTime - AB0T.CastTime - ABarT.CastTime, 1 - K2); } } else { Spell ABar = castingState.GetSpell(SpellId.ArcaneBarrage); Spell AM = castingState.GetSpell(SpellId.ArcaneMissiles); Spell AB0 = castingState.GetSpell(SpellId.ArcaneBlast0); Spell AB1 = castingState.GetSpell(SpellId.ArcaneBlast1); float MB = 0.4f; float K2 = (1 - MB) * (1 - MB); float K3 = K2 * (1 - MB); float K4 = K2 * K2; cycle.AddSpell(needsDisplayCalculations, AB0, K2 + 1 - K3); cycle.AddSpell(needsDisplayCalculations, AB1, K2); cycle.AddSpell(needsDisplayCalculations, ABar, K3 + 1 - K3); cycle.AddSpell(needsDisplayCalculations, AM, 1 - K3); if (AB0.CastTime + AB1.CastTime + ABar.CastTime < ABar.Cooldown) { cycle.AddPause(ABar.Cooldown - AB0.CastTime - AB1.CastTime - ABar.CastTime, K3); } if (AM.CastTime + AB0.CastTime + ABar.CastTime < ABar.Cooldown) { cycle.AddPause(ABar.Cooldown - AM.CastTime - AB0.CastTime - ABar.CastTime, 1 - K2); } } cycle.Calculate(); return cycle; }
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(); }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); Spell FB; Spell Sc; Spell Pyro; float K; float X; cycle.Name = "FBScPyro"; cycle.ProvidesScorch = true; FB = castingState.GetSpell(SpellId.Fireball); Sc = castingState.GetSpell(SpellId.Scorch); Pyro = castingState.GetSpell(SpellId.PyroblastPOMDotUptime); Spell PyroSpam = castingState.GetSpell(SpellId.PyroblastPOMSpammed); int averageScorchesNeeded = (int)Math.Ceiling(3f / (float)castingState.MageTalents.ImprovedScorch); int extraScorches = 0; // proportion of time casting non-scorch spells has to be less than gap := (30 - (averageScorchesNeeded + extraScorches)) / (30 - extraScorches) // 0 HS charge: // FB => 0 HS charge (1 - FBcrit) * X // => 1 HS charge FBcrit * X // Sc => 0 HS charge (1 - SCcrit) * (1 - X) // 1 HS charge SCcrit * (1 - X) // 1 HS charge: // FB => 0 HS charge (1 - FBcrit) * X + (1 - H) * FBcrit * X // FBPyro => 0 HS charge H * FBcrit * X // Sc => 0 HS charge (1 - SCcrit) * (1 - X) + (1 - H) * SCcrit * (1 - X) // ScPyro => 0 HS charge H * SCcrit * (1 - X) // S0 = FB0a + FB0b + Sc0a + Sc0b // S1 = FB1 + FBPyro + Sc1 + ScPyro // solve for stationary distribution // FB0a = (1 - FBcrit) * X * S0 // FB0b = FBcrit * X * S0 // Sc0a = (1 - SCcrit) * (1 - X) * S0 // Sc0b = SCcrit * (1 - X) * S0 // FB1 = ((1 - FBcrit) * X + (1 - H) * FBcrit * X) * S1 // FBPyro = H * FBcrit * X * S1 // Sc1 = ((1 - SCcrit) * (1 - X) + (1 - H) * SCcrit * (1 - X)) * S1 // ScPyro = H * SCcrit * (1 - X) * S1 // S0 + S1 = 1 // S0 = FB0a + Sc0a + S1 // S1 = FB0b + Sc0b // S1 = (FBcrit * X + SCcrit * (1 - X)) * S0 // C := (FBcrit * X + SCcrit * (1 - X)) // = X * (FBcrit - SCcrit) + SCcrit // S1 = C * (1 - S1) // S1 = C / (1 + C) // S0 = 1 / (1 + C) // value = (X * value(FB) + (1 - X) * value(Sc)) * 1 / (1 + C) + (X * value(FB) + (1 - X) * value(Sc) + H * (FBcrit * X + SCcrit * (1 - X)) * value(Pyro) / (1 - T8)) * C / (1 + C) // X * value(FB) + (1 - X) * value(Sc) + value(Pyro) * H * C * C / (1 + C) / (1 - T8) // (X * time(FB) + time(Pyro) * H * C * C / (1 + C)) / [X * time(FB) + (1 - X) * time(Sc) + time(Pyro) * H * C * C / (1 + C)] = gap // (X * time(FB) + time(Pyro) * H * C * C / (1 + C)) = gap * [X * time(FB) + time(Pyro) * H * C * C / (1 + C)] + gap * (1 - X) * time(Sc) // (X * time(FB) + time(Pyro) * H * C * C / (1 + C)) * (1 - gap) = gap * (1 - X) * time(Sc) // (X * (1 + C) * time(FB) + time(Pyro) * H * C * C) * (1 - gap) = gap * (1 - X) * (1 + C) * time(Sc) // (X * (1 + C) * time(FB) + time(Pyro) * H * C * C) * (1 - gap) = gap * (1 + C) * time(Sc) - gap * X * (1 + C) * time(Sc) // (X * time(FB) + X * C * time(FB) + time(Pyro) * H * C * C) * (1 - gap) = gap * time(Sc) + gap * C * time(Sc) - gap * X * time(Sc) - gap * X * C * time(Sc) // (X * time(FB) + X * (X * (FBcrit - SCcrit) + SCcrit) * time(FB) + time(Pyro) * H * (X * (FBcrit - SCcrit) + SCcrit) * (X * (FBcrit - SCcrit) + SCcrit)) * (1 - gap) = gap * time(Sc) + gap * (X * (FBcrit - SCcrit) + SCcrit) * time(Sc) - gap * X * time(Sc) - gap * X * (X * (FBcrit - SCcrit) + SCcrit) * time(Sc) // X * time(FB) * (1 - gap) + X * (X * (FBcrit - SCcrit) + SCcrit) * time(FB) * (1 - gap) + time(Pyro) * H * (X * (FBcrit - SCcrit) + SCcrit) * (X * (FBcrit - SCcrit) + SCcrit) * (1 - gap) = gap * time(Sc) + gap * (X * (FBcrit - SCcrit) + SCcrit) * time(Sc) - gap * X * time(Sc) - gap * X * (X * (FBcrit - SCcrit) + SCcrit) * time(Sc) // X * [time(FB) * (1 - gap) + SCcrit * time(FB) * (1 - gap) + 2 * (FBcrit - SCcrit) * SCcrit * time(Pyro) * H * (1 - gap)] + X * X * [(FBcrit - SCcrit) * time(FB) * (1 - gap) + (FBcrit - SCcrit) * (FBcrit - SCcrit) * time(Pyro) * H * (1 - gap)] + SCcrit * SCcrit * time(Pyro) * H * (1 - gap) = gap * time(Sc) + gap * (X * (FBcrit - SCcrit) + SCcrit) * time(Sc) - gap * X * time(Sc) - gap * X * (X * (FBcrit - SCcrit) + SCcrit) * time(Sc) // X * X * [(FBcrit - SCcrit) * time(FB) * (1 - gap) + (FBcrit - SCcrit) * (FBcrit - SCcrit) * time(Pyro) * H * (1 - gap) + gap * (FBcrit - SCcrit) * time(Sc)] + X * [time(FB) * (1 - gap) + SCcrit * time(FB) * (1 - gap) + 2 * (FBcrit - SCcrit) * SCcrit * time(Pyro) * H * (1 - gap) - gap * (FBcrit - SCcrit) * time(Sc) + gap * time(Sc) + gap * SCcrit * time(Sc)] + [SCcrit * SCcrit * time(Pyro) * H * (1 - gap) - gap * time(Sc) - gap * SCcrit * time(Sc)] = 0 // A2 := // (FBcrit - SCcrit) * time(FB) * (1 - gap) + (FBcrit - SCcrit) * (FBcrit - SCcrit) * time(Pyro) * H * (1 - gap) + gap * (FBcrit - SCcrit) * time(Sc) // (FBcrit - SCcrit) * [time(FB) * (1 - gap) + (FBcrit - SCcrit) * time(Pyro) * H * (1 - gap) + gap * time(Sc)] // (FBcrit - SCcrit) * [time(FB) * (1 - gap) + (FBcrit - SCcrit) * time(Pyro) * H * (1 - gap) - (1 - gap) * time(Sc) + time(Sc)] // (FBcrit - SCcrit) * [(1 - gap) * (time(FB) + (FBcrit - SCcrit) * time(Pyro) * H - time(Sc)) + time(Sc)] // A1 := // time(FB) * (1 - gap) + SCcrit * time(FB) * (1 - gap) + 2 * (FBcrit - SCcrit) * SCcrit * time(Pyro) * H * (1 - gap) - gap * (FBcrit - SCcrit) * time(Sc) + gap * time(Sc) + gap * SCcrit * time(Sc) // time(FB) * [(1 - gap) + SCcrit * (1 - gap)] + time(Pyro) * H * [2 * (FBcrit - SCcrit) * SCcrit * (1 - gap)] + time(Sc) * [gap + gap * SCcrit - gap * (FBcrit - SCcrit)] // time(FB) * (1 - gap) * (1 + SCcrit) + time(Pyro) * H * [2 * (FBcrit - SCcrit) * SCcrit * (1 - gap)] + time(Sc) * gap * [1 + 2 * SCcrit - FBcrit] // (1 - gap) * [time(FB) * (1 + SCcrit) + time(Pyro) * H * 2 * (FBcrit - SCcrit) * SCcrit - time(Sc) * (1 + 2 * SCcrit - FBcrit)] + time(Sc) * (1 + 2 * SCcrit - FBcrit) // A0 := // SCcrit * SCcrit * time(Pyro) * H * (1 - gap) - gap * time(Sc) * (1 + SCcrit) // (1 - gap) * (SCcrit * SCcrit * time(Pyro) * H + time(Sc) * (1 + SCcrit)) - time(Sc) * (1 + SCcrit) // A2 * X * X + A1 * X + A0 = 0 // X = [- A1 +/- sqrt[A1 * A1 - 4 * A2 * A0]] / (2 * A2) // A1 * A1 - 4 * A2 * A0 float T8 = 0; float gap = (30.0f - (averageScorchesNeeded + extraScorches) * Sc.CastTime) / (30.0f - extraScorches * Sc.CastTime); if (castingState.MageTalents.ImprovedScorch == 0) { cycle.ProvidesScorch = false; gap = 1.0f; } float FBcrit = FB.CritRate; float SCcrit = Sc.CritRate; float H = castingState.MageTalents.HotStreak / 3.0f; #if RAWR4 if (castingState.Solver.Specialization != Specialization.Fire) H = 0.0f; #else if (castingState.MageTalents.Pyroblast == 0) H = 0.0f; #endif float A2 = (FBcrit - SCcrit) * FB.CastTime * (1 - gap) + (FBcrit - SCcrit) * (FBcrit - SCcrit) * Pyro.CastTime / (1 - T8) * H * (1 - gap) + gap * (FBcrit - SCcrit) * Sc.CastTime; float A1 = FB.CastTime * (1 - gap) * (1 + SCcrit) + Pyro.CastTime / (1 - T8) * H * (2 * (FBcrit - SCcrit) * SCcrit * (1 - gap)) + Sc.CastTime * gap * (1 + 2 * SCcrit - FBcrit); float A0 = SCcrit * SCcrit * Pyro.CastTime / (1 - T8) * H * (1 - gap) - gap * Sc.CastTime * (1 + SCcrit); if (Math.Abs(A2) < 0.00001) { X = -A0 / A1; } else { X = (float)((-A1 + Math.Sqrt(A1 * A1 - 4 * A2 * A0)) / (2 * A2)); } if (gap == 1.0f) X = 1.0f; //avoid rounding errors float C = X * (FBcrit - SCcrit) + SCcrit; K = H * C * C / (1 + C) / (1 - T8); float hasteFactor = 1.0f; if (castingState.Solver.Mage2T10) { // p = K / (1 + K) // N = (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FB.CastTime * X + LB.CastTime * (1-X) + Pyro.CastTime * K) hasteFactor = 1.12f / ((float)Math.Pow(1 - K / (1.0f + K), (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FB.CastTime * X + Sc.CastTime * (1 - X) + Pyro.CastTime * K) + 0.5f) * 0.12f + 1f); } // pyro dot uptime //A := [x * cFB * DFB + (1 - x) * cLB * DLB] ~ (x * cFB + (1 - x) * cLB)* D[x * FB + (1 - x) * LB] //B := [x * (1-cFB) * DFB + (1 - x) * (1-cLB) * DLB] ~ (1 - (x * cFB + (1- x) * cLB)) * D[x * FB + (1 - x) * LB] float averageTicks = 0f; float k1 = 0; float k2 = C * C * H; float totalChance = k2; float averageCastTime = X * (FB.CastTime - Sc.CastTime) + Sc.CastTime; int n = 2; averageTicks += Math.Min((int)(Pyro.CastTime / hasteFactor / 3.0f), 4) * T8; averageTicks += Math.Min((int)((Pyro.CastTime + n * averageCastTime) / hasteFactor / 3.0f), 4) * (1 - T8) * k2; while ((Pyro.CastTime + n * averageCastTime) / hasteFactor < 12) { float tmp = k1; k1 = k2; k2 = C * (1 - C * H) * tmp + (1 - C) * k1; totalChance += k2; n++; averageTicks += Math.Min((int)((Pyro.CastTime + n * averageCastTime) / hasteFactor / 3.0f), 4) * (1 - T8) * k2; } averageTicks += 4 * (1 - T8) * (1 - totalChance); cycle.AddSpell(needsDisplayCalculations, FB, X); cycle.AddSpell(needsDisplayCalculations, Sc, 1 - X); cycle.AddSpell(needsDisplayCalculations, Pyro, K, averageTicks / 4.0f); cycle.CastTime /= hasteFactor; cycle.Calculate(); return cycle; }
public static Cycle GetSolvedCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); Spell FrB, FFB, FFBS, ILS, DFS; float KFrB, KFFB, KFFBS, KILS, KDFS; cycle.Name = "FrBDFFFBIL"; FrB = castingState.GetSpell(SpellId.Frostbolt); FFB = castingState.GetSpell(SpellId.FrostfireBoltBF); FFBS = castingState.FrozenState.GetSpell(SpellId.FrostfireBoltBF); ILS = castingState.FrozenState.GetSpell(SpellId.IceLance); DFS = castingState.FrozenState.GetSpell(SpellId.DeepFreeze); // get the right distribution bank float[,] castDistribution; float[,] castDistributionFFO; if (castingState.Solver.Mage4T11) { castDistribution = CastDistributionT11; castDistributionFFO = CastDistributionT11FFO; } else if (castingState.Solver.Mage4T12) { castDistribution = CastDistributionT12; castDistributionFFO = CastDistributionT12FFO; } else { castDistribution = CastDistribution; castDistributionFFO = CastDistributionFFO; } // check if we have interpolation nodes ready float r = (castingState.CastingSpeed - 1) / 0.05f; int i = (int)r; r -= i; // if we're out of bounds just use the edge if (i + 1 >= 21) { i = 19; r = 1; } // uncomment this to get code for cast distributions to paste /*lock (castDistribution) { for (i = 0; i < 21; i++) { CastingState state = castingState.Clone(); state.CastingSpeed = 1 + i * 0.05f; state.ReferenceCastingState = castingState; SolveCycle(state, false, out castDistribution[i, 0], out castDistribution[i, 1], out castDistribution[i, 2], out castDistribution[i, 3], out castDistribution[i, 4]); System.Diagnostics.Trace.WriteLine(string.Format("\t{{{0}f, {1}f, {2}f, {3}f, {4}f}},", castDistribution[i, 0], castDistribution[i, 1], castDistribution[i, 2], castDistribution[i, 3], castDistribution[i, 4])); } Console.WriteLine(); for (i = 0; i < 21; i++) { CastingState state = castingState.Clone(); state.CastingSpeed = 1 + i * 0.05f; state.ReferenceCastingState = castingState; SolveCycle(state, true, out castDistribution[i, 0], out castDistribution[i, 1], out castDistribution[i, 2], out castDistribution[i, 3], out castDistribution[i, 4]); System.Diagnostics.Trace.WriteLine(string.Format("\t{{{0}f, {1}f, {2}f, {3}f, {4}f}},", castDistribution[i, 0], castDistribution[i, 1], castDistribution[i, 2], castDistribution[i, 3], castDistribution[i, 4])); } Console.WriteLine(); }*/ if (castingState.CalculationOptions.FlameOrb == 0 || (castingState.CalculationOptions.FlameOrb == 2 && !castingState.FlameOrb)) { KFrB = castDistribution[i, 0] + r * (castDistribution[i + 1, 0] - castDistribution[i, 0]); KFFB = castDistribution[i, 1] + r * (castDistribution[i + 1, 1] - castDistribution[i, 1]); KFFBS = castDistribution[i, 2] + r * (castDistribution[i + 1, 2] - castDistribution[i, 2]); KILS = castDistribution[i, 3] + r * (castDistribution[i + 1, 3] - castDistribution[i, 3]); KDFS = castDistribution[i, 4] + r * (castDistribution[i + 1, 4] - castDistribution[i, 4]); } else if (castingState.CalculationOptions.FlameOrb == 1) { KFrB = 0.75f * (castDistribution[i, 0] + r * (castDistribution[i + 1, 0] - castDistribution[i, 0])) + 0.25f * (castDistributionFFO[i, 0] + r * (castDistributionFFO[i + 1, 0] - castDistributionFFO[i, 0])); KFFB = 0.75f * (castDistribution[i, 1] + r * (castDistribution[i + 1, 1] - castDistribution[i, 1])) + 0.25f * (castDistributionFFO[i, 1] + r * (castDistributionFFO[i + 1, 1] - castDistributionFFO[i, 1])); KFFBS = 0.75f * (castDistribution[i, 2] + r * (castDistribution[i + 1, 2] - castDistribution[i, 2])) + 0.25f * (castDistributionFFO[i, 2] + r * (castDistributionFFO[i + 1, 2] - castDistributionFFO[i, 2])); KILS = 0.75f * (castDistribution[i, 3] + r * (castDistribution[i + 1, 3] - castDistribution[i, 3])) + 0.25f * (castDistributionFFO[i, 3] + r * (castDistributionFFO[i + 1, 3] - castDistributionFFO[i, 3])); KDFS = 0.75f * (castDistribution[i, 4] + r * (castDistribution[i + 1, 4] - castDistribution[i, 4])) + 0.25f * (castDistributionFFO[i, 4] + r * (castDistributionFFO[i + 1, 4] - castDistributionFFO[i, 4])); } else //if (castingState.CalculationOptions.FlameOrb == 2 && castingState.FlameOrb) { KFrB = castDistributionFFO[i, 0] + r * (castDistributionFFO[i + 1, 0] - castDistributionFFO[i, 0]); KFFB = castDistributionFFO[i, 1] + r * (castDistributionFFO[i + 1, 1] - castDistributionFFO[i, 1]); KFFBS = castDistributionFFO[i, 2] + r * (castDistributionFFO[i + 1, 2] - castDistributionFFO[i, 2]); KILS = castDistributionFFO[i, 3] + r * (castDistributionFFO[i + 1, 3] - castDistributionFFO[i, 3]); KDFS = castDistributionFFO[i, 4] + r * (castDistributionFFO[i + 1, 4] - castDistributionFFO[i, 4]); } cycle.AddSpell(needsDisplayCalculations, FrB, KFrB); cycle.AddSpell(needsDisplayCalculations, FFB, KFFB); cycle.AddSpell(needsDisplayCalculations, FFBS, KFFBS); cycle.AddSpell(needsDisplayCalculations, ILS, KILS); cycle.AddSpell(needsDisplayCalculations, DFS, KDFS); cycle.Calculate(); return cycle; }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState, int targets) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); Spell FFB; Spell LB; Spell Pyro; float X; float K = 0; if (targets > 1) { cycle.Name = "FFBLB3Pyro"; LB = castingState.GetSpell(SpellId.LivingBombAOE); cycle.AreaEffect = true; } else { cycle.Name = "FFBLBPyro"; LB = castingState.GetSpell(SpellId.LivingBomb); } FFB = castingState.GetSpell(SpellId.FrostfireBolt); Pyro = castingState.GetSpell(SpellId.PyroblastPOMDotUptime); // CATA model // LB doesn't interact with hot streak, so we can just use the FBPyro HS model and insert LB as a filler // Pyro/FB K = ((FBc^2-FBc)*FBk-FBc^2)/(FBc*FBk-FBc-1) // LB/FB X // value = value(FB) + X * value(LB) + K * value(Pyro) // time(LB) * X / [time(FB) + time(LB) * X + time(Pyro) * K] = time(LB) / LBrecastInterval // LBrecastInterval * X = time(FB) + time(LB) * X + time(Pyro) * K // (LBrecastInterval - time(LB)) * X = time(FB) + time(Pyro) * K // X = (time(FB) + time(Pyro) * K) / (LBrecastInterval - time(LB)) // more accurate LB uptime model // LB is cast every dotduration + half of average non LB spell duration // average non lb cast time = (time(FB) + time(Pyro) * K) / (1 + K) // LBrecastInterval = LB.DotFullDuration + 0.5 * (time(FB) + time(Pyro) * K) / (1 + K) float FFBc = FFB.CritRate; float FFBHSc = FFB.CritRate - FFB.NonHSCritRate; float FFBk = Math.Max(-2.73f * FFBHSc + 0.95f, 0f); if (castingState.Solver.Mage4T12) { FFBk = Math.Min(0.3f + FFBk, 1f); } K = ((FFBc * FFBc - FFBc) * FFBk - FFBc * FFBc) / (FFBc * FFBk - FFBc - 1); if (castingState.Solver.Specialization != Specialization.Fire) K = 0.0f; float LBrecastInterval = LB.DotFullDuration + 0.5f * (FFB.CastTime + Pyro.CastTime * K) / (1 + K); X = (FFB.CastTime + Pyro.CastTime * K) / (LBrecastInterval - targets * LB.CastTime); // pyro dot uptime float averageDuration = 0f; float k0 = 1; float k1 = 0; float k2 = 0; float totalChance = k2; float n = 2; //averageDuration += Math.Min((Pyro.CastTime + n * (FB.CastTime + X * LB.CastTime)), Pyro.DotFullDuration) * k2; while ((Pyro.CastTime + n * (FFB.CastTime + targets * X * LB.CastTime)) < Pyro.DotFullDuration) { float tmp = k1; k2 = k0 * FFBc * FFBk + k1 * FFBc; k1 = k0 * FFBc * (1 - FFBk); k0 = (k0 + tmp) * (1 - FFBc); totalChance += k2; n++; averageDuration += Math.Min((Pyro.CastTime + n * (FFB.CastTime + targets * X * LB.CastTime)), Pyro.DotFullDuration) * k2; } averageDuration += Pyro.DotFullDuration * (1 - totalChance); cycle.AddSpell(needsDisplayCalculations, FFB, 1); cycle.AddSpell(needsDisplayCalculations, LB, targets * X); cycle.AddSpell(needsDisplayCalculations, Pyro, K, averageDuration / Pyro.DotFullDuration); cycle.Calculate(); return cycle; }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); Spell FFB; float K; cycle.Name = "FFBPyro"; FFB = castingState.GetSpell(SpellId.FrostfireBolt); Spell Pyro = castingState.GetSpell(SpellId.PyroblastPOMDotUptime); // no Pyro // FB => no Pyro 1 - c*c/(1+c) // => Pyro c*c/(1+c) // Pyro // Pyro => no Pyro // 1 - c*c/(1+c) // FB // c*c/(1+c) float T8 = 0; K = FFB.CritRate * FFB.CritRate / (1.0f + FFB.CritRate) * castingState.MageTalents.HotStreak / 3.0f / (1 - T8); #if RAWR4 if (castingState.Solver.Specialization != Specialization.Fire) K = 0.0f; #else if (castingState.MageTalents.Pyroblast == 0) K = 0.0f; #endif float hasteFactor = 1.0f; if (castingState.Solver.Mage2T10) { // p = K / (1 + K) // N = (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FB.CastTime * X + LB.CastTime * (1-X) + Pyro.CastTime * K) hasteFactor = 1.12f / ((float)Math.Pow(1 - K / (1.0f + K), (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FFB.CastTime + Pyro.CastTime * K) + 0.5f) * 0.12f + 1f); } // pyro dot uptime //A := [x * cFB * DFB + (1 - x) * cLB * DLB] ~ (x * cFB + (1 - x) * cLB)* D[x * FB + (1 - x) * LB] //B := [x * (1-cFB) * DFB + (1 - x) * (1-cLB) * DLB] ~ (1 - (x * cFB + (1- x) * cLB)) * D[x * FB + (1 - x) * LB] float averageTicks = 0f; float C = FFB.CritRate; float H = castingState.MageTalents.HotStreak / 3.0f; float k1 = 0; float k2 = C * C * H; float totalChance = k2; float averageCastTime = FFB.CastTime; int n = 2; averageTicks += Math.Min((int)(Pyro.CastTime / hasteFactor / 3.0f), 4) * T8; averageTicks += Math.Min((int)((Pyro.CastTime + n * averageCastTime) / hasteFactor / 3.0f), 4) * (1 - T8) * k2; while ((Pyro.CastTime + n * averageCastTime) / hasteFactor < 12) { float tmp = k1; k1 = k2; k2 = C * (1 - C * H) * tmp + (1 - C) * k1; totalChance += k2; n++; averageTicks += Math.Min((int)((Pyro.CastTime + n * averageCastTime) / hasteFactor / 3.0f), 4) * (1 - T8) * k2; } averageTicks += 4 * (1 - T8) * (1 - totalChance); cycle.AddSpell(needsDisplayCalculations, FFB, 1); cycle.AddSpell(needsDisplayCalculations, Pyro, K, averageTicks / 4.0f); cycle.CastTime /= hasteFactor; cycle.Calculate(); return cycle; }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); cycle.Name = "AB3ABar023AM"; if (castingState.Solver.Mage2T10) { // doesn't support haste transferring over several loops // S0: // AB0-AB1-AB2-ABar => S1 (1 - MB) * (1 - MB) // AB0-AB1-AB2-AM => S0 (1 - MB) * MB // AB0-AB1-AM => S0 MB // S1: // AB0-AB1-AB2-ABar => S1 (1 - MB) * (1 - MB) * (1 - MB) * (1 - MB) // AB0-AB1-AB2-AM => S0 (1 - MB) * (1 - MB) * (1 - MB) * MB // AB0-AB1-AM => S0 (1 - MB) * (1 - MB) * MB // AM => S0 (1 - (1 - MB) * (1 - MB)) // K2 := (1-MB)*(1-MB) // K4 := K2 * K2 // S0 = (1 - K2) * S0 + (1 - K4) * S1 // S1 = K2 * S0 + K4 * S1 // S0 + S1 = 1 // S0 = (1-K4)/(1-K4+K2) Spell ABar = castingState.GetSpell(SpellId.ArcaneBarrage); Spell AM = castingState.GetSpell(SpellId.ArcaneMissiles); Spell AMT = castingState.Tier10TwoPieceState.GetSpell(SpellId.ArcaneMissiles); float m2T10time = 5.0f - castingState.CalculationOptions.LatencyChannel; Spell AB0A = (m2T10time > 0.0f) ? castingState.Tier10TwoPieceState.GetSpell(SpellId.ArcaneBlast0) : castingState.GetSpell(SpellId.ArcaneBlast0); m2T10time -= AB0A.CastTime; Spell AB1A = (m2T10time > 0.0f) ? castingState.Tier10TwoPieceState.GetSpell(SpellId.ArcaneBlast1) : castingState.GetSpell(SpellId.ArcaneBlast1); m2T10time -= AB1A.CastTime; Spell AM2A = (m2T10time > 0.0f) ? castingState.Tier10TwoPieceState.GetSpell(SpellId.ArcaneMissiles) : castingState.GetSpell(SpellId.ArcaneMissiles); Spell AB2A = (m2T10time > 0.0f) ? castingState.Tier10TwoPieceState.GetSpell(SpellId.ArcaneBlast2) : castingState.GetSpell(SpellId.ArcaneBlast2); m2T10time -= AB2A.CastTime; Spell AM3A = (m2T10time > 0.0f) ? castingState.Tier10TwoPieceState.GetSpell(SpellId.ArcaneMissiles) : castingState.GetSpell(SpellId.ArcaneMissiles); Spell ABar3A = (m2T10time > 0.0f) ? castingState.Tier10TwoPieceState.GetSpell(SpellId.ArcaneBarrage) : castingState.GetSpell(SpellId.ArcaneBarrage); Spell AB0C = castingState.GetSpell(SpellId.ArcaneBlast0); Spell AB1C = castingState.GetSpell(SpellId.ArcaneBlast1); Spell AMC = castingState.GetSpell(SpellId.ArcaneMissiles); Spell AB2C = castingState.GetSpell(SpellId.ArcaneBlast2); Spell ABarC = castingState.GetSpell(SpellId.ArcaneBarrage); float MB = 0.4f; float K2 = (1 - MB) * (1 - MB); float K4 = K2 * K2; float S0 = (1 - K4) / (1 - K4 + K2); float S1 = 1 - S0; cycle.AddSpell(needsDisplayCalculations, AB0A, S0); cycle.AddSpell(needsDisplayCalculations, AB1A, S0); cycle.AddSpell(needsDisplayCalculations, AB2A, S0 * (1 - MB)); cycle.AddSpell(needsDisplayCalculations, AM2A, S0 * MB); cycle.AddSpell(needsDisplayCalculations, AM3A, S0 * (1 - MB) * MB); cycle.AddSpell(needsDisplayCalculations, ABar3A, S0 * K2); cycle.AddSpell(needsDisplayCalculations, AB0C, S1 * K2); cycle.AddSpell(needsDisplayCalculations, AB1C, S1 * K2); cycle.AddSpell(needsDisplayCalculations, AB2C, S1 * K2 * (1 - MB)); cycle.AddSpell(needsDisplayCalculations, AMC, S1 * (1 - K4)); cycle.AddSpell(needsDisplayCalculations, ABarC, S1 * K4); } else { // S0: // AB0-AB1-AB2-ABar => S1 (1 - MB) * (1 - MB) * (1 - MB) * (1 - MB) // AB0-AB1-AB2-ABar-AM => S0 (1 - MB) * (1 - MB) * (1 - (1 - MB) * (1 - MB)) // AB0-AB1-AB2-AM => S0 (1 - MB) * MB // AB0-AB1-AM => S0 MB Spell AM = castingState.GetSpell(SpellId.ArcaneMissiles); Spell ABar = castingState.GetSpell(SpellId.ArcaneBarrage); float MB = 0.4f; float K2 = (1 - MB) * (1 - MB); float K4 = K2 * K2; float ABcast; Spell AB0 = castingState.GetSpell(SpellId.ArcaneBlast0); Spell AB1 = castingState.GetSpell(SpellId.ArcaneBlast1); Spell AB2 = castingState.GetSpell(SpellId.ArcaneBlast2); cycle.AddSpell(needsDisplayCalculations, AB0, 1); cycle.AddSpell(needsDisplayCalculations, AB1, 1); cycle.AddSpell(needsDisplayCalculations, AB2, 1 - MB); ABcast = AB0.CastTime; cycle.AddSpell(needsDisplayCalculations, AM, 1 - K4); cycle.AddSpell(needsDisplayCalculations, ABar, K2); // a bit overkill on pause, but make sure we respect the ABar cooldown if (3 * ABcast + ABar.CastTime < ABar.Cooldown) { cycle.AddPause(ABar.Cooldown - 3 * ABcast - ABar.CastTime, K2); } } cycle.Calculate(); return cycle; }
public FireCycleGenerator(CastingState castingState) { FB = new Spell[2]; Sc = new Spell[2]; LB = new Spell[2]; Pyro = new Spell[2]; FB[0] = castingState.GetSpell(SpellId.Fireball); Sc[0] = castingState.GetSpell(SpellId.Scorch); LB[0] = castingState.GetSpell(SpellId.LivingBomb); Pyro[0] = castingState.GetSpell(SpellId.PyroblastPOM); // does not account for dot uptime FB[0].Label = "Fireball"; Sc[0].Label = "Scorch"; LB[0].Label = "Living Bomb"; Pyro[0].Label = "Pyroblast"; FB[1] = castingState.Tier10TwoPieceState.GetSpell(SpellId.Fireball); Sc[1] = castingState.Tier10TwoPieceState.GetSpell(SpellId.Scorch); LB[1] = castingState.Tier10TwoPieceState.GetSpell(SpellId.LivingBomb); Pyro[1] = castingState.Tier10TwoPieceState.GetSpell(SpellId.PyroblastPOM); // does not account for dot uptime FB[1].Label = "2T10:Fireball"; Sc[1].Label = "2T10:Scorch"; LB[1].Label = "2T10:Living Bomb"; Pyro[1].Label = "2T10:Pyroblast"; HS = castingState.MageTalents.HotStreak / 3.0f; T8 = 0; //maintainScorch = castingState.CalculationOptions.MaintainScorch; livingBombGlyph = castingState.MageTalents.GlyphOfLivingBomb; LBDotCritRate = castingState.FireCritRate; T10 = castingState.Solver.Mage2T10; GenerateStateDescription(); }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); Spell FFB; Spell Sc; Spell Pyro; float K; float X; cycle.Name = "FFBScPyro"; cycle.ProvidesScorch = true; FFB = castingState.GetSpell(SpellId.FrostfireBolt); Sc = castingState.GetSpell(SpellId.Scorch); Pyro = castingState.GetSpell(SpellId.PyroblastPOMDotUptime); int averageScorchesNeeded = (int)Math.Ceiling(3f / (float)castingState.MageTalents.ImprovedScorch); int extraScorches = 0; float T8 = 0; float gap = (30.0f - (averageScorchesNeeded + extraScorches) * Sc.CastTime) / (30.0f - extraScorches * Sc.CastTime); if (castingState.MageTalents.ImprovedScorch == 0) { cycle.ProvidesScorch = false; gap = 1.0f; } float FFBcrit = FFB.CritRate; float SCcrit = Sc.CritRate; float H = castingState.MageTalents.HotStreak / 3.0f; #if RAWR4 if (castingState.Solver.Specialization != Specialization.Fire) H = 0.0f; #else if (castingState.MageTalents.Pyroblast == 0) H = 0.0f; #endif float A2 = (FFBcrit - SCcrit) * FFB.CastTime * (1 - gap) + (FFBcrit - SCcrit) * (FFBcrit - SCcrit) * Pyro.CastTime / (1 - T8) * H * (1 - gap) + gap * (FFBcrit - SCcrit) * Sc.CastTime; float A1 = FFB.CastTime * (1 - gap) * (1 + SCcrit) + Pyro.CastTime / (1 - T8) * H * (2 * (FFBcrit - SCcrit) * SCcrit * (1 - gap)) + Sc.CastTime * gap * (1 + 2 * SCcrit - FFBcrit); float A0 = SCcrit * SCcrit * Pyro.CastTime / (1 - T8) * H * (1 - gap) - gap * Sc.CastTime * (1 + SCcrit); if (Math.Abs(A2) < 0.00001) { X = -A0 / A1; } else { X = (float)((-A1 + Math.Sqrt(A1 * A1 - 4 * A2 * A0)) / (2 * A2)); } if (gap == 1.0f) X = 1.0f; //avoid rounding errors float C = X * (FFBcrit - SCcrit) + SCcrit; K = H * C * C / (1 + C) / (1 - T8); float hasteFactor = 1.0f; if (castingState.Solver.Mage2T10) { // p = K / (1 + K) // N = (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FB.CastTime * X + LB.CastTime * (1-X) + Pyro.CastTime * K) hasteFactor = 1.12f / ((float)Math.Pow(1 - K / (1.0f + K), (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FFB.CastTime * X + Sc.CastTime * (1 - X) + Pyro.CastTime * K) + 0.5f) * 0.12f + 1f); } // pyro dot uptime //A := [x * cFB * DFB + (1 - x) * cLB * DLB] ~ (x * cFB + (1 - x) * cLB)* D[x * FB + (1 - x) * LB] //B := [x * (1-cFB) * DFB + (1 - x) * (1-cLB) * DLB] ~ (1 - (x * cFB + (1- x) * cLB)) * D[x * FB + (1 - x) * LB] float averageTicks = 0f; float k1 = 0; float k2 = C * C * H; float totalChance = k2; float averageCastTime = X * (FFB.CastTime - Sc.CastTime) + Sc.CastTime; int n = 2; averageTicks += Math.Min((int)(Pyro.CastTime / hasteFactor / 3.0f), 4) * T8; averageTicks += Math.Min((int)((Pyro.CastTime + n * averageCastTime) / hasteFactor / 3.0f), 4) * (1 - T8) * k2; while ((Pyro.CastTime + n * averageCastTime) / hasteFactor < 12) { float tmp = k1; k1 = k2; k2 = C * (1 - C * H) * tmp + (1 - C) * k1; totalChance += k2; n++; averageTicks += Math.Min((int)((Pyro.CastTime + n * averageCastTime) / hasteFactor / 3.0f), 4) * (1 - T8) * k2; } averageTicks += 4 * (1 - T8) * (1 - totalChance); cycle.AddSpell(needsDisplayCalculations, FFB, X); cycle.AddSpell(needsDisplayCalculations, Sc, 1 - X); cycle.AddSpell(needsDisplayCalculations, Pyro, K, averageTicks / 4.0f); cycle.CastTime /= hasteFactor; cycle.Calculate(); return cycle; }
public static void SolveCycle(CastingState castingState, bool useFFO, out float KFrB, out float KFFB, out float KFFBS, out float KILS, out float KDFS) { Spell FrB, FFB, FFBS, ILS, DFS; float RFrB = 0, RFFB = 0, RFFBS = 0, RILS = 0, RDFS = 0; int CFrB = 0, CFFB = 0, CFFBS = 0, CILS = 0, CDFS = 0; FrB = castingState.GetSpell(SpellId.Frostbolt); FFB = castingState.GetSpell(SpellId.FrostfireBoltBF); FFBS = castingState.FrozenState.GetSpell(SpellId.FrostfireBoltBF); ILS = castingState.FrozenState.GetSpell(SpellId.IceLance); DFS = castingState.FrozenState.GetSpell(SpellId.DeepFreeze); // FFB on FOF only, if Freeze is off cooldown only use it if FOF is off // IL on FOF if Freeze is off cooldown, otherwise on FOF2 only float bf = 0.05f * castingState.MageTalents.BrainFreeze; float fof = (castingState.MageTalents.FingersOfFrost == 3 ? 0.2f : 0.07f * castingState.MageTalents.FingersOfFrost); // override this so we don't have to store so many variations (means talent graphs won't show value for this talent) bf = 0.3f; // testing possible 4T12 value fof = 0.2f; float dfCooldown = 0; float freezeCooldown = 0; int fofActual = 0; int fofRegistered = 0; bool bfRegistered = false; bool bfActual = false; float ffo = 1; Random rnd = new Random(); bool stable; float errorMargin = 0.000001f; do { for (int c = 0; c < 1000000; c++) { if (dfCooldown == 0.0f && (fofRegistered > 0 || freezeCooldown == 0)) { CDFS++; if (fofRegistered > 0) { bfRegistered = bfActual; fofActual = Math.Max(0, fofActual - 1); fofRegistered = fofActual; dfCooldown = Math.Max(0, 30f - DFS.CastTime); freezeCooldown = Math.Max(0, freezeCooldown - DFS.CastTime); } else { bfRegistered = bfActual; fofActual = 1; fofRegistered = fofActual; dfCooldown = Math.Max(0, 30f - DFS.CastTime); freezeCooldown = Math.Max(0, 25f - DFS.CastTime); } while (useFFO && ffo < DFS.CastTime) { bool fofProc = rnd.NextDouble() < fof; bool bfProc = rnd.NextDouble() < bf; bfActual = bfProc || bfActual; if (fofProc) { fofActual = Math.Min(2, fofActual + 1); } ffo += 1; } if (useFFO) { ffo -= DFS.CastTime; } } else if (bfRegistered && ((fofRegistered > 0 && freezeCooldown > 0) || (fofRegistered == 0 && freezeCooldown == 0))) { CFFBS++; if (fofRegistered > 0) { bool fofProc = rnd.NextDouble() < fof; bfActual = false; bfRegistered = false; fofActual = Math.Max(0, fofActual - 1); dfCooldown = Math.Max(0, dfCooldown - FFBS.CastTime); freezeCooldown = Math.Max(0, freezeCooldown - FFBS.CastTime); if (fofProc) { fofActual++; } fofRegistered = fofActual; } else { bool fofProc = rnd.NextDouble() < fof; bfActual = false; bfRegistered = false; fofActual = Math.Max(0, 2 - 1); dfCooldown = Math.Max(0, dfCooldown - FFBS.CastTime); freezeCooldown = Math.Max(0, 25.0f - FFBS.CastTime); if (fofProc) { fofActual++; } fofRegistered = fofActual; } while (useFFO && ffo < FFBS.CastTime) { bool fofProc = rnd.NextDouble() < fof; bool bfProc = rnd.NextDouble() < bf; bfActual = bfProc || bfActual; if (fofProc) { fofActual = Math.Min(2, fofActual + 1); } ffo += 1; } if (useFFO) { ffo -= FFBS.CastTime; } } else if (fofRegistered == 2 || (fofRegistered == 1 && freezeCooldown < ILS.CastTime)) { CILS++; bfRegistered = bfActual; fofActual = Math.Max(0, fofActual - 1); fofRegistered = fofActual; dfCooldown = Math.Max(0, dfCooldown - ILS.CastTime); freezeCooldown = Math.Max(0, freezeCooldown - ILS.CastTime); while (useFFO && ffo < ILS.CastTime) { bool fofProc = rnd.NextDouble() < fof; bool bfProc = rnd.NextDouble() < bf; bfActual = bfProc || bfActual; if (fofProc) { fofActual = Math.Min(2, fofActual + 1); } ffo += 1; } if (useFFO) { ffo -= ILS.CastTime; } } else if (fofRegistered == 0 && freezeCooldown == 0) { CILS++; bfRegistered = bfActual; fofActual = Math.Max(0, 2 - 1); fofRegistered = fofActual; dfCooldown = Math.Max(0, dfCooldown - ILS.CastTime); freezeCooldown = Math.Max(0, 25.0f - ILS.CastTime); while (useFFO && ffo < ILS.CastTime) { bool fofProc = rnd.NextDouble() < fof; bool bfProc = rnd.NextDouble() < bf; bfActual = bfProc || bfActual; if (fofProc) { fofActual = Math.Min(2, fofActual + 1); } ffo += 1; } if (useFFO) { ffo -= ILS.CastTime; } } else { while (useFFO && ffo < FrB.CastTime) { bool fofProcffo = rnd.NextDouble() < fof; bool bfProcffo = rnd.NextDouble() < bf; bfActual = bfProcffo || bfActual; if (fofProcffo) { fofActual = Math.Min(2, fofActual + 1); } ffo += 1; } if (useFFO) { ffo -= FrB.CastTime; } CFrB++; bool fofProc = rnd.NextDouble() < fof; bool bfProc = rnd.NextDouble() < bf; bfRegistered = bfActual; bfActual = bfProc || bfActual; fofRegistered = fofActual; if (fofProc) { fofActual = Math.Min(2, fofActual + 1); } dfCooldown = Math.Max(0, dfCooldown - FrB.CastTime); freezeCooldown = Math.Max(0, freezeCooldown - FrB.CastTime); } } float total = CDFS + CFFB + CFFBS + CFrB + CILS; KDFS = CDFS / total; KFFB = CFFB / total; KFFBS = CFFBS / total; KFrB = CFrB / total; KILS = CILS / total; stable = Math.Abs(KDFS - RDFS) < errorMargin && Math.Abs(KFFB - RFFB) < errorMargin && Math.Abs(KFFBS - RFFBS) < errorMargin && Math.Abs(KFrB - RFrB) < errorMargin && Math.Abs(KILS - RILS) < errorMargin; RDFS = KDFS; RFFB = KFFB; RFFBS = KFFBS; RFrB = KFrB; RILS = KILS; } while (!stable); }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); float K; cycle.Name = "FBPyro"; Spell FB = castingState.GetSpell(SpellId.Fireball); Spell Pyro = castingState.GetSpell(SpellId.PyroblastPOMDotUptime); // in cata pyro can also proc hot streak (not true, only hardcasted pyro can proc...) // using T3 hot streak model from http://elitistjerks.com/f75/t104767-fire_cataclysm_discussion/p12/#post1766032 // using averaged crit rate model with reset on T3 proc // S0: no proc, 0 count // FB => S0 1-FBc // => S1 FBc*(1-FBk) // => S2 FBc*FBk // S1: no proc, 1 count // FB => S0 1-FBc // => S2 FBc // S2: proc, 0 count // Pyro => S0 1 // S0 = S0 * (1-FBc) + S1 * (1 - FBc) + S2 // S1 = S0 * FBc*(1-FBk) // S2 = S0 * FBc*FBk + S1 * FBc // S0 + S1 + S2 = 1 // full model // S0=-1/(FBc^2*(FBk-1)-FBc-1) // S1=(FBc*(FBk-1))/(FBc^2*(FBk-1)-FBc-1) // S2=(FBc^2*(FBk-1)-FBc*FBk)/(FBc^2*(FBk-1)-FBc-1) // pyro per FB ratio // ((FBc^2-FBc)*FBk-FBc^2)/(FBc*FBk-FBc-1) float FBc = FB.CritRate; float FBHSc = FB.CritRate - FB.NonHSCritRate; float FBk = Math.Max(-2.73f * FBHSc + 0.95f, 0f); if (castingState.Solver.Mage4T12) { // basing on http://elitistjerks.com/f75/t110326-cataclysm_fire_mage_compendium/p21/#post1946836 FBk = Math.Min(0.3f + FBk, 1f); } K = ((FBc * FBc - FBc) * FBk - FBc * FBc) / (FBc * FBk - FBc - 1); if (castingState.Solver.Specialization != Specialization.Fire) K = 0.0f; // CATA model for Pyro dot uptime // S0: no proc, 0 count // FB => S0 1-FBc // => S1 FBc*(1-FBk) // => S2 FBc*FBk // S1: no proc, 1 count // FB => S0 1-FBc // => S2 FBc // S2: proc, 0 count // Pyro => S0 1 // k0 = 1 // k1 = 0 // k2 = 0 // n=>n+1 // k0' = (k0+k1)*(1-FBc) // k1' = k0*FBc*(1-FBk) // k2' = k0*FBc*FBk + k1*FBc float averageDuration = 0f; float C = FB.CritRate; float HS = castingState.MageTalents.HotStreak / 3.0f; float k0 = 1; float k1 = 0; float k2 = 0; float totalChance = k2; float n = 2; // heuristic filler to account for realistic dot uptime //averageDuration += Math.Min((Pyro.CastTime + n * FB.CastTime), Pyro.DotFullDuration) * k2; while ((Pyro.CastTime + n * FB.CastTime) < Pyro.DotFullDuration) { float tmp = k1; k2 = k0 * FBc * FBk + k1 * FBc; k1 = k0 * FBc * (1 - FBk); k0 = (k0 + tmp) * (1 - FBc); totalChance += k2; n++; averageDuration += Math.Min((Pyro.CastTime + n * FB.CastTime), Pyro.DotFullDuration) * k2; } averageDuration += Pyro.DotFullDuration * (1 - totalChance); cycle.AddSpell(needsDisplayCalculations, FB, 1); cycle.AddSpell(needsDisplayCalculations, Pyro, K, averageDuration / Pyro.DotFullDuration); cycle.Calculate(); return cycle; }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); Spell FrB, FrBS, FB, FBS, ILS; float KFrB, KFrBS, KFB, KFBS, KILS; cycle.Name = "FrBFBIL"; float T8 = 0; // S00: FOF0, BF0 // FrB => S21 fof * bf // S20 fof * (1-bf) // S01 (1-fof) * bf // S00 (1-fof)*(1-bf) // S01: FOF0, BF1 // FrB => S22 fof // S02 (1-fof) // S02: FOF0, BF2 // FB => S00 (1-T8) // => S02 T8 // S10: FOF1, BF0 // FrBS-ILS => S12 fof * bf // S10 fof * (1-bf) // S02 (1-fof) * bf // S00 (1-fof)*(1-bf) // S11: FOF1, BF1 // FrBS-FBS => S10 fof*(1-T8) // S11 fof*T8 // S00 (1-fof)*(1-T8) // S02 (1-fof)*T8 // S12 = S11 // S20: FOF0, BF0 // FrBS => S21 fof * bf // S20 fof * (1-bf) // S11 (1-fof) * bf // S10 (1-fof)*(1-bf) // S21: FOF0, BF1 // FrBS => S22 fof // S12 (1-fof) // S22 = S21 // S00 = (1-fof)*(1-bf) * S00 + S02*(1-T8) + (1-fof)*(1-bf) * S10 + (1-fof)*(1-T8) * S11 // S01 = (1-fof) * bf * S00 // S02 = (1-fof) * S01 + T8 * S02 + (1-fof) * bf * S10 + (1-fof)*T8 * S11 // S10 = fof * (1-bf) * S10 + fof*(1-T8) * S11 + (1-fof)*(1-bf) * S20 // S11 = fof * bf * S10 + fof*T8 * S11 + (1-fof) * bf * S20 + (1-fof) * S21 // S20 = fof * (1-bf) * S00 + fof * (1-bf) * S20 // S21 = fof * bf * S00 + fof * S01 + fof * bf * S20 + fof * S21 // S00 + S01 + S02 + S10 + S11 + S20 + S21 = 1 // solved symbolically float bf = 0.05f * castingState.MageTalents.BrainFreeze; float fof = (castingState.MageTalents.FingersOfFrost == 2 ? 0.15f : 0.07f * castingState.MageTalents.FingersOfFrost); //float div = ((bf * bf * bf - bf) * fof * fof * fof * fof + (3 * bf - bf * bf * bf) * fof * fof * fof + (bf * bf * bf - 4 * bf + 1) * fof * fof * fof + (-bf * bf * bf - 2 * bf * bf + 2 * bf) * fof - 2 * bf - 1); //float S00 = ((bf * bf - bf) * fof * fof * fof + (-bf * bf + 3 * bf - 1) * fof * fof + (2 - 2 * bf) * fof - 1) / div; //float S01 = -((bf * bf * bf - bf * bf) * fof * fof * fof * fof + (-2 * bf * bf * bf + 4 * bf * bf - bf) * fof * fof * fof + (bf * bf * bf - 5 * bf * bf + 3 * bf) * fof * fof + (2 * bf * bf - 3 * bf) * fof + bf) / div; //float S02 = ((bf * bf - bf) * fof * fof * fof * fof + (-bf * bf * bf - bf * bf + 3 * bf) * fof * fof * fof + (2 * bf * bf * bf - 4 * bf) * fof * fof + (3 * bf - bf * bf * bf) * fof - bf) / div; //float S10 = ((bf * bf - bf) * fof * fof * fof * fof + (3 * bf - 2 * bf * bf) * fof * fof * fof + (2 * bf * bf - 5 * bf + 1) * fof * fof + (-bf * bf + 2 * bf - 1) * fof) / div; //float S11 = ((bf * bf * bf - 2 * bf * bf + bf) * fof * fof * fof * fof + (-bf * bf * bf + 4 * bf * bf - 3 * bf) * fof * fof * fof + (5 * bf - 4 * bf * bf) * fof * fof + (bf * bf - 3 * bf) * fof) / div; //float S20 = -((bf * bf - bf) * fof * fof * fof + (-bf * bf + 2 * bf - 1) * fof * fof + (1 - bf) * fof) / div; //float S21 = ((bf * bf * bf - bf * bf) * fof * fof * fof * fof + (-bf * bf * bf + 3 * bf * bf - bf) * fof * fof * fof + (2 * bf - 3 * bf * bf) * fof * fof - 2 * bf * fof) / div; float S00 = (fof - 1) * ((bf - 1) * fof + 1) * (T8 - 1) * (fof * (T8 - bf) - 1); float S01 = -bf * (fof - 1) * (fof - 1) * ((bf - 1) * fof + 1) * (T8 - 1) * (fof * (T8 - bf) - 1); float S02 = -bf * (fof - 1) * (fof * ((fof - 1) * ((bf - 1) * fof - bf + 2) * T8 - fof * ((bf - 1) * fof - bf * bf + 2) - bf * bf + 2) - 1); float S10 = fof * (T8 - 1) * (fof * ((bf * (fof - 1) - 1) * ((bf - 1) * fof + 1) * T8 - bf * (fof * ((bf - 1) * fof - 2 * bf + 3) + 2 * bf - 5) - 1) + (bf - 1) * (bf - 1)); float S11 = -bf * fof * (fof * ((bf - 1) * fof * ((bf - 1) * fof - bf + 3) - 4 * bf + 5) + bf - 3) * (T8 - 1); float S20 = -(bf - 1) * (fof - 1) * fof * (T8 - 1) * (fof * (T8 - bf) - 1); float S21 = bf * fof * (fof * ((bf - 1) * fof - bf + 2) - 2) * (T8 - 1) * (fof * (T8 - bf) - 1); float div = S00 + S01 + S02 + S10 + S11 + S20 + S21; KFrB = (S00 + S01) / div; KFB = S02 / div; KFrBS = (S10 + S11 + S20 + S21) / div; KFBS = S11 / div; KILS = S10 / div; FrB = castingState.GetSpell(SpellId.Frostbolt); FrBS = castingState.FrozenState.GetSpell(SpellId.Frostbolt); FB = castingState.GetSpell(SpellId.FireballBF); FBS = castingState.FrozenState.GetSpell(SpellId.FireballBF); ILS = castingState.FrozenState.GetSpell(SpellId.IceLance); cycle.AddSpell(needsDisplayCalculations, FrB, KFrB); cycle.AddSpell(needsDisplayCalculations, FB, KFB); cycle.AddSpell(needsDisplayCalculations, FrBS, KFrBS); cycle.AddSpell(needsDisplayCalculations, FBS, KFBS); cycle.AddSpell(needsDisplayCalculations, ILS, KILS); cycle.Calculate(); return cycle; }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); Spell FB; Spell Sc; Spell LB; Spell Pyro; float K; float X; float Y; cycle.Name = "FBScLBPyro"; cycle.ProvidesScorch = true; FB = castingState.GetSpell(SpellId.Fireball); Sc = castingState.GetSpell(SpellId.Scorch); LB = castingState.GetSpell(SpellId.LivingBomb); Pyro = castingState.GetSpell(SpellId.PyroblastPOMDotUptime); int averageScorchesNeeded = (int)Math.Ceiling(3f / (float)castingState.MageTalents.ImprovedScorch); int extraScorches = 0; float T8 = 0; float C, H, averageCastTime; // 3.1 calculations // 0 HS charge: // FB => 0 HS charge (1 - FBcrit) * X // => 1 HS charge FBcrit * X // LB => 0 HS charge (1 - LBcrit) * (1 - X - Y) // => 1 HS charge LBcrit * (1 - X - Y) // Sc => 0 HS charge (1 - SCcrit) * Y // 1 HS charge SCcrit * Y // 1 HS charge: // FB => 0 HS charge (1 - FBcrit) * X + (1 - H) * FBcrit * X // FBPyro => 0 HS charge H * FBcrit * X // LB => 0 HS charge (1 - LBcrit) * (1 - X - Y) + (1 - H) * LBcrit * (1 - X - Y) // LBPyro => 0 HS charge H * LBcrit * (1 - X - Y) // Sc => 0 HS charge (1 - SCcrit) * Y + (1 - H) * SCcrit * Y // ScPyro => 0 HS charge H * SCcrit * Y // S0 + S1 = 1 // C := FBcrit * X + SCcrit * Y + LBcrit * (1 - X - Y) // S0 = S0 * (1 - C) + S1 // S1 = S0 * C // 1 - S0 = S0 * C // S0 = 1 / (1 + C) // S1 = C / (1 + C) // value = (X * value(FB) + Y * value(Sc) + (1 - X - Y) * value(LB)) * 1 / (1 + C) + (X * value(FB) + Y * value(Sc) + (1 - X - Y) * value(LB) + H * C * value(Pyro)) * C / (1 + C) // X * value(FB) + Y * value(Sc) + (1 - X - Y) * value(LB) + value(Pyro) * H * C * C / (1 + C) / (1 - T8) // (1 - X - Y) * time(LB) / [X * time(FB) + Y * time(Sc) + (1 - X - Y) * time(LB) + time(Pyro) / (1 - T8) * H * C * C / (1 + C)] = time(LB) / 12 // Z = 1 - X - Y // Z * time(LB) / [X * time(FB) + Y * time(Sc) + Z * time(LB) + time(Pyro) / (1 - T8) * H * C * C / (1 + C)] = time(LB) / 12 // 12 * Z = X * time(FB) + Y * time(Sc) + Z * time(LB) + time(Pyro) * H * C * C / (1 + C) / (1 - T8) // T := X * time(FB) + Y * time(Sc) + Z * time(LB) + time(Pyro) * H * C * C / (1 + C) / (1 - T8) // 12 * Z = T // (X * time(FB) + Z * time(LB) + time(Pyro) * H * C * C / (1 + C) / (1 - T8)) / T = gap // (T - Y * time(Sc)) / T = gap // T * (1 - gap) = Y * time(Sc) // if gap = 1 => Y = 0 // 12 * Z * (1 - gap) = Y * time(Sc) // X = 1 - Y * [1 + time(Sc) / (12 * (1 - gap))] // P := 1 + time(Sc) / (12 * (1 - gap)) // X = 1 - P * Y // Z = 1 - X - Y = 1 - 1 + P * Y - Y = Y * (P - 1) // C = (FBcrit * X + SCcrit * Y + LBcrit * Z) // (FBcrit * (1 - P * Y) + SCcrit * Y + LBcrit * Y * (P - 1)) // (FBcrit + Y * (SCcrit - FBcrit * P + LBcrit * (P - 1))) // C / (1 + C) = (FBcrit + Y * (SCcrit - FBcrit * P + LBcrit * (P - 1))) / (1 + FBcrit + Y * (SCcrit - FBcrit * P + LBcrit * (P - 1))) // 12 * Y * (P - 1) = T // CY := SCcrit - FBcrit * P + LBcrit * (P - 1) // C = FBcrit + Y * CY // T = // X * time(FB) + Y * time(Sc) + Z * time(LB) + time(Pyro) * H * C * C / (1 + C) / (1 - T8) // (1 - P * Y) * time(FB) + Y * time(Sc) + Y * (P - 1) * time(LB) + time(Pyro) * H * C * C / (1 + C) / (1 - T8) // time(FB) + Y * [time(Sc) - P * time(FB) + (P - 1) * time(LB)] + time(Pyro) * H * C * C / (1 + C) / (1 - T8) // 0 = time(FB) + Y * [time(Sc) - P * time(FB) + (P - 1) * time(LB) - 12 * (P - 1)] + time(Pyro) * H * C * C / (1 + C) / (1 - T8) // T1 := time(Sc) - P * time(FB) + (P - 1) * time(LB) - 12 * (P - 1) // 0 = time(FB) + Y * T1 + time(Pyro) * H * C * C / (1 + C) / (1 - T8) // 0 = time(FB) + C * time(FB) + Y * T1 + Y * C * T1 + time(Pyro) / (1 - T8) * H * C * C // 0 = time(FB) + (FBcrit + Y * CY) * time(FB) + Y * T1 + Y * (FBcrit + Y * CY) * T1 + time(Pyro) / (1 - T8) * H * (FBcrit + Y * CY) * (FBcrit + Y * CY) // 0 = [time(FB) + FBcrit * time(FB) + time(Pyro) / (1 - T8) * H * FBcrit * FBcrit] + Y * [CY * time(FB) + T1 + FBcrit * T1 + 2 * time(Pyro) / (1 - T8) * H * FBcrit * CY] + Y * Y * [CY * T1 + time(Pyro) / (1 - T8) * H * CY * CY] float gap = (30.0f - (averageScorchesNeeded + extraScorches) * Sc.CastTime) / (30.0f - extraScorches * Sc.CastTime); if (castingState.MageTalents.ImprovedScorch == 0) { cycle.ProvidesScorch = false; gap = 1.0f; } float hasteFactor = 1.0f; if (gap == 1.0f) { Y = 0.0f; float FBcrit = FB.CritRate; float LBcrit = LB.CritRate; H = castingState.MageTalents.HotStreak / 3.0f; #if RAWR4 if (castingState.Solver.Specialization != Specialization.Fire) H = 0.0f; #else if (castingState.MageTalents.Pyroblast == 0) H = 0.0f; #endif float A2 = (FBcrit - LBcrit) * (LB.CastTime - FB.CastTime - 12) - (FBcrit - LBcrit) * (FBcrit - LBcrit) * Pyro.CastTime / (1 - T8) * H; float A1 = (FBcrit - LBcrit) * (12 - LB.CastTime) + (LB.CastTime - FB.CastTime - 12) * (1 + LBcrit) - Pyro.CastTime / (1 - T8) * H * 2 * LBcrit * (FBcrit - LBcrit); float A0 = (1 + LBcrit) * (12 - LB.CastTime) - Pyro.CastTime / (1 - T8) * H * LBcrit * LBcrit; if (Math.Abs(A2) < 0.00001) { X = -A0 / A1; } else { X = (float)((-A1 - Math.Sqrt(A1 * A1 - 4 * A2 * A0)) / (2 * A2)); } C = LBcrit + X * (FBcrit - LBcrit); K = H * C * C / (1 + C) / (1 - T8); // first order correction for lower LB uptime if (castingState.Solver.Mage2T10) { // p = K / (1 + K) // N = (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FB.CastTime * X + LB.CastTime * (1-X) + Pyro.CastTime * K) hasteFactor = 1.12f / ((float)Math.Pow(1 - K / (1.0f + K), (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FB.CastTime * X + LB.CastTime * (1 - X) + Pyro.CastTime * K) + 0.5f) * 0.12f + 1f); } // LBrecastInterval = 12 + 0.5 * ((time(FB) * X + time(Pyro) * K) / (X + K)) float LBrecastInterval = 12 + 0.5f * ((FB.CastTime * FB.CastTime * X + Pyro.CastTime * Pyro.CastTime * K) / (FB.CastTime * X + Pyro.CastTime * K)) / hasteFactor; // now recalculate the spell distribution under the new uptime assumption A2 = (FBcrit - LBcrit) * (LB.CastTime / hasteFactor - FB.CastTime / hasteFactor - LBrecastInterval) - (FBcrit - LBcrit) * (FBcrit - LBcrit) * Pyro.CastTime / hasteFactor / (1 - T8) * H; A1 = (FBcrit - LBcrit) * (LBrecastInterval - LB.CastTime / hasteFactor) + (LB.CastTime / hasteFactor - FB.CastTime / hasteFactor - LBrecastInterval) * (1 + LBcrit) - Pyro.CastTime / hasteFactor / (1 - T8) * H * 2 * LBcrit * (FBcrit - LBcrit); A0 = (1 + LBcrit) * (LBrecastInterval - LB.CastTime / hasteFactor) - Pyro.CastTime / hasteFactor / (1 - T8) * H * LBcrit * LBcrit; if (Math.Abs(A2) < 0.00001) { X = -A0 / A1; } else { X = (float)((-A1 - Math.Sqrt(A1 * A1 - 4 * A2 * A0)) / (2 * A2)); } C = LBcrit + X * (FBcrit - LBcrit); averageCastTime = LB.CastTime + X * (FB.CastTime - LB.CastTime); K = H * C * C / (1 + C) / (1 - T8); } else { float P = 1.0f + Sc.CastTime / (12.0f * (1.0f - gap)); float FBcrit = FB.CritRate; float SCcrit = Sc.CritRate; float LBcrit = LB.CritRate; H = castingState.MageTalents.HotStreak / 3.0f; #if RAWR4 if (castingState.Solver.Specialization != Specialization.Fire) H = 0.0f; #else if (castingState.MageTalents.Pyroblast == 0) H = 0.0f; #endif float T1 = Sc.CastTime - P * FB.CastTime + (P - 1) * LB.CastTime - 12 * (P - 1); float CY = SCcrit - FBcrit * P + LBcrit * (P - 1); float A2 = CY * T1 + Pyro.CastTime / (1 - T8) * H * CY * CY; float A1 = CY * FB.CastTime + T1 + FBcrit * T1 + 2 * Pyro.CastTime / (1 - T8) * H * FBcrit * CY; float A0 = FB.CastTime + FBcrit * FB.CastTime + Pyro.CastTime / (1 - T8) * H * FBcrit * FBcrit; if (Math.Abs(A2) < 0.00001) { Y = -A0 / A1; } else { Y = (float)((-A1 - Math.Sqrt(A1 * A1 - 4 * A2 * A0)) / (2 * A2)); } X = 1 - P * Y; C = (FBcrit * X + SCcrit * Y + LBcrit * (1 - X - Y)); K = H * C * C / (1 + C) / (1 - T8); if (castingState.Solver.Mage2T10) { // p = K / (1 + K) // N = (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FB.CastTime * X + LB.CastTime * (1-X) + Pyro.CastTime * K) hasteFactor = 1.12f / ((float)Math.Pow(1 - K / (1.0f + K), (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FB.CastTime * X + Sc.CastTime * Y + LB.CastTime * (1 - X - Y) + Pyro.CastTime * K) + 0.5f) * 0.12f + 1f); } float LBrecastInterval = 12 + 0.5f * ((FB.CastTime * FB.CastTime * X + Sc.CastTime * Sc.CastTime * Y + Pyro.CastTime * Pyro.CastTime * K) / (FB.CastTime * X + Sc.CastTime * Y + Pyro.CastTime * K)) / hasteFactor; P = 1.0f + Sc.CastTime / hasteFactor / (LBrecastInterval * (1.0f - gap)); T1 = Sc.CastTime / hasteFactor - P * FB.CastTime / hasteFactor + (P - 1) * LB.CastTime / hasteFactor - LBrecastInterval * (P - 1); CY = SCcrit - FBcrit * P + LBcrit * (P - 1); A2 = CY * T1 + Pyro.CastTime / hasteFactor / (1 - T8) * H * CY * CY; A1 = CY * FB.CastTime / hasteFactor + T1 + FBcrit * T1 + 2 * Pyro.CastTime / hasteFactor / (1 - T8) * H * FBcrit * CY; A0 = FB.CastTime / hasteFactor + FBcrit * FB.CastTime / hasteFactor + Pyro.CastTime / hasteFactor / (1 - T8) * H * FBcrit * FBcrit; if (Math.Abs(A2) < 0.00001) { Y = -A0 / A1; } else { Y = (float)((-A1 - Math.Sqrt(A1 * A1 - 4 * A2 * A0)) / (2 * A2)); } X = 1 - P * Y; C = (FBcrit * X + SCcrit * Y + LBcrit * (1 - X - Y)); averageCastTime = (FB.CastTime * X + Sc.CastTime * Y + LB.CastTime * (1 - X - Y)); K = H * C * C / (1 + C) / (1 - T8); } // pyro dot uptime //A := [x * cFB * DFB + (1 - x) * cLB * DLB] ~ (x * cFB + (1 - x) * cLB)* D[x * FB + (1 - x) * LB] //B := [x * (1-cFB) * DFB + (1 - x) * (1-cLB) * DLB] ~ (1 - (x * cFB + (1- x) * cLB)) * D[x * FB + (1 - x) * LB] float averageTicks = 0f; float k1 = 0; float k2 = C * C * H; float totalChance = k2; int n = 2; averageTicks += Math.Min((int)(Pyro.CastTime / hasteFactor / 3.0f), 4) * T8; averageTicks += Math.Min((int)((Pyro.CastTime + n * averageCastTime) / hasteFactor / 3.0f), 4) * (1 - T8) * k2; while ((Pyro.CastTime + n * averageCastTime) / hasteFactor < 12) { float tmp = k1; k1 = k2; k2 = C * (1 - C * H) * tmp + (1 - C) * k1; totalChance += k2; n++; averageTicks += Math.Min((int)((Pyro.CastTime + n * averageCastTime) / hasteFactor / 3.0f), 4) * (1 - T8) * k2; } averageTicks += 4 * (1 - T8) * (1 - totalChance); cycle.AddSpell(needsDisplayCalculations, FB, X); cycle.AddSpell(needsDisplayCalculations, Sc, Y); cycle.AddSpell(needsDisplayCalculations, LB, 1 - X - Y); cycle.AddSpell(needsDisplayCalculations, Pyro, K, averageTicks / 4.0f); cycle.CastTime /= hasteFactor; cycle.Calculate(); return cycle; }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); Spell FrB, FFB, FFBS, ILS, DFS; float KFrB, KFrB2, KFFB, KFFBS, KILS, KDFS; cycle.Name = "FrBDFFFBIL"; // S00: FOF00, BF00 // FrB => S11 fof * bf * (1-Y) // S10 fof * (1-bf) * (1-Y) // S01 (1-fof) * bf * (1-Y) // S00 (1-fof)*(1-bf) * (1-Y) // - => S40 Y // S01: FOF00, BF10 // FrB => S12 fof * (1-Y) // S02 (1-fof) * (1-Y) // - => S41 Y // S02: FOF00, BF11 // FrB => S12 fof * (1-Y) // S02 (1-fof) * (1-Y) // - => S42 Y // S10: FOF10, BF00 // FrB => S31 fof * bf * (1-Y) // S30 fof * (1-bf) * (1-Y) // S21 (1-fof) * bf * (1-Y) // S20 (1-fof)*(1-bf) * (1-Y) // - => S40 Y // S11: FOF10, BF10 // FrB => S32 fof * (1-Y) // S22 (1-fof) * (1-Y) // - => S41 Y // S12: FOF10, BF11 // FrB => S32 (1-fof) * (1-Y) // S22 fof * (1-Y) // - => S42 Y // S20: FOF11, BF00 // FrB => S31 fof * bf * (1-X) // S30 fof * (1-bf) * (1-X) // S21 (1-fof) * bf * (1-X) // S20 (1-fof)*(1-bf) * (1-X) // DF => S00 X // S21: FOF11, BF10 // FrB => S32 fof * (1-X) // S22 (1-fof) * (1-X) // DF => S02 X // S22: FOF11, BF11 // FFBS => S00 (1-fof)*(1-X) // S20 fof*(1-X) // DF => S02 X // S30: FOF21, B00 // FrB => S41 bf * (1-X) // S40 (1-bf) * (1-X) // DF => S20 X // S31: FOF21, BF10 // FrB => S42 (1-X) // DF => S22 X // S32: FOF21, BF11 // FFBS => S20 (1-fof)*(1-X) // S40 fof*(1-X) // DF => S22 X // S40: FOF22, B00 // IL => S20 1-X // DF => S20 X // S41: FOF22, B10 // IL => S22 1-X // DF => S22 X // S42: FOF22, BF11 // FFBS => S20 (1-fof)*(1-X) // S40 fof*(1-X) // DF => S22 X // S00 = S00 * (1-fof)*(1-bf) * (1-Y) + S20 * X + S22 * (1-fof)*(1-X) // S01 = S00 * (1-fof) * bf * (1-Y) // S02 = S01 * (1-fof) * (1-Y) + S02 * (1-fof) * (1-Y) + S21 * X + S22 * X // S10 = S00 * fof * (1-bf) * (1-Y) // S11 = S00 * fof * bf * (1-Y) // S12 = S01 * fof * (1-Y) + S02 * fof * (1-Y) // S20 = S10 * (1-fof)*(1-bf) * (1-Y) + S20 * (1-fof)*(1-bf) * (1-X) + S22 * fof*(1-X) + S30 * X + S32 * (1-fof)*(1-X) + S40 + S42 * (1-fof)*(1-X) // S21 = S10 * (1-fof) * bf * (1-Y) + S20 * (1-fof) * bf * (1-X) // S22 = S11 * (1-fof) * (1-Y) + S12 * fof * (1-Y) + S21 * (1-fof) * (1-X) + S31 * X + S32 * X + S41 + S42 * X // S30 = S10 * fof * (1-bf) * (1-Y) + S20 * fof * (1-bf) * (1-X) // S31 = S10 * fof * bf * (1-Y) + S20 * fof * bf * (1-X) // S32 = S11 * fof * (1-Y) + S12 * (1-fof) * (1-Y) + S21 * fof * (1-X) // S40 = S00 * Y + S10 * Y + S30 * (1-bf) * (1-X) + S32 * fof*(1-X) + S42 * fof*(1-X) // S41 = S01 * Y + S11 * Y + S30 * bf * (1-X) // S42 = S02 * Y + S12 * Y + S31 * (1-X) // S00 + S01 + S02 + S10 + S11 + S12 + S20 + S21 + S22 + S30 + S31 + S32 + S40 + S41 + S42 = 1 // solved symbolically // solve([S00 = S00 * (1-fof)*(1-bf) * (1-Y) + S20 * X + S22 * (1-fof)*(1-X),S01 = S00 * (1-fof) * bf * (1-Y),S02 = S01 * (1-fof) * (1-Y) + S02 * (1-fof) * (1-Y) + S21 * X + S22 * X,S10 = S00 * fof * (1-bf) * (1-Y),S11 = S00 * fof * bf * (1-Y),S12 = S01 * fof * (1-Y) + S02 * fof * (1-Y),S20 = S10 * (1-fof)*(1-bf) * (1-Y) + S20 * (1-fof)*(1-bf) * (1-X) + S22 * fof*(1-X) + S30 * X + S32 * (1-fof)*(1-X) + S40 + S42 * (1-fof)*(1-X),S21 = S10 * (1-fof) * bf * (1-Y) + S20 * (1-fof) * bf * (1-X),S22 = S11 * (1-fof) * (1-Y) + S12 * fof * (1-Y) + S21 * (1-fof) * (1-X) + S31 * X + S32 * X + S41 + S42 * X,S30 = S10 * fof * (1-bf) * (1-Y) + S20 * fof * (1-bf) * (1-X),S31 = S10 * fof * bf * (1-Y) + S20 * fof * bf * (1-X),S32 = S11 * fof * (1-Y) + S12 * (1-fof) * (1-Y) + S21 * fof * (1-X),S40 = S00 * Y + S10 * Y + S30 * (1-bf) * (1-X) + S32 * fof*(1-X) + S42 * fof*(1-X),S41 = S01 * Y + S11 * Y + S30 * bf * (1-X),S42 = S02 * Y + S12 * Y + S31 * (1-X),S00 + S01 + S02 + S10 + S11 + S12 + S20 + S21 + S22 + S30 + S31 + S32 + S40 + S41 + S42 = 1,fof=0.2,bf=0.15], [S00,S01,S02,S10,S11,S12,S20,S21,S22,S30,S31,S32,S40,S41,S42,fof,bf]); // S00=((480000*X^4-6440000*X^3+6440000*X^2-480000*X)*Y^2+(-6240000*X^4+126440000*X^3-33880000*X^2-76600000*X-9720000)*Y-840000*X^4+21950000*X^3+1190000*X^2-19870000*X-2430000) // S02=((57600*X^4-772800*X^3+772800*X^2-57600*X)*Y^3+(1853100*X^4-3543525*X^3+10315725*X^2-10646400*X-1166400)*Y^2+(-8469000*X^4+9007950*X^3+23115150*X^2-861900*X+2332800)*Y-191700*X^4-2629125*X^3+671325*X^2-18621600*X-1166400) // S20=((81600*X^3-163200*X^2+81600*X)*Y^4+(-1224000*X^3-11899600*X^2+22031200*X-8907600)*Y^3+(2060400*X^3+88837500*X^2-27086200*X-63811700)*Y^2+(-775200*X^3+43676600*X^2+2752400*X-45653800)*Y-142800*X^3+4548700*X^2+2221000*X-6626900) // S01 = S00 * (1-fof) * bf * (1-Y) // S10 = S00 * fof * (1-bf) * (1-Y) // S11 = S00 * fof * bf * (1-Y) // S21 = S10 * (1-fof) * bf * (1-Y) + S20 * (1-fof) * bf * (1-X) // S30 = S10 * fof * (1-bf) * (1-Y) + S20 * fof * (1-bf) * (1-X) // S31 = S10 * fof * bf * (1-Y) + S20 * fof * bf * (1-X) // S12 = S01 * fof * (1-Y) + S02 * fof * (1-Y) // S32 = S11 * fof * (1-Y) + S12 * (1-fof) * (1-Y) + S21 * fof * (1-X) // S41 = S01 * Y + S11 * Y + S30 * bf * (1-X) // S42 = S02 * Y + S12 * Y + S31 * (1-X) // S22 = S11 * (1-fof) * (1-Y) + S12 * fof * (1-Y) + S21 * (1-fof) * (1-X) + S31 * X + S32 * X + S41 + S42 * X // S40 = S00 * Y + S10 * Y + S30 * (1-bf) * (1-X) + S32 * fof*(1-X) + S42 * fof*(1-X) FrB = castingState.GetSpell(SpellId.Frostbolt); FFB = castingState.GetSpell(SpellId.FrostfireBoltBF); FFBS = castingState.FrozenState.GetSpell(SpellId.FrostfireBoltBF); ILS = castingState.FrozenState.GetSpell(SpellId.IceLance); DFS = castingState.FrozenState.GetSpell(SpellId.DeepFreeze); float bf = 0.05f * castingState.MageTalents.BrainFreeze; float fof = (castingState.MageTalents.FingersOfFrost == 3 ? 0.2f : 0.07f * castingState.MageTalents.FingersOfFrost); float fof2 = fof * fof; float fof3 = fof2 * fof; float fof4 = fof3 * fof; float bf2 = bf * bf; float bf3 = bf2 * bf; // states are split into S0,S1 (fof not registered) vs S2,S3,S4 (fof registered) // ABBBaaBBBaaBBBaABBBaaBBBaaBBB // A*Y freezes in B*time(B) + A*(1-Y)*time(a) // A*Y <= R / 25 * (B*time(B) + A*(1-Y)*time(a)) // A*Y <= R / 25 * B*time(B) + R / 25 * A*time(a) - R / 25 * A*time(a) * Y // A*Y*(1 + R / 25 * time(a)) <= R / 25 * B*time(B) + R / 25 * A*time(a) // Y = R/25 * (B*time(B) + A*time(a)) / (A * (1 + R / 25 * time(a))) // B*X*time(DF)/(B*time(B) + A*(1-Y)*time(a))=K*time(DF)/cool(DF) // heruistic tuning parameters float R = 1.05f; // overestimate because we don't have IL to eat down FOF when freeze is coming off cooldown in the model if (castingState.CalculationOptions.FlameOrb == 1) { fof *= 1.8f; } else if (castingState.CalculationOptions.FlameOrb == 2 && castingState.FlameOrb) { fof *= 5; } float K = 0.95f; // crude initial guess (fof=0.9,nonfof=0.1) float Y = R / 25f * (0.9f * (0.5f * DFS.CastTime + 0.5f * FrB.CastTime) + 0.1f * FrB.CastTime) / (0.1f * (1 + R / 25f * FrB.CastTime)); float X = K * DFS.CastTime / DFS.Cooldown * (0.9f * (0.5f * DFS.CastTime + 0.5f * FrB.CastTime) + 0.1f * (1 - Y) * FrB.CastTime) / (0.9f * DFS.CastTime); float S00 = -((480000 * X * X * X * X - 6440000 * X * X * X + 6440000 * X * X - 480000 * X) * Y * Y + (-6240000 * X * X * X * X + 126440000 * X * X * X - 33880000 * X * X - 76600000 * X - 9720000) * Y - 840000 * X * X * X * X + 21950000 * X * X * X + 1190000 * X * X - 19870000 * X - 2430000); float S02 = -((57600 * X * X * X * X - 772800 * X * X * X + 772800 * X * X - 57600 * X) * Y * Y * Y + (1853100 * X * X * X * X - 3543525 * X * X * X + 10315725 * X * X - 10646400 * X - 1166400) * Y * Y + (-8469000 * X * X * X * X + 9007950 * X * X * X + 23115150 * X * X - 861900 * X + 2332800) * Y - 191700 * X * X * X * X - 2629125 * X * X * X + 671325 * X * X - 18621600 * X - 1166400); float S20 = -((81600 * X * X * X - 163200 * X * X + 81600 * X) * Y * Y * Y * Y + (-1224000 * X * X * X - 11899600 * X * X + 22031200 * X - 8907600) * Y * Y * Y + (2060400 * X * X * X + 88837500 * X * X - 27086200 * X - 63811700) * Y * Y + (-775200 * X * X * X + 43676600 * X * X + 2752400 * X - 45653800) * Y - 142800 * X * X * X + 4548700 * X * X + 2221000 * X - 6626900); float S01 = S00 * (1-fof) * bf * (1-Y); float S10 = S00 * fof * (1-bf) * (1-Y); float S11 = S00 * fof * bf * (1-Y); float S21 = S10 * (1-fof) * bf * (1-Y) + S20 * (1-fof) * bf * (1-X); float S30 = S10 * fof * (1-bf) * (1-Y) + S20 * fof * (1-bf) * (1-X); float S31 = S10 * fof * bf * (1-Y) + S20 * fof * bf * (1-X); float S12 = S01 * fof * (1-Y) + S02 * fof * (1-Y); float S32 = S11 * fof * (1-Y) + S12 * (1-fof) * (1-Y) + S21 * fof * (1-X); float S41 = S01 * Y + S11 * Y + S30 * bf * (1-X); float S42 = S02 * Y + S12 * Y + S31 * (1-X); float S22 = S11 * (1-fof) * (1-Y) + S12 * fof * (1-Y) + S21 * (1-fof) * (1-X) + S31 * X + S32 * X + S41 + S42 * X; float S40 = S00 * Y + S10 * Y + S30 * (1-bf) * (1-X) + S32 * fof*(1-X) + S42 * fof*(1-X); float div = S00 + S01 + S02 + S10 + S11 + S12 + S20 + S21 + S22 + S30 + S31 + S32 + S40 + S41 + S42; KFrB = ((S00 + S01 + S02 + S10 + S11 + S12) * (1 - Y) + (S20 + S21 + S30 + S31) * (1 - X)) / div; KFrB2 = ((S00 + S01 + S02 + S10 + S11 + S12) + (S20 + S21 + S30 + S31) * (1 - X)) / div; KFFB = 0 / div; KFFBS = ((S22 + S32 + S42) * (1 - X)) / div; KILS = (S40 + S41) * (1 - X) / div; KDFS = (S20 + S21 + S22 + S30 + S31 + S32 + S40 + S41 + S42) * X / div; for (int i = 0; i < 5; i++) { float T = KFrB * FrB.CastTime + KFFB * FFB.CastTime + KFFBS * FFBS.CastTime + KILS * ILS.CastTime + KDFS * DFS.CastTime; float T2 = KFrB2 * FrB.CastTime + KFFB * FFB.CastTime + KFFBS * FFBS.CastTime + KILS * ILS.CastTime + KDFS * DFS.CastTime; // better estimate // TODO better probabilistic model Y = R / 25 * T2 / ((S00 + S01 + S02 + S10 + S11 + S12) / div * (1 + R / 25 * FrB.CastTime)); X = K / DFS.Cooldown * T / ((S20 + S21 + S22 + S30 + S31 + S32 + S40 + S41 + S42) / div); // recalculate shares based on revised estimate S00 = -((480000 * X * X * X * X - 6440000 * X * X * X + 6440000 * X * X - 480000 * X) * Y * Y + (-6240000 * X * X * X * X + 126440000 * X * X * X - 33880000 * X * X - 76600000 * X - 9720000) * Y - 840000 * X * X * X * X + 21950000 * X * X * X + 1190000 * X * X - 19870000 * X - 2430000); S02 = -((57600 * X * X * X * X - 772800 * X * X * X + 772800 * X * X - 57600 * X) * Y * Y * Y + (1853100 * X * X * X * X - 3543525 * X * X * X + 10315725 * X * X - 10646400 * X - 1166400) * Y * Y + (-8469000 * X * X * X * X + 9007950 * X * X * X + 23115150 * X * X - 861900 * X + 2332800) * Y - 191700 * X * X * X * X - 2629125 * X * X * X + 671325 * X * X - 18621600 * X - 1166400); S20 = -((81600 * X * X * X - 163200 * X * X + 81600 * X) * Y * Y * Y * Y + (-1224000 * X * X * X - 11899600 * X * X + 22031200 * X - 8907600) * Y * Y * Y + (2060400 * X * X * X + 88837500 * X * X - 27086200 * X - 63811700) * Y * Y + (-775200 * X * X * X + 43676600 * X * X + 2752400 * X - 45653800) * Y - 142800 * X * X * X + 4548700 * X * X + 2221000 * X - 6626900); S01 = S00 * (1 - fof) * bf * (1 - Y); S10 = S00 * fof * (1 - bf) * (1 - Y); S11 = S00 * fof * bf * (1 - Y); S21 = S10 * (1 - fof) * bf * (1 - Y) + S20 * (1 - fof) * bf * (1 - X); S30 = S10 * fof * (1 - bf) * (1 - Y) + S20 * fof * (1 - bf) * (1 - X); S31 = S10 * fof * bf * (1 - Y) + S20 * fof * bf * (1 - X); S12 = S01 * fof * (1 - Y) + S02 * fof * (1 - Y); S32 = S11 * fof * (1 - Y) + S12 * (1 - fof) * (1 - Y) + S21 * fof * (1 - X); S41 = S01 * Y + S11 * Y + S30 * bf * (1 - X); S42 = S02 * Y + S12 * Y + S31 * (1 - X); S22 = S11 * (1 - fof) * (1 - Y) + S12 * fof * (1 - Y) + S21 * (1 - fof) * (1 - X) + S31 * X + S32 * X + S41 + S42 * X; S40 = S00 * Y + S10 * Y + S30 * (1 - bf) * (1 - X) + S32 * fof * (1 - X) + S42 * fof * (1 - X); div = S00 + S01 + S02 + S10 + S11 + S12 + S20 + S21 + S22 + S30 + S31 + S32 + S40 + S41 + S42; KFrB = ((S00 + S01 + S02 + S10 + S11 + S12) * (1 - Y) + (S20 + S21 + S30 + S31) * (1 - X)) / div; KFrB2 = ((S00 + S01 + S02 + S10 + S11 + S12) + (S20 + S21 + S30 + S31) * (1 - X)) / div; KFFB = 0 / div; KFFBS = ((S22 + S32 + S42) * (1 - X)) / div; KILS = (S40 + S41) * (1 - X) / div; KDFS = (S20 + S21 + S22 + S30 + S31 + S32 + S40 + S41 + S42) * X / div; } //div = KFrB + KFFBS + KILS + KDFS; //KFrB /= div; //KFFBS /= div; //KILS /= div; //KDFS /= div; cycle.AddSpell(needsDisplayCalculations, FrB, KFrB); cycle.AddSpell(needsDisplayCalculations, FFB, KFFB); cycle.AddSpell(needsDisplayCalculations, FFBS, KFFBS); cycle.AddSpell(needsDisplayCalculations, ILS, KILS); cycle.AddSpell(needsDisplayCalculations, DFS, KDFS); cycle.Calculate(); return cycle; }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); cycle.Name = "ABABar1AM"; // S0: // AB0-ABar => S0 (1-MB)*(1-MB) // AB0-ABar-AB0-AM => S0 (1-MB)*MB // AB0-AM => S0 MB Spell ABar = castingState.GetSpell(SpellId.ArcaneBarrage); Spell AM = castingState.GetSpell(SpellId.ArcaneMissiles); Spell AB0 = castingState.GetSpell(SpellId.ArcaneBlast0); float MB = 0.4f; float K2 = (1 - MB) * (1 - MB); float K3 = MB * (1 - MB); cycle.AddSpell(needsDisplayCalculations, AB0, 1 + K3); cycle.AddSpell(needsDisplayCalculations, ABar, 1 - MB); cycle.AddSpell(needsDisplayCalculations, AM, 1 - K2); cycle.AddPause(ABar.Cooldown - ABar.CastTime - AB0.CastTime, 1 - MB); cycle.Calculate(); return cycle; }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); Spell FrB, FrBS, FB, FBS, ILS, DFS; float KFrB, KFrBS, KFB, KFBS, KILS, KDFS; cycle.Name = "FrBDFFBIL"; //float T8 = CalculationOptionsMage.SetBonus4T8ProcRate * castingState.BaseStats.Mage4T8; // S00: FOF0, BF0 // FrB => S21 fof * bf // S20 fof * (1-bf) // S01 (1-fof) * bf // S00 (1-fof)*(1-bf) // S01: FOF0, BF1 // FrB => S22 fof // S02 (1-fof) // S02: FOF0, BF2 // FB => S00 1 // S10: FOF1, BF0 // FrBS-ILS => S12 X*fof * bf // S10 X*fof * (1-bf) // S02 X*(1-fof) * bf // S00 X*(1-fof)*(1-bf) // FrBS-DFS => S12 (1-X)*fof*bf // S10 (1-X)*fof * (1-bf) // S02 (1-X)*(1-fof) * bf // S00 (1-X)*(1-fof)*(1-bf) // S11: FOF1, BF1 // FrBS-FBS => S10 X*fof // S00 X*(1-fof) // FrBS-DFS => S12 (1-X)*fof // S02 (1-X)*(1-fof) // S12 = S11 // S20: FOF0, BF0 // FrBS => S21 fof * bf // S20 fof * (1-bf) // S11 (1-fof) * bf // S10 (1-fof)*(1-bf) // S21: FOF0, BF1 // FrBS => S22 fof // S12 (1-fof) // S22 = S21 // S00 = (1-fof)*(1-bf) * S00 + S02 + (1-fof)*(1-bf) * S10 + X*(1-fof) * S11 // S01 = (1-fof) * bf * S00 // S02 = (1-fof) * S01 + (1-fof) * bf * S10 + (1-X)*(1-fof) * S11 // S10 = fof * (1-bf) * S10 + X*fof * S11 + (1-fof)*(1-bf) * S20 // S11 = fof * bf * S10 + (1-X)*fof * S11 + (1-fof) * bf * S20 + (1-fof) * S21 // S20 = fof * (1-bf) * S00 + fof * (1-bf) * S20 // S21 = fof * bf * S00 + fof * S01 + fof * bf * S20 + fof * S21 // S00 + S01 + S02 + S10 + S11 + S20 + S21 = 1 // solved symbolically FrB = castingState.GetSpell(SpellId.Frostbolt); FrBS = castingState.FrozenState.GetSpell(SpellId.Frostbolt); FB = castingState.GetSpell(SpellId.FireballBF); FBS = castingState.FrozenState.GetSpell(SpellId.FireballBF); ILS = castingState.FrozenState.GetSpell(SpellId.IceLance); DFS = castingState.FrozenState.GetSpell(SpellId.DeepFreeze); float bf = 0.05f * castingState.MageTalents.BrainFreeze; float fof = (castingState.MageTalents.FingersOfFrost == 2 ? 0.15f : 0.07f * castingState.MageTalents.FingersOfFrost); float fof2 = fof * fof; float fof3 = fof2 * fof; float fof4 = fof3 * fof; float bf2 = bf * bf; float bf3 = bf2 * bf; // shatters until deep freeze ~ Poisson // share of shatters that are deep freeze = sum_i=0..inf Pi / sum_i=0..inf (i+1)*Pi = 1 / (1 + mean) // crude initial guess float X = 1.0f - 1.0f / (1.0f + (DFS.Cooldown - DFS.CastTime) / (FrB.CastTime * (1 / fof + 1) + ILS.CastTime)); float S00 = (((bf - 1) * fof3 + (2 - bf) * fof2 - fof) * X + (bf2 - 2 * bf + 1) * fof3 + (-bf2 + 4 * bf - 3) * fof2 + (3 - 2 * bf) * fof - 1); float S01 = -(((bf2 - bf) * fof4 + (3 * bf - 2 * bf2) * fof3 + (bf2 - 3 * bf) * fof2 + bf * fof) * X + (bf3 - 2 * bf2 + bf) * fof4 + (-2 * bf3 + 6 * bf2 - 4 * bf) * fof3 + (bf3 - 6 * bf2 + 6 * bf) * fof2 + (2 * bf2 - 4 * bf) * fof + bf); float S02 = (((bf2 - bf) * fof4 + (4 * bf - 3 * bf2) * fof3 + (3 * bf2 - 5 * bf) * fof2 + (2 * bf - bf2) * fof) * X + (-bf3 + 2 * bf2 - bf) * fof3 + (2 * bf3 - 3 * bf2 + bf) * fof2 + (-bf3 + bf2 + bf) * fof - bf); float S10 = (((bf2 - bf) * fof4 + (-bf2 + bf + 1) * fof3 + (-bf - 1) * fof2) * X + (-bf2 + 2 * bf - 1) * fof3 + (2 * bf2 - 4 * bf + 2) * fof2 + (-bf2 + 2 * bf - 1) * fof); float S11 = ((bf3 - 2 * bf2 + bf) * fof4 + (-bf3 + 4 * bf2 - 3 * bf) * fof3 + (5 * bf - 4 * bf2) * fof2 + (bf2 - 3 * bf) * fof); float S20 = -(((bf - 1) * fof3 + (1 - bf) * fof2) * X + (bf2 - 2 * bf + 1) * fof3 + (-bf2 + 3 * bf - 2) * fof2 + (1 - bf) * fof); float S21 = (((bf2 - bf) * fof4 + (2 * bf - bf2) * fof3 - 2 * bf * fof2) * X + (bf3 - 2 * bf2 + bf) * fof4 + (-bf3 + 4 * bf2 - 3 * bf) * fof3 + (4 * bf - 3 * bf2) * fof2 - 2 * bf * fof); float div = S00 + S01 + S02 + S10 + S11 + S20 + S21; KFrB = (S00 + S01) / div; KFB = S02 / div; KFrBS = (S10 + S11 + S20 + S21) / div; KFBS = X * S11 / div; KILS = X * S10 / div; KDFS = (1 - X) * (S10 + S11) / div; float hasteFactor = 1.0f; float T = KFrB * FrB.CastTime + KFB * FB.CastTime + KFrBS * FrBS.CastTime + KFBS * FBS.CastTime + KILS * ILS.CastTime + KDFS * DFS.CastTime; float T0 = KFBS * FBS.CastTime + KILS * ILS.CastTime + KDFS * DFS.CastTime; float T1 = KFBS * FBS.CastTime + KFB * FB.CastTime; if (castingState.Solver.Mage2T10) { // we'll make a lot of assumptions here and just assume that 2T10 haste is uniformly distributed over all // spells and doesn't have an impact on state space // also ignore the possible refresh of 2T10 // each proc gives 12% haste for 5 sec // we have on average one proc every T/T1 * FB.CastTime // we have some feedback loop here, speeding up the cycle increases the rate of procs // hastedShare = (5-FB.CastTimeAverage) / (T/T1 * FB.CastTimeAverage) // hastedCastShare = (5-FB.CastTimeAverage) / (T/T1 * FB.CastTime) * 1.12 // average haste = 1 / (1-hastedCastShare*0.12/1.12) // TODO this is all a bunch of voodoo, redo the math when you're thinking straight hasteFactor = 1.0f / (1.0f - (5 - FB.CastTime) / (T / T1 * FB.CastTime) * 0.12f); // alternative model based on reduction to single state space and expanding for haste // probability of being hasted = 1 - (1-p)^(N-1) // where // K := (KFrB + KFB + KFrBS + KFBS + KILS + KDFS) // p = probability of haste generating spell = (KFBS + KFB) / K // N = average number of spells affected by haste = (haste duration - average cast time of haste generating spell) / (average cast time of hasted spells) // = (5 - T1 / 1.12 / K) / (T / 1.12 / K) // = (5 * 1.12 * K - T1) / T // hasteFactor = 1 / (((1-p)^(N-1)) * 1 + (1 - (1-p)^(N-1)) * 1/1.12) // = 1.12 / (((1-p)^(N-1)) * 0.12 + 1) // = 1.12 / (1 + 0.12 * (1 - (KFBS + KFB) / K)^((5 * 1.12 * K - T1) / T - 1)) //float K = KFrB + KFB + KFrBS + KFBS + KILS + KDFS; //hasteFactor = 1.12f / (1.0f + 0.12f * (float)Math.Pow(1.0f - ((KFBS + KFB) / K), ((5.0f * 1.12f * K - T1) / T - 1.0f))); } if (fof > 0) // nothing new here if we don't have fof { // better estimate for percentage of shatter combos that are deep freeze // TODO better probabilistic model for DF percentage X = 1.0f - 1.0f / (1.0f + (DFS.Cooldown - DFS.CastTime / hasteFactor) / (DFS.CastTime / hasteFactor * T / T0)); // recalculate shares based on revised estimate S00 = (((bf - 1) * fof3 + (2 - bf) * fof2 - fof) * X + (bf2 - 2 * bf + 1) * fof3 + (-bf2 + 4 * bf - 3) * fof2 + (3 - 2 * bf) * fof - 1); S01 = -(((bf2 - bf) * fof4 + (3 * bf - 2 * bf2) * fof3 + (bf2 - 3 * bf) * fof2 + bf * fof) * X + (bf3 - 2 * bf2 + bf) * fof4 + (-2 * bf3 + 6 * bf2 - 4 * bf) * fof3 + (bf3 - 6 * bf2 + 6 * bf) * fof2 + (2 * bf2 - 4 * bf) * fof + bf); S02 = (((bf2 - bf) * fof4 + (4 * bf - 3 * bf2) * fof3 + (3 * bf2 - 5 * bf) * fof2 + (2 * bf - bf2) * fof) * X + (-bf3 + 2 * bf2 - bf) * fof3 + (2 * bf3 - 3 * bf2 + bf) * fof2 + (-bf3 + bf2 + bf) * fof - bf); S10 = (((bf2 - bf) * fof4 + (-bf2 + bf + 1) * fof3 + (-bf - 1) * fof2) * X + (-bf2 + 2 * bf - 1) * fof3 + (2 * bf2 - 4 * bf + 2) * fof2 + (-bf2 + 2 * bf - 1) * fof); S11 = ((bf3 - 2 * bf2 + bf) * fof4 + (-bf3 + 4 * bf2 - 3 * bf) * fof3 + (5 * bf - 4 * bf2) * fof2 + (bf2 - 3 * bf) * fof); S20 = -(((bf - 1) * fof3 + (1 - bf) * fof2) * X + (bf2 - 2 * bf + 1) * fof3 + (-bf2 + 3 * bf - 2) * fof2 + (1 - bf) * fof); S21 = (((bf2 - bf) * fof4 + (2 * bf - bf2) * fof3 - 2 * bf * fof2) * X + (bf3 - 2 * bf2 + bf) * fof4 + (-bf3 + 4 * bf2 - 3 * bf) * fof3 + (4 * bf - 3 * bf2) * fof2 - 2 * bf * fof); div = S00 + S01 + S02 + S10 + S11 + S20 + S21; KFrB = (S00 + S01) / div; KFB = S02 / div; KFrBS = (S10 + S11 + S20 + S21) / div; KFBS = X * S11 / div; KILS = X * S10 / div; KDFS = (1 - X) * (S10 + S11) / div; } cycle.AddSpell(needsDisplayCalculations, FrB, KFrB); cycle.AddSpell(needsDisplayCalculations, FB, KFB); cycle.AddSpell(needsDisplayCalculations, FrBS, KFrBS); cycle.AddSpell(needsDisplayCalculations, FBS, KFBS); cycle.AddSpell(needsDisplayCalculations, ILS, KILS); cycle.AddSpell(needsDisplayCalculations, DFS, KDFS); cycle.CastTime /= hasteFactor; // ignores latency effects, but it'll have to do for now cycle.Calculate(); return cycle; }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); Spell FFB; Spell Sc; Spell LB; Spell Pyro; float K; float X; float Y; cycle.Name = "FFBScLBPyro"; cycle.ProvidesScorch = true; FFB = castingState.GetSpell(SpellId.FrostfireBolt); Sc = castingState.GetSpell(SpellId.Scorch); LB = castingState.GetSpell(SpellId.LivingBomb); Pyro = castingState.GetSpell(SpellId.PyroblastPOMDotUptime); int averageScorchesNeeded = (int)Math.Ceiling(3f / (float)castingState.MageTalents.ImprovedScorch); int extraScorches = 0; float T8 = 0; float gap = (30.0f - (averageScorchesNeeded + extraScorches) * Sc.CastTime) / (30.0f - extraScorches * Sc.CastTime); if (castingState.MageTalents.ImprovedScorch == 0) { cycle.ProvidesScorch = false; gap = 1.0f; } float hasteFactor = 1.0f; float C, H, averageCastTime; if (gap == 1.0f) { Y = 0.0f; float FFBcrit = FFB.CritRate; float LBcrit = LB.CritRate; H = castingState.MageTalents.HotStreak / 3.0f; #if RAWR4 if (castingState.Solver.Specialization != Specialization.Fire) H = 0.0f; #else if (castingState.MageTalents.Pyroblast == 0) H = 0.0f; #endif float A2 = (FFBcrit - LBcrit) * (LB.CastTime - FFB.CastTime - 12) - (FFBcrit - LBcrit) * (FFBcrit - LBcrit) * Pyro.CastTime / (1 - T8) * H; float A1 = (FFBcrit - LBcrit) * (12 - LB.CastTime) + (LB.CastTime - FFB.CastTime - 12) * (1 + LBcrit) - Pyro.CastTime / (1 - T8) * H * 2 * LBcrit * (FFBcrit - LBcrit); float A0 = (1 + LBcrit) * (12 - LB.CastTime) - Pyro.CastTime / (1 - T8) * H * LBcrit * LBcrit; if (Math.Abs(A2) < 0.00001) { X = -A0 / A1; } else { X = (float)((-A1 - Math.Sqrt(A1 * A1 - 4 * A2 * A0)) / (2 * A2)); } C = LBcrit + X * (FFBcrit - LBcrit); K = H * C * C / (1 + C) / (1 - T8); if (castingState.Solver.Mage2T10) { // p = K / (1 + K) // N = (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FB.CastTime * X + LB.CastTime * (1-X) + Pyro.CastTime * K) hasteFactor = 1.12f / ((float)Math.Pow(1 - K / (1.0f + K), (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FFB.CastTime * X + LB.CastTime * (1 - X) + Pyro.CastTime * K) + 0.5f) * 0.12f + 1f); } float LBrecastInterval = 12 + 0.5f * ((FFB.CastTime * FFB.CastTime * X + Pyro.CastTime * Pyro.CastTime * K) / (FFB.CastTime * X + Pyro.CastTime * K)) / hasteFactor; A2 = (FFBcrit - LBcrit) * (LB.CastTime / hasteFactor - FFB.CastTime / hasteFactor - LBrecastInterval) - (FFBcrit - LBcrit) * (FFBcrit - LBcrit) * Pyro.CastTime / hasteFactor / (1 - T8) * H; A1 = (FFBcrit - LBcrit) * (LBrecastInterval - LB.CastTime / hasteFactor) + (LB.CastTime / hasteFactor - FFB.CastTime / hasteFactor - LBrecastInterval) * (1 + LBcrit) - Pyro.CastTime / hasteFactor / (1 - T8) * H * 2 * LBcrit * (FFBcrit - LBcrit); A0 = (1 + LBcrit) * (LBrecastInterval - LB.CastTime / hasteFactor) - Pyro.CastTime / hasteFactor / (1 - T8) * H * LBcrit * LBcrit; if (Math.Abs(A2) < 0.00001) { X = -A0 / A1; } else { X = (float)((-A1 - Math.Sqrt(A1 * A1 - 4 * A2 * A0)) / (2 * A2)); } C = LBcrit + X * (FFBcrit - LBcrit); averageCastTime = LB.CastTime + X * (FFB.CastTime - LB.CastTime); K = H * C * C / (1 + C) / (1 - T8); } else { float P = 1.0f + Sc.CastTime / (12.0f * (1.0f - gap)); float FFBcrit = FFB.CritRate; float SCcrit = Sc.CritRate; float LBcrit = LB.CritRate; H = castingState.MageTalents.HotStreak / 3.0f; #if RAWR4 if (castingState.Solver.Specialization != Specialization.Fire) H = 0.0f; #else if (castingState.MageTalents.Pyroblast == 0) H = 0.0f; #endif float T1 = Sc.CastTime - P * FFB.CastTime + (P - 1) * LB.CastTime - 12 * (P - 1); float CY = SCcrit - FFBcrit * P + LBcrit * (P - 1); float A2 = CY * T1 + Pyro.CastTime / (1 - T8) * H * CY * CY; float A1 = CY * FFB.CastTime + T1 + FFBcrit * T1 + 2 * Pyro.CastTime / (1 - T8) * H * FFBcrit * CY; float A0 = FFB.CastTime + FFBcrit * FFB.CastTime + Pyro.CastTime / (1 - T8) * H * FFBcrit * FFBcrit; if (Math.Abs(A2) < 0.00001) { Y = -A0 / A1; } else { Y = (float)((-A1 - Math.Sqrt(A1 * A1 - 4 * A2 * A0)) / (2 * A2)); } X = 1 - P * Y; C = (FFBcrit * X + SCcrit * Y + LBcrit * (1 - X - Y)); K = H * C * C / (1 + C) / (1 - T8); if (castingState.Solver.Mage2T10) { // p = K / (1 + K) // N = (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FB.CastTime * X + LB.CastTime * (1-X) + Pyro.CastTime * K) hasteFactor = 1.12f / ((float)Math.Pow(1 - K / (1.0f + K), (5 * 1.12 - Pyro.CastTime) * (1 + K) / (FFB.CastTime * X + Sc.CastTime * Y + LB.CastTime * (1 - X - Y) + Pyro.CastTime * K) + 0.5f) * 0.12f + 1f); } float LBrecastInterval = 12 + 0.5f * ((FFB.CastTime * FFB.CastTime * X + Sc.CastTime * Sc.CastTime * Y + Pyro.CastTime * Pyro.CastTime * K) / (FFB.CastTime * X + Sc.CastTime * Y + Pyro.CastTime * K)) / hasteFactor; P = 1.0f + Sc.CastTime / hasteFactor / (LBrecastInterval * (1.0f - gap)); T1 = Sc.CastTime / hasteFactor - P * FFB.CastTime / hasteFactor + (P - 1) * LB.CastTime / hasteFactor - LBrecastInterval * (P - 1); CY = SCcrit - FFBcrit * P + LBcrit * (P - 1); A2 = CY * T1 + Pyro.CastTime / hasteFactor / (1 - T8) * H * CY * CY; A1 = CY * FFB.CastTime / hasteFactor + T1 + FFBcrit * T1 + 2 * Pyro.CastTime / hasteFactor / (1 - T8) * H * FFBcrit * CY; A0 = FFB.CastTime / hasteFactor + FFBcrit * FFB.CastTime / hasteFactor + Pyro.CastTime / hasteFactor / (1 - T8) * H * FFBcrit * FFBcrit; if (Math.Abs(A2) < 0.00001) { Y = -A0 / A1; } else { Y = (float)((-A1 - Math.Sqrt(A1 * A1 - 4 * A2 * A0)) / (2 * A2)); } X = 1 - P * Y; C = (FFBcrit * X + SCcrit * Y + LBcrit * (1 - X - Y)); averageCastTime = (FFB.CastTime * X + Sc.CastTime * Y + LB.CastTime * (1 - X - Y)); K = H * C * C / (1 + C) / (1 - T8); } // pyro dot uptime //A := [x * cFB * DFB + (1 - x) * cLB * DLB] ~ (x * cFB + (1 - x) * cLB)* D[x * FB + (1 - x) * LB] //B := [x * (1-cFB) * DFB + (1 - x) * (1-cLB) * DLB] ~ (1 - (x * cFB + (1- x) * cLB)) * D[x * FB + (1 - x) * LB] float averageTicks = 0f; float k1 = 0; float k2 = C * C * H; float totalChance = k2; int n = 2; averageTicks += Math.Min((int)(Pyro.CastTime / hasteFactor / 3.0f), 4) * T8; averageTicks += Math.Min((int)((Pyro.CastTime + n * averageCastTime) / hasteFactor / 3.0f), 4) * (1 - T8) * k2; while ((Pyro.CastTime + n * averageCastTime) / hasteFactor < 12) { float tmp = k1; k1 = k2; k2 = C * (1 - C * H) * tmp + (1 - C) * k1; totalChance += k2; n++; averageTicks += Math.Min((int)((Pyro.CastTime + n * averageCastTime) / hasteFactor / 3.0f), 4) * (1 - T8) * k2; } averageTicks += 4 * (1 - T8) * (1 - totalChance); cycle.AddSpell(needsDisplayCalculations, FFB, X); cycle.AddSpell(needsDisplayCalculations, Sc, Y); cycle.AddSpell(needsDisplayCalculations, LB, 1 - X - Y); cycle.AddSpell(needsDisplayCalculations, Pyro, K, averageTicks / 4.0f); cycle.CastTime /= hasteFactor; cycle.Calculate(); return cycle; }
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(); } }
public static Cycle GetCycle(bool needsDisplayCalculations, CastingState castingState) { Cycle cycle = Cycle.New(needsDisplayCalculations, castingState); Spell FrB, FrBS, ILS; float KFrB, KFrBS, KILS; cycle.Name = "FrBIL"; //float T8 = 0; // S00: FOF0 // FrB => S20 fof // S00 (1-fof) // S10: FOF1, BF0 // FrBS-ILS => S10 fof // S00 (1-fof) // S20: FOF0, BF0 // FrBS => S20 fof // S10 (1-fof) // S00 = (1-fof) * S00 + (1-fof) * S10 // S10 = fof * S10 + (1-fof) * S20 // S20 = fof * S00 + fof * S20 // S00 + S10 + S20 = 1 float fof = (castingState.MageTalents.FingersOfFrost == 2 ? 0.15f : 0.07f * castingState.MageTalents.FingersOfFrost); float S00 = (1 - fof) / (1 + fof); float S10 = fof / (1 + fof); float S20 = fof / (1 + fof); KFrB = S00; KFrBS = S10 + S20; KILS = S10; FrB = castingState.GetSpell(SpellId.Frostbolt); FrBS = castingState.FrozenState.GetSpell(SpellId.Frostbolt); ILS = castingState.FrozenState.GetSpell(SpellId.IceLance); cycle.AddSpell(needsDisplayCalculations, FrB, KFrB); cycle.AddSpell(needsDisplayCalculations, FrBS, KFrBS); cycle.AddSpell(needsDisplayCalculations, ILS, KILS); cycle.Calculate(); return cycle; }
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(); } }
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; }