Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions sim/core/dot.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,3 +494,46 @@ func (dot *Dot) RestoreState(state DotState, sim *Simulation) {
dot.tickAction = pa
sim.AddPendingAction(dot.tickAction)
}

type ExpectedTickConfig struct {
UseSnapshot bool
BaseDmgFn func(*Spell) float64 // for non-snapshot damage calc
SnapshotOutcome OutcomeApplier
NormalOutcome OutcomeApplier
ModifyDamage func(*SpellResult) // Modifies Damage before hasteNormalization
SkipHasteNormalization bool // disables haste normalization
ModifyResult func(*SpellResult) // optional final tweaks, apply multipliers, add crit mods
}

func (dot *Dot) CalcExpectedTickDamage(sim *Simulation, target *Unit, cfg ExpectedTickConfig) *SpellResult {
if cfg.UseSnapshot {
result := dot.CalcSnapshotDamage(sim, target, cfg.SnapshotOutcome)
if cfg.ModifyDamage != nil {
cfg.ModifyDamage(result)
}
if !cfg.SkipHasteNormalization {
result.Damage /= dot.TickPeriod().Seconds()
}
if cfg.ModifyResult != nil {
cfg.ModifyResult(result)
}
return result
}

baseDamage := 0.0
if cfg.BaseDmgFn != nil {
baseDamage = cfg.BaseDmgFn(dot.Spell)
}

result := dot.Spell.CalcPeriodicDamage(sim, target, baseDamage, cfg.NormalOutcome)
if cfg.ModifyDamage != nil {
cfg.ModifyDamage(result)
}
if !cfg.SkipHasteNormalization {
result.Damage /= dot.CalcTickPeriod().Round(time.Millisecond).Seconds()
}
if cfg.ModifyResult != nil {
cfg.ModifyResult(result)
}
return result
}
17 changes: 8 additions & 9 deletions sim/druid/balance/sunfire.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,14 @@ func (moonkin *BalanceDruid) registerSunfireDoTSpell() {

ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, useSnapshot bool) *core.SpellResult {
dot := spell.Dot(target)
if useSnapshot {
result := dot.CalcSnapshotDamage(sim, target, dot.OutcomeExpectedSnapshotCrit)
result.Damage /= dot.TickPeriod().Seconds()
return result
} else {
result := spell.CalcPeriodicDamage(sim, target, moonkin.CalcScalingSpellDmg(SunfireDotCoeff), spell.OutcomeExpectedMagicCrit)
result.Damage /= dot.CalcTickPeriod().Round(time.Millisecond).Seconds()
return result
}
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
UseSnapshot: useSnapshot,
BaseDmgFn: func(s *core.Spell) float64 {
return moonkin.CalcScalingSpellDmg(SunfireDotCoeff)
},
SnapshotOutcome: dot.OutcomeExpectedSnapshotCrit,
NormalOutcome: spell.OutcomeExpectedMagicCrit,
})
},
})
}
Expand Down
17 changes: 8 additions & 9 deletions sim/druid/moonfire.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,14 @@ func (druid *Druid) registerMoonfireDoTSpell() {

ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, useSnapshot bool) *core.SpellResult {
dot := spell.Dot(target)
if useSnapshot {
result := dot.CalcSnapshotDamage(sim, target, dot.OutcomeExpectedSnapshotCrit)
result.Damage /= dot.TickPeriod().Seconds()
return result
} else {
result := spell.CalcPeriodicDamage(sim, target, druid.CalcScalingSpellDmg(MoonfireDotCoeff), spell.OutcomeExpectedMagicCrit)
result.Damage /= dot.CalcTickPeriod().Round(time.Millisecond).Seconds()
return result
}
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
UseSnapshot: useSnapshot,
BaseDmgFn: func(s *core.Spell) float64 {
return druid.CalcScalingSpellDmg(MoonfireDotCoeff)
},
SnapshotOutcome: dot.OutcomeExpectedSnapshotCrit,
NormalOutcome: spell.OutcomeExpectedMagicCrit,
})
},
})
}
Expand Down
30 changes: 16 additions & 14 deletions sim/druid/rake.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,22 @@ func (druid *Druid) registerRakeSpell() {
},

ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, useSnapshot bool) *core.SpellResult {
if useSnapshot {
dot := spell.Dot(target)
return dot.CalcSnapshotDamage(sim, target, dot.OutcomeExpectedSnapshotCrit)
} else {
tickBase := flatBaseDamage + bonusCoefficientFromAP*spell.MeleeAttackPower()
ticks := spell.CalcPeriodicDamage(sim, target, tickBase, spell.OutcomeExpectedMagicAlwaysHit)

attackTable := spell.Unit.AttackTables[target.UnitIndex]
critChance := spell.PhysicalCritChance(attackTable)
critMod := (critChance * (spell.CritMultiplier - 1))
ticks.Damage *= 1 + critMod

return ticks
}
dot := spell.Dot(target)
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
UseSnapshot: useSnapshot,
BaseDmgFn: func(s *core.Spell) float64 {
return flatBaseDamage + bonusCoefficientFromAP*spell.MeleeAttackPower()
},
SnapshotOutcome: dot.OutcomeExpectedSnapshotCrit,
NormalOutcome: spell.OutcomeExpectedMagicAlwaysHit,
SkipHasteNormalization: true,
ModifyResult: func(sr *core.SpellResult) {
attackTable := spell.Unit.AttackTables[target.UnitIndex]
critChance := spell.PhysicalCritChance(attackTable)
critMod := (critChance * (spell.CritMultiplier - 1))
sr.Damage *= 1 + critMod
},
})
},
})

