diff --git a/proto/hunter.proto b/proto/hunter.proto index 217f68e5ce..ee95c11e36 100644 --- a/proto/hunter.proto +++ b/proto/hunter.proto @@ -57,51 +57,6 @@ message HunterTalents { bool wyvern_sting = 46; } -message HunterPetTalents { - // Cunning - int32 cobra_reflexes = 1; - bool dive = 2; - int32 great_stamina = 3; - int32 natural_armor = 4; - bool boars_speed = 5; - int32 mobility = 6; - int32 owls_focus = 7; - int32 spiked_collar = 8; - int32 culling_the_herd = 9; - int32 lionhearted = 10; - bool carrion_feeder = 11; - int32 great_resistance = 12; - int32 cornered = 13; - int32 feeding_frenzy = 14; - bool wolverine_bite = 15; - bool roar_of_recovery = 16; - bool bullheaded = 17; - int32 grace_of_the_mantis = 18; - int32 wild_hunt = 19; - bool roar_of_sacrifice = 20; - - // Ferocity - int32 improved_cower = 21; - int32 bloodthirsty = 22; - bool heart_of_the_pheonix = 23; - int32 spiders_bite = 24; - bool rabid = 25; - bool lick_your_wounds = 26; - bool call_of_the_wild = 27; - int32 shark_attack = 28; - - // Tenacity - bool charge = 29; - int32 blood_of_the_rhino = 30; - int32 pet_barding = 31; - int32 guard_dog = 32; - bool thunderstomp = 33; - bool last_stand = 34; - bool taunt = 35; - bool intervene = 36; - int32 silverback = 37; -} - message Hunter { message Rotation { enum RotationType { @@ -165,16 +120,15 @@ message Hunter { Owl = 6; Boar = 7; CarrionBird = 8; - CoreHound = 10; - Crab = 11; - Crocolisk = 12; - Gorilla = 15; - Hyena = 16; - Raptor = 17; - Scorpid = 18; - Spider = 21; - Tallstrider = 24; - Turtle = 25; + Crab = 9; + Crocolisk = 10; + Gorilla = 11; + Hyena = 12; + Raptor = 13; + Scorpid = 14; + Spider = 15; + Tallstrider = 16; + Turtle = 17; } enum PetAttackSpeed { @@ -191,16 +145,13 @@ message Hunter { } PetType pet_type = 3; - HunterPetTalents pet_talents = 4; - double pet_uptime = 5; - - double sniper_training_uptime = 6; + double pet_uptime = 4; - double pet_attack_speed_old = 7; + double pet_attack_speed_old = 5; - bool new_raptor_strike = 8; + bool new_raptor_strike = 6; - PetAttackSpeed pet_attack_speed = 9; + PetAttackSpeed pet_attack_speed = 7; } Options options = 2; } diff --git a/sim/core/attack.go b/sim/core/attack.go index a1ca049dee..d101c248e5 100644 --- a/sim/core/attack.go +++ b/sim/core/attack.go @@ -842,6 +842,14 @@ func (aa *AutoAttacks) StopMeleeUntil(sim *Simulation, readyAt time.Duration, de } } +func (aa *AutoAttacks) StopRangedUntil(sim *Simulation, readyAt time.Duration) { + if !aa.AutoSwingRanged { // if not auto swinging, don't auto restart. + return + } + aa.ranged.swingAt = readyAt + aa.ranged.curSwingDuration + sim.rescheduleWeaponAttack(aa.ranged.swingAt) +} + // Delays all swing timers for the specified amount. Only used by Slam. func (aa *AutoAttacks) DelayMeleeBy(sim *Simulation, delay time.Duration) { if delay <= 0 { diff --git a/sim/core/debuffs.go b/sim/core/debuffs.go index 2b856a8db4..851b5f53f8 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 8927f82ef7..7d347e1e42 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,225 @@ 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-BloodGuard'sChainGauntlets-16530" + value: { + dps: 292.04584 + tps: 144.84534 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-BloodGuard'sChainVices-22862" + value: { + dps: 293.022 + tps: 145.8215 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-Champion'sPursuance" + value: { + dps: 298.93967 + tps: 151.7376 + } +} +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-FieldMarshal'sPursuit" + value: { + dps: 306.67445 + tps: 159.47237 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-General'sChainGloves-16571" + value: { + dps: 294.73892 + tps: 147.53842 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-GiantstalkerArmor" + value: { + dps: 302.92974 + tps: 155.72767 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-Knight-Lieutenant'sChainGauntlets-16403" + value: { + dps: 292.04584 + tps: 144.84534 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-Knight-Lieutenant'sChainVices-23279" + value: { + dps: 293.022 + tps: 145.8215 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-LieutenantCommander'sPursuance" + value: { + dps: 298.93967 + tps: 151.7376 + } +} +dps_results: { + key: "TestP1Hunter-Phase1-AllItems-Marshal'sChainGrips-16463" + value: { + dps: 294.73892 + tps: 147.53842 + } +} +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-AllItems-Warlord'sPursuit" + value: { + dps: 306.67445 + tps: 159.47237 } } 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 2802ac1327..0000000000 --- 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/_item_sets_pvp.go b/sim/hunter/_item_sets_pvp.go deleted file mode 100644 index f5035860c0..0000000000 --- a/sim/hunter/_item_sets_pvp.go +++ /dev/null @@ -1,220 +0,0 @@ -package hunter - -import ( - "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/stats" -) - -/////////////////////////////////////////////////////////////////////////// -// SoD Phase 3 Item Sets -/////////////////////////////////////////////////////////////////////////// - -var ItemSetBloodGuardsChain = core.NewItemSet(core.ItemSet{ - Name: "Blood Guard's Chain", - Bonuses: map[int32]core.ApplyEffect{ - 3: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Stamina, 15) - }, - 6: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.AttackPower, 30) - c.AddStat(stats.RangedAttackPower, 20) - }, - }, -}) - -var ItemSetKnightLieutenantsChain = core.NewItemSet(core.ItemSet{ - Name: "Knight-Lieutenant's Chain", - Bonuses: map[int32]core.ApplyEffect{ - 3: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Stamina, 15) - }, - 6: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.AttackPower, 30) - c.AddStat(stats.RangedAttackPower, 20) - }, - }, -}) - -/////////////////////////////////////////////////////////////////////////// -// SoD Phase 4 Item Sets -/////////////////////////////////////////////////////////////////////////// - -var ItemSetChampionsPursuit = core.NewItemSet(core.ItemSet{ - Name: "Champion's Pursuit", - Bonuses: map[int32]core.ApplyEffect{ - // +20 Agility. - 2: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Agility, 20) - }, - // Reduces the cooldown of your Concussive Shot by 1 sec. - 4: func(agent core.Agent) { - // Nothing to do - }, - // +20 Stamina. - 6: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Stamina, 20) - }, - }, -}) - -var ItemSetChampionsProwess = core.NewItemSet(core.ItemSet{ - Name: "Champion's Prowess", - 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, - }) - }, - // Increases the duration of your Wing Clip by 2 sec. - 4: func(agent core.Agent) { - // Nothing to do - }, - // +20 Stamina. - 6: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Stamina, 20) - }, - }, -}) - -var ItemSetLieutenantCommandersPursuit = core.NewItemSet(core.ItemSet{ - Name: "Lieutenant Commander's Pursuit", - Bonuses: map[int32]core.ApplyEffect{ - // +20 Agility. - 2: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Agility, 20) - }, - // Reduces the cooldown of your Concussive Shot by 1 sec. - 4: func(agent core.Agent) { - // Nothing to do - }, - // +20 Stamina. - 6: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Stamina, 20) - }, - }, -}) - -var ItemSetLieutenantCommandersProwess = core.NewItemSet(core.ItemSet{ - Name: "Lieutenant Commander's Prowess", - 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, - }) - }, - // Increases the duration of your Wing Clip by 2 sec. - 4: func(agent core.Agent) { - // Nothing to do - }, - // +20 Stamina. - 6: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Stamina, 20) - }, - }, -}) - -/////////////////////////////////////////////////////////////////////////// -// SoD Phase 5 Item Sets -/////////////////////////////////////////////////////////////////////////// - -var ItemSetWarlordsPursuit = core.NewItemSet(core.ItemSet{ - Name: "Warlord's Pursuit", - Bonuses: map[int32]core.ApplyEffect{ - // 20 Stamina - 2: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Stamina, 20) - }, - // Reduces the cooldown of your Concussive Shot by 1 sec. - 4: func(agent core.Agent) { - // Nothing to do - }, - // +20 Agi - 6: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Agility, 20) - }, - }, -}) - -var ItemSetWarlordsProwess = core.NewItemSet(core.ItemSet{ - Name: "Warlord's Prowess", - Bonuses: map[int32]core.ApplyEffect{ - // +20 stamina - 2: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Stamina, 20) - }, - // Increases the duration of your Wing Clip by 2 sec. - 4: func(agent core.Agent) { - // Nothing to do - }, - // +40 Agnostic Attack Power - 6: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStats(stats.Stats{ - stats.AttackPower: 40, - stats.RangedAttackPower: 40, - }) - }, - }, -}) - -var ItemSetFieldMarshalsPursuit = core.NewItemSet(core.ItemSet{ - Name: "Field Marshal's Pursuit", - Bonuses: map[int32]core.ApplyEffect{ - // 20 stamina - 2: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Stamina, 20) - }, - // Reduces the cooldown of your Concussive Shot by 1 sec. - 4: func(agent core.Agent) { - // Nothing to do - }, - // +20 Agi - 6: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Agility, 20) - }, - }, -}) - -var ItemSetFieldMarshalsProwess = core.NewItemSet(core.ItemSet{ - Name: "Field Marshal's Prowess", - Bonuses: map[int32]core.ApplyEffect{ - //20 stamina - 2: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStat(stats.Stamina, 20) - }, - // Increases the duration of your Wing Clip by 2 sec. - 4: func(agent core.Agent) { - // Nothing to do - }, - // +40 Agnostic Attack Power. - 6: func(agent core.Agent) { - c := agent.GetCharacter() - c.AddStats(stats.Stats{ - stats.AttackPower: 40, - stats.RangedAttackPower: 40, - }) - }, - }, -}) diff --git a/sim/hunter/aimed_shot.go b/sim/hunter/aimed_shot.go index 681148f632..e619f6eae1 100644 --- a/sim/hunter/aimed_shot.go +++ b/sim/hunter/aimed_shot.go @@ -37,8 +37,9 @@ func (hunter *Hunter) getAimedShotConfig(rank int, timer *core.Timer) core.Spell Timer: timer, Duration: time.Second * 6, }, - ModifyCast: func(_ *core.Simulation, spell *core.Spell, cast *core.Cast) { + ModifyCast: func(sim *core.Simulation, spell *core.Spell, cast *core.Cast) { cast.CastTime = spell.CastTime() + hunter.Unit.AutoAttacks.CancelAutoSwing(sim) }, IgnoreHaste: true, // Hunter GCD is locked at 1.5s CastTime: func(spell *core.Spell) time.Duration { @@ -61,6 +62,7 @@ func (hunter *Hunter) getAimedShotConfig(rank int, timer *core.Timer) core.Spell baseDamage result := spell.CalcDamage(sim, target, baseDamage, spell.OutcomeRangedHitAndCrit) + hunter.Unit.AutoAttacks.EnableAutoSwing(sim) spell.WaitTravelTime(sim, func(s *core.Simulation) { spell.DealDamage(sim, result) }) diff --git a/sim/hunter/aspects.go b/sim/hunter/aspects.go index ceddc16ab0..02a8c6f3fa 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,70 +111,8 @@ 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) -} - -func (hunter *Hunter) registerAspectOfTheViperSpell() { - actionID := core.ActionID{SpellID: 415423} - manaMetrics := hunter.NewManaMetrics(actionID) - - var manaPA *core.PendingAction - - baseManaRegenMultiplier := 0.02 - - aspectOfTheViperAura := hunter.GetOrRegisterAura(core.Aura{ - Label: "Aspect of the Viper", - ActionID: actionID, - Duration: core.NeverExpires, - - OnGain: func(aura *core.Aura, sim *core.Simulation) { - hunter.PseudoStats.DamageDealtMultiplier *= 0.9 - - manaPA = core.StartPeriodicAction(sim, core.PeriodicActionOptions{ - Period: time.Second * 3, - OnAction: func(s *core.Simulation) { - hunter.AddMana(sim, hunter.MaxMana()*0.1, manaMetrics) - }, - }) - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - hunter.PseudoStats.DamageDealtMultiplier /= 0.9 - manaPA.Cancel(sim) - }, - - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if spell == hunter.AutoAttacks.RangedAuto() { - manaPerRangedHitMultiplier := baseManaRegenMultiplier * hunter.AutoAttacks.Ranged().SwingSpeed - hunter.AddMana(sim, hunter.MaxMana()*manaPerRangedHitMultiplier, manaMetrics) - } else if spell == hunter.AutoAttacks.MHAuto() { - manaPerMHHitMultiplier := baseManaRegenMultiplier * hunter.AutoAttacks.MH().SwingSpeed - hunter.AddMana(sim, hunter.MaxMana()*manaPerMHHitMultiplier, manaMetrics) - } else if spell == hunter.AutoAttacks.OHAuto() { - manaPerOHHitMultiplier := baseManaRegenMultiplier * hunter.AutoAttacks.OH().SwingSpeed - hunter.AddMana(sim, hunter.MaxMana()*manaPerOHHitMultiplier, manaMetrics) - } - }, - }) - - aspectOfTheViperAura.NewExclusiveEffect("Aspect", true, core.ExclusiveEffect{}) - - hunter.GetOrRegisterSpell(core.SpellConfig{ - ActionID: actionID, - Flags: core.SpellFlagAPL, - - Cast: core.CastConfig{ - DefaultCast: core.Cast{ - GCD: core.GCDDefault, - }, - }, - ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool { - return !aspectOfTheViperAura.IsActive() - }, - - ApplyEffects: func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) { - aspectOfTheViperAura.Activate(sim) - }, - }) -} +} \ No newline at end of file diff --git a/sim/hunter/hunter.go b/sim/hunter/hunter.go index 957fde5410..8aa9b10a68 100644 --- a/sim/hunter/hunter.go +++ b/sim/hunter/hunter.go @@ -47,7 +47,6 @@ const ( SpellCode_HunterPetClaw SpellCode_HunterPetBite SpellCode_HunterPetLightningBreath - SpellCode_HunterPetLavaBreath SpellCode_HunterPetScreech SpellCode_HunterPetScorpidPoison ) @@ -82,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 @@ -134,13 +133,6 @@ func (hunter *Hunter) AddRaidBuffs(raidBuffs *proto.RaidBuffs) { }[hunter.Level]) } - // Hunter gains an additional 10% stats from Aspect of the Lion - statMultiply := 1.1 - hunter.MultiplyStat(stats.Strength, statMultiply) - hunter.MultiplyStat(stats.Stamina, statMultiply) - hunter.MultiplyStat(stats.Agility, statMultiply) - hunter.MultiplyStat(stats.Intellect, statMultiply) - hunter.MultiplyStat(stats.Spirit, statMultiply) } func (hunter *Hunter) AddPartyBuffs(_ *proto.PartyBuffs) { } @@ -163,7 +155,6 @@ func (hunter *Hunter) Initialize() { }) hunter.registerAspectOfTheHawkSpell() - hunter.registerAspectOfTheViperSpell() multiShotTimer := hunter.NewTimer() arcaneShotTimer := hunter.NewTimer() @@ -185,7 +176,6 @@ func (hunter *Hunter) Initialize() { hunter.registerImmolationTrapSpell(traps) hunter.registerFreezingTrapSpell(traps) - // hunter.registerKillCommand() hunter.registerRapidFire() } diff --git a/sim/hunter/item_sets_pve.go b/sim/hunter/item_sets_pve.go new file mode 100644 index 0000000000..e0eb28210d --- /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/item_sets_pvp.go b/sim/hunter/item_sets_pvp.go new file mode 100644 index 0000000000..3a7649f40e --- /dev/null +++ b/sim/hunter/item_sets_pvp.go @@ -0,0 +1,96 @@ +package hunter + +import ( + "github.com/wowsims/classic/sim/core" + "github.com/wowsims/classic/sim/core/stats" +) + +/////////////////////////////////////////////////////////////////////////// +// Classic Phase 2 +/////////////////////////////////////////////////////////////////////////// + +// https://www.wowhead.com/classic/item-set=543/champions-pursuance +var ItemSetChampionsPursuance = core.NewItemSet(core.ItemSet{ + Name: "Champion's Pursuance", + Bonuses: map[int32]core.ApplyEffect{ + // +20 Agility. + 2: func(agent core.Agent) { + c := agent.GetCharacter() + c.AddStat(stats.Agility, 20) + }, + // Reduces the cooldown of your Concussive Shot by 1 sec. + 4: func(agent core.Agent) { + // Nothing to do + }, + // +20 Stamina. + 6: func(agent core.Agent) { + c := agent.GetCharacter() + c.AddStat(stats.Stamina, 20) + }, + }, +}) + +// https://www.wowhead.com/classic/item-set=550/lieutenant-commanders-pursuance +var ItemSetLieutenantCommandersPursuance = core.NewItemSet(core.ItemSet{ + Name: "Lieutenant Commander's Pursuance", + Bonuses: map[int32]core.ApplyEffect{ + // +20 Agility. + 2: func(agent core.Agent) { + c := agent.GetCharacter() + c.AddStat(stats.Agility, 20) + }, + // Reduces the cooldown of your Concussive Shot by 1 sec. + 4: func(agent core.Agent) { + // Nothing to do + }, + // +20 Stamina. + 6: func(agent core.Agent) { + c := agent.GetCharacter() + c.AddStat(stats.Stamina, 20) + }, + }, +}) + +/////////////////////////////////////////////////////////////////////////// +// Classic Phase 3 +/////////////////////////////////////////////////////////////////////////// + +var ItemSetWarlordsPursuit = core.NewItemSet(core.ItemSet{ + Name: "Warlord's Pursuit", + Bonuses: map[int32]core.ApplyEffect{ + // 20 Stamina + 2: func(agent core.Agent) { + c := agent.GetCharacter() + c.AddStat(stats.Stamina, 20) + }, + // Reduces the cooldown of your Concussive Shot by 1 sec. + 4: func(agent core.Agent) { + // Nothing to do + }, + // +20 Agi + 6: func(agent core.Agent) { + c := agent.GetCharacter() + c.AddStat(stats.Agility, 20) + }, + }, +}) + +var ItemSetFieldMarshalsPursuit = core.NewItemSet(core.ItemSet{ + Name: "Field Marshal's Pursuit", + Bonuses: map[int32]core.ApplyEffect{ + // 20 stamina + 2: func(agent core.Agent) { + c := agent.GetCharacter() + c.AddStat(stats.Stamina, 20) + }, + // Reduces the cooldown of your Concussive Shot by 1 sec. + 4: func(agent core.Agent) { + // Nothing to do + }, + // +20 Agi + 6: func(agent core.Agent) { + c := agent.GetCharacter() + c.AddStat(stats.Agility, 20) + }, + }, +}) \ No newline at end of file diff --git a/sim/hunter/items.go b/sim/hunter/items.go index 666c4cb3f1..dc26a75a01 100644 --- a/sim/hunter/items.go +++ b/sim/hunter/items.go @@ -8,52 +8,117 @@ import ( ) const ( + KnightLieutenantsChainGauntlets = 16403 + BloodGuardsChainGauntlets = 16530 + MarshalsChainGrips = 16463 + GeneralsChainGloves = 16571 + RenatakisCharmofBeasts = 19953 DevilsaurEye = 19991 DevilsaurTooth = 19992 - // SignetOfBeasts = 209823 - // BloodlashBow = 216516 - // GurubashiPitFightersBow = 221450 - // BloodChainVices = 227075 - // KnightChainVices = 227077 - // BloodChainGrips = 227081 - // KnightChainGrips = 227087 - // WhistleOfTheBeast = 228432 - // ArcaneInfusedGem = 230237 - // RenatakisCharmOfRavaging = 231288 - // MaelstromsWrath = 231320 - // ZandalarPredatorsMantle = 231321 - // ZandalarPredatorsBelt = 231322 - // ZandalarPredatorsBracers = 231323 - // MarshalChainGrips = 231560 - // GeneralChainGrips = 231569 - // GeneralChainVices = 231575 - // MarshalChainVices = 231578 - // Kestrel = 231754 - // Peregrine = 231755 - // CloakOfTheUnseenPath = 233420 - // ScytheOfTheUnseenPath = 233421 - // SignetOfTheUnseenPath = 233422 + KnightLieutenantsChainVices= 23279 + BloodGuardsChainVices = 22862 ) -/* func applyRaptorStrikeDamageEffect(agent core.Agent, multiplier float64) { - hunter := agent.(HunterAgent).GetHunter() - hunter.OnSpellRegistered(func(spell *core.Spell) { - if spell.SpellCode == SpellCode_HunterRaptorStrikeHit { - spell.DamageMultiplier *= multiplier - } +func init() { + // Equip: Reduces the mana cost of your Arcane Shot by 15. + core.NewItemEffect(KnightLieutenantsChainGauntlets, func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Arcane Shot Mana Reduction", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + if hunter.ArcaneShot != nil { + hunter.ArcaneShot.Cost.FlatModifier -= 15.0 + } + }, + })) }) -} - -func applyMultiShotDamageEffect(agent core.Agent, multiplier float64) { - hunter := agent.(HunterAgent).GetHunter() - hunter.OnSpellRegistered(func(spell *core.Spell) { - if spell.SpellCode == SpellCode_HunterMultiShot { - spell.DamageMultiplier *= multiplier - } + // Equip: Reduces the mana cost of your Arcane Shot by 15. + core.NewItemEffect(BloodGuardsChainGauntlets, func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Arcane Shot Mana Reduction", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + if hunter.ArcaneShot != nil { + hunter.ArcaneShot.Cost.FlatModifier -= 15.0 + } + }, + })) }) -} */ + + // Equip: Increases the damage done by your Multi-Shot by 4% + core.NewItemEffect(MarshalsChainGrips, func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Multi-Shot Damage Increase", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + hunter.MultiShot.BaseDamageMultiplierAdditive += 0.04 + }, + })) + }) + // Equip: Increases the damage done by your Multi-Shot by 4% + core.NewItemEffect(GeneralsChainGloves, func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Multi-Shot Damage Increase", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + hunter.MultiShot.BaseDamageMultiplierAdditive += 0.04 + }, + })) + }) + // Equip: Increases the damage done by your Multi-Shot by 4% + core.NewItemEffect(KnightLieutenantsChainVices, func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Multi-Shot Damage Increase", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + hunter.MultiShot.BaseDamageMultiplierAdditive += 0.04 + }, + })) + }) + // Equip: Increases the damage done by your Multi-Shot by 4% + core.NewItemEffect(BloodGuardsChainVices, func(agent core.Agent) { + hunter := agent.(HunterAgent).GetHunter() + core.MakePermanent(hunter.RegisterAura(core.Aura{ + Label: "Multi-Shot Damage Increase", + OnInit: func(aura *core.Aura, sim *core.Simulation) { + hunter.MultiShot.BaseDamageMultiplierAdditive += 0.04 + }, + })) + }) + // 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() -func init() { + 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() @@ -169,386 +234,4 @@ func init() { }, }) }) - - /* core.NewItemEffect(SignetOfBeasts, func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - if hunter.pet != nil { - hunter.pet.PseudoStats.DamageDealtMultiplier *= 1.01 - } - }) - - core.NewItemEffect(BloodlashBow, func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - hunter.newBloodlashProcItem(50, 436471) - }) - - core.NewItemEffect(GurubashiPitFightersBow, func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - hunter.newBloodlashProcItem(75, 446723) - }) - - // https://www.wowhead.com/classic/item=228432/whistle-of-the-beast - // Use: Your pet's next attack is guaranteed to critically strike if that attack is capable of striking critically. (1 Min Cooldown) - core.NewItemEffect(WhistleOfTheBeast, func(agent core.Agent) { - hunter := agent.(HunterAgent).GetHunter() - - if hunter.pet == nil { - return - } - - hunter.pet.PseudoStats.DamageDealtMultiplier *= 1.03 - hunter.pet.MultiplyStat(stats.Health, 1.03) - hunter.pet.MultiplyStat(stats.Armor, 1.10) - hunter.pet.AddStat(stats.MeleeCrit, 2*core.CritRatingPerCritChance) - hunter.pet.AddStat(stats.SpellCrit, 2*core.SpellCritRatingPerCritChance) - - actionID := core.ActionID{ItemID: WhistleOfTheBeast} - - trackingAura := hunter.GetOrRegisterAura(core.Aura{ - Label: "Whistle of the Beast Hunter", - ActionID: actionID, - Duration: core.NeverExpires, - }) - - aura := hunter.pet.GetOrRegisterAura(core.Aura{ - Label: "Whistle of the Beast", - ActionID: actionID, - Duration: core.NeverExpires, - - OnGain: func(aura *core.Aura, sim *core.Simulation) { - if hunter.pet.focusDump != nil { - hunter.pet.focusDump.BonusCritRating += 100 - } - if hunter.pet.specialAbility != nil { - hunter.pet.specialAbility.BonusCritRating += 100 - } - trackingAura.Activate(sim) - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - if hunter.pet.focusDump != nil { - hunter.pet.focusDump.BonusCritRating -= 100 - } - if hunter.pet.specialAbility != nil { - hunter.pet.specialAbility.BonusCritRating -= 100 - } - trackingAura.Deactivate(sim) - }, - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if spell == hunter.pet.focusDump || spell == hunter.pet.specialAbility { - aura.Deactivate(sim) - } - }, - }) - - spell := hunter.GetOrRegisterSpell(core.SpellConfig{ - ActionID: actionID, - Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagOffensiveEquipment, - - Cast: core.CastConfig{ - CD: core.Cooldown{ - Timer: hunter.NewTimer(), - Duration: time.Minute * 1, - }, - }, - ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool { - return hunter.pet.IsEnabled() - }, - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - aura.Activate(sim) - }, - }) - - hunter.AddMajorCooldown(core.MajorCooldown{ - Spell: spell, - Type: core.CooldownTypeDPS, - ShouldActivate: func(sim *core.Simulation, character *core.Character) bool { - return hunter.pet != nil && hunter.pet.IsEnabled() - }, - }) - }) - - core.NewItemEffect(BloodChainGrips, func(agent core.Agent) { - applyRaptorStrikeDamageEffect(agent, 1.04) - }) - - core.NewItemEffect(KnightChainGrips, func(agent core.Agent) { - applyRaptorStrikeDamageEffect(agent, 1.04) - }) - - core.NewItemEffect(GeneralChainGrips, func(agent core.Agent) { - applyRaptorStrikeDamageEffect(agent, 1.04) - }) - - core.NewItemEffect(MarshalChainGrips, func(agent core.Agent) { - applyRaptorStrikeDamageEffect(agent, 1.04) - }) - - core.NewItemEffect(BloodChainVices, func(agent core.Agent) { - applyMultiShotDamageEffect(agent, 1.04) - }) - - core.NewItemEffect(KnightChainVices, func(agent core.Agent) { - applyMultiShotDamageEffect(agent, 1.04) - }) - - core.NewItemEffect(GeneralChainVices, func(agent core.Agent) { - applyMultiShotDamageEffect(agent, 1.04) - }) - - core.NewItemEffect(MarshalChainVices, func(agent core.Agent) { - applyMultiShotDamageEffect(agent, 1.04) - }) - - core.NewItemEffect(MaelstromsWrath, func(a core.Agent) { - hunter := a.(HunterAgent).GetHunter() - if hunter.pet == nil { - return - } - - hunter.pet.PseudoStats.DamageDealtMultiplier *= 1.02 - - if !hunter.Talents.BestialWrath { - return - } - - hunter.RegisterAura(core.Aura{ - Label: "Maelstroms's Wrath Bestial Wrath", - OnInit: func(aura *core.Aura, sim *core.Simulation) { - hunter.BestialWrathPetAura.Duration += (time.Second * 3) - }, - }) - }) - - core.NewItemEffect(ZandalarPredatorsMantle, func(a core.Agent) { - hunter := a.(HunterAgent).GetHunter() - if hunter.pet == nil { - return - } - - hunter.pet.PseudoStats.DamageDealtMultiplier *= 1.03 - }) - - core.NewItemEffect(ZandalarPredatorsBelt, func(a core.Agent) { - hunter := a.(HunterAgent).GetHunter() - if hunter.pet == nil { - return - } - - hunter.pet.PseudoStats.DamageDealtMultiplier *= 1.02 - }) - - core.NewItemEffect(ZandalarPredatorsBracers, func(a core.Agent) { - hunter := a.(HunterAgent).GetHunter() - - if hunter.pet == nil { - return - } - - hunter.pet.PseudoStats.DamageDealtMultiplier *= 1.01 - }) - - // https://www.wowhead.com/classic/item=231755/peregrine - // Chance on hit: Instantly gain 1 extra attack with both weapons. - // Main-hand attack is treated like a normal extra-attack, Off-hand attack is a spell that uses your off-hand damage but won't glance - core.NewItemEffect(Peregrine, func(agent core.Agent) { - character := agent.GetCharacter() - peregrineOHAttack := character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 469140}, - SpellSchool: core.SpellSchoolPhysical, - DefenseType: core.DefenseTypeMelee, - ProcMask: core.ProcMaskMeleeOHSpecial, - Flags: core.SpellFlagMeleeMetrics, - - DamageMultiplier: 1, - ThreatMultiplier: 1, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - damage := character.OHWeaponDamage(sim, spell.MeleeAttackPower()) * character.AutoAttacks.OHConfig().DamageMultiplier - spell.CalcAndDealDamage(sim, target, damage, spell.OutcomeMeleeWeaponSpecialHitAndCrit) - }, - }) - core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ - Name: "Peregrine Trigger", - Callback: core.CallbackOnSpellHitDealt, - Outcome: core.OutcomeLanded, - ProcMask: core.ProcMaskMeleeOH, - SpellFlagsExclude: core.SpellFlagSuppressWeaponProcs, - PPM: 1.0, - Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - character.AutoAttacks.ExtraMHAttackProc(sim, 1, core.ActionID{SpellID: 469140}, spell) - peregrineOHAttack.Cast(sim, result.Target) - }, - }) - }) - - itemhelpers.CreateWeaponProcAura(Kestrel, "Kestrel", 1, func(character *core.Character) *core.Aura { - return character.GetOrRegisterAura(core.Aura{ - Label: "Kestrel Move Speed Aura", - ActionID: core.ActionID{SpellID: 469148}, - Duration: time.Second * 10, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - character.AddMoveSpeedModifier(&aura.ActionID, 1.40) - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - character.RemoveMoveSpeedModifier(&aura.ActionID) - }, - }) - }) - - // https://www.wowhead.com/classic/item=231288/renatakis-charm-of-ravaging - core.NewItemEffect(RenatakisCharmOfRavaging, func(agent core.Agent) { - character := agent.GetCharacter() - - lockedIn := character.RegisterAura(core.Aura{ - Label: "Locked In", - ActionID: core.ActionID{SpellID: 468388}, - Duration: time.Second * 20, - MaxStacks: 2, - OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) { - if spell.Flags.Matches(SpellFlagShot) || spell.ProcMask.Matches(core.ProcMaskMeleeSpecial) && spell.CD.Timer != nil { - spell.CD.Reset() - aura.RemoveStack(sim) - } - }, - }) - - spell := character.GetOrRegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 468388}, - Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagOffensiveEquipment, - - Cast: core.CastConfig{ - CD: core.Cooldown{ - Timer: character.NewTimer(), - Duration: time.Minute * 2, - }, - }, - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - lockedIn.Activate(sim) - lockedIn.SetStacks(sim, lockedIn.MaxStacks) - }, - }) - - character.AddMajorCooldown(core.MajorCooldown{ - Spell: spell, - Type: core.CooldownTypeDPS, - }) - }) - - // https://www.wowhead.com/classic/item=230237/arcane-infused-gem - core.NewItemEffect(ArcaneInfusedGem, func(agent core.Agent) { - character := agent.GetCharacter() - - arcaneDetonation := character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 467447}, - SpellSchool: core.SpellSchoolArcane, - DefenseType: core.DefenseTypeMagic, - ProcMask: core.ProcMaskSpellDamage, - Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, - - DamageMultiplier: 1, - ThreatMultiplier: 1, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - for _, aoeTarget := range sim.Encounter.TargetUnits { - damage := sim.Roll(185, 210) - spell.CalcAndDealDamage(sim, aoeTarget, damage, spell.OutcomeMagicHitAndCrit) - } - }, - }) - - maxCarveTargetsPerCast := int32(5) - maxMultishotTargetsPerCast := int32(3) - - arcaneInfused := character.RegisterAura(core.Aura{ - Label: "Arcane Infused", - ActionID: core.ActionID{SpellID: 467446}, - Duration: time.Second * 15, - OnInit: func(aura *core.Aura, sim *core.Simulation) { - maxCarveTargetsPerCast = min(sim.Environment.GetNumTargets(), 5) - maxMultishotTargetsPerCast = min(sim.Environment.GetNumTargets(), 3) - }, - OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) { - // Uses same targeting code as multi-shot however the detonations occur at cast time rather than when the shots land - if spell.SpellCode == SpellCode_HunterMultiShot { - curTarget := sim.Environment.Encounter.TargetUnits[0] - for hitIndex := int32(0); hitIndex < maxMultishotTargetsPerCast; hitIndex++ { - arcaneDetonation.Cast(sim, curTarget) - curTarget = sim.Environment.NextTargetUnit(curTarget) - } - } - // 1 explosion per target up to 5 targets per carve cast - if spell.SpellCode == SpellCode_HunterCarve { - curTarget := sim.Environment.Encounter.TargetUnits[0] - for hitIndex := int32(0); hitIndex < maxCarveTargetsPerCast; hitIndex++ { - arcaneDetonation.Cast(sim, curTarget) - curTarget = sim.Environment.NextTargetUnit(curTarget) - } - } - }, - }) - - spell := character.GetOrRegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: arcaneInfused.ActionID.SpellID}, - Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagOffensiveEquipment, - - Cast: core.CastConfig{ - CD: core.Cooldown{ - Timer: character.NewTimer(), - Duration: time.Second * 90, - }, - SharedCD: core.Cooldown{ - Timer: character.GetOffensiveTrinketCD(), - Duration: arcaneInfused.Duration, - }, - }, - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - arcaneInfused.Activate(sim) - }, - }) - - character.AddMajorCooldown(core.MajorCooldown{ - Spell: spell, - Type: core.CooldownTypeDPS, - }) - }) - - core.NewItemEffect(CloakOfTheUnseenPath, func(a core.Agent) { - hunter := a.(HunterAgent).GetHunter() - if hunter.pet == nil { - return - } - - hunter.pet.PseudoStats.DamageDealtMultiplier *= 1.02 - }) - - core.NewItemEffect(ScytheOfTheUnseenPath, func(a core.Agent) { - hunter := a.(HunterAgent).GetHunter() - if hunter.pet == nil { - return - } - - hunter.pet.PseudoStats.DamageDealtMultiplier *= 1.03 - }) - - core.NewItemEffect(SignetOfTheUnseenPath, func(a core.Agent) { - hunter := a.(HunterAgent).GetHunter() - if hunter.pet == nil { - return - } - - hunter.pet.PseudoStats.DamageDealtMultiplier *= 1.02 - }) */ -} - -/* func (hunter *Hunter) newBloodlashProcItem(bonusStrength float64, spellId int32) { - procAura := hunter.NewTemporaryStatsAura("Bloodlash", core.ActionID{SpellID: spellId}, stats.Stats{stats.Strength: bonusStrength}, time.Second*15) - ppm := hunter.AutoAttacks.NewPPMManager(1.0, core.ProcMaskMeleeOrRanged) - core.MakePermanent(hunter.GetOrRegisterAura(core.Aura{ - Label: "Bloodlash Trigger", - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if result.Landed() && ppm.Proc(sim, spell.ProcMask, "Bloodlash Proc") { - procAura.Activate(sim) - } - }, - })) -} */ +} \ No newline at end of file diff --git a/sim/hunter/kill_command.go b/sim/hunter/kill_command.go deleted file mode 100644 index a30f2dbbfb..0000000000 --- a/sim/hunter/kill_command.go +++ /dev/null @@ -1,70 +0,0 @@ -package hunter - -// TODO: 2024-06-13 - Rune changed from Kill Command to Kill Shot. Unsure if Kill Command still in the game. -// func (hunter *Hunter) registerKillCommand() { -// if hunter.pet == nil || !hunter.HasRune(proto.HunterRune_RuneLegsKillCommand) { -// return -// } - -// actionID := core.ActionID{SpellID: 409379} -// hasCatlikeReflexes := hunter.HasRune(proto.HunterRune_RuneHelmCatlikeReflexes) - -// cooldownModifier := 1.0 -// if hasCatlikeReflexes { -// cooldownModifier *= 0.5 -// } - -// // For tracking in timeline -// hunterAura := hunter.RegisterAura(core.Aura{ -// Label: "Kill Command", -// ActionID: actionID, -// Duration: time.Second * 30, -// MaxStacks: 3, -// }) - -// hunter.pet.killCommandAura = hunter.pet.RegisterAura(core.Aura{ -// Label: "Kill Command", -// ActionID: actionID, -// Duration: time.Second * 30, -// MaxStacks: 3, -// OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { -// // TODO: Make it only work on Claw/Bite after pet abilities refactor -// if spell.ProcMask.Matches(core.ProcMaskMeleeSpecial | core.ProcMaskSpellDamage) { -// aura.RemoveStack(sim) -// hunterAura.RemoveStack(sim) -// } -// }, -// }) - -// hunter.KillCommand = hunter.RegisterSpell(core.SpellConfig{ -// ActionID: actionID, -// SpellSchool: core.SpellSchoolPhysical, -// Flags: core.SpellFlagNoOnCastComplete, - -// ManaCost: core.ManaCostOptions{ -// BaseCost: 0.015, -// }, -// Cast: core.CastConfig{ -// CD: core.Cooldown{ -// Timer: hunter.NewTimer(), -// Duration: time.Second * time.Duration(60*cooldownModifier), -// }, -// }, -// ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool { -// return hunter.pet.IsEnabled() -// }, - -// ApplyEffects: func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) { -// hunter.pet.killCommandAura.Activate(sim) -// hunter.pet.killCommandAura.SetStacks(sim, 3) - -// hunterAura.Activate(sim) -// hunterAura.SetStacks(sim, 3) -// }, -// }) - -// hunter.AddMajorCooldown(core.MajorCooldown{ -// Spell: hunter.KillCommand, -// Type: core.CooldownTypeDPS, -// }) -// } diff --git a/sim/hunter/multi_shot.go b/sim/hunter/multi_shot.go index eb605c7065..7aaa59645a 100644 --- a/sim/hunter/multi_shot.go +++ b/sim/hunter/multi_shot.go @@ -36,8 +36,9 @@ func (hunter *Hunter) getMultiShotConfig(rank int, timer *core.Timer) core.Spell GCD: core.GCDDefault, CastTime: time.Millisecond * 500, }, - ModifyCast: func(_ *core.Simulation, spell *core.Spell, cast *core.Cast) { + ModifyCast: func(sim *core.Simulation, spell *core.Spell, cast *core.Cast) { cast.CastTime = spell.CastTime() + hunter.Unit.AutoAttacks.CancelAutoSwing(sim) }, IgnoreHaste: true, // Hunter GCD is locked at 1.5s CD: core.Cooldown{ @@ -70,7 +71,7 @@ func (hunter *Hunter) getMultiShotConfig(rank int, timer *core.Timer) core.Spell curTarget = sim.Environment.NextTargetUnit(curTarget) } - + hunter.Unit.AutoAttacks.EnableAutoSwing(sim) spell.WaitTravelTime(sim, func(s *core.Simulation) { for hitIndex := int32(0); hitIndex < numHits; hitIndex++ { spell.DealDamage(sim, results[hitIndex]) @@ -78,7 +79,7 @@ func (hunter *Hunter) getMultiShotConfig(rank int, timer *core.Timer) core.Spell curTarget = sim.Environment.NextTargetUnit(curTarget) } }) - + }, } } diff --git a/sim/hunter/pet.go b/sim/hunter/pet.go index 77d35b7301..9c1a4fcef3 100644 --- a/sim/hunter/pet.go +++ b/sim/hunter/pet.go @@ -16,8 +16,6 @@ type HunterPet struct { hunterOwner *Hunter - killCommandAura *core.Aura - specialAbility *core.Spell focusDump *core.Spell @@ -63,68 +61,17 @@ func (hunter *Hunter) NewHunterPet() *HunterPet { attackSpeed = 2.5 } - switch hunter.Level { - case 25: - baseMinDamage = 6.5 * attackSpeed - baseMaxDamage = 12.5 * attackSpeed - hunterPetBaseStats = stats.Stats{ - stats.Strength: 53, - stats.Agility: 45, - stats.Stamina: 120, - stats.Intellect: 29, - stats.Spirit: 39, - - stats.AttackPower: -20, - - // Add 1.8% because pets aren't affected by that component of crit suppression. - stats.MeleeCrit: (3.2 + 1.8) * core.CritRatingPerCritChance, - } - case 40: - baseMinDamage = 9.5 * attackSpeed - baseMaxDamage = 15.5 * attackSpeed - hunterPetBaseStats = stats.Stats{ - stats.Strength: 78, - stats.Agility: 66, - stats.Stamina: 160, - stats.Intellect: 37, - stats.Spirit: 55, - - stats.AttackPower: -20, - - // Add 1.8% because pets aren't affected by that component of crit suppression. - stats.MeleeCrit: (3.2 + 1.8) * core.CritRatingPerCritChance, - } - case 50: - baseMinDamage = 23.5 * attackSpeed - baseMaxDamage = 27.5 * attackSpeed - hunterPetBaseStats = stats.Stats{ - stats.Strength: 113, - stats.Agility: 82, - stats.Stamina: 257, - stats.Intellect: 43, - stats.Spirit: 67, - - stats.AttackPower: -20, - - // Add 1.8% because pets aren't affected by that component of crit suppression. - stats.MeleeCrit: (3.2 + 1.8) * core.CritRatingPerCritChance, - } - case 60: - // TODO: - baseMinDamage = 18.5 * attackSpeed - baseMaxDamage = 28.0 * attackSpeed - hunterPetBaseStats = stats.Stats{ - stats.Strength: 136, - stats.Agility: 100, - stats.Stamina: 274, - stats.Intellect: 50, - stats.Spirit: 80, - - stats.AttackPower: -20, - - // Add 1.8% because pets aren't affected by that component of crit suppression. - stats.MeleeCrit: (3.2 + 1.8) * core.CritRatingPerCritChance, - } + baseMinDamage = 18.17 * attackSpeed + baseMaxDamage = 27.66 * attackSpeed + + hunterPetBaseStats = stats.Stats{ + stats.Strength: 136, + stats.Agility: 100, + stats.Stamina: 274, + stats.Intellect: 50, + stats.Spirit: 80, + + stats.AttackPower: -20, } hp := &HunterPet{ @@ -146,11 +93,6 @@ func (hunter *Hunter) NewHunterPet() *HunterPet { AutoSwingMelee: true, }) - // After checking numerous logs it seems pet auto attacks are hitting for less then what they should if following standard attack formulas - // TODO: Figure out from where this difference comes - // TODO: Phase2 this no longer seems to apply - //hp.AutoAttacks.MHConfig().DamageMultiplier *= 0.45 - // Happiness hp.PseudoStats.DamageDealtMultiplier *= 1.25 @@ -176,13 +118,6 @@ func (hp *HunterPet) GetPet() *core.Pet { return &hp.Pet } -func (hp *HunterPet) Talents() *proto.HunterPetTalents { - if talents := hp.hunterOwner.Options.PetTalents; talents != nil { - return talents - } - return &proto.HunterPetTalents{} -} - func (hp *HunterPet) Initialize() { hp.specialAbility = hp.NewPetAbility(hp.config.SpecialAbility, true) hp.focusDump = hp.NewPetAbility(hp.config.FocusDump, false) @@ -252,32 +187,10 @@ func (hp *HunterPet) ExecuteCustomRotation(sim *core.Simulation) { } } -func (hp *HunterPet) killCommandMult() float64 { - if hp.killCommandAura == nil { - return 1 - } - return 1 + 0.2*float64(hp.killCommandAura.GetStacks()) -} - func (hunter *Hunter) makeStatInheritance() core.PetStatInheritance { return func(ownerStats stats.Stats) stats.Stats { - // EJ posts claim this value is passed through math.Floor, but in-game testing - // shows pets benefit from each point of owner hit rating in WotLK Classic. - // https://web.archive.org/web/20120112003252/http://elitistjerks.com/f80/t100099-demonology_releasing_demon_you - ownerHitChance := ownerStats[stats.MeleeHit] / core.MeleeHitRatingPerHitChance - hitRatingFromOwner := ownerHitChance * core.MeleeHitRatingPerHitChance - - return stats.Stats{ - stats.Stamina: ownerStats[stats.Stamina] * 0.3, - stats.Armor: ownerStats[stats.Armor] * 0.35, - stats.AttackPower: ownerStats[stats.RangedAttackPower] * 0.22, - - stats.MeleeCrit: ownerStats[stats.MeleeCrit], - stats.SpellCrit: ownerStats[stats.MeleeCrit], - - stats.MeleeHit: hitRatingFromOwner, - stats.SpellHit: hitRatingFromOwner * 2, - } + // No stat inheritance in classic + return stats.Stats{} } } @@ -399,16 +312,6 @@ var PetConfigs = map[proto.Hunter_Options_PetType]PetConfig{ Armor: 1.00, Damage: 1.07, }, - proto.Hunter_Options_CoreHound: { - Name: "Core Hound", - MobType: proto.MobType_MobTypeBeast, - - FocusDump: LavaBreath, - - Health: 1.06, - Armor: 1.01, - Damage: 1.02, - }, proto.Hunter_Options_Crab: { Name: "Crab", MobType: proto.MobType_MobTypeBeast, diff --git a/sim/hunter/pet_abilities.go b/sim/hunter/pet_abilities.go index 2ff0244a0e..87c3c95777 100644 --- a/sim/hunter/pet_abilities.go +++ b/sim/hunter/pet_abilities.go @@ -19,7 +19,6 @@ const ( FuriousHowl LightningBreath ScorpidPoison - LavaBreath ) func (hp *HunterPet) NewPetAbility(abilityType PetAbilityType, isPrimary bool) *core.Spell { @@ -38,8 +37,6 @@ func (hp *HunterPet) NewPetAbility(abilityType PetAbilityType, isPrimary bool) * return hp.newScorpidPoison() // case Swipe: // return hp.newSwipe() - case LavaBreath: - return hp.newLavaBreath() case Unknown: return nil default: @@ -69,8 +66,6 @@ func (hp *HunterPet) newClaw() *core.Spell { 60: 3009, }[hp.Owner.Level] - ApCoeff := 1.5 / 14 - return hp.RegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: spellID}, SpellCode: SpellCode_HunterPetClaw, @@ -94,9 +89,7 @@ func (hp *HunterPet) newClaw() *core.Spell { BonusCoefficient: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - baseDamage := sim.Roll(baseDamageMin, baseDamageMax) + (spell.MeleeAttackPower() * ApCoeff) - baseDamage *= hp.killCommandMult() - + baseDamage := sim.Roll(baseDamageMin, baseDamageMax) spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeSpecialHitAndCrit) }, }) @@ -124,8 +117,6 @@ func (hp *HunterPet) newBite() *core.Spell { 60: 17261, }[hp.Owner.Level] - ApCoeff := 2.15 / 14 - return hp.RegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: spellID}, SpellCode: SpellCode_HunterPetBite, @@ -153,9 +144,7 @@ func (hp *HunterPet) newBite() *core.Spell { BonusCoefficient: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - baseDamage := sim.Roll(baseDamageMin, baseDamageMax) + (spell.MeleeAttackPower() * ApCoeff) - baseDamage *= hp.killCommandMult() - + baseDamage := sim.Roll(baseDamageMin, baseDamageMax) spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeSpecialHitAndCrit) }, }) @@ -183,9 +172,6 @@ func (hp *HunterPet) newLightningBreath() *core.Spell { 60: 25012, }[hp.Owner.Level] - ApCoeff := 3.65 / 14 - SpCoeff := 0.429 - return hp.RegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: spellID}, SpellCode: SpellCode_HunterPetLightningBreath, @@ -205,10 +191,10 @@ func (hp *HunterPet) newLightningBreath() *core.Spell { DamageMultiplier: 1, ThreatMultiplier: 1, - BonusCoefficient: SpCoeff, + BonusCoefficient: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - baseDamage := sim.Roll(baseDamageMin, baseDamageMax) + (spell.MeleeAttackPower() * ApCoeff) + baseDamage := sim.Roll(baseDamageMin, baseDamageMax) spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMagicHitAndCrit) }, @@ -237,8 +223,6 @@ func (hp *HunterPet) newScreech() *core.Spell { 60: 24582, }[hp.Owner.Level] - ApCoeff := 1.15 / 14 - return hp.RegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: spellID}, SpellCode: SpellCode_HunterPetScreech, @@ -261,7 +245,7 @@ func (hp *HunterPet) newScreech() *core.Spell { ThreatMultiplier: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - baseDamage := sim.Roll(baseDamageMin, baseDamageMax) + (spell.MeleeAttackPower() * ApCoeff) + baseDamage := sim.Roll(baseDamageMin, baseDamageMax) // This ability also applies a melee attack power reduction similar to demoralizing shout - left it out for now spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeSpecialHitAndCrit) }, @@ -328,8 +312,6 @@ func (hp *HunterPet) newScorpidPoison() *core.Spell { 60: 24587, }[hp.Owner.Level] - ApCoeff := 0.07 / 5 - return hp.RegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: spellID}, SpellCode: SpellCode_HunterPetScorpidPoison, @@ -376,7 +358,7 @@ func (hp *HunterPet) newScorpidPoison() *core.Spell { dot.SnapshotBaseDamage = 0 } - dot.SnapshotBaseDamage += baseDamageTick + ApCoeff*dot.Spell.MeleeAttackPower() + dot.SnapshotBaseDamage += baseDamageTick }, OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { dot.CalcAndDealPeriodicSnapshotDamage(sim, target, dot.OutcomeTick) @@ -398,54 +380,3 @@ func (hp *HunterPet) newScorpidPoison() *core.Spell { }, }) } - -func (hp *HunterPet) newLavaBreath() *core.Spell { - baseDamageMin := map[int32]float64{ - 50: 78, - 60: 101, - }[hp.Owner.Level] - baseDamageMax := map[int32]float64{ - 50: 91, - 60: 116, - }[hp.Owner.Level] - spellID := map[int32]int32{ - 50: 444678, - 60: 444681, - }[hp.Owner.Level] - - ApCoeff := 3.65 / 14 - SpCoeff := 0.429 - - return hp.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: spellID}, - SpellCode: SpellCode_HunterPetLavaBreath, - SpellSchool: core.SpellSchoolFire, - DefenseType: core.DefenseTypeMagic, - ProcMask: core.ProcMaskSpellDamage, - Flags: core.SpellFlagMeleeMetrics, - - FocusCost: core.FocusCostOptions{ - Cost: 50, - }, - Cast: core.CastConfig{ - DefaultCast: core.Cast{ - GCD: PetGCD, - }, - IgnoreHaste: true, - }, - - DamageMultiplier: 1, - ThreatMultiplier: 1, - BonusCoefficient: SpCoeff, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - damage := sim.Roll(baseDamageMin, baseDamageMax) + ApCoeff*spell.MeleeAttackPower() - spell.CalcAndDealDamage(sim, target, damage, spell.OutcomeMagicHitAndCrit) - if sim.Environment.GetNumTargets() > 1 { - target = sim.Environment.NextTargetUnit(target) - spell.CalcAndDealDamage(sim, target, damage, spell.OutcomeMagicHitAndCrit) - } - - }, - }) -} diff --git a/sim/hunter/pet_talents.go b/sim/hunter/pet_talents.go deleted file mode 100644 index 4a0f02d5b8..0000000000 --- a/sim/hunter/pet_talents.go +++ /dev/null @@ -1,4 +0,0 @@ -package hunter - -func (hp *HunterPet) ApplyTalents() { -} diff --git a/sim/hunter/rapid_fire.go b/sim/hunter/rapid_fire.go index 92d71ec25f..c43b48f4e2 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 c3a9cf4433..f3fdeb7db3 100644 --- a/sim/hunter/serpent_sting.go +++ b/sim/hunter/serpent_sting.go @@ -53,8 +53,7 @@ func (hunter *Hunter) getSerpentStingConfig(rank int) core.SpellConfig { BonusCoefficient: spellCoeff, OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) { - // As of phase 5 the only time serpent sting scales with AP is using the Dragonstalker's Pursuit 6P - this AP scaling doesn't benefit from target AP modifiers - damage := baseDamage + (hunter.SerpentStingAPCoeff*dot.Spell.RangedAttackPower(target, true))/5 + damage := baseDamage dot.Snapshot(target, damage, isRollover) }, OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { @@ -77,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/hunter/talents.go b/sim/hunter/talents.go index 65fd958713..d27e57f0da 100644 --- a/sim/hunter/talents.go +++ b/sim/hunter/talents.go @@ -61,7 +61,13 @@ func (hunter *Hunter) ApplyTalents() { hunter.AddStat(stats.MeleeCrit, float64(hunter.Talents.KillerInstinct)*1*core.CritRatingPerCritChance) if hunter.Talents.LethalShots > 0 { - hunter.AddStat(stats.MeleeCrit, 1*float64(hunter.Talents.LethalShots)*core.CritRatingPerCritChance) + lethalBonus := 1*float64(hunter.Talents.LethalShots)*core.CritRatingPerCritChance + for _, spell := range hunter.Shots { + if spell != nil { + spell.BonusCritRating += lethalBonus + } + } + hunter.AutoAttacks.RangedConfig().BonusCritRating += lethalBonus } if hunter.Talents.RangedWeaponSpecialization > 0 { @@ -198,8 +204,8 @@ func (hunter *Hunter) applyCleverTraps() { func (hunter *Hunter) applyEfficiency() { hunter.OnSpellRegistered(func(spell *core.Spell) { - // applies to Stings, Shots, Strikes and Volley - if spell.Cost != nil && spell.Flags.Matches(SpellFlagSting|SpellFlagShot|SpellFlagStrike) || spell.SpellCode == SpellCode_HunterVolley { + // applies to Stings, Shots, and Volley + if spell.Cost != nil && spell.Flags.Matches(SpellFlagSting|SpellFlagShot) || spell.SpellCode == SpellCode_HunterVolley { spell.Cost.Multiplier -= 2 * hunter.Talents.Efficiency } }) diff --git a/sim/rogue/items.go b/sim/rogue/items.go index 3e74b62f48..c764751ab5 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/core/components/inputs/buffs_debuffs.ts b/ui/core/components/inputs/buffs_debuffs.ts index 768356c848..7ab90abc41 100644 --- a/ui/core/components/inputs/buffs_debuffs.ts +++ b/ui/core/components/inputs/buffs_debuffs.ts @@ -295,12 +295,13 @@ export const BattleSquawkBuff = makeMultistateRaidBuffInput({ // WORLD BUFFS /////////////////////////////////////////////////////////////////////////// -export const RallyingCryOfTheDragonslayer = makeBooleanIndividualBuffInput({ - actionId: () => ActionId.fromSpellId(22888), - fieldName: 'rallyingCryOfTheDragonslayer', -}); -export const DragonslayerBuffInput = InputHelpers.makeMultiIconInput({ values: [RallyingCryOfTheDragonslayer], label: 'Dragonslayer Buff' }); - +export const RallyingCryOfTheDragonslayer = withLabel( + makeBooleanIndividualBuffInput({ + actionId: () => ActionId.fromSpellId(22888), + fieldName: 'rallyingCryOfTheDragonslayer', + }), + 'Rallying Cry Of The Dragonslayer', +); export const SpiritOfZandalar = withLabel( makeBooleanIndividualBuffInput({ actionId: () => ActionId.fromSpellId(24425), @@ -678,20 +679,10 @@ export const MISC_BUFFS_CONFIG = [ ] as PickerStatOptions[]; export const WORLD_BUFFS_CONFIG = [ - // { - // config: RallyingCryOfTheDragonslayer, - // picker: IconPicker, - // stats: [ - // Stat.StatMeleeCrit, - // // TODO: Stat.StatRangedCrit, - // Stat.StatSpellCrit, - // Stat.StatAttackPower, - // ], - // }, { - config: DragonslayerBuffInput, - picker: MultiIconPicker, - stats: [], + config: RallyingCryOfTheDragonslayer, + picker: IconPicker, + stats: [Stat.StatMeleeCrit, Stat.StatSpellCrit, Stat.StatAttackPower], }, { config: SongflowerSerenade, diff --git a/ui/core/launched_sims.ts b/ui/core/launched_sims.ts index ce28e5eedd..142820e46e 100644 --- a/ui/core/launched_sims.ts +++ b/ui/core/launched_sims.ts @@ -58,7 +58,7 @@ export const simLaunchStatuses: Record = { }, [Spec.SpecHunter]: { phase: Phase.Phase1, - status: LaunchStatus.Unlaunched, + status: LaunchStatus.Alpha, }, [Spec.SpecMage]: { phase: Phase.Phase1, diff --git a/ui/core/talents/hunter_pet.ts b/ui/core/talents/hunter_pet.ts index 4712cdfde5..37aae6f50b 100644 --- a/ui/core/talents/hunter_pet.ts +++ b/ui/core/talents/hunter_pet.ts @@ -1,11 +1,9 @@ +import * as InputHelpers from '../components/input_helpers.js'; import { Player } from '../player.js'; import { Spec } from '../proto/common.js'; import { Hunter_Options_PetType as PetType } from '../proto/hunter.js'; import { ActionId } from '../proto_utils/action_id.js'; - -import * as InputHelpers from '../components/input_helpers.js'; - export function makePetTypeInputConfig(_: boolean): InputHelpers.TypedIconEnumPickerConfig, PetType> { return InputHelpers.makeSpecOptionsEnumIconInput({ fieldName: 'petType', @@ -15,7 +13,6 @@ export function makePetTypeInputConfig(_: boolean): InputHelpers.TypedIconEnumPi // TODO: Organize pets into phases maybe? { value: PetType.PetNone, tooltip: 'No Pet' }, { actionId: () => ActionId.fromPetName('Cat'), tooltip: 'Cat', value: PetType.Cat }, - { actionId: () => ActionId.fromPetName('Core Hound'), tooltip: 'Core Hound (Exotic)', value: PetType.CoreHound }, { actionId: () => ActionId.fromPetName('Raptor'), tooltip: 'Raptor', value: PetType.Raptor }, { actionId: () => ActionId.fromPetName('Owl'), tooltip: 'Owl', value: PetType.Owl }, { actionId: () => ActionId.fromPetName('Carrion Bird'), tooltip: 'Carrion Bird', value: PetType.CarrionBird }, diff --git a/ui/hunter/presets.ts b/ui/hunter/presets.ts index 519e5029c7..1b95d26d4b 100644 --- a/ui/hunter/presets.ts +++ b/ui/hunter/presets.ts @@ -86,10 +86,8 @@ export const DefaultOptions = HunterOptions.create({ ammo: Ammo.ThoriumHeadedArrow, quiverBonus: Hunter_Options_QuiverBonus.Speed15, petAttackSpeed: 2.0, - petTalents: {}, petType: PetType.PetNone, petUptime: 1, - sniperTrainingUptime: 1.0, }); export const DefaultConsumes = Consumes.create({ @@ -122,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, @@ -133,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({ diff --git a/ui/index.html b/ui/index.html index 9baf77679e..6e34ace359 100644 --- a/ui/index.html +++ b/ui/index.html @@ -159,12 +159,12 @@

Classic

- +