diff --git a/sim/common/wotlk/other_effects.go b/sim/common/wotlk/other_effects.go index afc0b4b980..eb9168e60c 100644 --- a/sim/common/wotlk/other_effects.go +++ b/sim/common/wotlk/other_effects.go @@ -869,6 +869,9 @@ func init() { }) }) + // https://web.archive.org/web/20100127195814/http://elitistjerks.com/f76/t68951-retribution_updated_3_3_a/p75/ + // suggests 2 ppm; there's a test with ~1k auto attacks on this page, and mention of a previous test + // with ~10k auto attacks. NewItemEffectWithHeroic(func(isHeroic bool) { name := "Bryntroll, the Bone Arbiter" itemID := int32(50415) @@ -885,7 +888,7 @@ func init() { character := agent.GetCharacter() mh, oh := character.GetWeaponHands(itemID) procMask := core.GetMeleeProcMaskForHands(mh, oh) - ppmm := character.AutoAttacks.NewPPMManager(1.0, procMask) + ppmm := character.AutoAttacks.NewPPMManager(2.0, procMask) procActionID := core.ActionID{ItemID: itemID} @@ -895,11 +898,10 @@ func init() { ProcMask: core.ProcMaskEmpty, DamageMultiplier: 1, - CritMultiplier: character.DefaultSpellCritMultiplier(), ThreatMultiplier: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - spell.CalcAndDealDamage(sim, target, sim.Roll(minDmg, maxDmg), spell.OutcomeMagicHitAndCrit) + spell.CalcAndDealDamage(sim, target, sim.Roll(minDmg, maxDmg), spell.OutcomeMagicHit) }, }) @@ -910,14 +912,12 @@ func init() { aura.Activate(sim) }, OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !result.Landed() || !spell.ProcMask.Matches(procMask) { + if !result.Landed() { return } - if !ppmm.Proc(sim, spell.ProcMask, name) { - return + if ppmm.Proc(sim, spell.ProcMask, name) { + proc.Cast(sim, result.Target) } - - proc.Cast(sim, result.Target) }, }) }) diff --git a/sim/common/wotlk/shadowmourne.go b/sim/common/wotlk/shadowmourne.go index 1f1afc7f56..8529994acf 100644 --- a/sim/common/wotlk/shadowmourne.go +++ b/sim/common/wotlk/shadowmourne.go @@ -15,12 +15,17 @@ import ( // granting you 270 Strength for 10 sec. func init() { - const drainChance = 0.5 - + // https://web.archive.org/web/20120509024819/http://elitistjerks.com/f81/t37680-depth_fury_dps_discussion/p129/ + // has some testing, and arrives at ~12 ppm (~75% for 3.7 speed) + // https://web.archive.org/web/20100508065259/http://elitistjerks.com/f81/t37462-warrior_dps_calculation_spreadsheet/p109/ + // arrives at ~80% with "2000 white swings" on a dummy. core.NewItemEffect(49623, func(agent core.Agent) { player := agent.GetCharacter() - tempStrProc := player.NewTemporaryStatsAura("Chaos Bane", core.ActionID{SpellID: 73422}, stats.Stats{stats.Strength: 270}, time.Second*10) + ppmm := player.AutoAttacks.NewPPMManager(12, core.ProcMaskMelee) + + chaosBaneAura := player.NewTemporaryStatsAura("Chaos Bane", core.ActionID{SpellID: 73422}, stats.Stats{stats.Strength: 270}, time.Second*10) + choasBaneSpell := player.RegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: 71904}, SpellSchool: core.SpellSchoolShadow, @@ -30,9 +35,10 @@ func init() { ThreatMultiplier: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - baseDamage := sim.Roll(1900, 2100) - // can miss, can't crit - spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMagicHit) + baseDamage := sim.Roll(1900, 2100) / float64(sim.GetNumTargets()) + for _, target := range sim.Encounter.TargetUnits { + spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMagicHit) // probably has a very low crit rate + } }, }) @@ -43,33 +49,27 @@ func init() { MaxStacks: 10, OnStacksChange: func(aura *core.Aura, sim *core.Simulation, oldStacks, newStacks int32) { player.AddStatDynamic(sim, stats.Strength, float64(newStacks-oldStacks)*30) + + if newStacks == aura.MaxStacks { + choasBaneSpell.Cast(sim, nil) + chaosBaneAura.Activate(sim) + aura.SetStacks(sim, 0) + return + } }, }) core.MakePermanent(player.GetOrRegisterAura(core.Aura{ Label: "Shadowmourne", OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !spell.ProcMask.Matches(core.ProcMaskMelee) { + if chaosBaneAura.IsActive() { return } - if stackingAura.GetStacks() == 10 { - stackingAura.Deactivate(sim) - tempStrProc.Activate(sim) - choasBaneSpell.Cast(sim, result.Target) - return + if ppmm.Proc(sim, spell.ProcMask, "Shadowmourne") { + stackingAura.Activate(sim) + stackingAura.AddStack(sim) } - - if tempStrProc.IsActive() { - return - } - - if sim.RandomFloat("shadowmourne") > drainChance { - return - } - - stackingAura.Activate(sim) - stackingAura.AddStack(sim) }, })) }) diff --git a/sim/core/stats/stats.go b/sim/core/stats/stats.go index 7e7543d1f5..9f473319a6 100644 --- a/sim/core/stats/stats.go +++ b/sim/core/stats/stats.go @@ -190,29 +190,25 @@ func FromFloatArray(values []float64) Stats { // Adds two Stats together, returning the new Stats. func (stats Stats) Add(other Stats) Stats { - newStats := Stats{} - - for i, thisStat := range stats { - newStats[i] = thisStat + other[i] + var newStats Stats + for k, v := range stats { + newStats[k] = v + other[k] } - return newStats } // Subtracts another Stats from this one, returning the new Stats. func (stats Stats) Subtract(other Stats) Stats { - newStats := Stats{} - + var newStats Stats for k, v := range stats { newStats[k] = v - other[k] } - return newStats } func (stats Stats) Multiply(multiplier float64) Stats { - newStats := stats - for k, v := range newStats { + var newStats Stats + for k, v := range stats { newStats[k] = v * multiplier } return newStats @@ -221,32 +217,28 @@ func (stats Stats) Multiply(multiplier float64) Stats { // Multiplies two Stats together by multiplying the values of corresponding // stats, like a dot product operation. func (stats Stats) DotProduct(other Stats) Stats { - newStats := Stats{} - + var newStats Stats for k, v := range stats { newStats[k] = v * other[k] } - return newStats } func (stats Stats) Equals(other Stats) bool { - for i := range stats { - if stats[i] != other[i] { + for k, v := range stats { + if v != other[k] { return false } } - return true } func (stats Stats) EqualsWithTolerance(other Stats, tolerance float64) bool { - for i := range stats { - if stats[i] < other[i]-tolerance || stats[i] > other[i]+tolerance { + for k, v := range stats { + if v < other[k]-tolerance || v > other[k]+tolerance { return false } } - return true } @@ -259,8 +251,7 @@ func (stats Stats) String() string { if name == "none" || statValue == 0 { continue } - - fmt.Fprintf(&sb, "\t%s: %0.3f,\n", name, statValue) + _, _ = fmt.Fprintf(&sb, "\t%s: %0.3f,\n", name, statValue) } sb.WriteString("\n}") @@ -277,7 +268,7 @@ func (stats Stats) FlatString() string { if name == "none" || statValue == 0 { continue } - fmt.Fprintf(&sb, "\"%s\": %0.3f,", name, statValue) + _, _ = fmt.Fprintf(&sb, "\"%s\": %0.3f,", name, statValue) } sb.WriteString("}") diff --git a/sim/deathknight/dps/TestBlood.results b/sim/deathknight/dps/TestBlood.results index fd9de55496..19f89b4d1f 100644 --- a/sim/deathknight/dps/TestBlood.results +++ b/sim/deathknight/dps/TestBlood.results @@ -130,15 +130,15 @@ dps_results: { dps_results: { key: "TestBlood-AllItems-Bryntroll,theBoneArbiter-50415" value: { - dps: 8396.49377 - tps: 4344.39306 + dps: 8456.8189 + tps: 4388.59242 } } dps_results: { key: "TestBlood-AllItems-Bryntroll,theBoneArbiter-50709" value: { - dps: 8476.26815 - tps: 4390.57082 + dps: 8573.17092 + tps: 4452.46303 } } dps_results: { @@ -629,8 +629,8 @@ dps_results: { dps_results: { key: "TestBlood-AllItems-Shadowmourne-49623" value: { - dps: 9382.87235 - tps: 4891.95635 + dps: 9437.43339 + tps: 4917.64139 } } dps_results: { diff --git a/sim/deathknight/tank/TestBloodTank.results b/sim/deathknight/tank/TestBloodTank.results index ac90bd0598..02162dda5b 100644 --- a/sim/deathknight/tank/TestBloodTank.results +++ b/sim/deathknight/tank/TestBloodTank.results @@ -130,15 +130,15 @@ dps_results: { dps_results: { key: "TestBloodTank-AllItems-Bryntroll,theBoneArbiter-50415" value: { - dps: 1778.63333 - tps: 5169.36197 + dps: 1820.31805 + tps: 5249.57027 } } dps_results: { key: "TestBloodTank-AllItems-Bryntroll,theBoneArbiter-50709" value: { - dps: 1815.64364 - tps: 5244.69106 + dps: 1863.17887 + tps: 5345.14668 } } dps_results: { @@ -629,8 +629,8 @@ dps_results: { dps_results: { key: "TestBloodTank-AllItems-Shadowmourne-49623" value: { - dps: 2030.09765 - tps: 5824.94556 + dps: 2053.01461 + tps: 5884.08289 } } dps_results: { diff --git a/sim/paladin/hand_of_reckoning.go b/sim/paladin/hand_of_reckoning.go index 30c210fe6e..9b56a41042 100644 --- a/sim/paladin/hand_of_reckoning.go +++ b/sim/paladin/hand_of_reckoning.go @@ -13,7 +13,7 @@ func (paladin *Paladin) registerHandOfReckoningSpell() { } paladin.HandOfReckoning = paladin.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 62124}, + ActionID: core.ActionID{SpellID: 67485}, // 62124 is the "taunt" part SpellSchool: core.SpellSchoolHoly, ProcMask: core.ProcMaskSpellDamage, Flags: core.SpellFlagMeleeMetrics, @@ -33,12 +33,10 @@ func (paladin *Paladin) registerHandOfReckoningSpell() { DamageMultiplier: 1, ThreatMultiplier: 1, CritMultiplier: paladin.SpellCritMultiplier(), - BonusHitRating: 100 * core.SpellHitRatingPerHitChance, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - baseDamage := 1 + - .5*spell.MeleeAttackPower() - spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMagicHitAndCrit) + baseDamage := 1 + .5*spell.MeleeAttackPower() + spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMagicCrit) // cannot miss }, }) } diff --git a/sim/paladin/retribution/TestRetribution.results b/sim/paladin/retribution/TestRetribution.results index b0b45a9975..9aeec3f6ea 100644 --- a/sim/paladin/retribution/TestRetribution.results +++ b/sim/paladin/retribution/TestRetribution.results @@ -768,8 +768,8 @@ dps_results: { dps_results: { key: "TestRetribution-AllItems-Shadowmourne-49623" value: { - dps: 7642.81583 - tps: 7744.30593 + dps: 7678.12977 + tps: 7779.52971 dtps: 9.92959 } } diff --git a/sim/paladin/soc.go b/sim/paladin/soc.go index 32b50b923c..3a5fa56027 100644 --- a/sim/paladin/soc.go +++ b/sim/paladin/soc.go @@ -30,7 +30,7 @@ func (paladin *Paladin) registerSealOfCommandSpellAndAura() { onJudgementProc := paladin.RegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: 20467}, // Judgement of Command SpellSchool: core.SpellSchoolHoly, - ProcMask: core.ProcMaskMeleeOrRangedSpecial, + ProcMask: core.ProcMaskMeleeOrRangedSpecial, // defense type is 2, so it's likely only ProcMaskMeleeSpecial Flags: core.SpellFlagMeleeMetrics | SpellFlagSecondaryJudgement, BonusCritRating: (6 * float64(paladin.Talents.Fanaticism) * core.CritRatingPerCritChance) + diff --git a/sim/paladin/sov.go b/sim/paladin/sov.go index b9e3c8a9bb..ae9c727f33 100644 --- a/sim/paladin/sov.go +++ b/sim/paladin/sov.go @@ -119,7 +119,7 @@ func (paladin *Paladin) registerSealOfVengeanceSpellAndAura() { onSpecialOrSwingProc := paladin.RegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: 42463}, // Seal of Vengeance damage bonus. SpellSchool: core.SpellSchoolHoly, - ProcMask: core.ProcMaskEmpty, + ProcMask: core.ProcMaskEmpty, // does proc certain spell damage-based items, e.g. Black Magic, Pendulum of Telluric Currents Flags: core.SpellFlagMeleeMetrics, // (mult * weaponScaling / stacks) diff --git a/sim/warrior/whirlwind.go b/sim/warrior/whirlwind.go index 5ef04fa8ef..cbe7289599 100644 --- a/sim/warrior/whirlwind.go +++ b/sim/warrior/whirlwind.go @@ -17,7 +17,7 @@ func (warrior *Warrior) registerWhirlwindSpell() { warrior.WhirlwindOH = warrior.RegisterSpell(core.SpellConfig{ ActionID: actionID, SpellSchool: core.SpellSchoolPhysical, - ProcMask: core.ProcMaskMeleeOHSpecial, + ProcMask: core.ProcMaskMeleeOHSpecial, // whirlwind offhand hits usually don't proc auras Flags: core.SpellFlagMeleeMetrics | core.SpellFlagIncludeTargetBonusDamage | core.SpellFlagNoOnCastComplete | SpellFlagBloodsurge, DamageMultiplier: 1 *