// This technique assumes that if you pick a random time during filler // spell(s) or downtime, the "cooldowns" remaining on the rest of your // spells are all equally likely to be at any value. This is unrealistic // (e.g. it's impossible for them all to be at their full value), but // for some classes is a reasonable approximation. private void RecordCollisionDelays(CastingState state) { float pRemaining = 1f; foreach (Spell spell in Priorities) { float p = spell.GetQueueProbability(state); if (p == 0f) { continue; } List <CastingState> nextStates = spell.SimulateCast(state, p * pRemaining); foreach (CastingState nextState in nextStates) { if (nextState.Probability > .0001f) { // Only calculate if the probabilty of the state is // large enough to make any difference at all. RecordCollisionDelays(nextState); } } if (p == 1f) { return; } pRemaining *= 1f - p; } //System.Console.WriteLine(state.ToString()); }
private void RecordCollisionDelays(CastingState state) { // This technique assumes that if you pick a random time during filler // spell(s) or downtime, the "cooldowns" remaining on the rest of your // spells are all equally likely to be at any value. This is unrealistic // (e.g. it's impossible for them all to be at their full value), but // for some classes is a reasonable approximation. float pRemaining = 1f; foreach (Spell spell in Priorities) { float p = spell.GetQueueProbability(state); if (p == 0f) { continue; } List <CastingState> nextStates = spell.SimulateCast(state, p * pRemaining); foreach (CastingState nextState in nextStates) { if (nextState.Probability > .0001f) { // Only calculate if the probabilty of the state is // large enough to make any difference at all. RecordCollisionDelays(nextState); } } if (p == 1f) { return; } pRemaining *= 1f - p; } }
public CastingState(CastingState toCopy) { Mommy = toCopy.Mommy; Probability = toCopy.Probability; Cooldowns = new Dictionary <Spell, float>(toCopy.Cooldowns); SeriesPriorities = new List <int>(toCopy.SeriesPriorities); Series = new List <Spell>(toCopy.Series); SeriesTimes = new List <float>(toCopy.SeriesTimes); SeriesHits = new List <bool>(toCopy.SeriesHits); BackdraftCharges = toCopy.BackdraftCharges; MoltenCoreCharges = toCopy.MoltenCoreCharges; }
public CastingState(CastingState toCopy) { Mommy = toCopy.Mommy; Probability = toCopy.Probability; Cooldowns = new Dictionary<Spell, float>(toCopy.Cooldowns); SeriesPriorities = new List<int>(toCopy.SeriesPriorities); Series = new List<Spell>(toCopy.Series); SeriesTimes = new List<float>(toCopy.SeriesTimes); SeriesHits = new List<bool>(toCopy.SeriesHits); BackdraftCharges = toCopy.BackdraftCharges; MoltenCoreCharges = toCopy.MoltenCoreCharges; }
public float GetUprate(CastingState state, Spell spell) { // Assumes the effect == requeue time. // If this method is going to be called, be sure to enable RecordMissesSeparately. Debug.Assert(RecordMissesSeparately); float castTime = spell.GetCastTime(state); if (state.Cooldowns.ContainsKey(this)) { float cooldown = state.Cooldowns[this] - castTime; if (cooldown <= 0 || !state.LastCastHit(this)) { return 0f; } else { return 1f; } } float maxQueued = castTime; if (Mommy.IsPriorityOrdered(spell, this)) { maxQueued += state.GetMaxTimeQueued(this); } float unqueuable = (state.Elapsed + castTime) - maxQueued; float chanceQueued = Math.Min(maxQueued / (GetAvgRequeueTime() - unqueuable), 1f); return 1 - chanceQueued; }
private float MaybeApplyBackdraft(float time, CastingState state) { if (state != null && state.BackdraftCharges > 0 && MySpellTree == SpellTree.Destruction) { time /= 1f + Mommy.Talents.Backdraft * .1f; } return time; }
public float GetCastTime(CastingState state) { if (BaseCastTime == 0f) { return 0f; } float avg = 0f; foreach (WeightedStat h in Mommy.Haste) { avg += h.Chance * BaseCastTime / h.Value; } return MaybeApplyBackdraft(avg, state); }
public float GetTimeUsed(CastingState state) { return MaybeApplyBackdraft(GetTimeUsed(BaseCastTime, GCDBonus, Mommy.Haste, Mommy.CalcOpts.Latency), state); }
private void PopulateNextState(List<CastingState> results, CastingState stateBeforeCast, float timeUsed, float cooldownAfterAdvance, float p, bool isHit) { CastingState nextState = new CastingState(stateBeforeCast); nextState.Probability *= p; nextState.AddSpell(this, timeUsed, isHit); nextState.Cooldowns[this] = cooldownAfterAdvance; if (MySpellTree == SpellTree.Destruction && nextState.BackdraftCharges > 0) { --nextState.BackdraftCharges; } results.Add(nextState); }
public virtual List<CastingState> SimulateCast(CastingState stateBeforeCast, float chanceOfCast) { // record stats about this spellcast float p = chanceOfCast * stateBeforeCast.Probability; float timeUsed = GetTimeUsed(stateBeforeCast); RecordSimulatedStat("delay", stateBeforeCast.GetMaxTimeQueued(this) / 2f, p); RecordSimulatedStat("time used", timeUsed, p); // construct the casting state(s) that can result from this cast List<CastingState> results = new List<CastingState>(); float hitChance = Mommy.HitChance; if (IsBinary()) { hitChance -= GetResist(); } float newCooldown = Cooldown - timeUsed + GetCastTime(stateBeforeCast); if (CanMiss && hitChance < 1f && (RecordMissesSeparately || Cooldown < RecastPeriod)) { // state when spell hits PopulateNextState(results, stateBeforeCast, timeUsed, Math.Max(RecastPeriod - timeUsed, newCooldown), hitChance * chanceOfCast, true); // state when spell misses if (newCooldown <= 0) { // ensure at least 1 spell is cast before this one is // requeued, to allow for travel time + reaction time for // the player to detect the miss newCooldown = .0001f; } PopulateNextState(results, stateBeforeCast, timeUsed, newCooldown, (1 - hitChance) * chanceOfCast, false); } else { PopulateNextState(results, stateBeforeCast, timeUsed, Math.Max(newCooldown, RecastPeriod - timeUsed), chanceOfCast, true); } return results; }
private void RecordCollisionDelays(CastingState state) { // This technique assumes that if you pick a random time during filler // spell(s) or downtime, the "cooldowns" remaining on the rest of your // spells are all equally likely to be at any value. This is unrealistic // (e.g. it's impossible for them all to be at their full value), but // for some classes is a reasonable approximation. float pRemaining = 1f; foreach (Spell spell in Priorities) { float p = spell.GetQueueProbability(state); if (p == 0f) { continue; } List<CastingState> nextStates = spell.SimulateCast(state, p * pRemaining); foreach (CastingState nextState in nextStates) { if (nextState.Probability > .0001f) { // Only calculate if the probabilty of the state is // large enough to make any difference at all. RecordCollisionDelays(nextState); } } if (p == 1f) { return; } pRemaining *= 1f - p; } }
public override List<CastingState> SimulateCast(CastingState stateBeforeCast, float chanceOfCast) { List<CastingState> states = base.SimulateCast(stateBeforeCast, chanceOfCast); foreach (CastingState state in states) { state.Cooldowns.Remove(this); if (state.MoltenCoreCharges > 0) { --state.MoltenCoreCharges; } else { state.MoltenCoreCharges = 2; } } return states; }
public override float GetQueueProbability(CastingState state) { if (state.MoltenCoreCharges > 0) { return 1f; } else { return base.GetQueueProbability(state); } }
public override float GetQueueProbability(CastingState state) { if (state.BackdraftCharges > 0) { return 1f; } else { return 0f; } }
/// <summary> /// Records the chance that a given spell is cast while immolate is on /// the target. /// </summary> public void RecordUpChance(Spell spell, CastingState state) { float chance; chance = GetUprate(state, spell); spell.RecordSimulatedStat("immolate up-chance", chance, state.Probability); }
public override List<CastingState> SimulateCast(CastingState stateBeforeCast, float chanceOfCast) { if (Mommy.Talents.FireAndBrimstone > 0) { ((Immolate)Mommy.GetSpell("Immolate")).RecordUpChance(this, stateBeforeCast); } return base.SimulateCast(stateBeforeCast, chanceOfCast); }
public override List<CastingState> SimulateCast(CastingState stateBeforeCast, float chanceOfCast) { List<CastingState> states = base.SimulateCast(stateBeforeCast, chanceOfCast); CastingState stateOnHit = states[0]; if (Mommy.Talents.Backdraft > 0) { stateOnHit.BackdraftCharges = 3; } return states; }
public virtual float GetQueueProbability(CastingState state) { if (state.Cooldowns.ContainsKey(this)) { if (state.Cooldowns[this] <= 0) { return 1f; } else { return 0f; } } else { float maxQueued = state.GetMaxTimeQueued(this); float unqueuable = state.Elapsed - maxQueued; return Math.Min(maxQueued / (GetAvgRequeueTime() - unqueuable), 1f); } }