diff --git a/sim/core/debuffs.go b/sim/core/debuffs.go index 2b856a8db..851b5f53f 100644 --- a/sim/core/debuffs.go +++ b/sim/core/debuffs.go @@ -744,6 +744,24 @@ func HuntersMarkAura(target *Unit, points int32) *Aura { return aura } +func ExposeWeaknessAura(target *Unit) *Aura { + bonus := 450.0 + + aura := target.GetOrRegisterAura(Aura{ + ActionID: ActionID{SpellID: 23577}, + Label: "Expose Weakness", + Duration: time.Second * 7, + OnGain: func(aura *Aura, sim *Simulation) { + target.PseudoStats.BonusRangedAttackPowerTaken += bonus + }, + OnExpire: func(aura *Aura, sim *Simulation) { + target.PseudoStats.BonusRangedAttackPowerTaken -= bonus + }, + }) + + return aura +} + func DemoralizingRoarAura(target *Unit, points int32) *Aura { baseAPReduction := 138.0 diff --git a/sim/hunter/TestP1Hunter.results b/sim/hunter/TestP1Hunter.results index 8927f82ef..8b0e08810 100644 --- a/sim/hunter/TestP1Hunter.results +++ b/sim/hunter/TestP1Hunter.results @@ -1,11 +1,11 @@ character_stats_results: { key: "TestP1Hunter-Phase1-CharacterStats-Default" value: { - final_stats: 264.385 - final_stats: 497.145 - final_stats: 490.25075 - final_stats: 217.58 - final_stats: 244.145 + final_stats: 240.35 + final_stats: 451.95 + final_stats: 445.6825 + final_stats: 197.8 + final_stats: 221.95 final_stats: 150 final_stats: 0 final_stats: 0 @@ -15,27 +15,27 @@ character_stats_results: { final_stats: 0 final_stats: 47.25 final_stats: 3 - final_stats: 28.19007 + final_stats: 27.8637 final_stats: 0 final_stats: 0 - final_stats: 1622.53 + final_stats: 1553.3 final_stats: 3 - final_stats: 31.39604 + final_stats: 25.54186 final_stats: 0 final_stats: 0 final_stats: 0 - final_stats: 4703.7 + final_stats: 4407 final_stats: 0 final_stats: 0 - final_stats: 3241.29 - final_stats: 1714.29 + final_stats: 3150.9 + final_stats: 1623.9 final_stats: 0 final_stats: 5 final_stats: 0 final_stats: 0 final_stats: 5 final_stats: 0 - final_stats: 6878.87795 + final_stats: 6406.4545 final_stats: 35 final_stats: 68 final_stats: 68 @@ -51,7 +51,7 @@ stat_weights_results: { key: "TestP1Hunter-Phase1-StatWeights-Default" value: { weights: 0 - weights: 0.27871 + weights: 0.13131 weights: 0 weights: 0 weights: 0 @@ -67,9 +67,9 @@ stat_weights_results: { weights: 0 weights: 0 weights: 0 - weights: 0.09096 - weights: 6.3706 - weights: 2.00452 + weights: 0.08555 + weights: 1.45092 + weights: 1.46158 weights: 0 weights: 0 weights: 0 @@ -77,7 +77,6 @@ stat_weights_results: { weights: 0 weights: 0 weights: 0 - weights: 0.04101 weights: 0 weights: 0 weights: 0 @@ -94,105 +93,155 @@ stat_weights_results: { weights: 0 weights: 0 weights: 0 + weights: 0 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-BeastmasterArmor" + value: { + dps: 295.25177 + tps: 148.05127 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-CryptstalkerArmor" + value: { + dps: 326.50148 + tps: 179.28647 } } dps_results: { key: "TestP1Hunter-Phase1-AllItems-DevilsaurEye-19991" value: { - dps: 486.76691 - tps: 162.94333 + dps: 295.59501 + tps: 148.39451 } } dps_results: { key: "TestP1Hunter-Phase1-AllItems-DevilsaurTooth-19992" value: { - dps: 481.62357 - tps: 159.59641 + dps: 293.0934 + tps: 145.25309 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-DragonstalkerArmor" + value: { + dps: 305.55196 + tps: 158.34988 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-GiantstalkerArmor" + value: { + dps: 302.92974 + tps: 155.72767 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-Predator'sArmor" + value: { + dps: 296.76353 + tps: 149.55692 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-Renataki'sCharmofBeasts-19953" + value: { + dps: 292.46447 + tps: 145.26397 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-Striker'sGarb" + value: { + dps: 301.97156 + tps: 154.88863 } } dps_results: { key: "TestP1Hunter-Phase1-Average-Default" value: { - dps: 477.36962 - tps: 159.07056 + dps: 291.06476 + tps: 144.34627 } } dps_results: { key: "TestP1Hunter-Phase1-Settings-Dwarf-p0.bis-Hunter-p1-FullBuffs-P1-Consumes-LongMultiTarget" value: { - dps: 305.40016 + dps: 138.81564 } } dps_results: { key: "TestP1Hunter-Phase1-Settings-Dwarf-p0.bis-Hunter-p1-FullBuffs-P1-Consumes-LongSingleTarget" value: { - dps: 305.40016 + dps: 138.81564 } } dps_results: { key: "TestP1Hunter-Phase1-Settings-Dwarf-p0.bis-Hunter-p1-FullBuffs-P1-Consumes-ShortSingleTarget" value: { - dps: 320.75686 + dps: 143.24669 } } dps_results: { key: "TestP1Hunter-Phase1-Settings-Dwarf-p0.bis-Hunter-p1-NoBuffs-P1-Consumes-LongMultiTarget" value: { - dps: 82.02021 + dps: 44.29107 } } dps_results: { key: "TestP1Hunter-Phase1-Settings-Dwarf-p0.bis-Hunter-p1-NoBuffs-P1-Consumes-LongSingleTarget" value: { - dps: 82.02021 + dps: 44.29107 } } dps_results: { key: "TestP1Hunter-Phase1-Settings-Dwarf-p0.bis-Hunter-p1-NoBuffs-P1-Consumes-ShortSingleTarget" value: { - dps: 88.52133 + dps: 46.16053 } } dps_results: { key: "TestP1Hunter-Phase1-Settings-Orc-p0.bis-Hunter-p1-FullBuffs-P1-Consumes-LongMultiTarget" value: { - dps: 319.99574 + dps: 146.2664 } } dps_results: { key: "TestP1Hunter-Phase1-Settings-Orc-p0.bis-Hunter-p1-FullBuffs-P1-Consumes-LongSingleTarget" value: { - dps: 319.99574 + dps: 146.2664 } } dps_results: { key: "TestP1Hunter-Phase1-Settings-Orc-p0.bis-Hunter-p1-FullBuffs-P1-Consumes-ShortSingleTarget" value: { - dps: 336.90654 + dps: 151.46541 } } dps_results: { key: "TestP1Hunter-Phase1-Settings-Orc-p0.bis-Hunter-p1-NoBuffs-P1-Consumes-LongMultiTarget" value: { - dps: 86.22379 + dps: 46.50562 } } dps_results: { key: "TestP1Hunter-Phase1-Settings-Orc-p0.bis-Hunter-p1-NoBuffs-P1-Consumes-LongSingleTarget" value: { - dps: 86.22379 + dps: 46.50562 } } dps_results: { key: "TestP1Hunter-Phase1-Settings-Orc-p0.bis-Hunter-p1-NoBuffs-P1-Consumes-ShortSingleTarget" value: { - dps: 93.06899 + dps: 48.46856 } } dps_results: { key: "TestP1Hunter-Phase1-SwitchInFrontOfTarget-Default" value: { - dps: 457.42663 - tps: 136.77363 + dps: 270.50999 + tps: 123.30949 } } diff --git a/sim/hunter/_item_sets_pve.go b/sim/hunter/_item_sets_pve.go deleted file mode 100644 index 2802ac132..000000000 --- a/sim/hunter/_item_sets_pve.go +++ /dev/null @@ -1,481 +0,0 @@ -package hunter - -import ( - "time" - - "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" - "github.com/wowsims/classic/sim/core/stats" -) - -/////////////////////////////////////////////////////////////////////////// -// SoD Phase 3 Item Sets -/////////////////////////////////////////////////////////////////////////// - -var ItemSetDreadHuntersChain = core.NewItemSet(core.ItemSet{ - Name: "Dread Hunter's Chain", - Bonuses: map[int32]core.ApplyEffect{ - 2: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.AttackPower, 20) - c.AddStat(stats.RangedAttackPower, 20) - }, - 3: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.MeleeCrit, 1*core.CritRatingPerCritChance) - }, - }, -}) - -/////////////////////////////////////////////////////////////////////////// -// SoD Phase 4 Item Sets -/////////////////////////////////////////////////////////////////////////// - -var ItemSetBeastmasterArmor = core.NewItemSet(core.ItemSet{ - Name: "Beastmaster Armor", - Bonuses: map[int32]core.ApplyEffect{ - // +40 Attack Power. - 2: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStats(stats.Stats{ - stats.AttackPower: 40, - stats.RangedAttackPower: 40, - }) - }, - // Your melee and ranged autoattacks have a 6% chance to energize you for 300 mana. - 4: func(agent core.Agent) { - c := agent.GetCharacter() - actionID := core.ActionID{SpellID: 450577} - manaMetrics := c.NewManaMetrics(actionID) - - core.MakeProcTriggerAura(&c.Unit, core.ProcTrigger{ - ActionID: actionID, - Name: "S03 - Mana Proc on Cast - Beaststalker Armor", - Callback: core.CallbackOnSpellHitDealt, - Outcome: core.OutcomeLanded, - ProcMask: core.ProcMaskWhiteHit, - ProcChance: 0.06, - Handler: func(sim *core.Simulation, spell *core.Spell, _ *core.SpellResult) { - if c.HasManaBar() { - c.AddMana(sim, 300, manaMetrics) - } - }, - }) - }, - // +8 All Resistances. - 6: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddResistances(8) - }, - // +200 Armor. - 8: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Armor, 200) - }, - }, -}) - -var ItemSetGiantstalkerProwess = core.NewItemSet(core.ItemSet{ - Name: "Giantstalker Prowess", - Bonuses: map[int32]core.ApplyEffect{ - // Your Mongoose Bite also reduces its target's chance to Dodge by 1% and increases your chance to hit by 1% for 30 sec. - 2: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - - procBonus := stats.Stats{ - stats.SpellHit: 1, - stats.MeleeHit: 1, - } - - stalkerAura := hunter.RegisterAura(core.Aura{ - ActionID: core.ActionID{SpellID: 458403}, - Label: "Stalker", - Duration: time.Second * 30, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - aura.Unit.AddStatsDynamic(sim, procBonus) - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - aura.Unit.AddStatsDynamic(sim, procBonus.Invert()) - }, - }) - - debuffAuras := hunter.NewEnemyAuraArray(core.MeleeHunterDodgeReductionAura) - core.MakePermanent(hunter.RegisterAura(core.Aura{ - Label: "S03 - Item - T1 - Hunter - Melee 2P Bonus Trigger", - OnSpellHitDealt: func(_ *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if spell.SpellCode == SpellCode_HunterMongooseBite && result.Landed() { - debuffAuras.Get(result.Target).Activate(sim) - stalkerAura.Activate(sim) - } - }, - })) - }, - // While tracking a creature type, you deal 3% increased damage to that creature type. - // Unsure if this stacks with the Pursuit 4p - 4: func(agent core.Agent) { - c := agent.GetCharacter() - // Just adding 3% damage to assume the hunter is tracking their target's type - c.PseudoStats.DamageDealtMultiplier *= 1.03 - }, - // Mongoose Bite also activates for 5 sec whenever your target Parries or Blocks or when your melee attack misses. - 6: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - core.MakePermanent(hunter.RegisterAura(core.Aura{ - Label: "S03 - Item - T1 - Hunter - Melee 6P Bonus Trigger", - OnSpellHitDealt: func(_ *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if spell.ProcMask.Matches(core.ProcMaskMelee) && (result.Outcome == core.OutcomeMiss || result.Outcome == core.OutcomeBlock || result.Outcome == core.OutcomeParry) { - hunter.DefensiveState.Activate(sim) - } - }, - })) - }, - }, -}) - -var ItemSetGiantstalkerPursuit = core.NewItemSet(core.ItemSet{ - Name: "Giantstalker Pursuit", - Bonuses: map[int32]core.ApplyEffect{ - // You generate 100% more threat for 8 sec after using Distracting Shot. - 2: func(agent core.Agent) { - // Nothing to do - }, - // While tracking a creature type, you deal 3% increased damage to that creature type. - // Unsure if this stacks with the Prowess 4p - 4: func(agent core.Agent) { - c := agent.GetCharacter() - // Just adding 3% damage to assume the hunter is tracking their target's type - c.PseudoStats.DamageDealtMultiplier *= 1.03 - }, - // Your next Shot ability within 12 sec after Aimed Shot deals 20% more damage. - 6: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - - if !hunter.Talents.AimedShot { - return - } - - procAura := hunter.RegisterAura(core.Aura{ - ActionID: core.ActionID{SpellID: 456379}, - Label: "S03 - Item - T1 - Hunter - Ranged 6P Bonus", - Duration: time.Second * 12, - - OnGain: func(aura *core.Aura, sim *core.Simulation) { - for _, spell := range hunter.Shots { - if spell != nil { - spell.DamageMultiplier *= 1.20 - } - } - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - for _, spell := range hunter.Shots { - if spell != nil { - spell.DamageMultiplier /= 1.20 - } - } - }, - OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) { - if !spell.Flags.Matches(SpellFlagShot) || (aura.RemainingDuration(sim) == aura.Duration && spell.SpellCode == SpellCode_HunterAimedShot) { - return - } - - aura.Deactivate(sim) - }, - }) - - core.MakePermanent(hunter.RegisterAura(core.Aura{ - Label: "S03 - Item - T1 - Hunter - Ranged 6P Bonus Trigger", - OnCastComplete: func(_ *core.Aura, sim *core.Simulation, spell *core.Spell) { - if spell.SpellCode == SpellCode_HunterAimedShot { - procAura.Activate(sim) - } - }, - })) - }, - }, -}) - -var ItemSetDragonstalkerProwess = core.NewItemSet(core.ItemSet{ - Name: "Dragonstalker's Prowess", - Bonuses: map[int32]core.ApplyEffect{ - // Raptor Strike increases the damage done by your next other melee ability within 5 sec by 20%. - 2: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - - affectedSpells := make(map[*core.Spell]bool) - - procAura := hunter.RegisterAura(core.Aura{ - ActionID: core.ActionID{SpellID: 467331}, - Label: "Clever Strikes", - Duration: time.Second * 5, - OnInit: func(aura *core.Aura, sim *core.Simulation) { - for _, spell := range hunter.MeleeSpells { - if spell.SpellCode != SpellCode_HunterRaptorStrikeHit && spell.SpellCode != SpellCode_HunterRaptorStrike && spell.SpellCode != SpellCode_HunterWingClip { - affectedSpells[spell] = true - } - } - }, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - for spell := range affectedSpells { - spell.DamageMultiplier *= 1.20 - } - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - for spell := range affectedSpells { - spell.DamageMultiplier /= 1.20 - } - }, - OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) { - if !affectedSpells[spell] { - return - } - - aura.Deactivate(sim) - }, - }) - - core.MakePermanent(hunter.RegisterAura(core.Aura{ - Label: "S03 - Item - T2 - Hunter - Melee 2P Bonus Trigger", - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if spell.SpellCode == SpellCode_HunterRaptorStrikeHit { - procAura.Activate(sim) - } - }, - })) - }, - // Increases damage dealt by your main hand weapon with Raptor Strike and Wyvern Strike by 20%. - 4: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - - hunter.OnSpellRegistered(func(spell *core.Spell) { - if spell.SpellCode == SpellCode_HunterWyvernStrike || (spell.SpellCode == SpellCode_HunterRaptorStrikeHit && spell.ProcMask.Matches(core.ProcMaskMeleeMHSpecial)) { - spell.DamageMultiplier *= 1.20 - } - }) - }, - // Your periodic damage has a 5% chance to reset the cooldown on one of your Strike abilities. The Strike with the longest remaining cooldown is always chosen. - 6: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - core.MakePermanent(hunter.RegisterAura(core.Aura{ - Label: "S03 - Item - T2 - Hunter - Melee 6P Bonus Trigger", - ActionID: core.ActionID{SpellID: 467334}, - OnPeriodicDamageDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if sim.Proc(0.05, "T2 Melee 6PC Strike Reset") { - maxSpell := hunter.RaptorStrike - - for _, strike := range hunter.Strikes { - if strike.TimeToReady(sim) > maxSpell.TimeToReady(sim) { - maxSpell = strike - } - } - - maxSpell.CD.Reset() - aura.Activate(sim) // used for metrics - } - }, - })) - }, - }, -}) - -var ItemSetDragonstalkerPursuit = core.NewItemSet(core.ItemSet{ - Name: "Dragonstalker's Pursuit", - Bonuses: map[int32]core.ApplyEffect{ - // Your Aimed Shot deals 20% more damage to targets afflicted by one of your trap effects. - 2: func(agent core.Agent) { - // Implemented in aimed_shot.go - }, - // Your damaging Shot abilities deal 10% increased damage if the previous damaging Shot used was different than the current one. - 4: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - - shotSpells := []*core.Spell{} - procAura := hunter.RegisterAura(core.Aura{ - ActionID: core.ActionID{SpellID: 467312}, - Label: "S03 - Item - T2 - Hunter - Ranged 4P Bonus", - Duration: time.Second * 12, - OnInit: func(aura *core.Aura, sim *core.Simulation) { - shotSpells = core.FilterSlice(hunter.Shots, func(s *core.Spell) bool { return s != nil }) - }, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - for _, spell := range shotSpells { - if spell.SpellCode != hunter.LastShot.SpellCode { - spell.DamageMultiplier *= 1.10 - } - } - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - for _, spell := range shotSpells { - if spell.SpellCode != hunter.LastShot.SpellCode { - spell.DamageMultiplier /= 1.10 - } - } - }, - }) - - core.MakePermanent(hunter.RegisterAura(core.Aura{ - Label: "S03 - Item - T2 - Hunter - Ranged 4P Bonus Trigger", - OnCastComplete: func(_ *core.Aura, sim *core.Simulation, spell *core.Spell) { - if spell.Flags.Matches(SpellFlagShot) { - procAura.Deactivate(sim) - hunter.LastShot = spell - procAura.Activate(sim) - } - }, - })) - }, - // Your Serpent Sting damage is increased by 25% of your Attack Power over its normal duration. - 6: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - core.MakePermanent(hunter.RegisterAura(core.Aura{ - Label: "S03 - Item - T2 - Hunter - Ranged 6P Bonus", - OnInit: func(aura *core.Aura, sim *core.Simulation) { - hunter.SerpentStingAPCoeff += 0.25 - }, - })) - }, - }, -}) - -var ItemSetPredatorArmor = core.NewItemSet(core.ItemSet{ - Name: "Predator's Armor", - Bonuses: map[int32]core.ApplyEffect{ - // +20 Attack Power. - 2: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.AttackPower, 20) - c.AddStat(stats.RangedAttackPower, 20) - }, - // Increases the Attack Power your Beast pet gains from your attributes by 20%. - 3: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - if hunter.pet == nil { - return - } - - core.MakePermanent(hunter.RegisterAura(core.Aura{ - Label: "Predator's Armor 3P", - OnInit: func(aura *core.Aura, sim *core.Simulation) { - oldStatInheritance := hunter.pet.GetStatInheritance() - hunter.pet.UpdateStatInheritance( - func(ownerStats stats.Stats) stats.Stats { - s := oldStatInheritance(ownerStats) - s[stats.AttackPower] *= 1.20 - return s - }, - ) - }, - })) - }, - // Increases the Focus regeneration of your Beast pet by 20%. - 5: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - if hunter.pet == nil { - return - } - - hunter.RegisterAura(core.Aura{ - Label: "Predator's Armor 5P", - OnInit: func(aura *core.Aura, sim *core.Simulation) { - hunter.pet.AddFocusRegenMultiplier(0.20) - }, - }) - }, - }, -}) - -var TrappingsOfTheUnseenPath = core.NewItemSet(core.ItemSet{ - Name: "Trappings of the Unseen Path", - Bonuses: map[int32]core.ApplyEffect{ - // Increases the Focus regeneration of your Beast pet by 100%. - 3: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - if hunter.pet == nil { - return - } - - hunter.RegisterAura(core.Aura{ - Label: "Trappings of the Unseen Path 3P", - OnInit: func(aura *core.Aura, sim *core.Simulation) { - hunter.pet.AddFocusRegenMultiplier(1.00) - }, - }) - }, - }, -}) - -var StrikersProwess = core.NewItemSet(core.ItemSet{ - Name: "Striker's Prowess", - Bonuses: map[int32]core.ApplyEffect{ - // Increases Wyvern Strike DoT by 50% - 2: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - if !hunter.Talents.WyvernSting || !hunter.HasRune(proto.HunterRune_RuneBootsWyvernStrike) { - return - } - - hunter.RegisterAura(core.Aura{ - Label: "Striker's Prowess 2P", - OnInit: func(aura *core.Aura, sim *core.Simulation) { - hunter.WyvernStrike.PeriodicDamageMultiplierAdditive += 0.50 - }, - }) - }, - // Increases the Impact Damage of Mongoose Bite and all Strikes by 10% - 4: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - hunter.RegisterAura(core.Aura{ - Label: "Striker's Prowess 4P", - OnInit: func(aura *core.Aura, sim *core.Simulation) { - for _, spell := range hunter.Strikes { - spell.ImpactDamageMultiplierAdditive += 0.10 - } - hunter.RaptorStrikeMH.ImpactDamageMultiplierAdditive += 0.10 - hunter.RaptorStrikeOH.ImpactDamageMultiplierAdditive += 0.10 - hunter.MongooseBite.ImpactDamageMultiplierAdditive += 0.10 - }, - }) - }, - }, -}) - -var StrikersPursuit = core.NewItemSet(core.ItemSet{ - Name: "Striker's Pursuit", - Bonuses: map[int32]core.ApplyEffect{ - // Kill Shot's remaining cooldown is reduced by 50% when used on targets between 20% and 50% health, and has no cooldown while your Rapid Fire is active - 2: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - if !hunter.HasRune(proto.HunterRune_RuneLegsKillShot) { - return - } - - core.MakePermanent(hunter.RegisterAura(core.Aura{ - Label: "Striker's Pursuit 2P", - OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) { - if spell.SpellCode != SpellCode_HunterKillShot { - return - } - - if hunter.HasActiveAura("Rapid Fire") { - spell.CD.Reset() - } else if sim.CurrentTime > sim.Encounter.Duration/2 { - spell.CD.Set(sim.CurrentTime + spell.CD.TimeToReady(sim)/2) - } - }, - })) - }, - // Increases Kill Shot damage by 50% - 4: func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - if !hunter.HasRune(proto.HunterRune_RuneLegsKillShot) { - return - } - - hunter.RegisterAura(core.Aura{ - Label: "Striker's Pursuit 4P", - OnInit: func(aura *core.Aura, sim *core.Simulation) { - hunter.KillShot.DamageMultiplier *= 1.50 - }, - }) - }, - }, -}) diff --git a/sim/hunter/aspects.go b/sim/hunter/aspects.go index c77d24448..02a8c6f3f 100644 --- a/sim/hunter/aspects.go +++ b/sim/hunter/aspects.go @@ -3,31 +3,22 @@ package hunter import ( "strconv" "time" - "github.com/wowsims/classic/sim/core" "github.com/wowsims/classic/sim/core/stats" ) // Utility function to create an Improved Hawk Aura -func (hunter *Hunter) createImprovedHawkAura(auraLabel string, actionID core.ActionID, isMelee bool) *core.Aura { +func (hunter *Hunter) createImprovedHawkAura(auraLabel string, actionID core.ActionID) *core.Aura { bonusMultiplier := 1.3 return hunter.GetOrRegisterAura(core.Aura{ Label: auraLabel, ActionID: actionID, Duration: time.Second * 12, OnGain: func(aura *core.Aura, sim *core.Simulation) { - if isMelee { - aura.Unit.MultiplyMeleeSpeed(sim, bonusMultiplier) - } else { - aura.Unit.MultiplyRangedSpeed(sim, bonusMultiplier) - } + aura.Unit.MultiplyRangedSpeed(sim, bonusMultiplier) }, OnExpire: func(aura *core.Aura, sim *core.Simulation) { - if isMelee { - aura.Unit.MultiplyMeleeSpeed(sim, 1/bonusMultiplier) - } else { - aura.Unit.MultiplyRangedSpeed(sim, 1/bonusMultiplier) - } + aura.Unit.MultiplyRangedSpeed(sim, 1/bonusMultiplier) }, }) } @@ -69,30 +60,33 @@ func (hunter *Hunter) getAspectOfTheHawkSpellConfig(rank int) core.SpellConfig { impHawkAura = hunter.createImprovedHawkAura( "Quick Shots", core.ActionID{SpellID: 6150}, - false, // Ranged ) } // Use utility function to get the attack power based on rank rap := hunter.getMaxAspectOfTheHawkAttackPower(rank) actionID := core.ActionID{SpellID: spellId} - aspectOfTheHawkAura := hunter.NewTemporaryStatsAuraWrapped( - "Aspect of the Hawk"+strconv.Itoa(rank), - actionID, - stats.Stats{stats.RangedAttackPower: rap}, - core.NeverExpires, - func(aura *core.Aura) { - aura.OnSpellHitDealt = func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !spell.ProcMask.Matches(core.ProcMaskRangedAuto) { - return - } - - if impHawkAura != nil && sim.Proc(improvedHawkProcChance, "Imp Aspect of the Hawk") { - impHawkAura.Activate(sim) - } + aspectOfTheHawkAura := hunter.GetOrRegisterAura(core.Aura{ + Label: "Aspect of the Hawk"+strconv.Itoa(rank), + ActionID: actionID, + Duration: core.NeverExpires, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.AddStatDynamic(sim, stats.RangedAttackPower, rap * hunter.AspectOfTheHawkAPMultiplier) + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.AddStatDynamic(sim, stats.RangedAttackPower, -rap * hunter.AspectOfTheHawkAPMultiplier) + }, + OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + if !spell.ProcMask.Matches(core.ProcMaskRangedAuto) { + return } - }) + if impHawkAura != nil && sim.Proc(improvedHawkProcChance, "Imp Aspect of the Hawk") { + impHawkAura.Activate(sim) + } + }, + }) + aspectOfTheHawkAura.NewExclusiveEffect("Aspect", true, core.ExclusiveEffect{}) return core.SpellConfig{ @@ -117,6 +111,7 @@ func (hunter *Hunter) getAspectOfTheHawkSpellConfig(rank int) core.SpellConfig { } func (hunter *Hunter) registerAspectOfTheHawkSpell() { + hunter.AspectOfTheHawkAPMultiplier = 1.0 maxRank := hunter.getMaxHawkRank() config := hunter.getAspectOfTheHawkSpellConfig(maxRank) hunter.GetOrRegisterSpell(config) diff --git a/sim/hunter/hunter.go b/sim/hunter/hunter.go index 9afb4dc5e..8aa9b10a6 100644 --- a/sim/hunter/hunter.go +++ b/sim/hunter/hunter.go @@ -81,7 +81,7 @@ type Hunter struct { NormalizedAmmoDamageBonus float64 // Miscellaneous set bonuses that require extra logic inside of spells - SerpentStingAPCoeff float64 + AspectOfTheHawkAPMultiplier float64 curQueueAura *core.Aura curQueuedAutoSpell *core.Spell diff --git a/sim/hunter/item_sets_pve.go b/sim/hunter/item_sets_pve.go new file mode 100644 index 000000000..e0eb28210 --- /dev/null +++ b/sim/hunter/item_sets_pve.go @@ -0,0 +1,290 @@ +package hunter + +import ( + "time" + "github.com/wowsims/classic/sim/core" + "github.com/wowsims/classic/sim/core/stats" +) + + +/////////////////////////////////////////////////////////////////////////// +// Phase 1 Item Sets - Molten Core +/////////////////////////////////////////////////////////////////////////// + +var ItemSetGiantStalkers = core.NewItemSet(core.ItemSet{ + Name: "Giantstalker Armor", + Bonuses: map[int32]core.ApplyEffect{ + // (3) Set: Increases the range of your Mend Pet spell by 50% and the effect by 10%. Also reduces the cost by 30%. + 3: func(agent core.Agent) { + // Not implemented in sim + }, + // (5) Set: Increases your pet's stamina by 30 and all spell resistances by 40. + 5: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + if hunter.pet == nil { + return + } + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Nature's Ally", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + hunter.pet.AddStatDynamic(sim, stats.Stamina, 30) + hunter.pet.AddResistancesDynamic(sim, 40) + }, + })) + }, + // (8) Set: Increases the damage of Multi-shot and Volley by 15%. + 8: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + hunter.RegisterAura(core.Aura{ + Label: "Improved Volley and Multishot", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + hunter.Volley.BaseDamageMultiplierAdditive += 0.15 + hunter.MultiShot.BaseDamageMultiplierAdditive += 0.15 + }, + }) + }, + }, +}) + +/////////////////////////////////////////////////////////////////////////// +// Phase 2 Item Sets - Dire Maul +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////// +// Phase 3 Item Sets - BWL +/////////////////////////////////////////////////////////////////////////// + +var ItemSetDragonstalkersArmor = core.NewItemSet(core.ItemSet{ + Name: "Dragonstalker Armor", + Bonuses: map[int32]core.ApplyEffect{ + // (3) Set: Increases the Ranged Attack Power bonus of your Aspect of the Hawk by 20%. + 3: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Improved Aspect of the Hawk", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + hunter.AspectOfTheHawkAPMultiplier += 0.25 + }, + })) + }, + // (5) Set: Increases your pet's stamina by 40 and all spell resistances by 60. + 5: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + if hunter.pet == nil { + return + } + + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Nature's Ally", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + hunter.pet.AddStatDynamic(sim, stats.Stamina, 40) + hunter.pet.AddResistancesDynamic(sim, 60) + }, + })) + }, + // (8) Set: You have a chance whenever you deal ranged damage to apply an Expose Weakness effect to the target. Expose Weakness increases the Ranged Attack Power of all attackers against that target by 450 for 7 sec. + 8: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + + debuffAuras := hunter.NewEnemyAuraArray(core.ExposeWeaknessAura) + + core.MakeProcTriggerAura(&hunter.Unit, core.ProcTrigger{ + Name: "T2 - Hunter - Ranged 8P Bonus Trigger", + Callback: core.CallbackOnSpellHitDealt, + Outcome: core.OutcomeLanded, + ProcMask: core.ProcMaskRanged, + PPM: 0.5, + Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + debuffAuras.Get(result.Target).Activate(sim) + }, + }) + }, + }, +}) + +var ItemSetPredatorsArmor = core.NewItemSet(core.ItemSet{ + Name: "Predator's Armor", + Bonuses: map[int32]core.ApplyEffect{ + // (2) Set: +20 Attack Power. + 2: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + hunter.AddStats(stats.Stats{ + stats.AttackPower: 20, + stats.RangedAttackPower: 20, + }) + }, + // (3) Set: Decreases the cooldown of Concussive Shot by 1 sec. + 3: func(agent core.Agent) { + // Concussive Shot not implemented in sim + }, + // (5) Set: Increases the duration of Serpent Sting by 3 sec. + 5: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Improved Serpent Sting", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + for _, dot := range hunter.SerpentSting.Dots() { + if dot != nil { + dot.NumberOfTicks += 1 + dot.RecomputeAuraDuration() + } + } + }, + })) + }, + }, +}) + +/////////////////////////////////////////////////////////////////////////// +// Phase 4 Item Sets - AQ +/////////////////////////////////////////////////////////////////////////// + +// hhttps://www.wowhead.com/classic/item-set=515/beastmaster-armor +var ItemSetBeastmasterArmor = core.NewItemSet(core.ItemSet{ + Name: "Beastmaster Armor", + Bonuses: map[int32]core.ApplyEffect{ + // +8 All Resistances. + 2: func(agent core.Agent) { + c := agent.GetCharacter() + c.AddResistances(8) + }, + // Your normal ranged attacks have a 4% chance of restoring 200 mana. + 4: func(agent core.Agent) { + c := agent.GetCharacter() + actionID := core.ActionID{SpellID: 27785} + manaMetrics := c.NewManaMetrics(actionID) + + core.MakeProcTriggerAura(&c.Unit, core.ProcTrigger{ + ActionID: actionID, + Name: "Hunter Armor Energize", + Callback: core.CallbackOnSpellHitDealt, + Outcome: core.OutcomeLanded, + ProcMask: core.ProcMaskWhiteHit, + ProcChance: 0.04, + Handler: func(sim *core.Simulation, spell *core.Spell, _ *core.SpellResult) { + if c.HasManaBar() { + c.AddMana(sim, 200, manaMetrics) + } + }, + }) + }, + // +40 Attack Power. + 6: func(agent core.Agent) { + c := agent.GetCharacter() + c.AddStats(stats.Stats{ + stats.AttackPower: 40, + stats.RangedAttackPower: 40, + }) + }, + // +200 Armor. + 8: func(agent core.Agent) { + c := agent.GetCharacter() + c.AddStat(stats.Armor, 200) + }, + }, +}) + +// https://www.wowhead.com/classic/item-set=509/strikers-garb +var ItemSetStrikersGarb = core.NewItemSet(core.ItemSet{ + Name: "Striker's Garb", + Bonuses: map[int32]core.ApplyEffect{ + // (3) Set : Reduces the cost of your Arcane Shots by 10%. + 3: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Striker's Arcane Shot Bonus", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + if hunter.ArcaneShot != nil { + hunter.ArcaneShot.Cost.Multiplier -= 10.0 + } + }, + })) + }, + // (5) Set : Reduces the cooldown of your Rapid Fire ability by 2 minutes. + 5: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Striker's Rapid Bonus", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + hunter.RapidFire.CD.Duration -= time.Minute * 2 + }, + })) + }, + }, +}) + +/////////////////////////////////////////////////////////////////////////// +// Phase 5 Item Sets - Naxx +/////////////////////////////////////////////////////////////////////////// + +// https://www.wowhead.com/classic/item-set=530/cryptstalker-armor +var ItemSetCryptstalkerArmor = core.NewItemSet(core.ItemSet{ + Name: "Cryptstalker Armor", + Bonuses: map[int32]core.ApplyEffect{ + // (2) Set : Increases the duration of your Rapid Fire by 4 secs. + 2: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Rapid Fire Duration", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + hunter.RapidFireAura.Duration += time.Second * 4 + }, + })) + }, + // (4) Set : Increases Attack Power by 50 for both you and your pet. + 4: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + hunter.AddStats(stats.Stats{ + stats.AttackPower: 50, + stats.RangedAttackPower: 50, + }) + if hunter.pet == nil { + return + } + + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Stalker's Ally", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + hunter.pet.AddStatsDynamic(sim, stats.Stats{ + stats.AttackPower: 50, + stats.RangedAttackPower: 50, + }) + }, + })) + }, + // (6) Set : Your ranged critical hits cause an Adrenaline Rush, granting you 50 mana. + 6: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + actionID := core.ActionID{SpellID: 28753} + manaMetrics := hunter.NewManaMetrics(actionID) + + hunter.RegisterAura(core.Aura{ + Label: "Adrenaline Rush", + Duration: core.NeverExpires, + OnReset: func(aura *core.Aura, sim *core.Simulation) { + aura.Activate(sim) + }, + OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + if spell.ProcMask.Matches(core.ProcMaskRanged) && result.DidCrit() { + hunter.AddMana(sim, 50, manaMetrics) + } + }, + }) + + }, + // (8) Set : Reduces the mana cost of your Multi-Shot and Aimed Shot by 20. + 8: func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Cryptstalker Aimed and Multishot Bonus", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + if hunter.AimedShot != nil { + hunter.AimedShot.Cost.FlatModifier -= 20.0 + } + hunter.MultiShot.Cost.FlatModifier -= 20.0 + }, + })) + }, + }, +}) diff --git a/sim/hunter/items.go b/sim/hunter/items.go index b1056bc9d..384b4680c 100644 --- a/sim/hunter/items.go +++ b/sim/hunter/items.go @@ -8,11 +8,46 @@ import ( ) const ( + RenatakisCharmofBeasts = 19953 DevilsaurEye = 19991 DevilsaurTooth = 19992 ) func init() { + // Use: Instantly clears the cooldowns of Aimed Shot, Multishot, Volley, and Arcane Shot. (cooldown 3 min) + core.NewItemEffect(RenatakisCharmofBeasts, func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + + spell := hunter.RegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{ItemID: RenatakisCharmofBeasts}, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagOffensiveEquipment, + + Cast: core.CastConfig{ + CD: core.Cooldown{ + Timer: hunter.NewTimer(), + Duration: time.Second * 180, + }, + SharedCD: core.Cooldown{ + Timer: hunter.GetOffensiveTrinketCD(), + Duration: time.Second * 10, + }, + }, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + hunter.AimedShot.CD.Reset() + hunter.MultiShot.CD.Reset() + hunter.Volley.CD.Reset() + hunter.ArcaneShot.CD.Reset() + }, + }) + + hunter.AddMajorCooldown(core.MajorCooldown{ + Type: core.CooldownTypeDPS, + Spell: spell, + }) + }) + core.NewItemEffect(DevilsaurEye, func(agent core.Agent) { hunter := agent.(HunterAgent).GetHunter() diff --git a/sim/hunter/rapid_fire.go b/sim/hunter/rapid_fire.go index 92d71ec25..c43b48f4e 100644 --- a/sim/hunter/rapid_fire.go +++ b/sim/hunter/rapid_fire.go @@ -38,10 +38,6 @@ func (hunter *Hunter) registerRapidFire() { Timer: hunter.NewTimer(), Duration: cooldown, }, - SharedCD: core.Cooldown{ - Timer: hunter.GetAttackSpeedBuffCD(), - Duration: cooldown, - }, }, ApplyEffects: func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) { diff --git a/sim/hunter/serpent_sting.go b/sim/hunter/serpent_sting.go index 1490c3fba..f3fdeb7db 100644 --- a/sim/hunter/serpent_sting.go +++ b/sim/hunter/serpent_sting.go @@ -76,7 +76,6 @@ func (hunter *Hunter) getSerpentStingConfig(rank int) core.SpellConfig { } func (hunter *Hunter) registerSerpentStingSpell() { - hunter.SerpentStingAPCoeff = 0 maxRank := core.TernaryInt(core.IncludeAQ, 9, 8) for rank := maxRank; rank >= 0; rank-- { diff --git a/sim/rogue/items.go b/sim/rogue/items.go index 3e74b62f4..c764751ab 100644 --- a/sim/rogue/items.go +++ b/sim/rogue/items.go @@ -49,7 +49,6 @@ func init() { return rogue.CurrentEnergy() <= 40 }, }) - }) // https://www.wowhead.com/classic/item=230250/venomous-totem diff --git a/ui/hunter/presets.ts b/ui/hunter/presets.ts index 475badde8..1b95d26d4 100644 --- a/ui/hunter/presets.ts +++ b/ui/hunter/presets.ts @@ -120,7 +120,7 @@ export const DefaultRaidBuffs = RaidBuffs.create({ fireResistanceTotem: true, giftOfTheWild: TristateEffect.TristateEffectImproved, graceOfAirTotem: TristateEffect.TristateEffectImproved, - leaderOfThePack: true, + leaderOfThePack: false, manaSpringTotem: TristateEffect.TristateEffectRegular, powerWordFortitude: TristateEffect.TristateEffectImproved, strengthOfEarthTotem: TristateEffect.TristateEffectImproved, @@ -131,13 +131,13 @@ export const DefaultIndividualBuffs = IndividualBuffs.create({ blessingOfMight: TristateEffect.TristateEffectRegular, blessingOfWisdom: TristateEffect.TristateEffectImproved, fengusFerocity: true, - moldarsMoxie: true, + moldarsMoxie: false, rallyingCryOfTheDragonslayer: true, saygesFortune: SaygesFortune.SaygesDamage, - slipkiksSavvy: true, + slipkiksSavvy: false, songflowerSerenade: true, - spiritOfZandalar: true, - warchiefsBlessing: true, + spiritOfZandalar: false, + warchiefsBlessing: false, }); export const DefaultDebuffs = Debuffs.create({