diff --git a/proto/shaman.proto b/proto/shaman.proto index 906a53ddfa..a1cab5b385 100644 --- a/proto/shaman.proto +++ b/proto/shaman.proto @@ -216,9 +216,9 @@ enum ShamanImbue { } enum ShamanSyncType { - NoSync = 0; - SyncMainhandOffhandSwings = 1; - DelayOffhandSwings = 2; + NoSync = 0; + SyncMainhandOffhandSwings = 1; + DelayOffhandSwings = 2; Auto = 3; } @@ -269,7 +269,7 @@ message EnhancementShaman { } RotationType rotation_type = 2; - //TODO: add spells here for custom rotation (if nessecary?) + //TODO: add spells here for custom rotation (if necessary?) enum CustomRotationSpell { NoSpell = 0; Stormstrike = 1; diff --git a/sim/common/tbc/melee_trinkets.go b/sim/common/tbc/melee_trinkets.go index 0d32aa8793..d4a5ca83d2 100644 --- a/sim/common/tbc/melee_trinkets.go +++ b/sim/common/tbc/melee_trinkets.go @@ -25,7 +25,7 @@ func init() { core.NewItemEffect(11815, func(agent core.Agent) { character := agent.GetCharacter() - if !character.AutoAttacks.IsEnabled() { + if !character.AutoAttacks.AutoSwingMelee { return } diff --git a/sim/common/wotlk/capacitors.go b/sim/common/wotlk/capacitors.go index 9c396dc5ed..b2ff4ecea7 100644 --- a/sim/common/wotlk/capacitors.go +++ b/sim/common/wotlk/capacitors.go @@ -172,7 +172,7 @@ func init() { core.NewItemEffect(itemID, func(agent core.Agent) { character := agent.GetCharacter() - if !character.AutoAttacks.IsEnabled() { + if !character.AutoAttacks.AutoSwingMelee { return } diff --git a/sim/core/attack.go b/sim/core/attack.go index aa4ef19a20..5dae44aea4 100644 --- a/sim/core/attack.go +++ b/sim/core/attack.go @@ -8,27 +8,24 @@ import ( "github.com/wowsims/wotlk/sim/core/stats" ) -// ReplaceMHSwing is called right before an auto attack fires -// -// If it returns nil, the attack takes place as normal. If it returns a Spell, -// that Spell is used in place of the attack. -// This allows for abilities that convert a white attack into yellow attack. +// ReplaceMHSwing is called right before a main hand auto attack fires. +// It must never return nil, but either a replacement spell or the passed in regular mhSwingSpell. +// This allows for abilities that convert a white attack into a yellow attack. type ReplaceMHSwing func(sim *Simulation, mhSwingSpell *Spell) *Spell -// Represents a generic weapon. Pets / unarmed / various other cases dont use +// Represents a generic weapon. Pets / unarmed / various other cases don't use // actual weapon items so this is an abstraction of a Weapon. type Weapon struct { - BaseDamageMin float64 - BaseDamageMax float64 - MeleeAttackRatingPerDamage float64 - SwingSpeed float64 - NormalizedSwingSpeed float64 - SwingDuration time.Duration // Duration between 2 swings. - CritMultiplier float64 - SpellSchool SpellSchool + BaseDamageMin float64 + BaseDamageMax float64 + AttackPowerPerDPS float64 + SwingSpeed float64 + NormalizedSwingSpeed float64 + CritMultiplier float64 + SpellSchool SpellSchool } -func (weapon Weapon) DPS() float64 { +func (weapon *Weapon) DPS() float64 { if weapon.SwingSpeed == 0 { return 0 } else { @@ -36,28 +33,19 @@ func (weapon Weapon) DPS() float64 { } } -func (weapon Weapon) WithBonusDPS(bonusDps float64) Weapon { - newWeapon := weapon - bonusSwingDamage := bonusDps * weapon.SwingSpeed - newWeapon.BaseDamageMin += bonusSwingDamage - newWeapon.BaseDamageMax += bonusSwingDamage - return newWeapon -} - func newWeaponFromUnarmed(critMultiplier float64) Weapon { // These numbers are probably wrong but nobody cares. return Weapon{ - BaseDamageMin: 0, - BaseDamageMax: 0, - SwingSpeed: 1, - NormalizedSwingSpeed: 1, - SwingDuration: time.Second, - CritMultiplier: critMultiplier, - MeleeAttackRatingPerDamage: MeleeAttackRatingPerDamage, + BaseDamageMin: 0, + BaseDamageMax: 0, + SwingSpeed: 1, + NormalizedSwingSpeed: 1, + CritMultiplier: critMultiplier, + AttackPowerPerDPS: DefaultAttackPowerPerDPS, } } -func newWeaponFromItem(item Item, critMultiplier float64) Weapon { +func newWeaponFromItem(item *Item, critMultiplier float64, bonusDps float64) Weapon { normalizedWeaponSpeed := 2.4 if item.WeaponType == proto.WeaponType_WeaponTypeDagger { normalizedWeaponSpeed = 1.7 @@ -68,29 +56,28 @@ func newWeaponFromItem(item Item, critMultiplier float64) Weapon { } return Weapon{ - BaseDamageMin: item.WeaponDamageMin, - BaseDamageMax: item.WeaponDamageMax, - SwingSpeed: item.SwingSpeed, - NormalizedSwingSpeed: normalizedWeaponSpeed, - SwingDuration: DurationFromSeconds(item.SwingSpeed), - CritMultiplier: critMultiplier, - MeleeAttackRatingPerDamage: MeleeAttackRatingPerDamage, + BaseDamageMin: item.WeaponDamageMin + bonusDps*item.SwingSpeed, + BaseDamageMax: item.WeaponDamageMax + bonusDps*item.SwingSpeed, + SwingSpeed: item.SwingSpeed, + NormalizedSwingSpeed: normalizedWeaponSpeed, + CritMultiplier: critMultiplier, + AttackPowerPerDPS: DefaultAttackPowerPerDPS, } } // Returns weapon stats using the main hand equipped weapon. func (character *Character) WeaponFromMainHand(critMultiplier float64) Weapon { if weapon := character.GetMHWeapon(); weapon != nil { - return newWeaponFromItem(*weapon, critMultiplier).WithBonusDPS(character.PseudoStats.BonusMHDps) + return newWeaponFromItem(weapon, critMultiplier, character.PseudoStats.BonusMHDps) } else { - return newWeaponFromUnarmed(critMultiplier).WithBonusDPS(character.PseudoStats.BonusMHDps) + return newWeaponFromUnarmed(critMultiplier) } } -// Returns weapon stats using the off hand equipped weapon. +// Returns weapon stats using the off-hand equipped weapon. func (character *Character) WeaponFromOffHand(critMultiplier float64) Weapon { if weapon := character.GetOHWeapon(); weapon != nil { - return newWeaponFromItem(*weapon, critMultiplier).WithBonusDPS(character.PseudoStats.BonusOHDps) + return newWeaponFromItem(weapon, critMultiplier, character.PseudoStats.BonusOHDps) } else { return Weapon{} } @@ -99,13 +86,13 @@ func (character *Character) WeaponFromOffHand(critMultiplier float64) Weapon { // Returns weapon stats using the ranged equipped weapon. func (character *Character) WeaponFromRanged(critMultiplier float64) Weapon { if weapon := character.GetRangedWeapon(); weapon != nil { - return newWeaponFromItem(*weapon, critMultiplier).WithBonusDPS(character.PseudoStats.BonusRangedDps) + return newWeaponFromItem(weapon, critMultiplier, character.PseudoStats.BonusRangedDps) } else { return Weapon{} } } -func (weapon Weapon) GetSpellSchool() SpellSchool { +func (weapon *Weapon) GetSpellSchool() SpellSchool { if weapon.SpellSchool == SpellSchoolNone { return SpellSchoolPhysical } else { @@ -113,8 +100,8 @@ func (weapon Weapon) GetSpellSchool() SpellSchool { } } -func (weapon Weapon) EnemyWeaponDamage(sim *Simulation, attackPower float64, damageSpread float64) float64 { - // Maximum damage range is 133% of minimum damage; AP contribution is % of minimum damage roll +func (weapon *Weapon) EnemyWeaponDamage(sim *Simulation, attackPower float64, damageSpread float64) float64 { + // Maximum damage range is 133% of minimum damage; AP contribution is % of minimum damage roll. // Patchwerk follows special damage range rules. // TODO: Scrape more logs to determine these values more accurately. AP defined in constants.go @@ -123,24 +110,24 @@ func (weapon Weapon) EnemyWeaponDamage(sim *Simulation, attackPower float64, dam return weapon.BaseDamageMin * (rand + attackPower*EnemyAutoAttackAPCoefficient) } -func (weapon Weapon) BaseDamage(sim *Simulation) float64 { +func (weapon *Weapon) BaseDamage(sim *Simulation) float64 { return weapon.BaseDamageMin + (weapon.BaseDamageMax-weapon.BaseDamageMin)*sim.RandomFloat("Weapon Base Damage") } -func (weapon Weapon) AverageDamage() float64 { +func (weapon *Weapon) AverageDamage() float64 { return (weapon.BaseDamageMin + weapon.BaseDamageMax) / 2 } -func (weapon Weapon) CalculateWeaponDamage(sim *Simulation, attackPower float64) float64 { - return weapon.BaseDamage(sim) + (weapon.SwingSpeed*attackPower)/weapon.MeleeAttackRatingPerDamage +func (weapon *Weapon) CalculateWeaponDamage(sim *Simulation, attackPower float64) float64 { + return weapon.BaseDamage(sim) + (weapon.SwingSpeed*attackPower)/weapon.AttackPowerPerDPS } -func (weapon Weapon) CalculateAverageWeaponDamage(attackPower float64) float64 { - return weapon.AverageDamage() + (weapon.SwingSpeed*attackPower)/weapon.MeleeAttackRatingPerDamage +func (weapon *Weapon) CalculateAverageWeaponDamage(attackPower float64) float64 { + return weapon.AverageDamage() + (weapon.SwingSpeed*attackPower)/weapon.AttackPowerPerDPS } -func (weapon Weapon) CalculateNormalizedWeaponDamage(sim *Simulation, attackPower float64) float64 { - return weapon.BaseDamage(sim) + (weapon.NormalizedSwingSpeed*attackPower)/weapon.MeleeAttackRatingPerDamage +func (weapon *Weapon) CalculateNormalizedWeaponDamage(sim *Simulation, attackPower float64) float64 { + return weapon.BaseDamage(sim) + (weapon.NormalizedSwingSpeed*attackPower)/weapon.AttackPowerPerDPS } func (unit *Unit) MHWeaponDamage(sim *Simulation, attackPower float64) float64 { @@ -195,11 +182,6 @@ type AutoAttacks struct { // use this. AutoSwingRanged bool - // Set this to 1 to sync your auto attacks together, or 2 to use the OH delay macro, mostly used by enhance shamans. - // This will intentionally perfectly sync or delay OH swings to that they always fall within the - // 0.5s window following a MH swing. - SyncType int32 - MainhandSwingAt time.Duration OffhandSwingAt time.Duration RangedSwingAt time.Duration @@ -214,12 +196,13 @@ type AutoAttacks struct { ReplaceMHSwing ReplaceMHSwing - // The time at which the last MH swing occurred. - previousMHSwingAt time.Duration - PreviousSwingAt time.Duration + // Current melee and ranged swing speeds, and corresponding swing durations, updated in UpdateSwingTimers. + curMeleeSpeed float64 + curMHSwingDuration time.Duration + curOHSwingDuration time.Duration - // Current melee swing speed, based on haste stat and melee swing multiplier pseudostat. - curSwingSpeed float64 + curRangedSpeed float64 + curRangedSwingDuration time.Duration // PendingAction which handles auto attacks. autoSwingAction *PendingAction @@ -238,11 +221,11 @@ type AutoAttackOptions struct { } func (unit *Unit) EnableAutoAttacks(agent Agent, options AutoAttackOptions) { - if options.MainHand.MeleeAttackRatingPerDamage == 0 { - options.MainHand.MeleeAttackRatingPerDamage = MeleeAttackRatingPerDamage + if options.MainHand.AttackPowerPerDPS == 0 { + options.MainHand.AttackPowerPerDPS = DefaultAttackPowerPerDPS } - if options.OffHand.MeleeAttackRatingPerDamage == 0 { - options.OffHand.MeleeAttackRatingPerDamage = MeleeAttackRatingPerDamage + if options.OffHand.AttackPowerPerDPS == 0 { + options.OffHand.AttackPowerPerDPS = DefaultAttackPowerPerDPS } unit.AutoAttacks = AutoAttacks{ agent: agent, @@ -252,7 +235,6 @@ func (unit *Unit) EnableAutoAttacks(agent Agent, options AutoAttackOptions) { Ranged: options.Ranged, AutoSwingMelee: options.AutoSwingMelee, AutoSwingRanged: options.AutoSwingRanged, - SyncType: options.SyncType, ReplaceMHSwing: options.ReplaceMHSwing, IsDualWielding: options.MainHand.SwingSpeed != 0 && options.OffHand.SwingSpeed != 0, } @@ -330,59 +312,51 @@ func (unit *Unit) EnableAutoAttacks(agent Agent, options AutoAttackOptions) { unit.AutoAttacks.autoSwingCancelled = true } -func (aa *AutoAttacks) IsEnabled() bool { - return aa.MH.SwingSpeed != 0 -} - // Empty handler so Agents don't have to provide one if they have no logic to add. func (unit *Unit) OnAutoAttack(_ *Simulation, _ *Spell) {} func (aa *AutoAttacks) finalize() { - if !aa.IsEnabled() { - return + if aa.AutoSwingMelee { + aa.MHAuto = aa.unit.GetOrRegisterSpell(aa.MHConfig) + aa.OHAuto = aa.unit.GetOrRegisterSpell(aa.OHConfig) } - aa.MHAuto = aa.unit.GetOrRegisterSpell(aa.MHConfig) - aa.OHAuto = aa.unit.GetOrRegisterSpell(aa.OHConfig) - - if aa.RangedConfig.ProcMask != ProcMaskUnknown { + if aa.AutoSwingRanged { aa.RangedAuto = aa.unit.GetOrRegisterSpell(aa.RangedConfig) } } func (aa *AutoAttacks) reset(sim *Simulation) { - if !aa.IsEnabled() { + if !aa.AutoSwingMelee && !aa.AutoSwingRanged { return } - aa.curSwingSpeed = aa.unit.SwingSpeed() - - aa.MainhandSwingAt = 0 - aa.OffhandSwingAt = 0 - aa.RangedSwingAt = 0 - aa.PreviousSwingAt = 0 - - // Apply random delay of 0 - 50% swing time, to one of the weapons if dual wielding - if aa.IsDualWielding { - // Set a fake value for previousMHSwing so that offhand swing delay works - // properly at the start. - aa.previousMHSwingAt = time.Second * -1 - - var delay time.Duration - var isMHDelay bool - if aa.unit.Type == EnemyUnit { - delay = aa.MH.SwingDuration / 2 - isMHDelay = false - } else { - delay = time.Duration(sim.RandomFloat("SwingResetDelay") * float64(aa.MH.SwingDuration/2)) - isMHDelay = sim.RandomFloat("SwingResetWeapon") < 0.5 + if aa.AutoSwingMelee { + aa.curMeleeSpeed = aa.unit.SwingSpeed() + aa.UpdateMeleeDurations() + + aa.MainhandSwingAt = 0 + aa.OffhandSwingAt = 0 + + // Apply random delay of 0 - 50% swing time, to one of the weapons if dual wielding + if aa.IsDualWielding { + if aa.unit.Type == EnemyUnit { + aa.OffhandSwingAt = DurationFromSeconds(aa.MH.SwingSpeed / 2) + } else { + if sim.RandomFloat("SwingResetWeapon") < 0.5 { + aa.MainhandSwingAt = DurationFromSeconds(sim.RandomFloat("SwingResetDelay") * aa.MH.SwingSpeed / 2) + } else { + aa.OffhandSwingAt = DurationFromSeconds(sim.RandomFloat("SwingResetDelay") * aa.MH.SwingSpeed / 2) + } + } } + } - if isMHDelay { - aa.MainhandSwingAt = delay - } else { - aa.OffhandSwingAt = delay - } + if aa.AutoSwingRanged { + aa.curRangedSpeed = aa.unit.RangedSwingSpeed() + aa.UpdateRangedDuration() + + aa.RangedSwingAt = 0 } aa.autoSwingAction = nil @@ -390,45 +364,67 @@ func (aa *AutoAttacks) reset(sim *Simulation) { } func (aa *AutoAttacks) startPull(sim *Simulation) { - if aa.IsEnabled() && aa.unit.IsEnabled() { - aa.resetAutoSwing(sim) + if aa.autoSwingCancelled { + return } -} -func (aa *AutoAttacks) resetAutoSwing(sim *Simulation) { - if aa.autoSwingCancelled || (!aa.AutoSwingMelee && !aa.AutoSwingRanged) || sim.CurrentTime < 0 { - return + if aa.AutoSwingMelee { + aa.rescheduleMelee(sim) } + if aa.AutoSwingRanged { + aa.rescheduleRanged(sim) + } +} + +func (aa *AutoAttacks) rescheduleRanged(sim *Simulation) { if aa.autoSwingAction != nil { aa.autoSwingAction.Cancel(sim) } - pa := &PendingAction{ - NextActionAt: TernaryDuration(aa.AutoSwingMelee, aa.NextAttackAt(), aa.RangedSwingAt), - Priority: ActionPriorityAuto, + if aa.autoSwingAction != nil { + aa.autoSwingAction.Cancel(sim) } - if aa.AutoSwingMelee { - pa.OnAction = func(sim *Simulation) { - aa.SwingMelee(sim, aa.unit.CurrentTarget) - pa.NextActionAt = aa.NextAttackAt() + var pa *PendingAction + + pa = &PendingAction{ + NextActionAt: aa.RangedSwingAt, + Priority: ActionPriorityAuto, + OnAction: func(sim *Simulation) { + aa.SwingRanged(sim, aa.unit.CurrentTarget) + pa.NextActionAt = aa.RangedSwingAt // Cancelled means we made a new one because of a swing speed change. if !pa.cancelled { sim.AddPendingAction(pa) } - } - } else { // Ranged - pa.OnAction = func(sim *Simulation) { - aa.SwingRanged(sim, aa.unit.CurrentTarget) - pa.NextActionAt = aa.RangedSwingAt + }, + } + + aa.autoSwingAction = pa + sim.AddPendingAction(pa) +} + +func (aa *AutoAttacks) rescheduleMelee(sim *Simulation) { + if aa.autoSwingAction != nil { + aa.autoSwingAction.Cancel(sim) + } + + var pa *PendingAction + + pa = &PendingAction{ + NextActionAt: aa.NextAttackAt(), + Priority: ActionPriorityAuto, + OnAction: func(sim *Simulation) { + aa.SwingMelee(sim, aa.unit.CurrentTarget) + pa.NextActionAt = aa.NextAttackAt() // Cancelled means we made a new one because of a swing speed change. if !pa.cancelled { sim.AddPendingAction(pa) } - } + }, } aa.autoSwingAction = pa @@ -445,43 +441,48 @@ func (aa *AutoAttacks) CancelAutoSwing(sim *Simulation) { aa.autoSwingCancelled = true } -// Renables the auto swing action for the iteration +// Re-enables the auto swing action for the iteration func (aa *AutoAttacks) EnableAutoSwing(sim *Simulation) { // Already enabled so nothing to do - if aa.autoSwingAction != nil { - return - } - if sim.CurrentTime < 0 { + if !aa.autoSwingCancelled { return } - if aa.MainhandSwingAt < sim.CurrentTime { - aa.MainhandSwingAt = sim.CurrentTime - } - if aa.OffhandSwingAt < sim.CurrentTime { - aa.OffhandSwingAt = sim.CurrentTime - } - if aa.RangedSwingAt < sim.CurrentTime { - aa.RangedSwingAt = sim.CurrentTime + aa.autoSwingCancelled = false + + if aa.AutoSwingMelee { + if aa.MainhandSwingAt < sim.CurrentTime { + aa.MainhandSwingAt = sim.CurrentTime + } + if aa.OffhandSwingAt < sim.CurrentTime { + aa.OffhandSwingAt = sim.CurrentTime + } + + aa.rescheduleMelee(sim) } - aa.autoSwingCancelled = false - aa.resetAutoSwing(sim) + if aa.AutoSwingRanged { + if aa.RangedSwingAt < sim.CurrentTime { + aa.RangedSwingAt = sim.CurrentTime + } + + aa.rescheduleRanged(sim) + } } // The amount of time between two MH swings. func (aa *AutoAttacks) MainhandSwingSpeed() time.Duration { - return time.Duration(float64(aa.MH.SwingDuration) / aa.unit.SwingSpeed()) + return aa.curMHSwingDuration } // The amount of time between two OH swings. func (aa *AutoAttacks) OffhandSwingSpeed() time.Duration { - return time.Duration(float64(aa.OH.SwingDuration) / aa.unit.SwingSpeed()) + return aa.curOHSwingDuration } -// The amount of time between two ranged swings. +// The amount of time between two Ranged swings. func (aa *AutoAttacks) RangedSwingSpeed() time.Duration { - return time.Duration(float64(aa.Ranged.SwingDuration) / aa.unit.RangedSwingSpeed()) + return aa.curRangedSwingDuration } // SwingMelee will check any swing timers if they are up, and if so, swing! @@ -494,7 +495,7 @@ func (aa *AutoAttacks) SwingRanged(sim *Simulation, target *Unit) { aa.TrySwingRanged(sim, target) } -// Performs an autoattack using the main hand weapon, if the MH CD is ready. +// Performs an auto attack using the main hand weapon, if the MH CD is ready. func (aa *AutoAttacks) TrySwingMH(sim *Simulation, target *Unit) { if aa.MainhandSwingAt > sim.CurrentTime { return @@ -503,9 +504,8 @@ func (aa *AutoAttacks) TrySwingMH(sim *Simulation, target *Unit) { attackSpell := aa.MaybeReplaceMHSwing(sim, aa.MHAuto) attackSpell.Cast(sim, target) - aa.MainhandSwingAt = sim.CurrentTime + aa.MainhandSwingSpeed() - aa.previousMHSwingAt = sim.CurrentTime - aa.PreviousSwingAt = sim.CurrentTime + aa.MainhandSwingAt = sim.CurrentTime + aa.curMHSwingDuration + if !sim.Options.Interactive { if aa.unit.IsUsingAPL { aa.unit.Rotation.DoNextAction(sim) @@ -523,39 +523,18 @@ func (aa *AutoAttacks) MaybeReplaceMHSwing(sim *Simulation, mhSwingSpell *Spell) } // Allow MH swing to be overridden for abilities like Heroic Strike. - replacementSpell := aa.ReplaceMHSwing(sim, mhSwingSpell) - if replacementSpell == nil { - return mhSwingSpell - } else { - return replacementSpell - } + return aa.ReplaceMHSwing(sim, mhSwingSpell) } -// Performs an autoattack using the main hand weapon, if the OH CD is ready. +// Performs an auto attack using the main hand weapon, if the OH CD is ready. func (aa *AutoAttacks) TrySwingOH(sim *Simulation, target *Unit) { if !aa.IsDualWielding || aa.OffhandSwingAt > sim.CurrentTime { return } - if (aa.SyncType == 1) && (sim.CurrentTime-aa.previousMHSwingAt) > time.Millisecond*500 { - // Perfectly Sync MH and OH attacks - aa.OffhandSwingAt = aa.MainhandSwingAt - if sim.Log != nil { - aa.unit.Log(sim, "Resyncing Weapons") - } - return - } else if (aa.SyncType == 2) && (sim.CurrentTime-aa.previousMHSwingAt) > time.Millisecond*500 { - // Delay the OH swing for later, so it follows the MH swing. - aa.OffhandSwingAt = aa.MainhandSwingAt + time.Millisecond*100 - if sim.Log != nil { - aa.unit.Log(sim, "Delaying OH swing by %s", aa.OffhandSwingAt-sim.CurrentTime) - } - return - } - aa.OHAuto.Cast(sim, target) - aa.OffhandSwingAt = sim.CurrentTime + aa.OffhandSwingSpeed() - aa.PreviousSwingAt = sim.CurrentTime + aa.OffhandSwingAt = sim.CurrentTime + aa.curOHSwingDuration + if !sim.Options.Interactive { if aa.unit.IsUsingAPL { aa.unit.Rotation.DoNextAction(sim) @@ -565,7 +544,7 @@ func (aa *AutoAttacks) TrySwingOH(sim *Simulation, target *Unit) { } } -// Performs an autoattack using the ranged weapon, if the ranged CD is ready. +// Performs an auto attack using the ranged weapon, if the Ranged CD is ready. func (aa *AutoAttacks) TrySwingRanged(sim *Simulation, target *Unit) { if aa.RangedSwingAt > sim.CurrentTime { return @@ -573,7 +552,7 @@ func (aa *AutoAttacks) TrySwingRanged(sim *Simulation, target *Unit) { aa.RangedAuto.Cast(sim, target) aa.RangedSwingAt = sim.CurrentTime + aa.RangedSwingSpeed() - aa.PreviousSwingAt = sim.CurrentTime + if !sim.Options.Interactive { if aa.unit.IsUsingAPL { aa.unit.Rotation.DoNextAction(sim) @@ -583,29 +562,53 @@ func (aa *AutoAttacks) TrySwingRanged(sim *Simulation, target *Unit) { } } -func (aa *AutoAttacks) UpdateSwingTime(sim *Simulation) { - if !aa.IsEnabled() || aa.AutoSwingRanged { - return +// This is used internally, and for druid shifts (where a weapon changes without resetting the swing timer). +func (aa *AutoAttacks) UpdateMeleeDurations() { + aa.curMHSwingDuration = DurationFromSeconds(aa.MH.SwingSpeed / aa.curMeleeSpeed) + if aa.IsDualWielding { + aa.curOHSwingDuration = DurationFromSeconds(aa.OH.SwingSpeed / aa.curMeleeSpeed) } +} - oldSwingSpeed := aa.curSwingSpeed - aa.curSwingSpeed = aa.unit.SwingSpeed() - - f := oldSwingSpeed / aa.curSwingSpeed +func (aa *AutoAttacks) UpdateRangedDuration() { + aa.curRangedSwingDuration = DurationFromSeconds(aa.Ranged.SwingSpeed / aa.curRangedSpeed) +} - remainingSwingTime := aa.MainhandSwingAt - sim.CurrentTime - if remainingSwingTime > 0 { - aa.MainhandSwingAt = sim.CurrentTime + time.Duration(float64(remainingSwingTime)*f) +func (aa *AutoAttacks) UpdateSwingTimers(sim *Simulation) { + if aa.AutoSwingRanged { + aa.curRangedSpeed = aa.unit.RangedSwingSpeed() + aa.UpdateRangedDuration() + // ranged attack speed changes aren't applied mid-"swing" } - if aa.IsDualWielding { - remainingSwingTime := aa.OffhandSwingAt - sim.CurrentTime - if remainingSwingTime > 0 { - aa.OffhandSwingAt = sim.CurrentTime + time.Duration(float64(remainingSwingTime)*f) + if aa.AutoSwingMelee { + oldSwingSpeed := aa.curMeleeSpeed + + aa.curMeleeSpeed = aa.unit.SwingSpeed() + aa.UpdateMeleeDurations() + + f := oldSwingSpeed / aa.curMeleeSpeed + + if remainingSwingTime := aa.MainhandSwingAt - sim.CurrentTime; remainingSwingTime > 0 { + aa.MainhandSwingAt = sim.CurrentTime + time.Duration(float64(remainingSwingTime)*f) + } + + if aa.IsDualWielding { + if remainingSwingTime := aa.OffhandSwingAt - sim.CurrentTime; remainingSwingTime > 0 { + aa.OffhandSwingAt = sim.CurrentTime + time.Duration(float64(remainingSwingTime)*f) + } + } + + if aa.autoSwingCancelled { + return + } + + if sim.CurrentTime < 0 { + return } - } - aa.resetAutoSwing(sim) + aa.rescheduleMelee(sim) + } } // StopMeleeUntil should be used whenever a non-melee spell is cast. It stops melee, then restarts it @@ -614,53 +617,36 @@ func (aa *AutoAttacks) StopMeleeUntil(sim *Simulation, readyAt time.Duration, de if !aa.AutoSwingMelee { // if not auto swinging, don't auto restart. return } - aa.CancelAutoSwing(sim) - // Used by warrior to desync offhand after Shattering Throw. - if desyncOH { - // schedule restart action - sim.AddPendingAction(&PendingAction{ - NextActionAt: readyAt, - Priority: ActionPriorityAuto, - OnAction: aa.desyncedRestartMelee, - }) - } else { - // schedule restart action - sim.AddPendingAction(&PendingAction{ - NextActionAt: readyAt, - Priority: ActionPriorityAuto, - OnAction: aa.restartMelee, - }) - } + aa.CancelAutoSwing(sim) + // schedule restart action + sim.AddPendingAction(&PendingAction{ + NextActionAt: readyAt, + Priority: ActionPriorityAuto, + OnAction: func(sim *Simulation) { + aa.restartMelee(sim, desyncOH) + }, + }) } -func (aa *AutoAttacks) restartMelee(sim *Simulation) { +func (aa *AutoAttacks) restartMelee(sim *Simulation, desyncOH bool) { if !aa.autoSwingCancelled { return } - aa.MainhandSwingAt = sim.CurrentTime + aa.MainhandSwingSpeed() - if aa.IsDualWielding { - aa.OffhandSwingAt = sim.CurrentTime + aa.OffhandSwingSpeed() - } aa.autoSwingCancelled = false - aa.resetAutoSwing(sim) -} -// Emulating how desyncing OH works in the game. -// After swing timer has passed half the swing time, Offhand swing timer will be reset. -func (aa *AutoAttacks) desyncedRestartMelee(sim *Simulation) { - if !aa.autoSwingCancelled { - return - } - - aa.MainhandSwingAt = sim.CurrentTime + aa.MainhandSwingSpeed() + aa.MainhandSwingAt = sim.CurrentTime + aa.curMHSwingDuration if aa.IsDualWielding { - aa.OffhandSwingAt = sim.CurrentTime + aa.OffhandSwingSpeed() + aa.OffhandSwingSpeed()/2 + aa.OffhandSwingAt = sim.CurrentTime + aa.curOHSwingDuration + if desyncOH { + // Used by warrior to desync offhand after unglyphed Shattering Throw. + aa.OffhandSwingAt += aa.curOHSwingDuration / 2 + } } - aa.autoSwingCancelled = false - aa.resetAutoSwing(sim) + + aa.rescheduleMelee(sim) } // Delays all swing timers for the specified amount. Only used by Slam. @@ -674,21 +660,24 @@ func (aa *AutoAttacks) DelayMeleeBy(sim *Simulation, delay time.Duration) { aa.OffhandSwingAt += delay } - aa.resetAutoSwing(sim) + aa.rescheduleMelee(sim) } func (aa *AutoAttacks) DelayRangedUntil(sim *Simulation, readyAt time.Duration) { - if readyAt > aa.RangedSwingAt { - aa.RangedSwingAt = readyAt - aa.resetAutoSwing(sim) + if readyAt <= aa.RangedSwingAt { + return } + + aa.RangedSwingAt = readyAt + + aa.rescheduleRanged(sim) } // Returns the time at which the next attack will occur. func (aa *AutoAttacks) NextAttackAt() time.Duration { nextAttack := aa.MainhandSwingAt - if aa.OH.SwingSpeed != 0 { - nextAttack = MinDuration(nextAttack, aa.OffhandSwingAt) + if aa.IsDualWielding && aa.OffhandSwingAt < nextAttack { + nextAttack = aa.OffhandSwingAt } return nextAttack } @@ -718,7 +707,7 @@ func (ppmm *PPMManager) Chance(procMask ProcMask) float64 { } func (aa *AutoAttacks) NewPPMManager(ppm float64, procMask ProcMask) PPMManager { - if !aa.IsEnabled() { + if !aa.AutoSwingMelee && !aa.AutoSwingRanged { return PPMManager{} } @@ -753,7 +742,7 @@ func (aa *AutoAttacks) NewPPMManager(ppm float64, procMask ProcMask) PPMManager // 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, spell *Spell) bool { - if !aa.IsEnabled() { + if !aa.AutoSwingMelee && !aa.AutoSwingRanged { return false } @@ -769,7 +758,7 @@ func (aa *AutoAttacks) PPMProc(sim *Simulation, ppm float64, procMask ProcMask, } func (unit *Unit) applyParryHaste() { - if !unit.PseudoStats.ParryHaste || !unit.AutoAttacks.IsEnabled() { + if !unit.PseudoStats.ParryHaste || !unit.AutoAttacks.AutoSwingMelee { return } @@ -785,7 +774,7 @@ func (unit *Unit) applyParryHaste() { } remainingTime := aura.Unit.AutoAttacks.MainhandSwingAt - sim.CurrentTime - swingSpeed := aura.Unit.AutoAttacks.MainhandSwingSpeed() + swingSpeed := aura.Unit.AutoAttacks.curMHSwingDuration minRemainingTime := time.Duration(float64(swingSpeed) * 0.2) // 20% of Swing Speed defaultReduction := minRemainingTime * 2 // 40% of Swing Speed @@ -800,7 +789,7 @@ func (unit *Unit) applyParryHaste() { } aura.Unit.AutoAttacks.MainhandSwingAt = newReadyAt - aura.Unit.AutoAttacks.resetAutoSwing(sim) + aura.Unit.AutoAttacks.rescheduleMelee(sim) }, }) } diff --git a/sim/core/character.go b/sim/core/character.go index 68ce40cde3..ed0f5451a8 100644 --- a/sim/core/character.go +++ b/sim/core/character.go @@ -673,9 +673,9 @@ func (character *Character) doneIteration(sim *Simulation) { func (character *Character) GetPseudoStatsProto() []float64 { vals := make([]float64, stats.PseudoStatsLen) - vals[proto.PseudoStat_PseudoStatMainHandDps] = character.WeaponFromMainHand(0).DPS() - vals[proto.PseudoStat_PseudoStatOffHandDps] = character.WeaponFromOffHand(0).DPS() - vals[proto.PseudoStat_PseudoStatRangedDps] = character.WeaponFromRanged(0).DPS() + vals[proto.PseudoStat_PseudoStatMainHandDps] = character.AutoAttacks.MH.DPS() + vals[proto.PseudoStat_PseudoStatOffHandDps] = character.AutoAttacks.OH.DPS() + vals[proto.PseudoStat_PseudoStatRangedDps] = character.AutoAttacks.Ranged.DPS() vals[proto.PseudoStat_PseudoStatBlockValueMultiplier] = character.PseudoStats.BlockValueMultiplier // Base values are modified by Enemy attackTables, but we display for LVL 80 enemy as paperdoll default vals[proto.PseudoStat_PseudoStatDodge] = character.PseudoStats.BaseDodge + character.GetDiminishedDodgeChance() diff --git a/sim/core/constants.go b/sim/core/constants.go index 73a532cfd4..f29fb823d2 100644 --- a/sim/core/constants.go +++ b/sim/core/constants.go @@ -9,7 +9,7 @@ const CharacterLevel = 80 const GCDMin = time.Second * 1 const GCDDefault = time.Millisecond * 1500 -const MeleeAttackRatingPerDamage = 14.0 +const DefaultAttackPowerPerDPS = 14.0 const ArmorPenPerPercentArmor = 13.99 const MissDodgeParryBlockCritChancePerDefense = 0.04 diff --git a/sim/core/item_swaps.go b/sim/core/item_swaps.go index f14aec33f1..4327fce764 100644 --- a/sim/core/item_swaps.go +++ b/sim/core/item_swaps.go @@ -117,7 +117,7 @@ func (swap *ItemSwap) SwapItems(sim *Simulation, slots []proto.ItemSlot, useGCD character := swap.character - meeleWeaponSwapped := false + meleeWeaponSwapped := false newStats := stats.Stats{} has2H := swap.GetItem(proto.ItemSlot_ItemSlotMainHand).HandType == proto.HandType_HandTypeTwoHand for _, slot := range slots { @@ -128,7 +128,7 @@ func (swap *ItemSwap) SwapItems(sim *Simulation, slots []proto.ItemSlot, useGCD if ok, swapStats := swap.swapItem(slot, has2H); ok { newStats = newStats.Add(swapStats) - meeleWeaponSwapped = slot == proto.ItemSlot_ItemSlotMainHand || slot == proto.ItemSlot_ItemSlotOffHand || meeleWeaponSwapped + meleeWeaponSwapped = slot == proto.ItemSlot_ItemSlotMainHand || slot == proto.ItemSlot_ItemSlotOffHand || meleeWeaponSwapped } } @@ -142,9 +142,9 @@ func (swap *ItemSwap) SwapItems(sim *Simulation, slots []proto.ItemSlot, useGCD onSwap(sim) } - if character.AutoAttacks.IsEnabled() && meeleWeaponSwapped && sim.CurrentTime > 0 { + if character.AutoAttacks.AutoSwingMelee && meleeWeaponSwapped && sim.CurrentTime > 0 { character.AutoAttacks.CancelAutoSwing(sim) - character.AutoAttacks.restartMelee(sim) + character.AutoAttacks.restartMelee(sim, false) } if useGCD { @@ -191,7 +191,7 @@ func (swap *ItemSwap) getItemStats(item Item) stats.Stats { func (swap *ItemSwap) swapWeapon(slot proto.ItemSlot) { character := swap.character - if !character.AutoAttacks.IsEnabled() { + if !character.AutoAttacks.AutoSwingMelee && !character.AutoAttacks.AutoSwingRanged { return } @@ -200,7 +200,7 @@ func (swap *ItemSwap) swapWeapon(slot proto.ItemSlot) { character.AutoAttacks.MH = character.WeaponFromMainHand(swap.mhCritMultiplier) case proto.ItemSlot_ItemSlotOffHand: character.AutoAttacks.OH = character.WeaponFromOffHand(swap.ohCritMultiplier) - //Special case for when the OHAuto Spell was setup with a non weapon and does not have a crit multiplier. + //Special case for when the OHAuto Spell was set up with a non weapon and does not have a crit multiplier. character.AutoAttacks.OHAuto.CritMultiplier = swap.ohCritMultiplier character.PseudoStats.CanBlock = character.OffHand().WeaponType == proto.WeaponType_WeaponTypeShield case proto.ItemSlot_ItemSlotRanged: diff --git a/sim/core/pending_action.go b/sim/core/pending_action.go index 3f33b87565..5cff7133fd 100644 --- a/sim/core/pending_action.go +++ b/sim/core/pending_action.go @@ -27,9 +27,9 @@ type PendingAction struct { Priority ActionPriority // Action to perform (required). - OnAction func(*Simulation) + OnAction func(sim *Simulation) // Cleanup when the action is cancelled (optional). - CleanUp func(*Simulation) + CleanUp func(sim *Simulation) cancelled bool consumed bool diff --git a/sim/core/target_ai.go b/sim/core/target_ai.go index e7a3b9bdec..8a722e252e 100644 --- a/sim/core/target_ai.go +++ b/sim/core/target_ai.go @@ -2,10 +2,8 @@ package core import ( "fmt" - "log" - "time" - "github.com/wowsims/wotlk/sim/core/proto" + "log" ) type TargetAI interface { @@ -25,7 +23,6 @@ func (target *Target) initialize(config *proto.Target) { MainHand: Weapon{ BaseDamageMin: config.MinBaseDamage, SwingSpeed: config.SwingSpeed, - SwingDuration: time.Duration(float64(time.Second) * config.SwingSpeed), CritMultiplier: 2, SpellSchool: SpellSchoolFromProto(config.SpellSchool), }, @@ -62,17 +59,17 @@ func (target *Target) initialize(config *proto.Target) { } // Empty Agent interface functions. -func (target *Target) AddRaidBuffs(raidBuffs *proto.RaidBuffs) {} -func (target *Target) AddPartyBuffs(partyBuffs *proto.PartyBuffs) {} -func (target *Target) ApplyTalents() {} -func (target *Target) GetCharacter() *Character { return nil } -func (target *Target) Initialize() {} +func (target *Target) AddRaidBuffs(_ *proto.RaidBuffs) {} +func (target *Target) AddPartyBuffs(_ *proto.PartyBuffs) {} +func (target *Target) ApplyTalents() {} +func (target *Target) GetCharacter() *Character { return nil } +func (target *Target) Initialize() {} func (target *Target) DoNothing() { target.doNothing = true } -func (target *Target) OnAutoAttack(sim *Simulation, spell *Spell) { +func (target *Target) OnAutoAttack(sim *Simulation, _ *Spell) { if target.GCD.IsReady(sim) { if target.AI != nil { target.AI.DoAction(sim) diff --git a/sim/core/unit.go b/sim/core/unit.go index e26092fbf2..0cc1c37215 100644 --- a/sim/core/unit.go +++ b/sim/core/unit.go @@ -251,7 +251,7 @@ func (unit *Unit) processDynamicBonus(sim *Simulation, bonus stats.Stats) { unit.UpdateManaRegenRates() } if bonus[stats.MeleeHaste] != 0 { - unit.AutoAttacks.UpdateSwingTime(sim) + unit.AutoAttacks.UpdateSwingTimers(sim) } if bonus[stats.SpellHaste] != 0 { unit.updateCastSpeed() @@ -353,18 +353,19 @@ func (unit *Unit) RangedSwingSpeed() float64 { // MultiplyMeleeSpeed will alter the attack speed multiplier and change swing speed of all autoattack swings in progress. func (unit *Unit) MultiplyMeleeSpeed(sim *Simulation, amount float64) { unit.PseudoStats.MeleeSpeedMultiplier *= amount - unit.AutoAttacks.UpdateSwingTime(sim) + unit.AutoAttacks.UpdateSwingTimers(sim) } func (unit *Unit) MultiplyRangedSpeed(sim *Simulation, amount float64) { unit.PseudoStats.RangedSpeedMultiplier *= amount + unit.AutoAttacks.UpdateSwingTimers(sim) } // Helper for when both MultiplyMeleeSpeed and MultiplyRangedSpeed are needed. func (unit *Unit) MultiplyAttackSpeed(sim *Simulation, amount float64) { unit.PseudoStats.MeleeSpeedMultiplier *= amount unit.PseudoStats.RangedSpeedMultiplier *= amount - unit.AutoAttacks.UpdateSwingTime(sim) + unit.AutoAttacks.UpdateSwingTimers(sim) } func (unit *Unit) AddBonusRangedHitRating(amount float64) { @@ -427,7 +428,7 @@ func (unit *Unit) init(sim *Simulation) { unit.auraTracker.init(sim) } -func (unit *Unit) reset(sim *Simulation, agent Agent) { +func (unit *Unit) reset(sim *Simulation, _ Agent) { unit.enabled = true unit.resetCDs(sim) unit.Hardcast.Expires = startingCDTime @@ -469,7 +470,7 @@ func (unit *Unit) startPull(sim *Simulation) { } // Advance moves time forward counting down auras, CDs, mana regen, etc -func (unit *Unit) advance(sim *Simulation, elapsedTime time.Duration) { +func (unit *Unit) advance(sim *Simulation, _ time.Duration) { unit.auraTracker.advance(sim) if hc := &unit.Hardcast; hc.Expires != startingCDTime && hc.Expires <= sim.CurrentTime { diff --git a/sim/deathknight/bloodworm_pet.go b/sim/deathknight/bloodworm_pet.go index 6c01fd731b..d7fc795b4e 100644 --- a/sim/deathknight/bloodworm_pet.go +++ b/sim/deathknight/bloodworm_pet.go @@ -1,8 +1,6 @@ package deathknight import ( - "time" - "github.com/wowsims/wotlk/sim/core" "github.com/wowsims/wotlk/sim/core/stats" ) @@ -13,7 +11,7 @@ type BloodwormPet struct { dkOwner *Deathknight } -func (dk *Deathknight) NewBloodwormPet(index int) *BloodwormPet { +func (dk *Deathknight) NewBloodwormPet(_ int) *BloodwormPet { bloodworm := &BloodwormPet{ Pet: core.NewPet("Bloodworm", &dk.Character, bloodwormPetBaseStats, dk.bloodwormStatInheritance(), nil, false, true), dkOwner: dk, @@ -24,7 +22,6 @@ func (dk *Deathknight) NewBloodwormPet(index int) *BloodwormPet { BaseDamageMin: 37, BaseDamageMax: 42, SwingSpeed: 2, - SwingDuration: time.Second * 2, CritMultiplier: 2, }, AutoSwingMelee: true, @@ -57,10 +54,10 @@ func (bloodworm *BloodwormPet) Initialize() { } -func (bloodworm *BloodwormPet) Reset(sim *core.Simulation) { +func (bloodworm *BloodwormPet) Reset(_ *core.Simulation) { } -func (bloodworm *BloodwormPet) OnGCDReady(sim *core.Simulation) { +func (bloodworm *BloodwormPet) OnGCDReady(_ *core.Simulation) { } func (bloodworm *BloodwormPet) enable(sim *core.Simulation) { diff --git a/sim/deathknight/dancing_rune_weapon.go b/sim/deathknight/dancing_rune_weapon.go index fffd4c1cb2..4709b13580 100644 --- a/sim/deathknight/dancing_rune_weapon.go +++ b/sim/deathknight/dancing_rune_weapon.go @@ -176,7 +176,6 @@ func (dk *Deathknight) NewRuneWeapon() *RuneWeaponPet { runeWeapon.AutoAttacks.MH.SwingSpeed = 3.5 runeWeapon.AutoAttacks.MH.NormalizedSwingSpeed = 3.3 - runeWeapon.AutoAttacks.MH.SwingDuration = time.Duration(runeWeapon.AutoAttacks.MH.SwingSpeed * float64(time.Second)) runeWeapon.PseudoStats.DamageTakenMultiplier = 0 if dk.Inputs.NewDrw { @@ -195,10 +194,10 @@ func (runeWeapon *RuneWeaponPet) GetPet() *core.Pet { return &runeWeapon.Pet } -func (runeWeapon *RuneWeaponPet) Reset(sim *core.Simulation) { +func (runeWeapon *RuneWeaponPet) Reset(_ *core.Simulation) { } -func (runeWeapon *RuneWeaponPet) OnGCDReady(sim *core.Simulation) { +func (runeWeapon *RuneWeaponPet) OnGCDReady(_ *core.Simulation) { // No GCD system on Rune Weapon runeWeapon.DoNothing() } diff --git a/sim/deathknight/dps/dps_deathknight.go b/sim/deathknight/dps/dps_deathknight.go index cfc2823de0..c00b7b64f7 100644 --- a/sim/deathknight/dps/dps_deathknight.go +++ b/sim/deathknight/dps/dps_deathknight.go @@ -71,13 +71,6 @@ func NewDpsDeathknight(character core.Character, player *proto.Player) *DpsDeath MainHand: dpsDk.WeaponFromMainHand(dpsDk.DefaultMeleeCritMultiplier()), OffHand: dpsDk.WeaponFromOffHand(dpsDk.DefaultMeleeCritMultiplier()), AutoSwingMelee: true, - // ReplaceMHSwing: func(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell { - // if dpsDk.RuneStrike.CanCast(sim, nil) { - // return dpsDk.RuneStrike - // } else { - // return nil - // } - // }, }) if dpsDk.Talents.SummonGargoyle && dpsDk.Rotation.UseGargoyle && dpsDk.Rotation.EnableWeaponSwap { diff --git a/sim/deathknight/ghoul_pet.go b/sim/deathknight/ghoul_pet.go index c3f4f886ff..bcf4e5a460 100644 --- a/sim/deathknight/ghoul_pet.go +++ b/sim/deathknight/ghoul_pet.go @@ -1,8 +1,6 @@ package deathknight import ( - "time" - "github.com/wowsims/wotlk/sim/core" "github.com/wowsims/wotlk/sim/core/proto" "github.com/wowsims/wotlk/sim/core/stats" @@ -22,7 +20,7 @@ type GhoulPet struct { ownerMeleeMultiplier float64 } -func (dk *Deathknight) NewArmyGhoulPet(index int) *GhoulPet { +func (dk *Deathknight) NewArmyGhoulPet(_ int) *GhoulPet { // Remove any hit that would be given by NocS as it does not translate to pets nocsHit := 0.0 if dk.nervesOfColdSteelActive() { @@ -56,12 +54,11 @@ func (dk *Deathknight) NewArmyGhoulPet(index int) *GhoulPet { ghoulPet.EnableAutoAttacks(ghoulPet, core.AutoAttackOptions{ MainHand: core.Weapon{ - BaseDamageMin: 30, - BaseDamageMax: 74, - SwingSpeed: 2, - SwingDuration: time.Second * 2, - CritMultiplier: 2, - MeleeAttackRatingPerDamage: 17.5, + BaseDamageMin: 30, + BaseDamageMax: 74, + SwingSpeed: 2, + CritMultiplier: 2, + AttackPowerPerDPS: 17.5, }, AutoSwingMelee: true, }) @@ -117,12 +114,11 @@ func (dk *Deathknight) NewGhoulPet(permanent bool) *GhoulPet { ghoulPet.EnableAutoAttacks(ghoulPet, core.AutoAttackOptions{ MainHand: core.Weapon{ - BaseDamageMin: 50, - BaseDamageMax: 90, - SwingSpeed: 2, - SwingDuration: time.Second * 2, - CritMultiplier: 2, - MeleeAttackRatingPerDamage: 17.5, + BaseDamageMin: 50, + BaseDamageMax: 90, + SwingSpeed: 2, + CritMultiplier: 2, + AttackPowerPerDPS: 17.5, }, AutoSwingMelee: true, }) diff --git a/sim/deathknight/tank/tank_deathknight.go b/sim/deathknight/tank/tank_deathknight.go index d647e6e079..d750e31879 100644 --- a/sim/deathknight/tank/tank_deathknight.go +++ b/sim/deathknight/tank/tank_deathknight.go @@ -51,10 +51,10 @@ func NewTankDeathknight(character core.Character, options *proto.Player) *TankDe OffHand: tankDk.WeaponFromOffHand(tankDk.DefaultMeleeCritMultiplier()), AutoSwingMelee: true, ReplaceMHSwing: func(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell { - if tankDk.RuneStrike.CanCast(sim, nil) && (!tankDk.IsUsingAPL || tankDk.RuneStrikeQueued) { + if (!tankDk.IsUsingAPL || tankDk.RuneStrikeQueued) && tankDk.RuneStrike.CanCast(sim, nil) { return tankDk.RuneStrike } else { - return nil + return mhSwingSpell } }, }) diff --git a/sim/druid/feral/feral.go b/sim/druid/feral/feral.go index 89a251971b..43342e6ce0 100644 --- a/sim/druid/feral/feral.go +++ b/sim/druid/feral/feral.go @@ -59,7 +59,7 @@ func NewFeralDruid(character core.Character, options *proto.Player) *FeralDruid AutoSwingMelee: true, }) cat.ReplaceBearMHFunc = func(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell { - return cat.checkReplaceMaul(sim) + return cat.checkReplaceMaul(sim, mhSwingSpell) } return cat diff --git a/sim/druid/feral/rotation.go b/sim/druid/feral/rotation.go index 8ba6658ba0..c8d66b02a3 100644 --- a/sim/druid/feral/rotation.go +++ b/sim/druid/feral/rotation.go @@ -56,7 +56,7 @@ func (cat *FeralDruid) OnGCDReady(sim *core.Simulation) { } } -func (cat *FeralDruid) OnAutoAttack(sim *core.Simulation, spell *core.Spell) { +func (cat *FeralDruid) OnAutoAttack(sim *core.Simulation, _ *core.Spell) { if cat.IsUsingAPL { return } @@ -92,12 +92,12 @@ func (cat *FeralDruid) NextRotationAction(sim *core.Simulation, kickAt time.Dura // Ported from https://github.com/NerdEgghead/WOTLK_cat_sim -func (cat *FeralDruid) checkReplaceMaul(sim *core.Simulation) *core.Spell { +func (cat *FeralDruid) checkReplaceMaul(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell { if cat.IsUsingAPL { - return nil + return mhSwingSpell } - // If we will have enough time and Energy leeway to stay in + // If we have enough time and Energy leeway to stay in // Dire Bear Form once the GCD expires, then only Maul if we // will be left with enough Rage to cast Mangle or Lacerate // on that global. @@ -144,7 +144,7 @@ func (cat *FeralDruid) checkReplaceMaul(sim *core.Simulation) *core.Spell { if cat.CurrentRage() >= maulRageThresh { return cat.Maul.Spell } else { - return nil + return mhSwingSpell } } diff --git a/sim/druid/force_of_nature.go b/sim/druid/force_of_nature.go index cb02adc2ec..f5809a62c3 100644 --- a/sim/druid/force_of_nature.go +++ b/sim/druid/force_of_nature.go @@ -76,7 +76,6 @@ func (druid *Druid) NewTreant() *TreantPet { BaseDamageMin: 252, BaseDamageMax: 357, SwingSpeed: 2, - SwingDuration: time.Second * 2, CritMultiplier: druid.BalanceCritMultiplier(), }, AutoSwingMelee: true, diff --git a/sim/druid/forms.go b/sim/druid/forms.go index c2b3f88a3a..53293dfbe4 100644 --- a/sim/druid/forms.go +++ b/sim/druid/forms.go @@ -1,12 +1,10 @@ package druid import ( - "math" - "time" - "github.com/wowsims/wotlk/sim/core" "github.com/wowsims/wotlk/sim/core/proto" "github.com/wowsims/wotlk/sim/core/stats" + "math" ) type DruidForm uint8 @@ -49,25 +47,23 @@ func (druid *Druid) ClearForm(sim *core.Simulation) { func (druid *Druid) GetCatWeapon() core.Weapon { return core.Weapon{ - BaseDamageMin: 43, - BaseDamageMax: 66, - SwingSpeed: 1.0, - NormalizedSwingSpeed: 1.0, - SwingDuration: time.Second, - CritMultiplier: druid.MeleeCritMultiplier(Cat), - MeleeAttackRatingPerDamage: core.MeleeAttackRatingPerDamage, + BaseDamageMin: 43, + BaseDamageMax: 66, + SwingSpeed: 1.0, + NormalizedSwingSpeed: 1.0, + CritMultiplier: druid.MeleeCritMultiplier(Cat), + AttackPowerPerDPS: core.DefaultAttackPowerPerDPS, } } func (druid *Druid) GetBearWeapon() core.Weapon { return core.Weapon{ - BaseDamageMin: 109, - BaseDamageMax: 165, - SwingSpeed: 2.5, - NormalizedSwingSpeed: 2.5, - SwingDuration: time.Millisecond * 2500, - CritMultiplier: druid.MeleeCritMultiplier(Bear), - MeleeAttackRatingPerDamage: core.MeleeAttackRatingPerDamage, + BaseDamageMin: 109, + BaseDamageMax: 165, + SwingSpeed: 2.5, + NormalizedSwingSpeed: 2.5, + CritMultiplier: druid.MeleeCritMultiplier(Bear), + AttackPowerPerDPS: core.DefaultAttackPowerPerDPS, } } @@ -139,6 +135,7 @@ func (druid *Druid) registerCatFormSpell() { druid.SetCurrentPowerBar(core.EnergyBar) druid.AutoAttacks.MH = clawWeapon + druid.AutoAttacks.UpdateMeleeDurations() druid.PseudoStats.ThreatMultiplier *= 0.71 druid.PseudoStats.SpiritRegenMultiplier *= AnimalSpiritRegenSuppression @@ -170,7 +167,9 @@ func (druid *Druid) registerCatFormSpell() { }, OnExpire: func(aura *core.Aura, sim *core.Simulation) { druid.form = Humanoid + druid.AutoAttacks.MH = druid.WeaponFromMainHand(druid.MeleeCritMultiplier(Humanoid)) + druid.AutoAttacks.UpdateMeleeDurations() druid.PseudoStats.ThreatMultiplier /= 0.71 druid.PseudoStats.SpiritRegenMultiplier /= AnimalSpiritRegenSuppression @@ -271,6 +270,8 @@ func (druid *Druid) registerBearFormSpell() { druid.SetCurrentPowerBar(core.RageBar) druid.AutoAttacks.MH = clawWeapon + druid.AutoAttacks.UpdateMeleeDurations() + druid.PseudoStats.ThreatMultiplier *= 2.1021 druid.PseudoStats.DamageDealtMultiplier *= 1.0 + 0.02*float64(druid.Talents.MasterShapeshifter) druid.PseudoStats.DamageTakenMultiplier *= potpdtm @@ -304,6 +305,7 @@ func (druid *Druid) registerBearFormSpell() { OnExpire: func(aura *core.Aura, sim *core.Simulation) { druid.form = Humanoid druid.AutoAttacks.MH = druid.WeaponFromMainHand(druid.MeleeCritMultiplier(Humanoid)) + druid.AutoAttacks.UpdateMeleeDurations() druid.PseudoStats.ThreatMultiplier /= 2.1021 druid.PseudoStats.DamageDealtMultiplier /= 1.0 + 0.02*float64(druid.Talents.MasterShapeshifter) diff --git a/sim/druid/maul.go b/sim/druid/maul.go index 7a0a148e29..7eba7599b3 100644 --- a/sim/druid/maul.go +++ b/sim/druid/maul.go @@ -103,22 +103,22 @@ func (druid *Druid) QueueMaul(sim *core.Simulation) { // Returns true if the regular melee swing should be used, false otherwise. func (druid *Druid) MaulReplaceMH(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell { if !druid.MaulQueueAura.IsActive() { - return nil + return mhSwingSpell } - if druid.CurrentRage() < druid.Maul.DefaultCast.Cost { + if druid.CurrentRage() < druid.MaulRageThreshold { druid.MaulQueueAura.Deactivate(sim) - return nil - } else if druid.CurrentRage() < druid.MaulRageThreshold { - if mhSwingSpell == druid.AutoAttacks.MHAuto { - druid.MaulQueueAura.Deactivate(sim) - return nil - } + return mhSwingSpell + } + + if !druid.Maul.Spell.CanCast(sim, druid.CurrentTarget) { + druid.MaulQueueAura.Deactivate(sim) + return mhSwingSpell } return druid.Maul.Spell } -func (druid *Druid) ShouldQueueMaul(sim *core.Simulation) bool { +func (druid *Druid) ShouldQueueMaul(_ *core.Simulation) bool { return druid.CurrentRage() >= druid.MaulRageThreshold } diff --git a/sim/hunter/TestBM.results b/sim/hunter/TestBM.results index 75a6f6a730..3ba85398ea 100644 --- a/sim/hunter/TestBM.results +++ b/sim/hunter/TestBM.results @@ -46,907 +46,907 @@ character_stats_results: { dps_results: { key: "TestBM-AllItems-Ahn'KaharBloodHunter'sBattlegear" value: { - dps: 6427.92138 - tps: 4454.51853 + dps: 6430.60948 + tps: 4462.81428 } } dps_results: { key: "TestBM-AllItems-Althor'sAbacus-50359" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-Althor'sAbacus-50366" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-AshtongueTalismanofSwiftness-32487" value: { - dps: 6176.73735 - tps: 4082.75315 + dps: 6172.49079 + tps: 4066.65692 } } dps_results: { key: "TestBM-AllItems-AustereEarthsiegeDiamond" value: { - dps: 6184.82109 - tps: 4085.62255 + dps: 6189.84348 + tps: 4077.72613 } } dps_results: { key: "TestBM-AllItems-Bandit'sInsignia-40371" value: { - dps: 6268.88891 - tps: 4166.70758 + dps: 6264.36623 + tps: 4150.37989 } } dps_results: { key: "TestBM-AllItems-BaubleofTrueBlood-50354" value: { - dps: 6115.08225 - tps: 4037.92093 + dps: 6110.44287 + tps: 4021.55623 hps: 92.00776 } } dps_results: { key: "TestBM-AllItems-BaubleofTrueBlood-50726" value: { - dps: 6115.08225 - tps: 4037.92093 + dps: 6110.44287 + tps: 4021.55623 hps: 92.00776 } } dps_results: { key: "TestBM-AllItems-BeamingEarthsiegeDiamond" value: { - dps: 6207.66233 - tps: 4112.6517 + dps: 6208.97708 + tps: 4089.41938 } } dps_results: { key: "TestBM-AllItems-Beast-tamer'sShoulders-30892" value: { - dps: 6073.2422 - tps: 4007.48047 + dps: 6062.97191 + tps: 4002.02078 } } dps_results: { key: "TestBM-AllItems-BlackBowoftheBetrayer-32336" value: { - dps: 5773.76093 - tps: 3676.98242 + dps: 5753.0558 + tps: 3662.00551 } } dps_results: { key: "TestBM-AllItems-BlackBruise-50035" value: { - dps: 5973.54965 - tps: 3916.58917 + dps: 5994.2142 + tps: 3931.42938 } } dps_results: { key: "TestBM-AllItems-BlackBruise-50692" value: { - dps: 5963.5975 - tps: 3909.39228 + dps: 5984.21826 + tps: 3924.19684 } } dps_results: { key: "TestBM-AllItems-BlessedGarboftheUndeadSlayer" value: { - dps: 5165.9398 - tps: 3408.11239 + dps: 5157.66381 + tps: 3392.77837 } } dps_results: { key: "TestBM-AllItems-BlessedRegaliaofUndeadCleansing" value: { - dps: 5003.13031 - tps: 3275.35224 + dps: 5010.24552 + tps: 3284.07215 } } dps_results: { key: "TestBM-AllItems-BracingEarthsiegeDiamond" value: { - dps: 6184.82109 - tps: 4004.95927 + dps: 6189.84348 + tps: 3997.22095 } } dps_results: { key: "TestBM-AllItems-Bryntroll,theBoneArbiter-50415" value: { - dps: 6289.84013 - tps: 4191.29907 + dps: 6284.72175 + tps: 4163.86842 } } dps_results: { key: "TestBM-AllItems-Bryntroll,theBoneArbiter-50709" value: { - dps: 6289.84013 - tps: 4191.29907 + dps: 6284.72175 + tps: 4163.86842 } } dps_results: { key: "TestBM-AllItems-ChaoticSkyflareDiamond" value: { - dps: 6282.47327 - tps: 4187.17711 + dps: 6280.96311 + tps: 4161.57265 } } dps_results: { key: "TestBM-AllItems-CorpseTongueCoin-50349" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-CorpseTongueCoin-50352" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-CorrodedSkeletonKey-50356" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 hps: 64 } } dps_results: { key: "TestBM-AllItems-CryptstalkerBattlegear" value: { - dps: 5724.68274 - tps: 3749.96193 + dps: 5731.84077 + tps: 3761.00352 } } dps_results: { key: "TestBM-AllItems-DarkmoonCard:Berserker!-42989" value: { - dps: 6189.82327 - tps: 4104.74645 + dps: 6176.49382 + tps: 4083.06025 } } dps_results: { key: "TestBM-AllItems-DarkmoonCard:Death-42990" value: { - dps: 6235.16521 - tps: 4155.75925 + dps: 6230.44491 + tps: 4140.92156 } } dps_results: { key: "TestBM-AllItems-DarkmoonCard:Greatness-44255" value: { - dps: 6257.00613 - tps: 4148.56953 + dps: 6259.71724 + tps: 4145.39512 } } dps_results: { key: "TestBM-AllItems-Death'sChoice-47464" value: { - dps: 6405.97419 - tps: 4273.18748 + dps: 6414.77522 + tps: 4260.39337 } } dps_results: { key: "TestBM-AllItems-DeathKnight'sAnguish-38212" value: { - dps: 6164.6684 - tps: 4088.98001 + dps: 6156.62706 + tps: 4066.15729 } } dps_results: { key: "TestBM-AllItems-Deathbringer'sWill-50362" value: { - dps: 6450.37803 - tps: 4332.54336 + dps: 6436.92672 + tps: 4310.24998 } } dps_results: { key: "TestBM-AllItems-Deathbringer'sWill-50363" value: { - dps: 6489.16415 - tps: 4368.95874 + dps: 6473.38342 + tps: 4352.55927 } } dps_results: { key: "TestBM-AllItems-Defender'sCode-40257" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-DestructiveSkyflareDiamond" value: { - dps: 6206.57293 - tps: 4111.08807 + dps: 6207.20626 + tps: 4087.6877 } } dps_results: { key: "TestBM-AllItems-DislodgedForeignObject-50348" value: { - dps: 6175.71432 - tps: 4094.51404 + dps: 6190.20706 + tps: 4117.68152 } } dps_results: { key: "TestBM-AllItems-DislodgedForeignObject-50353" value: { - dps: 6165.73871 - tps: 4089.55522 + dps: 6168.41023 + tps: 4087.37038 } } dps_results: { key: "TestBM-AllItems-EffulgentSkyflareDiamond" value: { - dps: 6184.82109 - tps: 4085.62255 + dps: 6189.84348 + tps: 4077.72613 } } dps_results: { key: "TestBM-AllItems-EmberSkyflareDiamond" value: { - dps: 6194.6621 - tps: 4094.04522 + dps: 6199.01857 + tps: 4085.30306 } } dps_results: { key: "TestBM-AllItems-EnigmaticSkyflareDiamond" value: { - dps: 6201.69699 - tps: 4106.40083 + dps: 6201.26834 + tps: 4081.87788 } } dps_results: { key: "TestBM-AllItems-EnigmaticStarflareDiamond" value: { - dps: 6200.68831 - tps: 4104.98234 + dps: 6197.07562 + tps: 4079.06355 } } dps_results: { key: "TestBM-AllItems-EphemeralSnowflake-50260" value: { - dps: 6141.72616 - tps: 4069.08609 + dps: 6147.54012 + tps: 4080.58107 } } dps_results: { key: "TestBM-AllItems-EssenceofGossamer-37220" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-EternalEarthsiegeDiamond" value: { - dps: 6184.82109 - tps: 4085.62255 + dps: 6189.84348 + tps: 4077.72613 } } dps_results: { key: "TestBM-AllItems-ExtractofNecromanticPower-40373" value: { - dps: 6214.40574 - tps: 4128.22237 + dps: 6206.97348 + tps: 4115.66367 } } dps_results: { key: "TestBM-AllItems-EyeoftheBroodmother-45308" value: { - dps: 6176.96197 - tps: 4094.84142 + dps: 6171.0574 + tps: 4079.83524 } } dps_results: { key: "TestBM-AllItems-Figurine-SapphireOwl-42413" value: { - dps: 6145.67951 - tps: 4063.18799 + dps: 6140.1 + tps: 4045.59432 } } dps_results: { key: "TestBM-AllItems-ForethoughtTalisman-40258" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-ForgeEmber-37660" value: { - dps: 6163.4393 - tps: 4079.42262 + dps: 6156.86838 + tps: 4067.41335 } } dps_results: { key: "TestBM-AllItems-ForlornSkyflareDiamond" value: { - dps: 6184.82109 - tps: 4085.62255 + dps: 6189.84348 + tps: 4077.72613 } } dps_results: { key: "TestBM-AllItems-ForlornStarflareDiamond" value: { - dps: 6184.82109 - tps: 4085.62255 + dps: 6189.84348 + tps: 4077.72613 } } dps_results: { key: "TestBM-AllItems-FuryoftheFiveFlights-40431" value: { - dps: 6264.60934 - tps: 4146.71366 + dps: 6260.09906 + tps: 4130.13967 } } dps_results: { key: "TestBM-AllItems-FuturesightRune-38763" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-Gladiator'sPursuit" value: { - dps: 6008.48357 - tps: 4088.03646 + dps: 5999.81565 + tps: 4073.00616 } } dps_results: { key: "TestBM-AllItems-GlowingTwilightScale-54573" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-GlowingTwilightScale-54589" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-GnomishLightningGenerator-41121" value: { - dps: 6210.76242 - tps: 4131.33849 + dps: 6205.90568 + tps: 4116.35069 } } dps_results: { key: "TestBM-AllItems-Gronnstalker'sArmor" value: { - dps: 4826.21606 - tps: 3131.24996 + dps: 4826.55577 + tps: 3133.37978 } } dps_results: { key: "TestBM-AllItems-Heartpierce-49982" value: { - dps: 6335.18193 - tps: 4224.45609 + dps: 6330.00574 + tps: 4196.84209 } } dps_results: { key: "TestBM-AllItems-Heartpierce-50641" value: { - dps: 6336.15702 - tps: 4225.16915 + dps: 6330.97959 + tps: 4197.5512 } } dps_results: { key: "TestBM-AllItems-IllustrationoftheDragonSoul-40432" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-ImpassiveSkyflareDiamond" value: { - dps: 6201.69699 - tps: 4106.40083 + dps: 6201.26834 + tps: 4081.87788 } } dps_results: { key: "TestBM-AllItems-ImpassiveStarflareDiamond" value: { - dps: 6200.68831 - tps: 4104.98234 + dps: 6197.07562 + tps: 4079.06355 } } dps_results: { key: "TestBM-AllItems-IncisorFragment-37723" value: { - dps: 6256.37302 - tps: 4159.83991 + dps: 6251.59056 + tps: 4143.12575 } } dps_results: { key: "TestBM-AllItems-InsightfulEarthsiegeDiamond" value: { - dps: 6213.37038 - tps: 4117.25807 + dps: 6214.86711 + tps: 4105.86008 } } dps_results: { key: "TestBM-AllItems-InvigoratingEarthsiegeDiamond" value: { - dps: 6205.90114 - tps: 4101.24958 - hps: 11.19844 + dps: 6211.25555 + tps: 4093.11478 + hps: 11.12548 } } dps_results: { key: "TestBM-AllItems-Lavanthor'sTalisman-37872" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-MajesticDragonFigurine-40430" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-MeteoriteWhetstone-37390" value: { - dps: 6197.59835 - tps: 4112.07008 + dps: 6205.51321 + tps: 4121.70118 } } dps_results: { key: "TestBM-AllItems-NevermeltingIceCrystal-50259" value: { - dps: 6223.52856 - tps: 4140.86868 + dps: 6204.63211 + tps: 4122.20467 } } dps_results: { key: "TestBM-AllItems-OfferingofSacrifice-37638" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-PersistentEarthshatterDiamond" value: { - dps: 6201.11619 - tps: 4097.45574 + dps: 6206.16725 + tps: 4089.56131 } } dps_results: { key: "TestBM-AllItems-PersistentEarthsiegeDiamond" value: { - dps: 6204.95033 - tps: 4100.24002 + dps: 6210.00814 + tps: 4092.34606 } } dps_results: { key: "TestBM-AllItems-PetrifiedScarab-21685" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-PetrifiedTwilightScale-54571" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-PetrifiedTwilightScale-54591" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-PowerfulEarthshatterDiamond" value: { - dps: 6184.82109 - tps: 4085.62255 + dps: 6189.84348 + tps: 4077.72613 } } dps_results: { key: "TestBM-AllItems-PowerfulEarthsiegeDiamond" value: { - dps: 6184.82109 - tps: 4085.62255 + dps: 6189.84348 + tps: 4077.72613 } } dps_results: { key: "TestBM-AllItems-PurifiedShardoftheGods" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-ReignoftheDead-47316" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-ReignoftheDead-47477" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-RelentlessEarthsiegeDiamond" value: { - dps: 6289.84013 - tps: 4191.29907 + dps: 6284.72175 + tps: 4163.86842 } } dps_results: { key: "TestBM-AllItems-RevitalizingSkyflareDiamond" value: { - dps: 6189.71516 - tps: 4090.17008 + dps: 6193.18139 + tps: 4080.75089 } } dps_results: { key: "TestBM-AllItems-RuneofRepulsion-40372" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-ScourgestalkerBattlegear" value: { - dps: 6155.43295 - tps: 4108.34572 + dps: 6146.02396 + tps: 4106.23907 } } dps_results: { key: "TestBM-AllItems-SealofthePantheon-36993" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-Shadowmourne-49623" value: { - dps: 6490.76004 - tps: 4383.10962 + dps: 6493.34829 + tps: 4379.73722 } } dps_results: { key: "TestBM-AllItems-ShinyShardoftheGods" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-Sindragosa'sFlawlessFang-50361" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-SliverofPureIce-50339" value: { - dps: 6130.55897 - tps: 4058.10018 + dps: 6124.51987 + tps: 4039.91887 } } dps_results: { key: "TestBM-AllItems-SliverofPureIce-50346" value: { - dps: 6131.81399 - tps: 4059.87408 + dps: 6126.01836 + tps: 4041.93623 } } dps_results: { key: "TestBM-AllItems-SoulPreserver-37111" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-SouloftheDead-40382" value: { - dps: 6182.36628 - tps: 4096.3567 + dps: 6174.5744 + tps: 4083.55112 } } dps_results: { key: "TestBM-AllItems-SparkofLife-37657" value: { - dps: 6141.21099 - tps: 4059.54431 + dps: 6155.03367 + tps: 4064.96946 } } dps_results: { key: "TestBM-AllItems-SphereofRedDragon'sBlood-37166" value: { - dps: 6239.1761 - tps: 4100.48542 + dps: 6233.62006 + tps: 4099.84603 } } dps_results: { key: "TestBM-AllItems-StormshroudArmor" value: { - dps: 4862.96503 - tps: 3143.35809 + dps: 4872.86861 + tps: 3163.52785 } } dps_results: { key: "TestBM-AllItems-SwiftSkyflareDiamond" value: { - dps: 6204.95033 - tps: 4100.24002 + dps: 6210.00814 + tps: 4092.34606 } } dps_results: { key: "TestBM-AllItems-SwiftStarflareDiamond" value: { - dps: 6201.11619 - tps: 4097.45574 + dps: 6206.16725 + tps: 4089.56131 } } dps_results: { key: "TestBM-AllItems-SwiftWindfireDiamond" value: { - dps: 6194.40644 - tps: 4092.58325 + dps: 6199.4457 + tps: 4084.688 } } dps_results: { key: "TestBM-AllItems-TalismanofTrollDivinity-37734" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-TearsoftheVanquished-47215" value: { - dps: 6172.90985 - tps: 4083.14426 + dps: 6166.47993 + tps: 4064.57412 } } dps_results: { key: "TestBM-AllItems-TheFistsofFury" value: { - dps: 6001.00312 - tps: 3939.3207 + dps: 6019.24932 + tps: 3953.00966 } } dps_results: { key: "TestBM-AllItems-TheGeneral'sHeart-45507" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-TheTwinBladesofAzzinoth" value: { - dps: 6148.33875 - tps: 4063.66805 + dps: 6139.80315 + tps: 4055.95006 } } dps_results: { key: "TestBM-AllItems-ThunderingSkyflareDiamond" value: { - dps: 6209.24898 - tps: 4093.24049 + dps: 6201.75171 + tps: 4096.32299 } } dps_results: { key: "TestBM-AllItems-TinyAbominationinaJar-50351" value: { - dps: 6159.30697 - tps: 4043.22612 + dps: 6154.00385 + tps: 4042.58673 } } dps_results: { key: "TestBM-AllItems-TinyAbominationinaJar-50706" value: { - dps: 6159.30697 - tps: 4043.22612 + dps: 6154.00385 + tps: 4042.58673 } } dps_results: { key: "TestBM-AllItems-TirelessSkyflareDiamond" value: { - dps: 6184.82109 - tps: 4085.62255 + dps: 6189.84348 + tps: 4077.72613 } } dps_results: { key: "TestBM-AllItems-TirelessStarflareDiamond" value: { - dps: 6184.82109 - tps: 4085.62255 + dps: 6189.84348 + tps: 4077.72613 } } dps_results: { key: "TestBM-AllItems-TomeofArcanePhenomena-36972" value: { - dps: 6151.04116 - tps: 4070.44847 + dps: 6134.58586 + tps: 4053.01247 } } dps_results: { key: "TestBM-AllItems-TrenchantEarthshatterDiamond" value: { - dps: 6184.82109 - tps: 4085.62255 + dps: 6189.84348 + tps: 4077.72613 } } dps_results: { key: "TestBM-AllItems-TrenchantEarthsiegeDiamond" value: { - dps: 6184.82109 - tps: 4085.62255 + dps: 6189.84348 + tps: 4077.72613 } } dps_results: { key: "TestBM-AllItems-UndeadSlayer'sBlessedArmor" value: { - dps: 5092.79832 - tps: 3341.85349 + dps: 5095.50417 + tps: 3340.43113 } } dps_results: { key: "TestBM-AllItems-Val'anyr,HammerofAncientKings-46017" value: { - dps: 6074.55791 - tps: 4016.43427 + dps: 6076.22112 + tps: 4019.31575 } } dps_results: { key: "TestBM-AllItems-Windrunner'sPursuit" value: { - dps: 6263.73876 - tps: 4210.83312 + dps: 6285.83794 + tps: 4240.88252 } } dps_results: { key: "TestBM-AllItems-WingedTalisman-37844" value: { - dps: 6115.00344 - tps: 4037.92093 + dps: 6110.45978 + tps: 4021.55623 } } dps_results: { key: "TestBM-AllItems-Zod'sRepeatingLongbow-50034" value: { - dps: 6677.17253 - tps: 4581.63779 + dps: 6677.17643 + tps: 4597.13156 } } dps_results: { key: "TestBM-AllItems-Zod'sRepeatingLongbow-50638" value: { - dps: 6892.97042 - tps: 4805.3936 + dps: 6876.74098 + tps: 4791.07846 } } dps_results: { key: "TestBM-Average-Default" value: { - dps: 6222.25284 - tps: 4122.27208 + dps: 6222.64804 + tps: 4123.73162 } } dps_results: { key: "TestBM-Settings-Dwarf-P1-BM-FullBuffs-LongMultiTarget" value: { - dps: 6842.50543 - tps: 5848.97452 + dps: 6845.8155 + tps: 5845.40652 } } dps_results: { key: "TestBM-Settings-Dwarf-P1-BM-FullBuffs-LongSingleTarget" value: { - dps: 6195.54417 - tps: 4196.21767 + dps: 6194.78462 + tps: 4185.55652 } } dps_results: { key: "TestBM-Settings-Dwarf-P1-BM-FullBuffs-ShortSingleTarget" value: { - dps: 7407.75668 - tps: 4938.46472 + dps: 7410.33732 + tps: 4922.58113 } } dps_results: { key: "TestBM-Settings-Dwarf-P1-BM-NoBuffs-LongMultiTarget" value: { - dps: 3468.60274 - tps: 4320.19411 + dps: 3451.89812 + tps: 4306.02312 } } dps_results: { key: "TestBM-Settings-Dwarf-P1-BM-NoBuffs-LongSingleTarget" value: { - dps: 3011.47165 - tps: 2355.35463 + dps: 3002.99743 + tps: 2351.25841 } } dps_results: { key: "TestBM-Settings-Dwarf-P1-BM-NoBuffs-ShortSingleTarget" value: { - dps: 3519.51814 - tps: 2740.86768 + dps: 3521.75427 + tps: 2736.47636 } } dps_results: { key: "TestBM-Settings-Orc-P1-BM-FullBuffs-LongMultiTarget" value: { - dps: 6947.21574 - tps: 5841.97299 + dps: 6946.78131 + tps: 5845.03148 } } dps_results: { key: "TestBM-Settings-Orc-P1-BM-FullBuffs-LongSingleTarget" value: { - dps: 6289.84013 - tps: 4191.29907 + dps: 6284.72175 + tps: 4163.86842 } } dps_results: { key: "TestBM-Settings-Orc-P1-BM-FullBuffs-ShortSingleTarget" value: { - dps: 7572.07399 - tps: 4959.7574 + dps: 7570.43523 + tps: 4940.69444 } } dps_results: { key: "TestBM-Settings-Orc-P1-BM-NoBuffs-LongMultiTarget" value: { - dps: 3496.63561 - tps: 4310.93305 + dps: 3491.37097 + tps: 4310.44333 } } dps_results: { key: "TestBM-Settings-Orc-P1-BM-NoBuffs-LongSingleTarget" value: { - dps: 3047.6321 - tps: 2355.51769 + dps: 3041.82941 + tps: 2347.98207 } } dps_results: { key: "TestBM-Settings-Orc-P1-BM-NoBuffs-ShortSingleTarget" value: { - dps: 3566.6841 - tps: 2740.75773 + dps: 3574.18477 + tps: 2740.69022 } } dps_results: { key: "TestBM-SwitchInFrontOfTarget-Default" value: { - dps: 6120.45849 - tps: 4169.46044 + dps: 6126.27821 + tps: 4179.1492 } } diff --git a/sim/hunter/aspects.go b/sim/hunter/aspects.go index 211a536a4f..ea44433ec6 100644 --- a/sim/hunter/aspects.go +++ b/sim/hunter/aspects.go @@ -21,10 +21,10 @@ func (hunter *Hunter) registerAspectOfTheDragonhawkSpell() { ActionID: core.ActionID{SpellID: 19556}, Duration: time.Second * 12, OnGain: func(aura *core.Aura, sim *core.Simulation) { - aura.Unit.PseudoStats.RangedSpeedMultiplier *= improvedHawkBonus + aura.Unit.MultiplyRangedSpeed(sim, improvedHawkBonus) }, OnExpire: func(aura *core.Aura, sim *core.Simulation) { - aura.Unit.PseudoStats.RangedSpeedMultiplier /= improvedHawkBonus + aura.Unit.MultiplyRangedSpeed(sim, 1/improvedHawkBonus) }, }) } @@ -91,7 +91,7 @@ func (hunter *Hunter) registerAspectOfTheViperSpell() { OnGain: func(aura *core.Aura, sim *core.Simulation) { aura.Unit.PseudoStats.DamageDealtMultiplier *= damagePenalty if hasCryptstalker4pc { - aura.Unit.PseudoStats.RangedSpeedMultiplier *= 1.2 + aura.Unit.MultiplyRangedSpeed(sim, 1.2) } tickPA = core.StartPeriodicAction(sim, core.PeriodicActionOptions{ @@ -104,7 +104,7 @@ func (hunter *Hunter) registerAspectOfTheViperSpell() { OnExpire: func(aura *core.Aura, sim *core.Simulation) { aura.Unit.PseudoStats.DamageDealtMultiplier /= damagePenalty if hasCryptstalker4pc { - aura.Unit.PseudoStats.RangedSpeedMultiplier /= 1.2 + aura.Unit.MultiplyRangedSpeed(sim, 1/1.2) } tickPA.Cancel(sim) tickPA = nil diff --git a/sim/hunter/hunter.go b/sim/hunter/hunter.go index 40df017df3..101740bf52 100644 --- a/sim/hunter/hunter.go +++ b/sim/hunter/hunter.go @@ -112,7 +112,7 @@ func (hunter *Hunter) AddRaidBuffs(raidBuffs *proto.RaidBuffs) { raidBuffs.FerociousInspiration = true } } -func (hunter *Hunter) AddPartyBuffs(partyBuffs *proto.PartyBuffs) { +func (hunter *Hunter) AddPartyBuffs(_ *proto.PartyBuffs) { } func (hunter *Hunter) Initialize() { @@ -164,7 +164,7 @@ func (hunter *Hunter) Initialize() { } } -func (hunter *Hunter) Reset(sim *core.Simulation) { +func (hunter *Hunter) Reset(_ *core.Simulation) { hunter.mayMoveAt = 0 hunter.manaSpentPerSecondAtFirstAspectSwap = 0 hunter.permaHawk = false @@ -216,8 +216,8 @@ func NewHunter(character core.Character, options *proto.Player) *Hunter { MainHand: hunter.WeaponFromMainHand(0), OffHand: hunter.WeaponFromOffHand(0), Ranged: rangedWeapon, - ReplaceMHSwing: func(sim *core.Simulation, _ *core.Spell) *core.Spell { - return hunter.TryRaptorStrike(sim) + ReplaceMHSwing: func(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell { + return hunter.TryRaptorStrike(sim, mhSwingSpell) }, AutoSwingRanged: true, }) diff --git a/sim/hunter/pet.go b/sim/hunter/pet.go index 4b7bc50683..0d5922e005 100644 --- a/sim/hunter/pet.go +++ b/sim/hunter/pet.go @@ -52,7 +52,6 @@ func (hunter *Hunter) NewHunterPet() *HunterPet { BaseDamageMin: 50, BaseDamageMax: 78, SwingSpeed: atkSpd, - SwingDuration: core.DurationFromSeconds(atkSpd), CritMultiplier: 2, }, AutoSwingMelee: true, @@ -89,7 +88,7 @@ func (hp *HunterPet) Initialize() { hp.focusDump = hp.NewPetAbility(hp.config.FocusDump, false) } -func (hp *HunterPet) Reset(sim *core.Simulation) { +func (hp *HunterPet) Reset(_ *core.Simulation) { hp.uptimePercent = core.MinFloat(1, core.MaxFloat(0, hp.hunterOwner.Options.PetUptime)) } diff --git a/sim/hunter/rapid_fire.go b/sim/hunter/rapid_fire.go index 0700b66d2d..b52876b6f2 100644 --- a/sim/hunter/rapid_fire.go +++ b/sim/hunter/rapid_fire.go @@ -22,7 +22,7 @@ func (hunter *Hunter) registerRapidFireCD() { ActionID: actionID, Duration: time.Second * 15, OnGain: func(aura *core.Aura, sim *core.Simulation) { - aura.Unit.PseudoStats.RangedSpeedMultiplier *= hasteMultiplier + aura.Unit.MultiplyRangedSpeed(sim, hasteMultiplier) if manaMetrics != nil { manaPerTick := 0.02 * float64(hunter.Talents.RapidRecuperation) * hunter.MaxMana() @@ -36,7 +36,7 @@ func (hunter *Hunter) registerRapidFireCD() { } }, OnExpire: func(aura *core.Aura, sim *core.Simulation) { - aura.Unit.PseudoStats.RangedSpeedMultiplier /= hasteMultiplier + aura.Unit.MultiplyRangedSpeed(sim, 1/hasteMultiplier) }, }) diff --git a/sim/hunter/raptor_strike.go b/sim/hunter/raptor_strike.go index e74d457449..a4186ac1ea 100644 --- a/sim/hunter/raptor_strike.go +++ b/sim/hunter/raptor_strike.go @@ -41,10 +41,9 @@ func (hunter *Hunter) registerRaptorStrikeSpell() { } // Returns true if the regular melee swing should be used, false otherwise. -func (hunter *Hunter) TryRaptorStrike(sim *core.Simulation) *core.Spell { +func (hunter *Hunter) TryRaptorStrike(_ *core.Simulation, mhSwingSpell *core.Spell) *core.Spell { //if hunter.Rotation.Weave == proto.Hunter_Rotation_WeaveAutosOnly || !hunter.RaptorStrike.IsReady(sim) || hunter.CurrentMana() < hunter.RaptorStrike.DefaultCast.Cost { // return nil //} - - return hunter.RaptorStrike + return mhSwingSpell } diff --git a/sim/hunter/talents.go b/sim/hunter/talents.go index 973a551d70..f1a1900fdf 100644 --- a/sim/hunter/talents.go +++ b/sim/hunter/talents.go @@ -374,10 +374,10 @@ func (hunter *Hunter) applyFrenzy() { ActionID: core.ActionID{SpellID: 19625}, Duration: time.Second * 8, OnGain: func(aura *core.Aura, sim *core.Simulation) { - aura.Unit.PseudoStats.MeleeSpeedMultiplier *= 1.3 + aura.Unit.MultiplyMeleeSpeed(sim, 1.3) }, OnExpire: func(aura *core.Aura, sim *core.Simulation) { - aura.Unit.PseudoStats.MeleeSpeedMultiplier /= 1.3 + aura.Unit.MultiplyMeleeSpeed(sim, 1/1.3) }, }) diff --git a/sim/paladin/retribution/rotation.go b/sim/paladin/retribution/rotation.go index 18ef77ab8e..c8d2a1cc62 100644 --- a/sim/paladin/retribution/rotation.go +++ b/sim/paladin/retribution/rotation.go @@ -8,7 +8,7 @@ import ( "github.com/wowsims/wotlk/sim/core/proto" ) -func (ret *RetributionPaladin) OnAutoAttack(sim *core.Simulation, spell *core.Spell) { +func (ret *RetributionPaladin) OnAutoAttack(sim *core.Simulation, _ *core.Spell) { if ret.SealOfVengeanceAura.IsActive() && core.MinInt32(ret.MaxSoVTargets, ret.Env.GetNumTargets()) > 1 { minVengeanceDotDuration := time.Second * 15 var minVengeanceDotDurationTarget *core.Unit @@ -31,7 +31,7 @@ func (ret *RetributionPaladin) OnAutoAttack(sim *core.Simulation, spell *core.Sp } } - if minVengeanceDotDuration < ret.WeaponFromMainHand(0).SwingDuration*2 { + if minVengeanceDotDuration < core.DurationFromSeconds(ret.AutoAttacks.MH.SwingSpeed*2) { ret.CurrentTarget = minVengeanceDotDurationTarget } else if ret.SovDotSpell.Dot(ret.CurrentTarget).GetStacks() == 5 && minVengeanceDotStacks < 5 { ret.CurrentTarget = minVengeanceDotStacksTarget diff --git a/sim/priest/shadowfiend_pet.go b/sim/priest/shadowfiend_pet.go index 8d6e5be185..38f20e5eb6 100644 --- a/sim/priest/shadowfiend_pet.go +++ b/sim/priest/shadowfiend_pet.go @@ -69,7 +69,6 @@ func (priest *Priest) NewShadowfiend() *Shadowfiend { BaseDamageMax: 210, SwingSpeed: 1.5, NormalizedSwingSpeed: 1.5, - SwingDuration: time.Millisecond * 1500, CritMultiplier: 2, SpellSchool: core.SpellSchoolShadow, }, @@ -101,7 +100,7 @@ func (priest *Priest) shadowfiendStatInheritance() core.PetStatInheritance { } } -func (shadowfiend *Shadowfiend) OnAutoAttack(sim *core.Simulation, spell *core.Spell) { +func (shadowfiend *Shadowfiend) OnAutoAttack(sim *core.Simulation, _ *core.Spell) { priest := shadowfiend.Priest restoreMana := priest.MaxMana() * 0.05 diff --git a/sim/shaman/enhancement/TestEnhancement.results b/sim/shaman/enhancement/TestEnhancement.results index 24d4c96196..23326c7a70 100644 --- a/sim/shaman/enhancement/TestEnhancement.results +++ b/sim/shaman/enhancement/TestEnhancement.results @@ -46,747 +46,747 @@ character_stats_results: { dps_results: { key: "TestEnhancement-AllItems-Althor'sAbacus-50359" value: { - dps: 7613.99397 - tps: 4172.1063 + dps: 7599.43763 + tps: 4165.31745 } } dps_results: { key: "TestEnhancement-AllItems-Althor'sAbacus-50366" value: { - dps: 7634.38243 - tps: 4183.84949 + dps: 7619.79234 + tps: 4177.05199 } } dps_results: { key: "TestEnhancement-AllItems-AustereEarthsiegeDiamond" value: { - dps: 7522.36758 - tps: 4128.61636 + dps: 7497.27676 + tps: 4116.54284 } } dps_results: { key: "TestEnhancement-AllItems-Bandit'sInsignia-40371" value: { - dps: 7613.34362 - tps: 4177.30108 + dps: 7618.92015 + tps: 4182.33393 } } dps_results: { key: "TestEnhancement-AllItems-BaubleofTrueBlood-50354" value: { - dps: 7447.09954 - tps: 4076.00702 - hps: 95.1095 + dps: 7432.80009 + tps: 4069.3606 + hps: 94.81599 } } dps_results: { key: "TestEnhancement-AllItems-BaubleofTrueBlood-50726" value: { - dps: 7447.09954 - tps: 4076.00702 - hps: 95.1095 + dps: 7432.80009 + tps: 4069.3606 + hps: 94.81599 } } dps_results: { key: "TestEnhancement-AllItems-BeamingEarthsiegeDiamond" value: { - dps: 7513.49854 - tps: 4117.06773 + dps: 7525.06647 + tps: 4131.21344 } } dps_results: { key: "TestEnhancement-AllItems-Beast-tamer'sShoulders-30892" value: { - dps: 7286.34469 - tps: 3984.14198 + dps: 7305.04217 + tps: 3992.19613 } } dps_results: { key: "TestEnhancement-AllItems-Bizuri'sTotemofShatteredIce-50458" value: { - dps: 7920.51187 - tps: 4373.30363 + dps: 7898.59888 + tps: 4356.68848 } } dps_results: { key: "TestEnhancement-AllItems-BlackBruise-50035" value: { - dps: 7489.44038 - tps: 4100.07408 + dps: 7485.5139 + tps: 4099.91392 } } dps_results: { key: "TestEnhancement-AllItems-BlackBruise-50692" value: { - dps: 7558.44387 - tps: 4139.24594 + dps: 7546.19747 + tps: 4133.63317 } } dps_results: { key: "TestEnhancement-AllItems-BlessedGarboftheUndeadSlayer" value: { - dps: 6192.12097 - tps: 3366.0143 + dps: 6146.56001 + tps: 3337.27192 } } dps_results: { key: "TestEnhancement-AllItems-BlessedRegaliaofUndeadCleansing" value: { - dps: 6100.67815 - tps: 3318.79635 + dps: 6111.44949 + tps: 3330.62396 } } dps_results: { key: "TestEnhancement-AllItems-BracingEarthsiegeDiamond" value: { - dps: 7545.69985 - tps: 4027.51803 + dps: 7520.5369 + tps: 4015.77431 } } dps_results: { key: "TestEnhancement-AllItems-Bryntroll,theBoneArbiter-50415" value: { - dps: 7700.58349 - tps: 4236.83571 + dps: 7672.52563 + tps: 4221.33363 } } dps_results: { key: "TestEnhancement-AllItems-Bryntroll,theBoneArbiter-50709" value: { - dps: 7700.58349 - tps: 4236.83571 + dps: 7672.52563 + tps: 4221.33363 } } dps_results: { key: "TestEnhancement-AllItems-ChaoticSkyflareDiamond" value: { - dps: 7713.75424 - tps: 4242.06557 + dps: 7698.53072 + tps: 4235.65353 } } dps_results: { key: "TestEnhancement-AllItems-CorpseTongueCoin-50349" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-CorpseTongueCoin-50352" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-CorrodedSkeletonKey-50356" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 hps: 64 } } dps_results: { key: "TestEnhancement-AllItems-DarkmoonCard:Berserker!-42989" value: { - dps: 7591.51569 - tps: 4163.47921 + dps: 7542.95169 + tps: 4138.66033 } } dps_results: { key: "TestEnhancement-AllItems-DarkmoonCard:Death-42990" value: { - dps: 7597.65011 - tps: 4177.52106 + dps: 7617.31613 + tps: 4188.8891 } } dps_results: { key: "TestEnhancement-AllItems-DarkmoonCard:Greatness-44255" value: { - dps: 7664.15464 - tps: 4192.71248 + dps: 7630.97415 + tps: 4173.2754 } } dps_results: { key: "TestEnhancement-AllItems-DeadlyGladiator'sTotemofSurvival-42602" value: { - dps: 7706.6126 - tps: 4235.42825 + dps: 7684.59928 + tps: 4226.93142 } } dps_results: { key: "TestEnhancement-AllItems-Death'sChoice-47464" value: { - dps: 7864.75193 - tps: 4296.50986 + dps: 7821.58944 + tps: 4272.35185 } } dps_results: { key: "TestEnhancement-AllItems-DeathKnight'sAnguish-38212" value: { - dps: 7552.58696 - tps: 4143.23127 + dps: 7510.94619 + tps: 4111.04299 } } dps_results: { key: "TestEnhancement-AllItems-Deathbringer'sWill-50362" value: { - dps: 7794.34076 - tps: 4257.21747 + dps: 7766.10568 + tps: 4243.77867 } } dps_results: { key: "TestEnhancement-AllItems-Deathbringer'sWill-50363" value: { - dps: 7826.62022 - tps: 4271.20145 + dps: 7838.22209 + tps: 4275.65734 } } dps_results: { key: "TestEnhancement-AllItems-Defender'sCode-40257" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-DestructiveSkyflareDiamond" value: { - dps: 7547.34363 - tps: 4140.90352 + dps: 7536.13083 + tps: 4136.38287 } } dps_results: { key: "TestEnhancement-AllItems-DislodgedForeignObject-50348" value: { - dps: 7910.18791 - tps: 4350.52891 + dps: 7937.05238 + tps: 4368.34768 } } dps_results: { key: "TestEnhancement-AllItems-DislodgedForeignObject-50353" value: { - dps: 7839.21294 - tps: 4307.57233 + dps: 7865.69669 + tps: 4332.01075 } } dps_results: { key: "TestEnhancement-AllItems-EarthshatterBattlegear" value: { - dps: 6898.35799 - tps: 3754.23139 + dps: 6871.23414 + tps: 3741.80152 } } dps_results: { key: "TestEnhancement-AllItems-EarthshatterGarb" value: { - dps: 6525.38208 - tps: 3555.40149 + dps: 6531.4726 + tps: 3563.5617 } } dps_results: { key: "TestEnhancement-AllItems-EffulgentSkyflareDiamond" value: { - dps: 7522.36758 - tps: 4128.61636 + dps: 7497.27676 + tps: 4116.54284 } } dps_results: { key: "TestEnhancement-AllItems-EmberSkyflareDiamond" value: { - dps: 7541.19931 - tps: 4136.05593 + dps: 7560.32819 + tps: 4154.86739 } } dps_results: { key: "TestEnhancement-AllItems-EnigmaticSkyflareDiamond" value: { - dps: 7550.65784 - tps: 4142.74348 + dps: 7535.91127 + tps: 4136.57516 } } dps_results: { key: "TestEnhancement-AllItems-EnigmaticStarflareDiamond" value: { - dps: 7546.33774 - tps: 4141.4326 + dps: 7533.96483 + tps: 4135.5455 } } dps_results: { key: "TestEnhancement-AllItems-EphemeralSnowflake-50260" value: { - dps: 7602.67686 - tps: 4176.54601 + dps: 7603.6195 + tps: 4177.7323 } } dps_results: { key: "TestEnhancement-AllItems-EssenceofGossamer-37220" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-EternalEarthsiegeDiamond" value: { - dps: 7522.36758 - tps: 4128.61636 + dps: 7497.27676 + tps: 4116.54284 } } dps_results: { key: "TestEnhancement-AllItems-ExtractofNecromanticPower-40373" value: { - dps: 7589.22142 - tps: 4166.51024 + dps: 7588.05783 + tps: 4164.26702 } } dps_results: { key: "TestEnhancement-AllItems-EyeoftheBroodmother-45308" value: { - dps: 7694.37975 - tps: 4221.6309 + dps: 7630.46098 + tps: 4185.37338 } } dps_results: { key: "TestEnhancement-AllItems-Figurine-SapphireOwl-42413" value: { - dps: 7500.61452 - tps: 4105.11576 + dps: 7491.99399 + tps: 4106.13192 } } dps_results: { key: "TestEnhancement-AllItems-ForethoughtTalisman-40258" value: { - dps: 7550.9751 - tps: 4135.80918 + dps: 7536.5231 + tps: 4129.04707 } } dps_results: { key: "TestEnhancement-AllItems-ForgeEmber-37660" value: { - dps: 7656.23342 - tps: 4181.51379 + dps: 7615.00137 + tps: 4160.39196 } } dps_results: { key: "TestEnhancement-AllItems-ForlornSkyflareDiamond" value: { - dps: 7545.69985 - tps: 4142.12265 + dps: 7520.5369 + tps: 4130.01088 } } dps_results: { key: "TestEnhancement-AllItems-ForlornStarflareDiamond" value: { - dps: 7541.0334 - tps: 4139.42139 + dps: 7515.88487 + tps: 4127.31727 } } dps_results: { key: "TestEnhancement-AllItems-FrostWitch'sBattlegear" value: { - dps: 7684.30826 - tps: 4180.98304 + dps: 7691.81494 + tps: 4194.38705 } } dps_results: { key: "TestEnhancement-AllItems-FrostWitch'sRegalia" value: { - dps: 7271.61546 - tps: 3988.52303 + dps: 7270.38046 + tps: 3989.20468 } } dps_results: { key: "TestEnhancement-AllItems-FuriousGladiator'sTotemofSurvival-42603" value: { - dps: 7719.64021 - tps: 4242.94504 + dps: 7697.61777 + tps: 4234.44356 } } dps_results: { key: "TestEnhancement-AllItems-FuryoftheFiveFlights-40431" value: { - dps: 7650.38616 - tps: 4185.25964 + dps: 7638.97582 + tps: 4180.16628 } } dps_results: { key: "TestEnhancement-AllItems-FuturesightRune-38763" value: { - dps: 7505.55828 - tps: 4109.6539 + dps: 7491.18762 + tps: 4102.91106 } } dps_results: { key: "TestEnhancement-AllItems-Gladiator'sEarthshaker" value: { - dps: 7109.56529 - tps: 3828.41572 + dps: 7114.84361 + tps: 3834.2798 } } dps_results: { key: "TestEnhancement-AllItems-Gladiator'sWartide" value: { - dps: 6283.52821 - tps: 3397.08753 + dps: 6249.71949 + tps: 3377.67123 } } dps_results: { key: "TestEnhancement-AllItems-GlowingTwilightScale-54573" value: { - dps: 7624.1882 - tps: 4177.9779 + dps: 7609.61499 + tps: 4171.18472 } } dps_results: { key: "TestEnhancement-AllItems-GlowingTwilightScale-54589" value: { - dps: 7647.3569 - tps: 4191.32243 + dps: 7632.74533 + tps: 4184.51942 } } dps_results: { key: "TestEnhancement-AllItems-GnomishLightningGenerator-41121" value: { - dps: 7565.75393 - tps: 4154.97208 + dps: 7586.61418 + tps: 4168.08943 } } dps_results: { key: "TestEnhancement-AllItems-HatefulGladiator'sTotemofSurvival-42601" value: { - dps: 7680.76628 - tps: 4220.14671 + dps: 7659.20636 + tps: 4211.48644 } } dps_results: { key: "TestEnhancement-AllItems-Heartpierce-49982" value: { - dps: 7700.58349 - tps: 4236.83571 + dps: 7672.52563 + tps: 4221.33363 } } dps_results: { key: "TestEnhancement-AllItems-Heartpierce-50641" value: { - dps: 7700.58349 - tps: 4236.83571 + dps: 7672.52563 + tps: 4221.33363 } } dps_results: { key: "TestEnhancement-AllItems-IllustrationoftheDragonSoul-40432" value: { - dps: 7617.84771 - tps: 4178.43695 + dps: 7603.07055 + tps: 4171.75126 } } dps_results: { key: "TestEnhancement-AllItems-ImpassiveSkyflareDiamond" value: { - dps: 7550.65784 - tps: 4142.74348 + dps: 7535.91127 + tps: 4136.57516 } } dps_results: { key: "TestEnhancement-AllItems-ImpassiveStarflareDiamond" value: { - dps: 7546.33774 - tps: 4141.4326 + dps: 7533.96483 + tps: 4135.5455 } } dps_results: { key: "TestEnhancement-AllItems-IncisorFragment-37723" value: { - dps: 7578.00936 - tps: 4145.40305 + dps: 7557.77686 + tps: 4135.45465 } } dps_results: { key: "TestEnhancement-AllItems-InsightfulEarthsiegeDiamond" value: { - dps: 7539.93591 - tps: 4136.08755 + dps: 7533.66584 + tps: 4131.15807 } } dps_results: { key: "TestEnhancement-AllItems-InvigoratingEarthsiegeDiamond" value: { - dps: 7548.28453 - tps: 4141.85719 + dps: 7526.43873 + tps: 4131.62373 hps: 11.01615 } } dps_results: { key: "TestEnhancement-AllItems-Lavanthor'sTalisman-37872" value: { - dps: 7448.0999 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-MajesticDragonFigurine-40430" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-NevermeltingIceCrystal-50259" value: { - dps: 7631.1449 - tps: 4189.33051 + dps: 7606.65722 + tps: 4168.83959 } } dps_results: { key: "TestEnhancement-AllItems-OfferingofSacrifice-37638" value: { - dps: 7448.10311 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-PersistentEarthshatterDiamond" value: { - dps: 7543.01813 - tps: 4139.06289 + dps: 7523.97235 + tps: 4130.03347 } } dps_results: { key: "TestEnhancement-AllItems-PersistentEarthsiegeDiamond" value: { - dps: 7548.28453 - tps: 4141.85719 + dps: 7526.43873 + tps: 4131.62373 } } dps_results: { key: "TestEnhancement-AllItems-PetrifiedScarab-21685" value: { - dps: 7448.10926 - tps: 4076.55946 + dps: 7433.83052 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-PetrifiedTwilightScale-54571" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-PetrifiedTwilightScale-54591" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-PowerfulEarthshatterDiamond" value: { - dps: 7522.36758 - tps: 4128.61636 + dps: 7497.27676 + tps: 4116.54284 } } dps_results: { key: "TestEnhancement-AllItems-PowerfulEarthsiegeDiamond" value: { - dps: 7522.36758 - tps: 4128.61636 + dps: 7497.27676 + tps: 4116.54284 } } dps_results: { key: "TestEnhancement-AllItems-PurifiedShardoftheGods" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-ReignoftheDead-47316" value: { - dps: 7834.02925 - tps: 4334.95342 + dps: 7845.98347 + tps: 4334.69184 } } dps_results: { key: "TestEnhancement-AllItems-ReignoftheDead-47477" value: { - dps: 7885.76916 - tps: 4369.10291 + dps: 7897.35145 + tps: 4368.56655 } } dps_results: { key: "TestEnhancement-AllItems-RelentlessEarthsiegeDiamond" value: { - dps: 7700.58349 - tps: 4236.83571 + dps: 7672.52563 + tps: 4221.33363 } } dps_results: { key: "TestEnhancement-AllItems-RelentlessGladiator'sTotemofSurvival-42604" value: { - dps: 7735.45945 - tps: 4252.07257 + dps: 7713.42595 + tps: 4243.56544 } } dps_results: { key: "TestEnhancement-AllItems-RevitalizingSkyflareDiamond" value: { - dps: 7506.95877 - tps: 4116.94884 + dps: 7521.98185 + tps: 4128.95108 } } dps_results: { key: "TestEnhancement-AllItems-RuneofRepulsion-40372" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-SavageGladiator'sTotemofSurvival-42594" value: { - dps: 7674.42997 - tps: 4216.54955 + dps: 7652.8032 + tps: 4207.91939 } } dps_results: { key: "TestEnhancement-AllItems-SealofthePantheon-36993" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-Shadowmourne-49623" value: { - dps: 7700.58349 - tps: 4236.83571 + dps: 7672.52563 + tps: 4221.33363 } } dps_results: { key: "TestEnhancement-AllItems-ShinyShardoftheGods" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-Sindragosa'sFlawlessFang-50361" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-SkycallTotem-33506" value: { - dps: 7632.69274 - tps: 4190.97661 + dps: 7624.78193 + tps: 4180.16626 } } dps_results: { key: "TestEnhancement-AllItems-SkyshatterHarness" value: { - dps: 5512.77338 - tps: 2973.32421 + dps: 5490.16351 + tps: 2958.99873 } } dps_results: { key: "TestEnhancement-AllItems-SkyshatterRegalia" value: { - dps: 5471.68115 - tps: 2951.65664 + dps: 5419.10587 + tps: 2921.63627 } } dps_results: { key: "TestEnhancement-AllItems-SliverofPureIce-50339" value: { - dps: 7577.22373 - tps: 4152.07363 + dps: 7593.1934 + tps: 4168.2068 } } dps_results: { key: "TestEnhancement-AllItems-SliverofPureIce-50346" value: { - dps: 7599.91306 - tps: 4166.49292 + dps: 7614.0392 + tps: 4179.05119 } } dps_results: { key: "TestEnhancement-AllItems-SoulPreserver-37111" value: { - dps: 7517.61216 - tps: 4116.59305 + dps: 7503.2154 + tps: 4109.8451 } } dps_results: { key: "TestEnhancement-AllItems-SouloftheDead-40382" value: { - dps: 7560.33123 - tps: 4145.47276 + dps: 7558.97534 + tps: 4147.67463 } } dps_results: { key: "TestEnhancement-AllItems-SparkofLife-37657" value: { - dps: 7572.28313 - tps: 4151.37552 + dps: 7561.17659 + tps: 4146.44435 } } dps_results: { key: "TestEnhancement-AllItems-SphereofRedDragon'sBlood-37166" value: { - dps: 7605.31839 - tps: 4152.63767 + dps: 7606.0628 + tps: 4155.68351 } } dps_results: { key: "TestEnhancement-AllItems-Stonebreaker'sTotem-33507" value: { - dps: 7671.96806 - tps: 4214.33843 + dps: 7653.49218 + tps: 4207.54069 } } dps_results: { key: "TestEnhancement-AllItems-StormshroudArmor" value: { - dps: 5780.76198 - tps: 3128.98256 + dps: 5775.5539 + tps: 3126.06697 } } dps_results: { key: "TestEnhancement-AllItems-SwiftSkyflareDiamond" value: { - dps: 7548.28453 - tps: 4141.85719 + dps: 7526.43873 + tps: 4131.62373 } } dps_results: { key: "TestEnhancement-AllItems-SwiftStarflareDiamond" value: { - dps: 7543.01813 - tps: 4139.06289 + dps: 7523.97235 + tps: 4130.03347 } } dps_results: { key: "TestEnhancement-AllItems-SwiftWindfireDiamond" value: { - dps: 7534.99599 - tps: 4136.04534 + dps: 7509.70741 + tps: 4122.71272 } } dps_results: { key: "TestEnhancement-AllItems-TalismanofTrollDivinity-37734" value: { - dps: 7448.0999 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-TearsoftheVanquished-47215" value: { - dps: 7551.42095 - tps: 4132.51265 + dps: 7550.22201 + tps: 4142.7351 } } dps_results: { @@ -799,120 +799,120 @@ dps_results: { dps_results: { key: "TestEnhancement-AllItems-TheGeneral'sHeart-45507" value: { - dps: 7448.10605 - tps: 4076.55946 + dps: 7433.82436 + tps: 4069.84101 } } dps_results: { key: "TestEnhancement-AllItems-Thrall'sBattlegear" value: { - dps: 7448.8377 - tps: 4093.05523 + dps: 7413.67723 + tps: 4074.71586 } } dps_results: { key: "TestEnhancement-AllItems-Thrall'sRegalia" value: { - dps: 7044.95921 - tps: 3879.47399 + dps: 7059.91375 + tps: 3894.58839 } } dps_results: { key: "TestEnhancement-AllItems-ThunderingSkyflareDiamond" value: { - dps: 7580.00353 - tps: 4162.08257 + dps: 7557.24335 + tps: 4151.51398 } } dps_results: { key: "TestEnhancement-AllItems-TidefuryRaiment" value: { - dps: 5478.99337 - tps: 2949.13922 + dps: 5430.65197 + tps: 2919.82826 } } dps_results: { key: "TestEnhancement-AllItems-TinyAbominationinaJar-50351" value: { - dps: 7648.99142 - tps: 4192.90626 + dps: 7634.77945 + tps: 4187.38961 } } dps_results: { key: "TestEnhancement-AllItems-TinyAbominationinaJar-50706" value: { - dps: 7696.30964 - tps: 4221.91916 + dps: 7689.43328 + tps: 4220.75554 } } dps_results: { key: "TestEnhancement-AllItems-TirelessSkyflareDiamond" value: { - dps: 7545.69985 - tps: 4142.12265 + dps: 7520.5369 + tps: 4130.01088 } } dps_results: { key: "TestEnhancement-AllItems-TirelessStarflareDiamond" value: { - dps: 7541.0334 - tps: 4139.42139 + dps: 7515.88487 + tps: 4127.31727 } } dps_results: { key: "TestEnhancement-AllItems-TomeofArcanePhenomena-36972" value: { - dps: 7611.26602 - tps: 4180.90939 + dps: 7601.74053 + tps: 4168.03919 } } dps_results: { key: "TestEnhancement-AllItems-TotemofElectrifyingWind-47666" value: { - dps: 7869.06607 - tps: 4339.13849 + dps: 7836.78802 + tps: 4319.61961 } } dps_results: { key: "TestEnhancement-AllItems-TotemofQuakingEarth-47667" value: { - dps: 7787.70206 - tps: 4274.68558 + dps: 7762.91516 + tps: 4264.45874 } } dps_results: { key: "TestEnhancement-AllItems-TotemoftheAvalanche-50463" value: { - dps: 7932.11473 - tps: 4350.79929 + dps: 7910.27338 + tps: 4343.70913 } } dps_results: { key: "TestEnhancement-AllItems-TotemoftheElementalPlane-40708" value: { - dps: 7673.238 - tps: 4213.99998 + dps: 7649.6034 + tps: 4206.8637 } } dps_results: { key: "TestEnhancement-AllItems-TrenchantEarthshatterDiamond" value: { - dps: 7541.0334 - tps: 4139.42139 + dps: 7515.88487 + tps: 4127.31727 } } dps_results: { key: "TestEnhancement-AllItems-TrenchantEarthsiegeDiamond" value: { - dps: 7545.69985 - tps: 4142.12265 + dps: 7520.5369 + tps: 4130.01088 } } dps_results: { key: "TestEnhancement-AllItems-UndeadSlayer'sBlessedArmor" value: { - dps: 6090.30325 - tps: 3300.78875 + dps: 6086.80532 + tps: 3300.83114 } } dps_results: { @@ -925,36 +925,36 @@ dps_results: { dps_results: { key: "TestEnhancement-AllItems-WingedTalisman-37844" value: { - dps: 7548.66451 - tps: 4116.82098 + dps: 7533.16829 + tps: 4109.33017 } } dps_results: { key: "TestEnhancement-AllItems-WorldbreakerBattlegear" value: { - dps: 7397.40742 - tps: 4067.88076 + dps: 7389.27268 + tps: 4060.17013 } } dps_results: { key: "TestEnhancement-AllItems-WorldbreakerGarb" value: { - dps: 7080.32849 - tps: 3906.25305 + dps: 7055.53295 + tps: 3886.84412 } } dps_results: { key: "TestEnhancement-AllItems-WrathfulGladiator'sTotemofSurvival-51513" value: { - dps: 7752.20924 - tps: 4261.73701 + dps: 7730.16401 + tps: 4253.22391 } } dps_results: { key: "TestEnhancement-Average-Default" value: { - dps: 7687.15492 - tps: 4221.86848 + dps: 7682.97665 + tps: 4218.7695 } } dps_results: { @@ -1002,85 +1002,85 @@ dps_results: { dps_results: { key: "TestEnhancement-Settings-Orc-P1-FT-FullBuffs-LongMultiTarget" value: { - dps: 26716.48801 - tps: 15977.81878 + dps: 26912.38052 + tps: 16144.41127 } } dps_results: { key: "TestEnhancement-Settings-Orc-P1-FT-FullBuffs-LongSingleTarget" value: { - dps: 7757.89683 - tps: 4226.06316 + dps: 7747.87222 + tps: 4215.92799 } } dps_results: { key: "TestEnhancement-Settings-Orc-P1-FT-FullBuffs-ShortSingleTarget" value: { - dps: 9522.509 - tps: 4561.5718 + dps: 9551.18819 + tps: 4582.38167 } } dps_results: { key: "TestEnhancement-Settings-Orc-P1-FT-NoBuffs-LongMultiTarget" value: { - dps: 13046.33739 - tps: 8122.06514 + dps: 12944.42942 + tps: 8082.70364 } } dps_results: { key: "TestEnhancement-Settings-Orc-P1-FT-NoBuffs-LongSingleTarget" value: { - dps: 4139.59547 - tps: 2180.17434 + dps: 4136.3294 + tps: 2183.88249 } } dps_results: { key: "TestEnhancement-Settings-Orc-P1-FT-NoBuffs-ShortSingleTarget" value: { - dps: 5863.54446 - tps: 2797.29136 + dps: 5883.15924 + tps: 2808.35774 } } dps_results: { key: "TestEnhancement-Settings-Orc-P1-Wf-FullBuffs-LongMultiTarget" value: { - dps: 5281.81255 - tps: 4104.96728 + dps: 5290.5369 + tps: 4105.28184 } } dps_results: { key: "TestEnhancement-Settings-Orc-P1-Wf-FullBuffs-LongSingleTarget" value: { - dps: 5281.81255 - tps: 3089.38066 + dps: 5290.5369 + tps: 3093.78295 } } dps_results: { key: "TestEnhancement-Settings-Orc-P1-Wf-FullBuffs-ShortSingleTarget" value: { - dps: 6517.18441 - tps: 3386.08895 + dps: 6553.14712 + tps: 3411.82039 } } dps_results: { key: "TestEnhancement-Settings-Orc-P1-Wf-NoBuffs-LongMultiTarget" value: { - dps: 2753.26899 - tps: 3017.6888 + dps: 2759.93461 + tps: 3000.72136 } } dps_results: { key: "TestEnhancement-Settings-Orc-P1-Wf-NoBuffs-LongSingleTarget" value: { - dps: 2753.26899 - tps: 1689.92656 + dps: 2759.93461 + tps: 1692.86763 } } dps_results: { key: "TestEnhancement-Settings-Orc-P1-Wf-NoBuffs-ShortSingleTarget" value: { - dps: 3288.16416 - tps: 1763.43524 + dps: 3299.8264 + tps: 1770.62534 } } dps_results: { @@ -1128,91 +1128,91 @@ dps_results: { dps_results: { key: "TestEnhancement-Settings-Troll-P1-FT-FullBuffs-LongMultiTarget" value: { - dps: 26641.89719 - tps: 16123.09291 + dps: 26561.07405 + tps: 16133.26105 } } dps_results: { key: "TestEnhancement-Settings-Troll-P1-FT-FullBuffs-LongSingleTarget" value: { - dps: 7700.58349 - tps: 4236.83571 + dps: 7672.52563 + tps: 4221.33363 } } dps_results: { key: "TestEnhancement-Settings-Troll-P1-FT-FullBuffs-ShortSingleTarget" value: { - dps: 9496.37751 - tps: 4653.36855 + dps: 9475.38482 + tps: 4638.15273 } } dps_results: { key: "TestEnhancement-Settings-Troll-P1-FT-NoBuffs-LongMultiTarget" value: { - dps: 12952.54902 - tps: 8192.41192 + dps: 12925.2173 + tps: 8188.5874 } } dps_results: { key: "TestEnhancement-Settings-Troll-P1-FT-NoBuffs-LongSingleTarget" value: { - dps: 4061.51072 - tps: 2168.37473 + dps: 4088.32964 + tps: 2190.07335 } } dps_results: { key: "TestEnhancement-Settings-Troll-P1-FT-NoBuffs-ShortSingleTarget" value: { - dps: 5845.32061 - tps: 2865.73151 + dps: 5807.90202 + tps: 2836.06828 } } dps_results: { key: "TestEnhancement-Settings-Troll-P1-Wf-FullBuffs-LongMultiTarget" value: { - dps: 5293.2543 - tps: 4135.67711 + dps: 5307.09409 + tps: 4138.05853 } } dps_results: { key: "TestEnhancement-Settings-Troll-P1-Wf-FullBuffs-LongSingleTarget" value: { - dps: 5293.2543 - tps: 3107.28494 + dps: 5307.09409 + tps: 3113.64811 } } dps_results: { key: "TestEnhancement-Settings-Troll-P1-Wf-FullBuffs-ShortSingleTarget" value: { - dps: 6549.56551 - tps: 3443.37775 + dps: 6589.32805 + tps: 3462.033 } } dps_results: { key: "TestEnhancement-Settings-Troll-P1-Wf-NoBuffs-LongMultiTarget" value: { - dps: 2726.68724 - tps: 2963.06677 + dps: 2735.11266 + tps: 2973.82857 } } dps_results: { key: "TestEnhancement-Settings-Troll-P1-Wf-NoBuffs-LongSingleTarget" value: { - dps: 2726.68724 - tps: 1676.14762 + dps: 2735.11266 + tps: 1681.26911 } } dps_results: { key: "TestEnhancement-Settings-Troll-P1-Wf-NoBuffs-ShortSingleTarget" value: { - dps: 3229.57563 - tps: 1743.28024 + dps: 3241.93506 + tps: 1752.21379 } } dps_results: { key: "TestEnhancement-SwitchInFrontOfTarget-Default" value: { - dps: 7323.33454 - tps: 4001.50447 + dps: 7330.97558 + tps: 4013.85062 } } diff --git a/sim/shaman/enhancement/enhancement.go b/sim/shaman/enhancement/enhancement.go index 5f5fd4e7c3..7f840588fa 100644 --- a/sim/shaman/enhancement/enhancement.go +++ b/sim/shaman/enhancement/enhancement.go @@ -48,27 +48,23 @@ func NewEnhancementShaman(character core.Character, options *proto.Player) *Enha enh.EnableResumeAfterManaWait(enh.OnGCDReady) enh.rotation = NewPriorityRotation(enh, enhOptions.Rotation) - syncType := int32(enhOptions.Options.SyncType) - if syncType == int32(proto.ShamanSyncType_Auto) { - syncType = enh.AutoSyncWeapons() - } - // Enable Auto Attacks for this spec enh.EnableAutoAttacks(enh, core.AutoAttackOptions{ MainHand: enh.WeaponFromMainHand(enh.DefaultMeleeCritMultiplier()), OffHand: enh.WeaponFromOffHand(enh.DefaultMeleeCritMultiplier()), AutoSwingMelee: true, - SyncType: syncType, }) + enh.ApplySyncType(enhOptions.Options.SyncType) + if enh.Totems.UseFireElemental && enhOptions.Rotation.EnableItemSwap { enh.EnableItemSwap(enhOptions.Rotation.ItemSwap, enh.DefaultMeleeCritMultiplier(), enh.DefaultMeleeCritMultiplier(), 0) } if enhOptions.Rotation.LightningboltWeave { - enh.maelstromweaponMinStack = enhOptions.Rotation.MaelstromweaponMinStack + enh.maelstromWeaponMinStack = enhOptions.Rotation.MaelstromweaponMinStack } else { - enh.maelstromweaponMinStack = 5 + enh.maelstromWeaponMinStack = 5 } if !enh.HasMHWeapon() { @@ -103,7 +99,10 @@ type EnhancementShaman struct { *shaman.Shaman rotation Rotation - maelstromweaponMinStack int32 + maelstromWeaponMinStack int32 + + // for weaving Lava Burst or Lightning Bolt + previousSwingAt time.Duration scheduler common.GCDScheduler } @@ -124,31 +123,59 @@ func (enh *EnhancementShaman) Initialize() { enh.ApplyFlametongueImbueToItem(mh, true) oh := enh.ItemSwap.GetItem(proto.ItemSlot_ItemSlotOffHand) enh.ApplyFlametongueImbueToItem(oh, false) - enh.RegisterOnItemSwap(func(s *core.Simulation) { - enh.AutoAttacks.SyncType = enh.AutoSyncWeapons() + enh.RegisterOnItemSwap(func(_ *core.Simulation) { + enh.ApplySyncType(proto.ShamanSyncType_Auto) }) } enh.DelayDPSCooldowns(3 * time.Second) } func (enh *EnhancementShaman) Reset(sim *core.Simulation) { + enh.previousSwingAt = 0 enh.Shaman.Reset(sim) enh.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand, proto.ItemSlot_ItemSlotOffHand}, false) } -func (enh *EnhancementShaman) AutoSyncWeapons() int32 { - mh := enh.GetMHWeapon() - oh := enh.GetOHWeapon() +func (enh *EnhancementShaman) AutoSyncWeapons() proto.ShamanSyncType { + if mh, oh := enh.MainHand(), enh.OffHand(); mh.SwingSpeed != oh.SwingSpeed { + return proto.ShamanSyncType_NoSync + } + return proto.ShamanSyncType_SyncMainhandOffhandSwings +} + +func (enh *EnhancementShaman) ApplySyncType(syncType proto.ShamanSyncType) { + const FlurryICD = time.Millisecond * 500 - if mh == nil || oh == nil || mh.SwingSpeed != oh.SwingSpeed { - return int32(proto.ShamanSyncType_NoSync) + if syncType == proto.ShamanSyncType_Auto { + syncType = enh.AutoSyncWeapons() } - return int32(proto.ShamanSyncType_SyncMainhandOffhandSwings) + switch syncType { + case proto.ShamanSyncType_SyncMainhandOffhandSwings: + enh.AutoAttacks.ReplaceMHSwing = func(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell { + if aa := &enh.AutoAttacks; aa.OffhandSwingAt-sim.CurrentTime > FlurryICD { + if nextMHSwingAt := sim.CurrentTime + aa.MainhandSwingSpeed(); nextMHSwingAt > aa.OffhandSwingAt { + aa.OffhandSwingAt = nextMHSwingAt + } + } + return mhSwingSpell + } + case proto.ShamanSyncType_DelayOffhandSwings: + enh.AutoAttacks.ReplaceMHSwing = func(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell { + if aa := &enh.AutoAttacks; aa.OffhandSwingAt-sim.CurrentTime > FlurryICD { + if nextMHSwingAt := sim.CurrentTime + aa.MainhandSwingSpeed() + 100*time.Millisecond; nextMHSwingAt > aa.OffhandSwingAt { + aa.OffhandSwingAt = nextMHSwingAt + } + } + return mhSwingSpell + } + default: + enh.AutoAttacks.ReplaceMHSwing = nil + } } func (enh *EnhancementShaman) CastLightningBoltWeave(sim *core.Simulation, reactionTime time.Duration) bool { - previousAttack := sim.CurrentTime - enh.AutoAttacks.PreviousSwingAt + previousAttack := sim.CurrentTime - enh.previousSwingAt reactionTime = core.TernaryDuration(previousAttack < reactionTime, reactionTime-previousAttack, 0) //calculate cast times for weaving @@ -175,7 +202,7 @@ func (enh *EnhancementShaman) CastLightningBoltWeave(sim *core.Simulation, react } func (enh *EnhancementShaman) CastLavaBurstWeave(sim *core.Simulation, reactionTime time.Duration) bool { - previousAttack := sim.CurrentTime - enh.AutoAttacks.PreviousSwingAt + previousAttack := sim.CurrentTime - enh.previousSwingAt reactionTime = core.TernaryDuration(previousAttack < reactionTime, reactionTime-previousAttack, 0) //calculate cast times for weaving diff --git a/sim/shaman/enhancement/rotation.go b/sim/shaman/enhancement/rotation.go index d564cab58e..53476c86d5 100644 --- a/sim/shaman/enhancement/rotation.go +++ b/sim/shaman/enhancement/rotation.go @@ -4,7 +4,8 @@ import ( "github.com/wowsims/wotlk/sim/core" ) -func (enh *EnhancementShaman) OnAutoAttack(sim *core.Simulation, spell *core.Spell) { +func (enh *EnhancementShaman) OnAutoAttack(sim *core.Simulation, _ *core.Spell) { + enh.previousSwingAt = sim.CurrentTime } func (enh *EnhancementShaman) OnGCDReady(sim *core.Simulation) { diff --git a/sim/shaman/fire_elemental_pet.go b/sim/shaman/fire_elemental_pet.go index 0343e35d1a..1387359629 100644 --- a/sim/shaman/fire_elemental_pet.go +++ b/sim/shaman/fire_elemental_pet.go @@ -38,7 +38,6 @@ func (shaman *Shaman) NewFireElemental(bonusSpellPower float64) *FireElemental { BaseDamageMin: 1, // Estimated from base AP BaseDamageMax: 23, // Estimated from base AP SwingSpeed: 2, - SwingDuration: time.Second * 2, CritMultiplier: 2, // Pretty sure this is right. SpellSchool: core.SpellSchoolFire, }, @@ -86,7 +85,7 @@ func (fireElemental *FireElemental) Initialize() { fireElemental.registerFireShieldAura() } -func (fireElemental *FireElemental) Reset(sim *core.Simulation) { +func (fireElemental *FireElemental) Reset(_ *core.Simulation) { } diff --git a/sim/shaman/spirit_wolves.go b/sim/shaman/spirit_wolves.go index 0d1130f332..9ba2b26512 100644 --- a/sim/shaman/spirit_wolves.go +++ b/sim/shaman/spirit_wolves.go @@ -56,7 +56,6 @@ func (shaman *Shaman) NewSpiritWolf(index int) *SpiritWolf { BaseDamageMin: 246, BaseDamageMax: 372, SwingSpeed: 1.5, - SwingDuration: time.Millisecond * 1500, CritMultiplier: 2, }, AutoSwingMelee: true, @@ -94,7 +93,7 @@ func (spiritWolf *SpiritWolf) Initialize() { // Nothing } -func (spiritWolf *SpiritWolf) OnGCDReady(sim *core.Simulation) { +func (spiritWolf *SpiritWolf) OnGCDReady(_ *core.Simulation) { spiritWolf.DoNothing() } diff --git a/sim/warlock/inferno.go b/sim/warlock/inferno.go index d334112343..69525a4848 100644 --- a/sim/warlock/inferno.go +++ b/sim/warlock/inferno.go @@ -125,7 +125,6 @@ func (warlock *Warlock) NewInfernal() *InfernalPet { BaseDamageMin: 330, BaseDamageMax: 494.9, SwingSpeed: 2, - SwingDuration: time.Second * 2, CritMultiplier: 2, }, AutoSwingMelee: true, @@ -188,7 +187,7 @@ func (infernal *InfernalPet) Initialize() { }) } -func (infernal *InfernalPet) Reset(sim *core.Simulation) { +func (infernal *InfernalPet) Reset(_ *core.Simulation) { } func (infernal *InfernalPet) OnGCDReady(sim *core.Simulation) { diff --git a/sim/warlock/pet.go b/sim/warlock/pet.go index 0cb730f1dc..b6ff16a847 100644 --- a/sim/warlock/pet.go +++ b/sim/warlock/pet.go @@ -50,7 +50,6 @@ func (warlock *Warlock) NewWarlockPet() *WarlockPet { BaseDamageMin: 88.8, BaseDamageMax: 133.3, SwingSpeed: 2, - SwingDuration: time.Second * 2, CritMultiplier: 2, }, AutoSwingMelee: true, @@ -87,7 +86,6 @@ func (warlock *Warlock) NewWarlockPet() *WarlockPet { BaseDamageMin: 98, BaseDamageMax: 147, SwingSpeed: 2, - SwingDuration: time.Second * 2, CritMultiplier: 2, }, AutoSwingMelee: true, @@ -110,7 +108,6 @@ func (warlock *Warlock) NewWarlockPet() *WarlockPet { BaseDamageMin: 88.8, BaseDamageMax: 133.3, SwingSpeed: 2, - SwingDuration: time.Second * 2, CritMultiplier: 2, }, AutoSwingMelee: true, @@ -298,7 +295,7 @@ func (wp *WarlockPet) Initialize() { } } -func (wp *WarlockPet) Reset(sim *core.Simulation) { +func (wp *WarlockPet) Reset(_ *core.Simulation) { } func (wp *WarlockPet) OnGCDReady(sim *core.Simulation) { diff --git a/sim/warrior/heroic_strike_cleave.go b/sim/warrior/heroic_strike_cleave.go index dbbbbbc17d..dcc2ada7e7 100644 --- a/sim/warrior/heroic_strike_cleave.go +++ b/sim/warrior/heroic_strike_cleave.go @@ -145,24 +145,17 @@ func (warrior *Warrior) QueueHSOrCleave(sim *core.Simulation) { // Returns true if the regular melee swing should be used, false otherwise. func (warrior *Warrior) TryHSOrCleave(sim *core.Simulation, mhSwingSpell *core.Spell) *core.Spell { if !warrior.curQueueAura.IsActive() { - return nil + return mhSwingSpell } - if sim.CurrentTime < warrior.Hardcast.Expires { + if warrior.CurrentRage() < warrior.HSRageThreshold { warrior.curQueueAura.Deactivate(sim) - return nil + return mhSwingSpell } if !warrior.curQueuedAutoSpell.CanCast(sim, warrior.CurrentTarget) { warrior.curQueueAura.Deactivate(sim) - return nil - } - - if warrior.CurrentRage() < warrior.HSRageThreshold { - if mhSwingSpell == warrior.AutoAttacks.MHAuto { - warrior.curQueueAura.Deactivate(sim) - return nil - } + return mhSwingSpell } return warrior.curQueuedAutoSpell diff --git a/sim/warrior/shattering_throw.go b/sim/warrior/shattering_throw.go index 4a2cff2d47..bbe683d3ad 100644 --- a/sim/warrior/shattering_throw.go +++ b/sim/warrior/shattering_throw.go @@ -30,7 +30,7 @@ func (warrior *Warrior) RegisterShatteringThrowCD() { Duration: time.Minute * 5, }, ModifyCast: func(sim *core.Simulation, spell *core.Spell, cast *core.Cast) { - if warrior.AutoAttacks.MainhandSwingSpeed() == warrior.AutoAttacks.OffhandSwingSpeed() { + if warrior.AutoAttacks.MH.SwingSpeed == warrior.AutoAttacks.OH.SwingSpeed { warrior.AutoAttacks.StopMeleeUntil(sim, sim.CurrentTime+cast.CastTime, true) } else { warrior.AutoAttacks.StopMeleeUntil(sim, sim.CurrentTime+cast.CastTime, false) diff --git a/sim/web/main_test.go b/sim/web/main_test.go index a36614f7b4..03e23b0e7a 100644 --- a/sim/web/main_test.go +++ b/sim/web/main_test.go @@ -28,23 +28,23 @@ var basicSpec = &proto.Player_ElementalShaman{ var p1Equip = &proto.EquipmentSpec{ Items: []*proto.ItemSpec{ - {Id: 29035, Gems: []int32{34220, 24059}, Enchant: 29191}, - {Id: 28762}, - {Id: 29037, Gems: []int32{24059, 24059}, Enchant: 28909}, - {Id: 28766}, - {Id: 29519}, - {Id: 29521}, - {Id: 28780}, - {Id: 29520}, - {Id: 30541}, - {Id: 28810}, - {Id: 30667}, - {Id: 28753}, - {Id: 28785}, - {Id: 29370}, - {Id: 28248}, - {Id: 28770, Enchant: 22555}, - {Id: 29268}, + {Id: 40516, Enchant: 3820, Gems: []int32{41285, 40027}}, + {Id: 44661, Gems: []int32{39998}}, + {Id: 40286, Enchant: 3810}, + {Id: 44005, Enchant: 3722, Gems: []int32{40027}}, + {Id: 40514, Enchant: 3832, Gems: []int32{42144, 42144}}, + {Id: 40324, Enchant: 2332, Gems: []int32{42144, 0}}, + {Id: 40302, Enchant: 3246, Gems: []int32{0}}, + {Id: 40301, Gems: []int32{40014}}, + {Id: 40560, Enchant: 3721}, + {Id: 40519, Enchant: 3826}, + {Id: 37694}, + {Id: 40399}, + {Id: 40432}, + {Id: 40255}, + {Id: 40395, Enchant: 3834}, + {Id: 40401, Enchant: 1128}, + {Id: 40267}, }, }