Expand Down
34 changes: 20 additions & 14 deletions sim/druid/rip.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,26 @@ func (druid *Druid) registerRipSpell() {
},

ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, useSnapshot bool) *core.SpellResult {
if useSnapshot {
dot := spell.Dot(target)
return dot.CalcSnapshotDamage(sim, target, dot.OutcomeExpectedSnapshotCrit)
} else {
cp := 5.0 // Hard-code this so that snapshotting calculations can be performed at any CP value.
ap := spell.MeleeAttackPower()
baseTickDamage := baseDamage + comboPointCoeff*cp + attackPowerCoeff*cp*ap
result := spell.CalcPeriodicDamage(sim, target, baseTickDamage, spell.OutcomeExpectedMagicAlwaysHit)
attackTable := spell.Unit.AttackTables[target.UnitIndex]
critChance := spell.PhysicalCritChance(attackTable)
critMod := critChance * (spell.CritMultiplier - 1)
result.Damage *= 1 + critMod
return result
}
dot := spell.Dot(target)
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
UseSnapshot: useSnapshot,
BaseDmgFn: func(s *core.Spell) float64 {
cp := 5.0 // Hard-code this so that snapshotting calculations can be performed at any CP value.
ap := spell.MeleeAttackPower()
return baseDamage + comboPointCoeff*cp + attackPowerCoeff*cp*ap
},
SnapshotOutcome: dot.OutcomeExpectedSnapshotCrit,
NormalOutcome: spell.OutcomeExpectedMagicAlwaysHit,
SkipHasteNormalization: true,
ModifyResult: func(sr *core.SpellResult) {
if !useSnapshot {
attackTable := spell.Unit.AttackTables[target.UnitIndex]
critChance := spell.PhysicalCritChance(attackTable)
critMod := critChance * (spell.CritMultiplier - 1)
sr.Damage *= 1 + critMod
}
},
})
},
})

Expand Down
17 changes: 10 additions & 7 deletions sim/druid/thrash.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,16 @@ func (druid *Druid) registerThrashCatSpell() {
},

ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, useSnapshot bool) *core.SpellResult {
if useSnapshot {
dot := spell.Dot(target)
return dot.CalcSnapshotDamage(sim, target, dot.OutcomeExpectedSnapshotCrit)
} else {
baseTickDamage := flatTickDamage + 0.141*spell.MeleeAttackPower()
return spell.CalcPeriodicDamage(sim, target, baseTickDamage, spell.OutcomeExpectedPhysicalCrit)
}
dot := spell.Dot(target)
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
UseSnapshot: useSnapshot,
BaseDmgFn: func(s *core.Spell) float64 {
return flatTickDamage + 0.141*spell.MeleeAttackPower()
},
SnapshotOutcome: dot.OutcomeExpectedSnapshotCrit,
NormalOutcome: spell.OutcomeExpectedPhysicalCrit,
SkipHasteNormalization: true,
})
},

