diff --git a/sim/common/tbc/enchant_effects.go b/sim/common/tbc/enchant_effects.go index 4a1d27bcc9..00996ba113 100644 --- a/sim/common/tbc/enchant_effects.go +++ b/sim/common/tbc/enchant_effects.go @@ -56,16 +56,12 @@ func init() { }) // ApplyCrusaderEffect will be applied twice if there is two weapons with this enchant. - // However it will automatically overwrite one of them so it should be ok. + // However, it will automatically overwrite one of them, so it should be ok. // A single application of the aura will handle both mh and oh procs. core.NewEnchantEffect(1900, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 1900 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 1900 - if !mh && !oh { - return - } - procMask := core.GetMeleeProcMaskForHands(mh, oh) + + procMask := character.GetMeleeProcMaskForEnchant(1900) ppmm := character.AutoAttacks.NewPPMManager(1.0, procMask) // -4 str per level over 60 @@ -80,7 +76,7 @@ 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(core.ProcMaskMelee) { + if !result.Landed() { return } @@ -102,13 +98,12 @@ func init() { }) // ApplyMongooseEffect will be applied twice if there is two weapons with this enchant. - // However it will automatically overwrite one of them so it should be ok. + // However, it will automatically overwrite one of them, so it should be ok. // A single application of the aura will handle both mh and oh procs. core.NewEnchantEffect(2673, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 2673 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 2673 - procMask := core.GetMeleeProcMaskForHands(mh, oh) + + procMask := character.GetMeleeProcMaskForEnchant(2673) ppmm := character.AutoAttacks.NewPPMManager(0.73, procMask) mhAura := character.NewTemporaryStatsAura("Lightning Speed MH", core.ActionID{SpellID: 28093, Tag: 1}, stats.Stats{stats.MeleeHaste: 30.0, stats.Agility: 120}, time.Second*15) @@ -121,7 +116,7 @@ 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(core.ProcMaskMelee) { + if !result.Landed() { return } @@ -155,12 +150,8 @@ func init() { core.NewEnchantEffect(3225, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3225 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3225 - if !mh && !oh { - return - } - procMask := core.GetMeleeProcMaskForHands(mh, oh) + + procMask := character.GetMeleeProcMaskForEnchant(3225) ppmm := character.AutoAttacks.NewPPMManager(1.0, procMask) procAura := character.NewTemporaryStatsAura("Executioner Proc", core.ActionID{SpellID: 42976}, stats.Stats{stats.ArmorPenetration: 120}, time.Second*15) @@ -172,7 +163,7 @@ 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(core.ProcMaskMelee) { + if !result.Landed() { return } @@ -212,16 +203,14 @@ func init() { } if spell.ProcMask.Matches(core.ProcMaskMelee) { - if !ppmm.Proc(sim, spell.ProcMask, "Deathfrost") { - return + if ppmm.Proc(sim, spell.ProcMask, "Deathfrost") { + procSpell.Cast(sim, result.Target) } - procSpell.Cast(sim, result.Target) } else if spell.ProcMask.Matches(core.ProcMaskSpellDamage) { - if !icd.IsReady(sim) || sim.RandomFloat("Deathfrost") > 0.5 { - return + if icd.IsReady(sim) && sim.RandomFloat("Deathfrost") < 0.5 { + icd.Use(sim) + procSpell.Cast(sim, result.Target) } - icd.Use(sim) - procSpell.Cast(sim, result.Target) } }, }) @@ -230,9 +219,9 @@ func init() { } core.NewEnchantEffect(3273, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3273 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3273 - if !mh && !oh { + + procMask := character.GetMeleeProcMaskForEnchant(3273) + if procMask == core.ProcMaskUnknown { return } @@ -272,10 +261,10 @@ func init() { }, }) - if mh { + if procMask.Matches(core.ProcMaskMeleeMH) { applyDeathfrostForWeapon(character, procSpell, true) } - if oh { + if procMask.Matches(core.ProcMaskMeleeOH) { applyDeathfrostForWeapon(character, procSpell, false) } }) diff --git a/sim/common/tbc/melee_items.go b/sim/common/tbc/melee_items.go index 03d2df2b99..b5521fcc35 100644 --- a/sim/common/tbc/melee_items.go +++ b/sim/common/tbc/melee_items.go @@ -21,8 +21,8 @@ func init() { core.NewItemEffect(19019, func(agent core.Agent) { character := agent.GetCharacter() - mh, oh := character.GetWeaponHands(19019) - procMask := core.GetMeleeProcMaskForHands(mh, oh) + + procMask := character.GetMeleeProcMaskForItem(19019) ppmm := character.AutoAttacks.NewPPMManager(6.0, procMask) procActionID := core.ActionID{SpellID: 21992} @@ -89,15 +89,14 @@ 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) { - return - } - if !ppmm.Proc(sim, spell.ProcMask, "Thunderfury") { + if !result.Landed() { return } - singleTargetSpell.Cast(sim, result.Target) - bounceSpell.Cast(sim, result.Target) + if ppmm.Proc(sim, spell.ProcMask, "Thunderfury") { + singleTargetSpell.Cast(sim, result.Target) + bounceSpell.Cast(sim, result.Target) + } }, }) }) @@ -156,29 +155,28 @@ func init() { aura.Activate(sim) }, OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - // mask 340 - if !result.Landed() || !spell.ProcMask.Matches(core.ProcMaskMeleeOrRanged) { + if !result.Landed() { return } + if !icd.IsReady(sim) { return } - if !ppmm.Proc(sim, spell.ProcMask, "Band of the Eternal Champion") { - return - } - icd.Use(sim) - procAura.Activate(sim) + if ppmm.Proc(sim, spell.ProcMask, "Band of the Eternal Champion") { + icd.Use(sim) + procAura.Activate(sim) + } }, }) }) core.NewItemEffect(29996, func(agent core.Agent) { character := agent.GetCharacter() - mh, oh := character.GetWeaponHands(29996) - procMask := core.GetMeleeProcMaskForHands(mh, oh) - const procChance = 2.7 / 60.0 + procMask := character.GetMeleeProcMaskForItem(29996) + pppm := character.AutoAttacks.NewPPMManager(1.0, procMask) + actionID := core.ActionID{ItemID: 29996} var resourceMetricsRage *core.ResourceMetrics @@ -197,21 +195,17 @@ 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 } - cpb := spell.Unit.GetCurrentPowerBar() - if cpb == core.RageBar { - if sim.RandomFloat("Rod of the Sun King") > procChance { - return - } - spell.Unit.AddRage(sim, 5, resourceMetricsRage) - } else if cpb == core.EnergyBar { - if sim.RandomFloat("Rod of the Sun King") > procChance { - return + if pppm.Proc(sim, spell.ProcMask, "Rod of the Sun King") { + switch spell.Unit.GetCurrentPowerBar() { + case core.RageBar: + spell.Unit.AddRage(sim, 5, resourceMetricsRage) + case core.EnergyBar: + spell.Unit.AddEnergy(sim, 10, resourceMetricsEnergy) } - spell.Unit.AddEnergy(sim, 10, resourceMetricsEnergy) } }, }) @@ -219,8 +213,8 @@ func init() { core.NewItemEffect(31193, func(agent core.Agent) { character := agent.GetCharacter() - mh, oh := character.GetWeaponHands(31193) - procMask := core.GetMeleeProcMaskForHands(mh, oh) + + procMask := character.GetMeleeProcMaskForItem(31193) procSpell := character.GetOrRegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: 24585}, @@ -252,8 +246,8 @@ func init() { core.NewItemEffect(32262, func(agent core.Agent) { character := agent.GetCharacter() - mh, oh := character.GetWeaponHands(32262) - procMask := core.GetMeleeProcMaskForHands(mh, oh) + + procMask := character.GetMeleeProcMaskForItem(32262) ppmm := character.AutoAttacks.NewPPMManager(1.0, procMask) procSpell := character.GetOrRegisterSpell(core.SpellConfig{ @@ -290,7 +284,7 @@ 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(core.ProcMaskMelee) { + if !result.Landed() { return } @@ -358,11 +352,12 @@ func init() { core.NewItemEffect(12590, func(agent core.Agent) { character := agent.GetCharacter() - effectAura := character.NewTemporaryStatsAura("Felstriker Proc", core.ActionID{SpellID: 16551}, stats.Stats{stats.MeleeCrit: 100 * core.CritRatingPerCritChance}, time.Second*3) - mh, oh := character.GetWeaponHands(12590) - procMask := core.GetMeleeProcMaskForHands(mh, oh) + + procMask := character.GetMeleeProcMaskForItem(12590) ppmm := character.AutoAttacks.NewPPMManager(1.0, procMask) + effectAura := character.NewTemporaryStatsAura("Felstriker Proc", core.ActionID{SpellID: 16551}, stats.Stats{stats.MeleeCrit: 100 * core.CritRatingPerCritChance}, time.Second*3) + character.GetOrRegisterAura(core.Aura{ Label: "Felstriker", Duration: core.NeverExpires, @@ -370,13 +365,13 @@ 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, "Felstriker") { - return + + if ppmm.Proc(sim, spell.ProcMask, "Felstriker") { + effectAura.Activate(sim) } - effectAura.Activate(sim) }, }) }) diff --git a/sim/common/tbc/melee_sets.go b/sim/common/tbc/melee_sets.go index c96a49a8ec..ab80e14b2b 100644 --- a/sim/common/tbc/melee_sets.go +++ b/sim/common/tbc/melee_sets.go @@ -30,7 +30,7 @@ var ItemSetFistsOfFury = core.NewItemSet(core.ItemSet{ }, }) - ppmm := character.AutoAttacks.NewPPMManager(2, core.ProcMaskMelee) + ppmm := character.AutoAttacks.NewPPMManager(2.0, core.ProcMaskMelee) character.RegisterAura(core.Aura{ Label: "Fists of Fury", @@ -39,14 +39,13 @@ var ItemSetFistsOfFury = core.NewItemSet(core.ItemSet{ aura.Activate(sim) }, OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !result.Landed() || !spell.ProcMask.Matches(core.ProcMaskMelee) { - return - } - if !ppmm.Proc(sim, spell.ProcMask, "The Fists of Fury") { + if !result.Landed() { return } - procSpell.Cast(sim, result.Target) + if ppmm.Proc(sim, spell.ProcMask, "The Fists of Fury") { + procSpell.Cast(sim, result.Target) + } }, }) }, @@ -157,20 +156,14 @@ var ItemSetTwinBladesOfAzzinoth = core.NewItemSet(core.ItemSet{ return } - // https://wotlk.wowhead.com/spell=41434/the-twin-blades-of-azzinoth, proc mask = 20. - if !spell.ProcMask.Matches(core.ProcMaskMelee) { - return - } - if !icd.IsReady(sim) { return } - if !ppmm.Proc(sim, spell.ProcMask, "Twin Blades of Azzinoth") { - return + if ppmm.Proc(sim, spell.ProcMask, "Twin Blades of Azzinoth") { + icd.Use(sim) + procAura.Activate(sim) } - icd.Use(sim) - procAura.Activate(sim) }, }) }, diff --git a/sim/common/tbc/melee_trinkets.go b/sim/common/tbc/melee_trinkets.go index a5da6ab6ab..0d32aa8793 100644 --- a/sim/common/tbc/melee_trinkets.go +++ b/sim/common/tbc/melee_trinkets.go @@ -92,19 +92,18 @@ func init() { aura.Activate(sim) }, OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - // mask: 340 - if !result.Landed() || !spell.ProcMask.Matches(core.ProcMaskMeleeOrRanged) { + if !result.Landed() { return } + if !icd.IsReady(sim) { return } - if !ppmm.Proc(sim, spell.ProcMask, "dragonspine") { - return - } - icd.Use(sim) - procAura.Activate(sim) + if ppmm.Proc(sim, spell.ProcMask, "dragonspine") { + icd.Use(sim) + procAura.Activate(sim) + } }, }) }) @@ -158,15 +157,13 @@ func init() { aura.Activate(sim) }, OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - // mask 340 - if !result.Landed() || !spell.ProcMask.Matches(core.ProcMaskMeleeOrRanged) { - return - } - if !ppmm.Proc(sim, spell.ProcMask, "Madness of the Betrayer") { + if !result.Landed() { return } - procAura.Activate(sim) + if ppmm.Proc(sim, spell.ProcMask, "Madness of the Betrayer") { + procAura.Activate(sim) + } }, }) }) diff --git a/sim/common/tbc/metagems.go b/sim/common/tbc/metagems.go index fe494ea2d9..6fd0a0160a 100644 --- a/sim/common/tbc/metagems.go +++ b/sim/common/tbc/metagems.go @@ -72,7 +72,7 @@ func init() { Timer: character.NewTimer(), Duration: time.Second * 40, } - ppmm := character.AutoAttacks.NewPPMManager(1.5, core.ProcMaskMeleeOrRanged) + ppmm := character.AutoAttacks.NewPPMManager(1.5, core.ProcMaskWhiteHit) // Mask 68, melee or ranged auto attacks. character.RegisterAura(core.Aura{ Label: "Thundering Skyfire Diamond", @@ -81,18 +81,18 @@ func init() { aura.Activate(sim) }, OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - // Mask 68, melee or ranged auto attacks. - if !result.Landed() || !spell.ProcMask.Matches(core.ProcMaskWhiteHit) { + if !result.Landed() { return } + if !icd.IsReady(sim) { return } - if !ppmm.Proc(sim, spell.ProcMask, "Thundering Skyfire Diamond") { - return + + if ppmm.Proc(sim, spell.ProcMask, "Thundering Skyfire Diamond") { + icd.Use(sim) + procAura.Activate(sim) } - icd.Use(sim) - procAura.Activate(sim) }, }) }) @@ -106,7 +106,7 @@ func init() { agent.GetCharacter().MultiplyStat(stats.Intellect, 1.02) }) - // These are handled in character.go, but create empty effects so they are included in tests. + // These are handled in character.go, but create empty effects, so they are included in tests. core.NewItemEffect(34220, func(_ core.Agent) {}) // Chaotic Skyfire Diamond core.NewItemEffect(32409, func(_ core.Agent) {}) // Relentless Earthstorm Diamond diff --git a/sim/common/wotlk/capacitors.go b/sim/common/wotlk/capacitors.go index a3427333b2..9c396dc5ed 100644 --- a/sim/common/wotlk/capacitors.go +++ b/sim/common/wotlk/capacitors.go @@ -4,7 +4,6 @@ import ( "time" "github.com/wowsims/wotlk/sim/core" - //"github.com/wowsims/wotlk/sim/core/stats" ) type CapacitorHandler func(*core.Simulation) diff --git a/sim/common/wotlk/enchant_effects.go b/sim/common/wotlk/enchant_effects.go index 616d328915..8a912413ff 100644 --- a/sim/common/wotlk/enchant_effects.go +++ b/sim/common/wotlk/enchant_effects.go @@ -17,10 +17,8 @@ func init() { core.NewEnchantEffect(3251, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3251 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3251 - procMask := core.GetMeleeProcMaskForHands(mh, oh) + procMask := character.GetMeleeProcMaskForEnchant(3251) ppmm := character.AutoAttacks.NewPPMManager(4.0, procMask) procSpell := character.RegisterSpell(core.SpellConfig{ @@ -44,7 +42,7 @@ 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(core.ProcMaskMelee) { + if !result.Landed() { return } @@ -63,9 +61,8 @@ func init() { core.NewEnchantEffect(3239, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3239 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3239 - procMask := core.GetMeleeProcMaskForHands(mh, oh) + + procMask := character.GetMeleeProcMaskForEnchant(3239) ppmm := character.AutoAttacks.NewPPMManager(4.0, procMask) procSpell := character.RegisterSpell(core.SpellConfig{ @@ -89,7 +86,7 @@ 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(core.ProcMaskMelee) { + if !result.Landed() { return } @@ -169,10 +166,8 @@ func init() { core.NewEnchantEffect(3789, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3789 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3789 - procMask := core.GetMeleeProcMaskForHands(mh, oh) + procMask := character.GetMeleeProcMaskForEnchant(3789) ppmm := character.AutoAttacks.NewPPMManager(1.0, procMask) // Modify only gear armor, including from agility @@ -187,7 +182,7 @@ 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(core.ProcMaskMelee) { + if !result.Landed() { return } @@ -207,9 +202,8 @@ func init() { // TODO: These are stand-in values without any real reference. core.NewEnchantEffect(3241, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3241 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3241 - procMask := core.GetMeleeProcMaskForHands(mh, oh) + + procMask := character.GetMeleeProcMaskForEnchant(3241) ppmm := character.AutoAttacks.NewPPMManager(3.0, procMask) healthMetrics := character.NewHealthMetrics(core.ActionID{ItemID: 44494}) @@ -221,7 +215,7 @@ 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(core.ProcMaskMelee) { + if !result.Landed() { return } diff --git a/sim/common/wotlk/other_effects.go b/sim/common/wotlk/other_effects.go index 64aa9272b8..1ca837edae 100644 --- a/sim/common/wotlk/other_effects.go +++ b/sim/common/wotlk/other_effects.go @@ -906,8 +906,8 @@ func init() { core.NewItemEffect(itemID, func(agent core.Agent) { character := agent.GetCharacter() - mh, oh := character.GetWeaponHands(itemID) - procMask := core.GetMeleeProcMaskForHands(mh, oh) + + procMask := character.GetMeleeProcMaskForItem(itemID) ppmm := character.AutoAttacks.NewPPMManager(2.0, procMask) procActionID := core.ActionID{ItemID: itemID} @@ -935,6 +935,7 @@ func init() { if !result.Landed() { return } + if ppmm.Proc(sim, spell.ProcMask, name) { proc.Cast(sim, result.Target) } diff --git a/sim/core/attack.go b/sim/core/attack.go index fae3b49dcf..8e61e0163a 100644 --- a/sim/core/attack.go +++ b/sim/core/attack.go @@ -1,6 +1,7 @@ package core import ( + "golang.org/x/exp/slices" "time" "github.com/wowsims/wotlk/sim/core/proto" @@ -27,17 +28,17 @@ type Weapon struct { SpellSchool SpellSchool } -func (w Weapon) DPS() float64 { - if w.SwingSpeed == 0 { +func (weapon Weapon) DPS() float64 { + if weapon.SwingSpeed == 0 { return 0 } else { - return (w.BaseDamageMin + w.BaseDamageMax) / 2.0 / w.SwingSpeed + return (weapon.BaseDamageMin + weapon.BaseDamageMax) / 2.0 / weapon.SwingSpeed } } -func (w Weapon) WithBonusDPS(bonusDps float64) Weapon { - newWeapon := w - bonusSwingDamage := bonusDps * w.SwingSpeed +func (weapon Weapon) WithBonusDPS(bonusDps float64) Weapon { + newWeapon := weapon + bonusSwingDamage := bonusDps * weapon.SwingSpeed newWeapon.BaseDamageMin += bonusSwingDamage newWeapon.BaseDamageMax += bonusSwingDamage return newWeapon @@ -334,7 +335,7 @@ func (aa *AutoAttacks) IsEnabled() bool { } // Empty handler so Agents don't have to provide one if they have no logic to add. -func (unit *Unit) OnAutoAttack(sim *Simulation, spell *Spell) {} +func (unit *Unit) OnAutoAttack(_ *Simulation, _ *Spell) {} func (aa *AutoAttacks) finalize() { if !aa.IsEnabled() { @@ -370,7 +371,7 @@ func (aa *AutoAttacks) reset(sim *Simulation) { var delay time.Duration var isMHDelay bool if aa.unit.Type == EnemyUnit { - delay = time.Duration(float64(aa.MH.SwingDuration / 2)) + delay = aa.MH.SwingDuration / 2 isMHDelay = false } else { delay = time.Duration(sim.RandomFloat("SwingResetDelay") * float64(aa.MH.SwingDuration/2)) @@ -693,35 +694,26 @@ func (aa *AutoAttacks) NextAttackAt() time.Duration { } type PPMManager struct { - mhProcChance float64 - ohProcChance float64 - rangedProcChance float64 - procMask ProcMask + procMasks []ProcMask + procChances []float64 } // Returns whether the effect procced. func (ppmm *PPMManager) Proc(sim *Simulation, procMask ProcMask, label string) bool { - // Without this procs that can proc only from white attacks - // are still procing from specials - if !procMask.Matches(ppmm.procMask) { - return false + for i, m := range ppmm.procMasks { + if m.Matches(procMask) { + return sim.RandomFloat(label) < ppmm.procChances[i] + } } - - chance := ppmm.Chance(procMask) - return chance > 0 && sim.RandomFloat(label) < chance + return false } func (ppmm *PPMManager) Chance(procMask ProcMask) float64 { - if procMask.Matches(ProcMaskMeleeMH) { - return ppmm.mhProcChance - } else if procMask.Matches(ProcMaskMeleeOH) { - return ppmm.ohProcChance - } else if procMask.Matches(ProcMaskRanged) { - return ppmm.rangedProcChance - } else if procMask.Matches(ppmm.procMask) { - return ppmm.mhProcChance // probably a 'proc from proc' so use main hand. + for i, m := range ppmm.procMasks { + if m.Matches(procMask) { + return ppmm.procChances[i] + } } - return 0 } @@ -730,40 +722,50 @@ func (aa *AutoAttacks) NewPPMManager(ppm float64, procMask ProcMask) PPMManager return PPMManager{} } - ppmm := PPMManager{} - ppmm.procMask = procMask - if procMask.Matches(ProcMaskMeleeMH) { - ppmm.mhProcChance = ppm * aa.MH.SwingSpeed / 60.0 - } - if procMask.Matches(ProcMaskMeleeOH) { - ppmm.ohProcChance = ppm * aa.OH.SwingSpeed / 60.0 + ppmm := PPMManager{procMasks: make([]ProcMask, 0, 2), procChances: make([]float64, 0, 2)} + + mergeOrAppend := func(speed float64, mask ProcMask) { + if speed == 0 || mask == 0 { + return + } + + if i := slices.Index(ppmm.procChances, speed); i != -1 { + ppmm.procMasks[i] |= mask + return + } + + ppmm.procMasks = append(ppmm.procMasks, mask) + ppmm.procChances = append(ppmm.procChances, speed) } - if procMask.Matches(ProcMaskRanged) { - ppmm.rangedProcChance = ppm * aa.Ranged.SwingSpeed / 60.0 + + mergeOrAppend(aa.MH.SwingSpeed, procMask&^ProcMaskRanged&^ProcMaskMeleeOH) // "everything else", even if not explicitly flagged MH + mergeOrAppend(aa.OH.SwingSpeed, procMask&ProcMaskMeleeOH) + mergeOrAppend(aa.Ranged.SwingSpeed, procMask&ProcMaskRanged) + + for i := range ppmm.procChances { + ppmm.procChances[i] *= ppm / 60 } return ppmm } // Returns whether a PPM-based effect procced. -// // Using NewPPMManager() is preferred; this function should only be used when // the attacker is not known at initialization time. -func (aa *AutoAttacks) PPMProc(sim *Simulation, ppm float64, procMask ProcMask, label string) bool { +func (aa *AutoAttacks) PPMProc(sim *Simulation, ppm float64, procMask ProcMask, label string, spell *Spell) bool { if !aa.IsEnabled() { return false } - procChance := 0.0 - if procMask.Matches(ProcMaskMeleeMH) { - procChance = ppm * aa.MH.SwingSpeed / 60.0 - } else if procMask.Matches(ProcMaskMeleeOH) { - procChance = ppm * aa.OH.SwingSpeed / 60.0 - } else if procMask.Matches(ProcMaskRanged) { - procChance = ppm * aa.Ranged.SwingSpeed / 60.0 + switch { + case spell.ProcMask.Matches(procMask &^ ProcMaskMeleeOH &^ ProcMaskRanged): + return sim.RandomFloat(label) < ppm*aa.MH.SwingSpeed/60.0 + case spell.ProcMask.Matches(procMask & ProcMaskMeleeOH): + return sim.RandomFloat(label) < ppm*aa.OH.SwingSpeed/60.0 + case spell.ProcMask.Matches(procMask & ProcMaskRanged): + return sim.RandomFloat(label) < ppm*aa.Ranged.SwingSpeed/60.0 } - - return procChance > 0 && sim.RandomFloat(label) < procChance + return false } func (unit *Unit) applyParryHaste() { diff --git a/sim/core/character.go b/sim/core/character.go index e94e53a4c0..290c34509f 100644 --- a/sim/core/character.go +++ b/sim/core/character.go @@ -142,7 +142,7 @@ func NewCharacter(party *Party, partyIndex int, player *proto.Player) Character character.AddStats(character.baseStats) character.addUniversalStatDependencies() - for i, _ := range character.itemStatMultipliers { + for i := range character.itemStatMultipliers { character.itemStatMultipliers[i] = 1 } @@ -175,7 +175,7 @@ func (character *Character) applyEquipScaling(stat stats.Stat, multiplier float6 var oldValue = character.EquipStats()[stat] character.itemStatMultipliers[stat] *= multiplier var newValue = character.EquipStats()[stat] - return (newValue - oldValue) + return newValue - oldValue } func (character *Character) ApplyEquipScaling(stat stats.Stat, multiplier float64) { @@ -414,7 +414,7 @@ func (character *Character) DefaultHealingCritMultiplier() float64 { return character.HealingCritMultiplier(1, 0) } -func (character *Character) AddRaidBuffs(raidBuffs *proto.RaidBuffs) { +func (character *Character) AddRaidBuffs(_ *proto.RaidBuffs) { } func (character *Character) AddPartyBuffs(partyBuffs *proto.PartyBuffs) { if character.Race == proto.Race_RaceDraenei { @@ -517,7 +517,7 @@ func (character *Character) FillPlayerStats(playerStats *proto.PlayerStats) { } } -func (character *Character) init(sim *Simulation, agent Agent) { +func (character *Character) init(sim *Simulation, _ Agent) { character.Unit.init(sim) } @@ -625,21 +625,30 @@ func (character *Character) HasRangedWeapon() bool { return character.GetRangedWeapon() != nil } -// Returns the hands that the item is equipped in, as (MH, OH). -func (character *Character) GetWeaponHands(itemID int32) (bool, bool) { - mh := false - oh := false - if weapon := character.GetMHWeapon(); weapon != nil && weapon.ID == itemID { - mh = true +func (character *Character) GetMeleeProcMaskForEnchant(effectID int32) ProcMask { + mask := ProcMaskUnknown + if w := &character.Equip[proto.ItemSlot_ItemSlotMainHand]; w.Enchant.EffectID == effectID { + mask |= ProcMaskMeleeMH } - if weapon := character.GetOHWeapon(); weapon != nil && weapon.ID == itemID { - oh = true + if w := &character.Equip[proto.ItemSlot_ItemSlotOffHand]; w.Enchant.EffectID == effectID { + mask |= ProcMaskMeleeOH } - return mh, oh + return mask +} + +func (character *Character) GetMeleeProcMaskForItem(itemID int32) ProcMask { + mask := ProcMaskUnknown + if w := &character.Equip[proto.ItemSlot_ItemSlotMainHand]; w.ID == itemID { + mask |= ProcMaskMeleeMH + } + if w := &character.Equip[proto.ItemSlot_ItemSlotOffHand]; w.ID == itemID { + mask |= ProcMaskMeleeOH + } + return mask } func (character *Character) doneIteration(sim *Simulation) { - // Need to do pets first so we can add their results to the owners. + // Need to do pets first, so we can add their results to the owners. if len(character.Pets) > 0 { for _, petAgent := range character.Pets { pet := petAgent.GetPet() @@ -691,7 +700,7 @@ var BaseStats = map[BaseStatsKey]stats.Stats{} // Base Spell Crit is calculated by // 1. Take as-shown value (troll shaman have 3.5%) // 2. Calculate the bonus from int (for troll shaman that would be 104/78.1=1.331% crit) -// 3. Subtract as-shown from int bouns (3.5-1.331=2.169) +// 3. Subtract as-shown from int bonus (3.5-1.331=2.169) // 4. 2.169*22.08 (rating per crit percent) = 47.89 crit rating. // Base mana can be looked up here: https://wowwiki-archive.fandom.com/wiki/Base_mana @@ -736,7 +745,7 @@ func GetPrimaryTalentTreeIndex(talentStr string) uint8 { // Uses proto reflection to set fields in a talents proto (e.g. MageTalents, // WarriorTalents) based on a talentsStr. treeSizes should contain the number // of talents in each tree, usually around 30. This is needed because talent -// strings truncate 0's at the end of each tree so we can't infer the start index +// strings truncate 0's at the end of each tree, so we can't infer the start index // of the tree from the string. func FillTalentsProto(data protoreflect.Message, talentsStr string, treeSizes [3]int) { treeStrs := strings.Split(talentsStr, "-") diff --git a/sim/core/debuffs.go b/sim/core/debuffs.go index 0d631f6341..7004283170 100644 --- a/sim/core/debuffs.go +++ b/sim/core/debuffs.go @@ -228,12 +228,12 @@ func JudgementOfWisdomAura(target *Unit) *Aura { } if spell.ProcMask.Matches(ProcMaskEmpty | ProcMaskProc | ProcMaskWeaponProc) { - return // Phantom spells (Romulo's, Lightning Capacitor, etc) don't proc JoW. + return // Phantom spells (Romulo's, Lightning Capacitor, etc.) don't proc JoW. } if spell.ProcMask.Matches(ProcMaskMeleeOrRanged) { // Apparently ranged/melee can still proc on miss - if !unit.AutoAttacks.PPMProc(sim, 15, spell.ProcMask, "jow") { + if !unit.AutoAttacks.PPMProc(sim, 15, ProcMaskMeleeOrRanged, "jow", spell) { return } } else { // spell casting @@ -247,7 +247,7 @@ func JudgementOfWisdomAura(target *Unit) *Aura { // Perhaps this is a bug introduced in classic when converting JoW to wotlk. ct = 0.75 } - procChance := ct * 0.25 // ct / 60.0 * 15.0PPM (algabra) = ct*0.25 + procChance := ct * 0.25 // ct / 60.0 * 15.0PPM (algebra) = ct*0.25 if sim.RandomFloat("jow") > procChance { return } diff --git a/sim/core/flags.go b/sim/core/flags.go index 34dc6a531c..780577640e 100644 --- a/sim/core/flags.go +++ b/sim/core/flags.go @@ -1,11 +1,10 @@ package core import ( - "math/bits" - "strconv" - "github.com/wowsims/wotlk/sim/core/proto" "github.com/wowsims/wotlk/sim/core/stats" + "math/bits" + "strconv" ) type ProcMask uint32 diff --git a/sim/core/item_swaps.go b/sim/core/item_swaps.go index bf41824421..cffccb9128 100644 --- a/sim/core/item_swaps.go +++ b/sim/core/item_swaps.go @@ -58,10 +58,7 @@ func (character *Character) RegisterOnItemSwap(callback OnSwapItem) { func (swap *ItemSwap) RegisterOnSwapItemForEffectWithPPMManager(effectID int32, ppm float64, ppmm *PPMManager, aura *Aura) { character := swap.character character.RegisterOnItemSwap(func(sim *Simulation) { - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == effectID - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == effectID - - procMask := GetMeleeProcMaskForHands(mh, oh) + procMask := character.GetMeleeProcMaskForEnchant(effectID) *ppmm = character.AutoAttacks.NewPPMManager(ppm, procMask) if ppmm.Chance(procMask) == 0 { @@ -77,10 +74,9 @@ func (swap *ItemSwap) RegisterOnSwapItemForEffectWithPPMManager(effectID int32, func (swap *ItemSwap) RegisterOnSwapItemForEffect(effectID int32, aura *Aura) { character := swap.character character.RegisterOnItemSwap(func(sim *Simulation) { - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == effectID - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == effectID + procMask := character.GetMeleeProcMaskForEnchant(effectID) - if !mh && !oh { + if procMask == ProcMaskUnknown { aura.Deactivate(sim) } else { aura.Activate(sim) diff --git a/sim/core/sim.go b/sim/core/sim.go index 8c347b8e92..2dc24f4e5f 100644 --- a/sim/core/sim.go +++ b/sim/core/sim.go @@ -152,13 +152,13 @@ func (sim *Simulation) labelRand(label string) Rand { return sim.rand } - labelRand, isPresent := sim.testRands[label] - if !isPresent { - // Add rseed to the label to we still have run-run variance for stat weights. - labelRand = NewSplitMix(uint64(makeTestRandSeed(sim.rseed, label))) - sim.testRands[label] = labelRand + labelRng, ok := sim.testRands[label] + if !ok { + // Add rseed to the label, so we still have run-run variance for stat weights. + labelRng = NewSplitMix(uint64(makeTestRandSeed(sim.rseed, label))) + sim.testRands[label] = labelRng } - return labelRand + return labelRng } func (sim *Simulation) reseedRands(i int64) { @@ -166,8 +166,8 @@ func (sim *Simulation) reseedRands(i int64) { sim.rand.Seed(rseed) if sim.isTest { - for label, rand := range sim.testRands { - rand.Seed(makeTestRandSeed(rseed, label)) + for label, rng := range sim.testRands { + rng.Seed(makeTestRandSeed(rseed, label)) } } } diff --git a/sim/deathknight/blood_strike.go b/sim/deathknight/blood_strike.go index c7fcfc1abe..399725e619 100644 --- a/sim/deathknight/blood_strike.go +++ b/sim/deathknight/blood_strike.go @@ -96,7 +96,7 @@ func (dk *Deathknight) registerDrwBloodStrikeSpell() { dk.RuneWeapon.BloodStrike = dk.RuneWeapon.RegisterSpell(core.SpellConfig{ ActionID: BloodStrikeActionID.WithTag(1), SpellSchool: core.SpellSchoolPhysical, - ProcMask: core.ProcMaskMeleeSpecial, + ProcMask: core.ProcMaskMeleeMHSpecial, Flags: core.SpellFlagMeleeMetrics | core.SpellFlagIncludeTargetBonusDamage, BonusCritRating: (dk.subversionCritBonus() + dk.annihilationCritBonus()) * core.CritRatingPerCritChance, diff --git a/sim/deathknight/death_strike.go b/sim/deathknight/death_strike.go index 4541188ae4..06d2efee09 100644 --- a/sim/deathknight/death_strike.go +++ b/sim/deathknight/death_strike.go @@ -104,7 +104,7 @@ func (dk *Deathknight) registerDrwDeathStrikeSpell() { dk.RuneWeapon.DeathStrike = dk.RuneWeapon.RegisterSpell(core.SpellConfig{ ActionID: DeathStrikeActionID.WithTag(1), SpellSchool: core.SpellSchoolPhysical, - ProcMask: core.ProcMaskMeleeSpecial, + ProcMask: core.ProcMaskMeleeMHSpecial, Flags: core.SpellFlagMeleeMetrics | core.SpellFlagIncludeTargetBonusDamage, BonusCritRating: (dk.annihilationCritBonus() + dk.improvedDeathStrikeCritBonus()) * core.CritRatingPerCritChance, diff --git a/sim/deathknight/heart_strike.go b/sim/deathknight/heart_strike.go index 0a841a2841..28ea32f67e 100644 --- a/sim/deathknight/heart_strike.go +++ b/sim/deathknight/heart_strike.go @@ -15,7 +15,7 @@ func (dk *Deathknight) newHeartStrikeSpell(isMainTarget bool, isDrw bool) *core. conf := core.SpellConfig{ ActionID: HeartStrikeActionID.WithTag(core.TernaryInt32(isMainTarget, 1, 2)), SpellSchool: core.SpellSchoolPhysical, - ProcMask: core.ProcMaskMeleeSpecial, + ProcMask: core.ProcMaskMeleeMHSpecial, Flags: core.SpellFlagMeleeMetrics | core.SpellFlagIncludeTargetBonusDamage, RuneCost: core.RuneCostOptions{ diff --git a/sim/deathknight/items.go b/sim/deathknight/items.go index 79312dd2c4..964e815b71 100644 --- a/sim/deathknight/items.go +++ b/sim/deathknight/items.go @@ -347,8 +347,6 @@ func (dk *Deathknight) registerItems() { addEnchantEffect(3370, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3370 - oh := character.HasOHWeapon() && character.Equip[proto.ItemSlot_ItemSlotOffHand].HandType != proto.HandType_HandTypeTwoHand && character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3370 actionID := core.ActionID{SpellID: 50401} if spell := character.GetSpell(actionID); spell != nil { @@ -357,7 +355,8 @@ func (dk *Deathknight) registerItems() { return } - procMask := core.GetMeleeProcMaskForHands(mh, oh) + procMask := character.GetMeleeProcMaskForEnchant(3370) + vulnAuras := character.NewEnemyAuraArray(core.RuneOfRazoriceVulnerabilityAura) mhRazoriceSpell := newRazoriceHitSpell(character, true) ohRazoriceSpell := newRazoriceHitSpell(character, false) @@ -386,11 +385,7 @@ func (dk *Deathknight) registerItems() { }) character.RegisterOnItemSwap(func(sim *core.Simulation) { - mh = character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3370 - oh = character.HasOHWeapon() && character.Equip[proto.ItemSlot_ItemSlotOffHand].HandType != proto.HandType_HandTypeTwoHand && character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3370 - procMask = core.GetMeleeProcMaskForHands(mh, oh) - - if !mh && !oh { + if character.GetMeleeProcMaskForEnchant(3370) == core.ProcMaskUnknown { aura.Deactivate(sim) } else { aura.Activate(sim) @@ -414,14 +409,12 @@ func (dk *Deathknight) registerItems() { } // ApplyRuneOfTheFallenCrusader will be applied twice if there is two weapons with this enchant. - // However it will automatically overwrite one of them so it should be ok. + // However, it will automatically overwrite one of them, so it should be ok. // A single application of the aura will handle both mh and oh procs. addEnchantEffect(3368, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3368 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3368 - procMask := core.GetMeleeProcMaskForHands(mh, oh) + procMask := character.GetMeleeProcMaskForEnchant(3368) ppmm := character.AutoAttacks.NewPPMManager(2.0, procMask) rfcAura := newRuneOfTheFallenCrusaderAura(character, "Rune Of The Fallen Crusader Proc", core.ActionID{SpellID: 53365}) @@ -433,7 +426,7 @@ func (dk *Deathknight) registerItems() { aura.Activate(sim) }, OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !result.Landed() || !spell.ProcMask.Matches(core.ProcMaskMelee) { + if !result.Landed() { return } @@ -449,11 +442,6 @@ func (dk *Deathknight) registerItems() { // Rune of the Nerubian Carapace addEnchantEffect(3883, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3883 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3883 - if !mh && !oh { - return - } character.AddStat(stats.Defense, 13*core.DefenseRatingPerDefense) character.MultiplyStat(stats.Stamina, 1.01) @@ -462,15 +450,6 @@ func (dk *Deathknight) registerItems() { // Rune of the Stoneskin Gargoyle addEnchantEffect(3847, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3847 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3847 - if !mh { - return - } - - if oh { - return - } character.AddStat(stats.Defense, 25*core.DefenseRatingPerDefense) character.MultiplyStat(stats.Stamina, 1.02) @@ -479,11 +458,6 @@ func (dk *Deathknight) registerItems() { // Rune of the Swordbreaking addEnchantEffect(3594, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3594 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3594 - if !mh && !oh { - return - } character.AddStat(stats.Parry, 2*core.ParryRatingPerParryChance) }) @@ -491,45 +465,18 @@ func (dk *Deathknight) registerItems() { // Rune of Swordshattering addEnchantEffect(3365, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3365 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3365 - if !mh { - return - } - - if oh { - return - } character.AddStat(stats.Parry, 4*core.ParryRatingPerParryChance) }) // Rune of the Spellbreaking addEnchantEffect(3595, func(agent core.Agent) { - character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3595 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3595 - if !mh && !oh { - return - } - // TODO: // Add 2% magic deflection }) // Rune of Spellshattering addEnchantEffect(3367, func(agent core.Agent) { - character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3367 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3367 - if !mh { - return - } - - if oh { - return - } - // TODO: // Add 4% magic deflection }) @@ -595,13 +542,7 @@ func (dk *Deathknight) registerItems() { addEnchantEffect(3369, func(agent core.Agent) { character := agent.GetCharacter() - mh := character.Equip[proto.ItemSlot_ItemSlotMainHand].Enchant.EffectID == 3369 - oh := character.Equip[proto.ItemSlot_ItemSlotOffHand].Enchant.EffectID == 3369 - if !mh && !oh { - return - } - - procMask := core.GetMeleeProcMaskForHands(mh, oh) + procMask := character.GetMeleeProcMaskForEnchant(3369) ppmm := character.AutoAttacks.NewPPMManager(1.0, procMask) core.MakePermanent(character.GetOrRegisterAura(core.Aura{ @@ -611,20 +552,6 @@ func (dk *Deathknight) registerItems() { return } - if mh && !oh { - if !spell.ProcMask.Matches(core.ProcMaskMeleeMH) { - return - } - } else if oh && !mh { - if !spell.ProcMask.Matches(core.ProcMaskMeleeOH) { - return - } - } else if mh && oh { - if !spell.ProcMask.Matches(core.ProcMaskMelee) { - return - } - } - if ppmm.Proc(sim, spell.ProcMask, "rune of cinderglacier") { cinderProcAura.Activate(sim) } diff --git a/sim/druid/talents.go b/sim/druid/talents.go index 229b995068..a37dc1834e 100644 --- a/sim/druid/talents.go +++ b/sim/druid/talents.go @@ -377,6 +377,9 @@ func (druid *Druid) applyOmenOfClarity() { hasOocGlyph := druid.HasMajorGlyph(proto.DruidMajorGlyph_GlyphOfOmenOfClarity) + // Based on ingame testing by druid discord, subject to change or incorrectness + chanceToProcGotW := 1.0 - math.Pow(1.0-0.0875, float64(druid.RaidBuffTargets)) + druid.RegisterAura(core.Aura{ Label: "Omen of Clarity", Duration: core.NeverExpires, @@ -390,7 +393,7 @@ func (druid *Druid) applyOmenOfClarity() { hurricaneCoeff := 1.0 - (7.0 / 9.0) spellCoeff := hurricaneCoeff * curCastTickSpeed chanceToProc := ((1.5 / 60) * 3.5) * spellCoeff - if sim.RandomFloat("Clearcasting") <= chanceToProc { + if sim.RandomFloat("Clearcasting") < chanceToProc { druid.ProcOoc(sim) } } @@ -399,9 +402,7 @@ func (druid *Druid) applyOmenOfClarity() { if !result.Landed() { return } - // Not ideal to have new ppm manager here, but this needs to account for feral druid bear<->cat swaps - ppmm := druid.AutoAttacks.NewPPMManager(3.5, core.ProcMaskMeleeWhiteHit) - if ppmm.Proc(sim, spell.ProcMask, "Omen of Clarity") { // Melee + if druid.AutoAttacks.PPMProc(sim, 3.5, core.ProcMaskMeleeWhiteHit, "Omen of Clarity", spell) { // Melee druid.ProcOoc(sim) } else if spell.Flags.Matches(SpellFlagOmenTrigger) { // Spells // Heavily based on comment here @@ -420,7 +421,7 @@ func (druid *Druid) applyOmenOfClarity() { } else { chanceToProc *= 0.666 } - if sim.RandomFloat("Clearcasting") <= chanceToProc { + if sim.RandomFloat("Clearcasting") < chanceToProc { druid.ProcOoc(sim) } } @@ -430,9 +431,7 @@ func (druid *Druid) applyOmenOfClarity() { druid.ProcOoc(sim) } if druid.GiftOfTheWild.IsEqual(spell) { - // Based on ingame testing by druid discord, subject to change or incorrectness - chanceToProc := 1.0 - math.Pow(1.0-0.0875, float64(druid.RaidBuffTargets)) - if sim.RandomFloat("Clearcasting") <= chanceToProc { + if sim.RandomFloat("Clearcasting") < chanceToProcGotW { druid.ProcOoc(sim) } } diff --git a/sim/rogue/poisons.go b/sim/rogue/poisons.go index 544a1f03fb..d347b24522 100644 --- a/sim/rogue/poisons.go +++ b/sim/rogue/poisons.go @@ -132,11 +132,19 @@ func (rogue *Rogue) procDeadlyPoison(sim *core.Simulation, spell *core.Spell, re rogue.DeadlyPoison.Cast(sim, result.Target) } -func (rogue *Rogue) applyDeadlyPoison() { - procMask := core.GetMeleeProcMaskForHands( - rogue.Options.MhImbue == proto.Rogue_Options_DeadlyPoison, - rogue.Options.OhImbue == proto.Rogue_Options_DeadlyPoison) +func (rogue *Rogue) getPoisonProcMask(imbue proto.Rogue_Options_PoisonImbue) core.ProcMask { + var mask core.ProcMask + if rogue.Options.MhImbue == imbue { + mask |= core.ProcMaskMeleeMH + } + if rogue.Options.OhImbue == imbue { + mask |= core.ProcMaskMeleeOH + } + return mask +} +func (rogue *Rogue) applyDeadlyPoison() { + procMask := rogue.getPoisonProcMask(proto.Rogue_Options_DeadlyPoison) if procMask == core.ProcMaskUnknown { return } @@ -159,10 +167,7 @@ func (rogue *Rogue) applyDeadlyPoison() { } func (rogue *Rogue) applyWoundPoison() { - procMask := core.GetMeleeProcMaskForHands( - rogue.Options.MhImbue == proto.Rogue_Options_WoundPoison, - rogue.Options.OhImbue == proto.Rogue_Options_WoundPoison) - + procMask := rogue.getPoisonProcMask(proto.Rogue_Options_WoundPoison) if procMask == core.ProcMaskUnknown { return } @@ -180,6 +185,7 @@ func (rogue *Rogue) applyWoundPoison() { if !result.Landed() { return } + if rogue.woundPoisonPPMM.Proc(sim, spell.ProcMask, "Wound Poison") { rogue.WoundPoison[NormalProc].Cast(sim, result.Target) } @@ -295,10 +301,7 @@ func (rogue *Rogue) GetDeadlyPoisonProcChance() float64 { } func (rogue *Rogue) UpdateInstantPoisonPPM(bonusChance float64) { - procMask := core.GetMeleeProcMaskForHands( - rogue.Options.MhImbue == proto.Rogue_Options_InstantPoison, - rogue.Options.OhImbue == proto.Rogue_Options_InstantPoison) - + procMask := rogue.getPoisonProcMask(proto.Rogue_Options_InstantPoison) if procMask == core.ProcMaskUnknown { return } @@ -310,10 +313,7 @@ func (rogue *Rogue) UpdateInstantPoisonPPM(bonusChance float64) { } func (rogue *Rogue) applyInstantPoison() { - procMask := core.GetMeleeProcMaskForHands( - rogue.Options.MhImbue == proto.Rogue_Options_InstantPoison, - rogue.Options.OhImbue == proto.Rogue_Options_InstantPoison) - + procMask := rogue.getPoisonProcMask(proto.Rogue_Options_InstantPoison) if procMask == core.ProcMaskUnknown { return } @@ -330,6 +330,7 @@ func (rogue *Rogue) applyInstantPoison() { if !result.Landed() { return } + if rogue.instantPoisonPPMM.Proc(sim, spell.ProcMask, "Instant Poison") { rogue.InstantPoison[NormalProc].Cast(sim, result.Target) } diff --git a/sim/shaman/shamanistic_rage.go b/sim/shaman/shamanistic_rage.go index cdf0a82acd..42fcde55ef 100644 --- a/sim/shaman/shamanistic_rage.go +++ b/sim/shaman/shamanistic_rage.go @@ -37,15 +37,14 @@ func (shaman *Shaman) registerShamanisticRageCD() { } }, OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - // proc mask: 20 - if !result.Landed() || !spell.ProcMask.Matches(core.ProcMaskMelee) { + if !result.Landed() { return } - if !ppmm.Proc(sim, spell.ProcMask, "shamanistic rage") { - return + + if ppmm.Proc(sim, spell.ProcMask, "shamanistic rage") { + mana := aura.Unit.GetStat(stats.AttackPower) * 0.15 + aura.Unit.AddMana(sim, mana, manaMetrics) } - mana := aura.Unit.GetStat(stats.AttackPower) * 0.15 - aura.Unit.AddMana(sim, mana, manaMetrics) }, }) diff --git a/sim/shaman/talents.go b/sim/shaman/talents.go index dafe0feeec..1341a54d74 100644 --- a/sim/shaman/talents.go +++ b/sim/shaman/talents.go @@ -440,14 +440,14 @@ func (shaman *Shaman) applyMaelstromWeapon() { aura.Activate(sim) }, OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !spell.ProcMask.Matches(core.ProcMaskMelee) || !result.Landed() { + if !result.Landed() { return } - if !ppmm.Proc(sim, spell.ProcMask, "Maelstrom Weapon") { - return + + if ppmm.Proc(sim, spell.ProcMask, "Maelstrom Weapon") { + shaman.MaelstromWeaponAura.Activate(sim) + shaman.MaelstromWeaponAura.AddStack(sim) } - shaman.MaelstromWeaponAura.Activate(sim) - shaman.MaelstromWeaponAura.AddStack(sim) }, }) } diff --git a/sim/shaman/weapon_imbues.go b/sim/shaman/weapon_imbues.go index 2f8114e342..f54c831812 100644 --- a/sim/shaman/weapon_imbues.go +++ b/sim/shaman/weapon_imbues.go @@ -13,11 +13,16 @@ var TotemOfSplintering int32 = 40710 func (shaman *Shaman) RegisterOnItemSwapWithImbue(effectID int32, procMask *core.ProcMask, aura *core.Aura) { shaman.RegisterOnItemSwap(func(sim *core.Simulation) { - mh := shaman.Equip[proto.ItemSlot_ItemSlotMainHand].TempEnchant == effectID - oh := shaman.Equip[proto.ItemSlot_ItemSlotOffHand].TempEnchant == effectID - *procMask = core.GetMeleeProcMaskForHands(mh, oh) + mask := core.ProcMaskUnknown + if shaman.Equip[proto.ItemSlot_ItemSlotMainHand].TempEnchant == effectID { + mask |= core.ProcMaskMeleeMH + } + if shaman.Equip[proto.ItemSlot_ItemSlotOffHand].TempEnchant == effectID { + mask |= core.ProcMaskMeleeOH + } + *procMask = mask - if !mh && !oh { + if mask == core.ProcMaskUnknown { aura.Deactivate(sim) } else { aura.Activate(sim) @@ -114,15 +119,14 @@ func (shaman *Shaman) RegisterWindfuryImbue(mh bool, oh bool) { return } - if sim.RandomFloat("Windfury Imbue") > proc { - return - } - icd.Use(sim) + if sim.RandomFloat("Windfury Imbue") < proc { + icd.Use(sim) - if isMHHit { - mhSpell.Cast(sim, result.Target) - } else { - ohSpell.Cast(sim, result.Target) + if isMHHit { + mhSpell.Cast(sim, result.Target) + } else { + ohSpell.Cast(sim, result.Target) + } } }, }) @@ -332,7 +336,7 @@ func (shaman *Shaman) FrostbrandDebuffAura(target *core.Unit) *core.Aura { }) } -func (shaman *Shaman) newFrostbrandImbueSpell(isMH bool) *core.Spell { +func (shaman *Shaman) newFrostbrandImbueSpell() *core.Spell { return shaman.RegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: 58796}, SpellSchool: core.SpellSchoolFrost, @@ -355,14 +359,15 @@ func (shaman *Shaman) RegisterFrostbrandImbue(mh bool, oh bool) { return } - mhSpell := shaman.newFrostbrandImbueSpell(true) - ohSpell := shaman.newFrostbrandImbueSpell(false) + mhSpell := shaman.newFrostbrandImbueSpell() + ohSpell := shaman.newFrostbrandImbueSpell() procMask := core.GetMeleeProcMaskForHands(mh, oh) ppmm := shaman.AutoAttacks.NewPPMManager(9.0, procMask) if mh { shaman.Equip[proto.ItemSlot_ItemSlotMainHand].TempEnchant = 3784 - } else { + } + if oh { shaman.Equip[proto.ItemSlot_ItemSlotOffHand].TempEnchant = 3784 } @@ -375,27 +380,25 @@ func (shaman *Shaman) RegisterFrostbrandImbue(mh bool, oh bool) { aura.Activate(sim) }, OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !result.Landed() || !spell.ProcMask.Matches(procMask) { - return - } - - if !ppmm.Proc(sim, spell.ProcMask, "Frostbrand Weapon") { + if !result.Landed() { return } - if spell.IsMH() { - mhSpell.Cast(sim, result.Target) - } else { - ohSpell.Cast(sim, result.Target) + if ppmm.Proc(sim, spell.ProcMask, "Frostbrand Weapon") { + if spell.IsMH() { + mhSpell.Cast(sim, result.Target) + } else { + ohSpell.Cast(sim, result.Target) + } + fbDebuffAuras.Get(result.Target).Activate(sim) } - fbDebuffAuras.Get(result.Target).Activate(sim) }, }) shaman.ItemSwap.RegisterOnSwapItemForEffectWithPPMManager(3784, 9.0, &ppmm, aura) } -func (shaman *Shaman) newEarthlivingImbueSpell(isMH bool) *core.Spell { +func (shaman *Shaman) newEarthlivingImbueSpell() *core.Spell { return shaman.RegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: 51994}, SpellSchool: core.SpellSchoolNature, @@ -455,8 +458,8 @@ func (shaman *Shaman) RegisterEarthlivingImbue(mh bool, oh bool) { shaman.ApplyEarthlivingImbueToItem(shaman.GetOHWeapon(), false) } - mhSpell := shaman.newEarthlivingImbueSpell(true) - ohSpell := shaman.newEarthlivingImbueSpell(false) + mhSpell := shaman.newEarthlivingImbueSpell() + ohSpell := shaman.newEarthlivingImbueSpell() procChance := 0.2 if shaman.HasMajorGlyph(proto.ShamanMajorGlyph_GlyphOfEarthlivingWeapon) { diff --git a/sim/warrior/talents.go b/sim/warrior/talents.go index 573e02484c..f9c32177dc 100644 --- a/sim/warrior/talents.go +++ b/sim/warrior/talents.go @@ -480,7 +480,8 @@ func (warrior *Warrior) applyUnbridledWrath() { return } - ppmm := warrior.AutoAttacks.NewPPMManager(3*float64(warrior.Talents.UnbridledWrath), core.ProcMaskMelee) + ppmm := warrior.AutoAttacks.NewPPMManager(3*float64(warrior.Talents.UnbridledWrath), core.ProcMaskMeleeWhiteHit) + rageMetrics := warrior.NewRageMetrics(core.ActionID{SpellID: 13002}) warrior.RegisterAura(core.Aura{ @@ -494,15 +495,9 @@ func (warrior *Warrior) applyUnbridledWrath() { return } - if !spell.ProcMask.Matches(core.ProcMaskMeleeWhiteHit) { - return - } - - if !ppmm.Proc(sim, spell.ProcMask, "Unbrided Wrath") { - return + if ppmm.Proc(sim, spell.ProcMask, "Unbrided Wrath") { + warrior.AddRage(sim, 1, rageMetrics) } - - warrior.AddRage(sim, 1, rageMetrics) }, }) }