RelatedAuraArrays: druid.WeakenedBlowsAuras.ToMap(),
Expand Down
10 changes: 8 additions & 2 deletions sim/hunter/pet_abilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,14 @@ func (hp *HunterPet) newFrostStormBreath() *core.Spell {
}
},
ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, _ bool) *core.SpellResult {
baseDamage := 206 + (spell.MeleeAttackPower() * 0.40)
return spell.CalcPeriodicDamage(sim, target, baseDamage, spell.OutcomeExpectedMagicCrit)
dot := spell.Dot(target)
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
BaseDmgFn: func(s *core.Spell) float64 {
return 206 + (spell.MeleeAttackPower() * 0.40)
},
NormalOutcome: spell.OutcomeExpectedMagicCrit,
SkipHasteNormalization: true,
})
},
})
return hp.frostStormBreath
Expand Down
20 changes: 13 additions & 7 deletions sim/mage/fire/combustion.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,19 @@ func (fire *FireMage) registerCombustionSpell() {
},
},
ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, useSnapshot bool) *core.SpellResult {
tickBase := calculatedDotTick(sim, target)
result := spell.CalcPeriodicDamage(sim, target, tickBase, spell.OutcomeExpectedMagicAlwaysHit)
critChance := spell.SpellCritChance(target)
critMod := (critChance * (spell.CritMultiplier - 1))
result.Damage *= 1 + critMod

return result
dot := spell.Dot(target)
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
BaseDmgFn: func(s *core.Spell) float64 {
return calculatedDotTick(sim, target)
},
NormalOutcome: spell.OutcomeExpectedMagicAlwaysHit,
SkipHasteNormalization: true,
ModifyResult: func(sr *core.SpellResult) {
critChance := spell.SpellCritChance(target)
critMod := (critChance * (spell.CritMultiplier - 1))
sr.Damage *= 1 + critMod
},
})
},
ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
spell.Dot(target).Apply(sim)
Expand Down
16 changes: 9 additions & 7 deletions sim/priest/_shadow_word_pain.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,15 @@ func (priest *Priest) registerShadowWordPainSpell() {
},

ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, useSnapshot bool) *core.SpellResult {
if useSnapshot {
dot := spell.Dot(target)
return dot.CalcSnapshotDamage(sim, target, dot.OutcomeExpectedMagicSnapshotCrit)
} else {
baseDamage := 194.709
return spell.CalcPeriodicDamage(sim, target, baseDamage, spell.OutcomeExpectedMagicCrit)
}
dot := spell.Dot(target)
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
UseSnapshot: useSnapshot,
BaseDmgFn: func(s *core.Spell) float64 {
return priest.CalcScalingSpellDmg(SwpScaleCoeff)
},
SnapshotOutcome: dot.OutcomeExpectedSnapshotCrit,
NormalOutcome: spell.OutcomeExpectedMagicCrit,
})
},
})
}
9 changes: 8 additions & 1 deletion sim/priest/shadow/mind_flay.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,14 @@ func (shadow *ShadowPriest) registerMindFlaySpell() *core.Spell {
}
},
ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, _ bool) *core.SpellResult {
return spell.CalcPeriodicDamage(sim, target, shadow.CalcScalingSpellDmg(MfScale), spell.OutcomeExpectedMagicCrit)
dot := spell.Dot(target)
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
BaseDmgFn: func(s *core.Spell) float64 {
return shadow.CalcScalingSpellDmg(MfScale)
},
NormalOutcome: spell.OutcomeExpectedMagicCrit,
SkipHasteNormalization: true,
})
},
})
}
9 changes: 8 additions & 1 deletion sim/priest/shadow/talents.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,14 @@ func (shadow *ShadowPriest) registerSolaceAndInstanity() {
}
},
ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, _ bool) *core.SpellResult {
return spell.CalcPeriodicDamage(sim, target, shadow.CalcScalingSpellDmg(MfScale), spell.OutcomeExpectedMagicCrit)
dot := spell.Dot(target)
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
BaseDmgFn: func(s *core.Spell) float64 {
return shadow.CalcScalingSpellDmg(MfScale)
},
NormalOutcome: spell.OutcomeExpectedMagicCrit,
SkipHasteNormalization: true,
})
},
ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool {
return shadow.DevouringPlague.Dot(target).IsActive()
Expand Down
17 changes: 8 additions & 9 deletions sim/priest/shadow_word_pain.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,14 @@ func (priest *Priest) registerShadowWordPainSpell() {

ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, useSnapshot bool) *core.SpellResult {
dot := spell.Dot(target)
if useSnapshot {
result := dot.CalcSnapshotDamage(sim, target, dot.OutcomeExpectedSnapshotCrit)
result.Damage /= dot.TickPeriod().Seconds()
return result
} else {
result := spell.CalcPeriodicDamage(sim, target, priest.CalcScalingSpellDmg(SwpScaleCoeff), spell.OutcomeExpectedMagicCrit)
result.Damage /= dot.CalcTickPeriod().Round(time.Millisecond).Seconds()
return result
}
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
UseSnapshot: useSnapshot,
BaseDmgFn: func(s *core.Spell) float64 {
return priest.CalcScalingSpellDmg(SwpScaleCoeff)
},
SnapshotOutcome: dot.OutcomeExpectedSnapshotCrit,
NormalOutcome: spell.OutcomeExpectedMagicCrit,
})
},
})
}
17 changes: 8 additions & 9 deletions sim/priest/vampiric_touch.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,14 @@ func (priest *Priest) registerVampiricTouchSpell() {
},
ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, useSnapshot bool) *core.SpellResult {
dot := spell.Dot(target)
if useSnapshot {
result := dot.CalcSnapshotDamage(sim, target, dot.OutcomeExpectedSnapshotCrit)
result.Damage /= dot.TickPeriod().Seconds()
return result
} else {
result := spell.CalcPeriodicDamage(sim, target, priest.CalcScalingSpellDmg(VtScaleCoeff), spell.OutcomeExpectedMagicCrit)
result.Damage /= dot.CalcTickPeriod().Round(time.Millisecond).Seconds()
return result
}
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
UseSnapshot: useSnapshot,
BaseDmgFn: func(s *core.Spell) float64 {
return priest.CalcScalingSpellDmg(VtScaleCoeff)
},
SnapshotOutcome: dot.OutcomeExpectedSnapshotCrit,
NormalOutcome: spell.OutcomeExpectedMagicCrit,
})
},
})
}
25 changes: 12 additions & 13 deletions sim/warlock/affliction/agony.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,18 @@ func (affliction *AfflictionWarlock) registerAgony() {

ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, useSnapshot bool) *core.SpellResult {
dot := spell.Dot(target)

// Always compare fully stacked agony damage
if useSnapshot {
result := dot.CalcSnapshotDamage(sim, target, dot.OutcomeExpectedSnapshotCrit)
result.Damage *= 10
result.Damage /= dot.TickPeriod().Seconds()
return result
} else {
result := spell.CalcPeriodicDamage(sim, target, affliction.CalcScalingSpellDmg(agonyScale), spell.OutcomeExpectedMagicCrit)
result.Damage *= 10
result.Damage /= dot.CalcTickPeriod().Round(time.Millisecond).Seconds()
return result
}
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
UseSnapshot: useSnapshot,
BaseDmgFn: func(s *core.Spell) float64 {
return affliction.CalcScalingSpellDmg(agonyScale)
},
SnapshotOutcome: dot.OutcomeExpectedSnapshotCrit,
NormalOutcome: spell.OutcomeExpectedMagicCrit,
ModifyDamage: func(sr *core.SpellResult) {
// Always compare fully stacked agony damage
sr.Damage *= 10
},
})
},
})
}
17 changes: 8 additions & 9 deletions sim/warlock/affliction/unstable_affliction.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,14 @@ func (affliction *AfflictionWarlock) registerUnstableAffliction() {

ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, useSnapshot bool) *core.SpellResult {
dot := spell.Dot(target)
if useSnapshot {
result := dot.CalcSnapshotDamage(sim, target, dot.OutcomeExpectedSnapshotCrit)
result.Damage /= dot.TickPeriod().Seconds()
return result
} else {
result := spell.CalcPeriodicDamage(sim, target, affliction.CalcScalingSpellDmg(uaScale), spell.OutcomeExpectedMagicCrit)
result.Damage /= dot.CalcTickPeriod().Round(time.Millisecond).Seconds()
return result
}
return dot.CalcExpectedTickDamage(sim, target, core.ExpectedTickConfig{
UseSnapshot: useSnapshot,
BaseDmgFn: func(s *core.Spell) float64 {
return affliction.CalcScalingSpellDmg(uaScale)
},
SnapshotOutcome: dot.OutcomeExpectedSnapshotCrit,
NormalOutcome: spell.OutcomeExpectedMagicCrit,
})
},
})
}
Loading
Loading