diff --git a/assets/database/db.bin b/assets/database/db.bin
index 6cad37e21b..c2233a1a0e 100644
Binary files a/assets/database/db.bin and b/assets/database/db.bin differ
diff --git a/assets/database/db.json b/assets/database/db.json
index ac13bd9dae..8dd66a7222 100644
--- a/assets/database/db.json
+++ b/assets/database/db.json
@@ -11389,6 +11389,7 @@
{"id":1219966,"name":"Soul of the Destroyer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[9]},
{"id":1219968,"name":"Soul of the Deathbound","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[9]},
{"id":1219970,"name":"Soul of the Sanguinist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[9]},
+{"id":1219972,"name":"Soul of the Pristine Blocker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
{"id":1219974,"name":"Soul of the Savage","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[9]},
{"id":1219976,"name":"Soul of Enmity","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[9,4]},
{"id":1219978,"name":"Soul of the Deflective","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[9]},
@@ -11437,6 +11438,24 @@
{"id":1220064,"name":"Soul of the Umbral Blade","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[8]},
{"id":1220066,"name":"Soul of the Ritualist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[8]},
{"id":1220068,"name":"Soul of the Pain Spreader","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[8]},
+{"id":1220070,"name":"Soul of the Misleader","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220072,"name":"Soul of the Preyseeker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220074,"name":"Soul of the Sharpshooter","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220076,"name":"Soul of the Hazard Harrier","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220078,"name":"Soul of the Alternator","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220080,"name":"Soul of the Toxinologist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220082,"name":"Soul of the Bounty Hunter","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220084,"name":"Soul of the Trick Shooter","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220086,"name":"Soul of the Beast Tender","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220088,"name":"Soul of the Hound Master","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220090,"name":"Soul of the Alpha Tamer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220092,"name":"Soul of the Huntsman","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220094,"name":"Soul of the Retaliator","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220096,"name":"Soul of Echoes","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220098,"name":"Soul of the Lethal Lasher","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220100,"name":"Soul of the Kineticist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220102,"name":"Soul of the Strategist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220104,"name":"Soul of the Deadly Striker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
{"id":1220106,"name":"Soul of the Hastened Healer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[5]},
{"id":1220108,"name":"Soul of the Refined","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[5]},
{"id":1220110,"name":"Soul of the Celebrant","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[5]},
@@ -11474,6 +11493,31 @@
{"id":1220174,"name":"Soul of the Pyromaniac","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[3]},
{"id":1220176,"name":"Soul of the Igniter","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[3]},
{"id":1220178,"name":"Soul of the Torcher","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[3]},
+{"id":1220182,"name":"Soul of the Lightwarden","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220184,"name":"Soul of the Radiant Defender","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220186,"name":"Soul of the Shieldbearer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220188,"name":"Soul of the Bastion","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220190,"name":"Soul of the Reckoner","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220192,"name":"Soul of the Ironclad","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220194,"name":"Soul of the Guardian","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220196,"name":"Soul of the Peacekeeper","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220198,"name":"Soul of the Refined","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220200,"name":"Soul of the Exemplar","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220202,"name":"Soul of the Inquisitor","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220204,"name":"Soul of the Sovereign","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220206,"name":"Soul of the Dominus","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220208,"name":"Soul of the Vindicator","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220210,"name":"Soul of the Altruist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220212,"name":"Soul of the Arbiter","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220214,"name":"Soul of the Sealbearer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220216,"name":"Soul of the Justicar","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220218,"name":"Soul of the Judicator","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220220,"name":"Soul of the Ascendant","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220222,"name":"Soul of the Retributor","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220224,"name":"Soul of the Excommunicator","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220226,"name":"Soul of the Lightbringer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220228,"name":"Soul of the Exile","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220230,"name":"Soul of the Templar","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
{"id":1220232,"name":"Soul of the Windwalker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
{"id":1220234,"name":"Soul of the Shield Master","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
{"id":1220236,"name":"Soul of the Totemic Protector","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
@@ -11508,6 +11552,7 @@
{"id":1220295,"name":"Soul of the Ancestral Warden","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
{"id":1220297,"name":"Soul of the Corrupt","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
{"id":1220299,"name":"Soul of the Gentle Paw","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[1]},
+{"id":1220301,"name":"Soul of the Ferocious","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[1]},
{"id":1220303,"name":"Soul of the Shifter","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[1]},
{"id":1220305,"name":"Soul of the Territorial","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[1]},
{"id":1220307,"name":"Soul of the Beast","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[1]},
diff --git a/assets/database/leftover_db.bin b/assets/database/leftover_db.bin
index a8d3de4311..5bca59f168 100644
Binary files a/assets/database/leftover_db.bin and b/assets/database/leftover_db.bin differ
diff --git a/assets/database/leftover_db.json b/assets/database/leftover_db.json
index 2c712cde96..d488b598e1 100644
--- a/assets/database/leftover_db.json
+++ b/assets/database/leftover_db.json
@@ -1488,8 +1488,8 @@
{"effectId":63,"spellId":13538,"name":"Enchant Chest - Lesser Absorption","type":5,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1},
{"effectId":66,"spellId":7457,"name":"Enchant Bracer - Minor Stamina","type":6,"stats":[0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1},
{"effectId":66,"spellId":7863,"name":"Enchant Boots - Minor Stamina","type":10,"stats":[0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1},
-{"effectId":241,"spellId":13503,"name":"Enchant Weapon - Lesser Striking","type":13,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1},
{"effectId":241,"spellId":7745,"name":"Enchant 2H Weapon - Minor Impact","type":13,"enchantType":1,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1},
+{"effectId":241,"spellId":13503,"name":"Enchant Weapon - Lesser Striking","type":13,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":1},
{"effectId":242,"spellId":7748,"name":"Enchant Chest - Lesser Health","type":5,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,0,0,0,0,0],"quality":1},
{"effectId":243,"spellId":7766,"name":"Enchant Bracer - Minor Spirit","type":6,"stats":[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":2},
{"effectId":246,"spellId":7776,"name":"Enchant Chest - Lesser Mana","type":5,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":2},
@@ -1944,6 +1944,7 @@
{"id":1219966,"name":"Soul of the Destroyer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[9]},
{"id":1219968,"name":"Soul of the Deathbound","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[9]},
{"id":1219970,"name":"Soul of the Sanguinist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[9]},
+{"id":1219972,"name":"Soul of the Pristine Blocker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
{"id":1219974,"name":"Soul of the Savage","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[9]},
{"id":1219976,"name":"Soul of Enmity","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[9,4]},
{"id":1219978,"name":"Soul of the Deflective","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[9]},
@@ -1992,6 +1993,24 @@
{"id":1220064,"name":"Soul of the Umbral Blade","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[8]},
{"id":1220066,"name":"Soul of the Ritualist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[8]},
{"id":1220068,"name":"Soul of the Pain Spreader","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[8]},
+{"id":1220070,"name":"Soul of the Misleader","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220072,"name":"Soul of the Preyseeker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220074,"name":"Soul of the Sharpshooter","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220076,"name":"Soul of the Hazard Harrier","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220078,"name":"Soul of the Alternator","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220080,"name":"Soul of the Toxinologist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220082,"name":"Soul of the Bounty Hunter","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220084,"name":"Soul of the Trick Shooter","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220086,"name":"Soul of the Beast Tender","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220088,"name":"Soul of the Hound Master","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220090,"name":"Soul of the Alpha Tamer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220092,"name":"Soul of the Huntsman","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220094,"name":"Soul of the Retaliator","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220096,"name":"Soul of Echoes","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220098,"name":"Soul of the Lethal Lasher","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220100,"name":"Soul of the Kineticist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220102,"name":"Soul of the Strategist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
+{"id":1220104,"name":"Soul of the Deadly Striker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[2]},
{"id":1220106,"name":"Soul of the Hastened Healer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[5]},
{"id":1220108,"name":"Soul of the Refined","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[5]},
{"id":1220110,"name":"Soul of the Celebrant","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[5]},
@@ -2029,6 +2048,31 @@
{"id":1220174,"name":"Soul of the Pyromaniac","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[3]},
{"id":1220176,"name":"Soul of the Igniter","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[3]},
{"id":1220178,"name":"Soul of the Torcher","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[3]},
+{"id":1220182,"name":"Soul of the Lightwarden","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220184,"name":"Soul of the Radiant Defender","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220186,"name":"Soul of the Shieldbearer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220188,"name":"Soul of the Bastion","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220190,"name":"Soul of the Reckoner","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220192,"name":"Soul of the Ironclad","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220194,"name":"Soul of the Guardian","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220196,"name":"Soul of the Peacekeeper","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220198,"name":"Soul of the Refined","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220200,"name":"Soul of the Exemplar","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220202,"name":"Soul of the Inquisitor","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220204,"name":"Soul of the Sovereign","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220206,"name":"Soul of the Dominus","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220208,"name":"Soul of the Vindicator","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220210,"name":"Soul of the Altruist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220212,"name":"Soul of the Arbiter","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220214,"name":"Soul of the Sealbearer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220216,"name":"Soul of the Justicar","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220218,"name":"Soul of the Judicator","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220220,"name":"Soul of the Ascendant","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220222,"name":"Soul of the Retributor","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220224,"name":"Soul of the Excommunicator","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220226,"name":"Soul of the Lightbringer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220228,"name":"Soul of the Exile","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
+{"id":1220230,"name":"Soul of the Templar","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[4]},
{"id":1220232,"name":"Soul of the Windwalker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
{"id":1220234,"name":"Soul of the Shield Master","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
{"id":1220236,"name":"Soul of the Totemic Protector","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
@@ -2063,6 +2107,7 @@
{"id":1220295,"name":"Soul of the Ancestral Warden","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
{"id":1220297,"name":"Soul of the Corrupt","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
{"id":1220299,"name":"Soul of the Gentle Paw","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[1]},
+{"id":1220301,"name":"Soul of the Ferocious","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[1]},
{"id":1220303,"name":"Soul of the Shifter","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[1]},
{"id":1220305,"name":"Soul of the Territorial","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[1]},
{"id":1220307,"name":"Soul of the Beast","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[1]},
diff --git a/assets/db_inputs/wowhead_shoulder_rune_tooltips.csv b/assets/db_inputs/wowhead_shoulder_rune_tooltips.csv
index 13eeb8bc88..920621fe70 100644
--- a/assets/db_inputs/wowhead_shoulder_rune_tooltips.csv
+++ b/assets/db_inputs/wowhead_shoulder_rune_tooltips.csv
@@ -196,7 +196,7 @@
1220264,{"name":"Soul of the Refined","quality":3,"icon":"spell_holy_divinespirit","tooltip":"
Soul of the Refined Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
1220282,{"name":"Soul of the Elemental Master","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Elemental Master Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
1220301,{"name":"Soul of the Ferocious","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Ferocious Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
-1220301,{"name":"Soul of Echoes","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of Echoes Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220096,{"name":"Soul of Echoes","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of Echoes Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
1220170,{"name":"Soul of Fiery Convergence","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of Fiery Convergence Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
1220068,{"name":"Soul of the Pain Spreader","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Pain Spreader Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
1220260,{"name":"Soul of the Vitalist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Vitalist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
diff --git a/proto/hunter.proto b/proto/hunter.proto
index d030f828fc..dee4a1fb6e 100644
--- a/proto/hunter.proto
+++ b/proto/hunter.proto
@@ -114,6 +114,7 @@ enum HunterRune {
RuneShouldersBeastTender = 1220086;
RuneShouldersBountyHunter = 1220082;
RuneShouldersDeadlyStriker = 1220104;
+ RuneShouldersEchoes = 1220096;
RuneShouldersFerocious = 1220301;
RuneShouldersHazardHarrier = 1220076;
RuneShouldersHoundMaster = 1220088;
diff --git a/sim/core/focus.go b/sim/core/focus.go
index ca8e715fba..951d195459 100644
--- a/sim/core/focus.go
+++ b/sim/core/focus.go
@@ -56,6 +56,10 @@ func (fb *focusBar) MaxFocus() float64 {
return fb.maxFocus
}
+func (fb *focusBar) IncreaseMaxFocus(value float64) {
+ fb.maxFocus += value
+}
+
func (fb *focusBar) CurrentFocusPerTick() float64 {
return fb.focusPerTick * fb.focusRegenMultiplier
}
diff --git a/sim/hunter/aimed_shot.go b/sim/hunter/aimed_shot.go
index f475b6bc86..fdfe5fede0 100644
--- a/sim/hunter/aimed_shot.go
+++ b/sim/hunter/aimed_shot.go
@@ -13,8 +13,6 @@ func (hunter *Hunter) getAimedShotConfig(rank int, timer *core.Timer) core.Spell
manaCost := [7]float64{0, 75, 115, 160, 210, 260, 310}[rank]
level := [7]int{0, 0, 28, 36, 44, 52, 60}[rank]
- has2PDragonStalkerPursuit := hunter.HasSetBonus(ItemSetDragonstalkerPursuit, 2)
-
return core.SpellConfig{
SpellCode: SpellCode_HunterAimedShot,
ActionID: core.ActionID{SpellID: spellId},
@@ -65,9 +63,6 @@ func (hunter *Hunter) getAimedShotConfig(rank int, timer *core.Timer) core.Spell
hunter.AmmoDamageBonus +
baseDamage
- if has2PDragonStalkerPursuit && (target.HasActiveAuraWithTag("ImmolationTrap") || hunter.HasActiveAuraWithTag("ExplosiveTrap")) {
- baseDamage *= 1.20
- }
result := spell.CalcDamage(sim, target, baseDamage, spell.OutcomeRangedHitAndCrit)
spell.WaitTravelTime(sim, func(s *core.Simulation) {
spell.DealDamage(sim, result)
diff --git a/sim/hunter/item_sets_pve.go b/sim/hunter/item_sets_pve.go
index aa2c18f9f1..d8e8ebc25e 100644
--- a/sim/hunter/item_sets_pve.go
+++ b/sim/hunter/item_sets_pve.go
@@ -1,10 +1,7 @@
package hunter
import (
- "time"
-
"github.com/wowsims/sod/sim/core"
- "github.com/wowsims/sod/sim/core/proto"
"github.com/wowsims/sod/sim/core/stats"
)
@@ -26,494 +23,3 @@ var ItemSetDreadHuntersChain = core.NewItemSet(core.ItemSet{
},
},
})
-
-///////////////////////////////////////////////////////////////////////////
-// 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.DamageMultiplierAdditive += 0.20
- }
- }
- },
- OnExpire: func(aura *core.Aura, sim *core.Simulation) {
- for _, spell := range hunter.Shots {
- if spell != nil {
- spell.DamageMultiplierAdditive -= 0.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.DamageMultiplierAdditive += 0.20
- }
- },
- OnExpire: func(aura *core.Aura, sim *core.Simulation) {
- for spell := range affectedSpells {
- spell.DamageMultiplierAdditive -= 0.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.DamageMultiplierAdditive += 0.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.DamageMultiplierAdditive += 0.10
- }
- }
- },
- OnExpire: func(aura *core.Aura, sim *core.Simulation) {
- for _, spell := range shotSpells {
- if spell.SpellCode != hunter.LastShot.SpellCode {
- spell.DamageMultiplierAdditive -= 0.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% and increases your pet's maximum focus by 50.
- 2: func(agent core.Agent) {
- hunter := agent.(HunterAgent).GetHunter()
-
- hunter.RegisterAura(core.Aura{
- Label: "Striker's Prowess 2P",
- OnInit: func(aura *core.Aura, sim *core.Simulation) {
- if hunter.WyvernStrike != nil {
- hunter.WyvernStrike.PeriodicDamageMultiplierAdditive += 0.50
- }
-
- // Focus implementation in pet.go
- },
- })
- },
- // Increases the Impact Damage of Mongoose Bite and all Strikes by 15%
- 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.15
- }
- hunter.RaptorStrikeMH.ImpactDamageMultiplierAdditive += 0.15
- hunter.RaptorStrikeOH.ImpactDamageMultiplierAdditive += 0.15
- hunter.MongooseBite.ImpactDamageMultiplierAdditive += 0.15
- },
- })
- },
- },
-})
-
-var StrikersPursuit = core.NewItemSet(core.ItemSet{
- Name: "Striker's Pursuit",
- Bonuses: map[int32]core.ApplyEffect{
- // Increases Kill Shot damage by 50% against non-player targets.
- 2: 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.DamageMultiplierAdditive += 0.20
- },
- })
- },
- // Kill Shot's cooldown is reduced by 50%.
- // While Rapid Fire is active with Rapid killing engraved, Kill Shot has no cooldown and fires 3 additional Kill Shots at 33% damage, with a minimum range.
- 4: func(agent core.Agent) {
- hunter := agent.(HunterAgent).GetHunter()
- if !hunter.HasRune(proto.HunterRune_RuneLegsKillShot) {
- return
- }
-
- clonedShotConfig := hunter.newKillShotConfig()
- clonedShotConfig.ActionID.Tag = 1
- clonedShotConfig.Flags |= core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell
- clonedShotConfig.Flags ^= core.SpellFlagAPL
- clonedShotConfig.Cast.DefaultCast.GCD = 0
- clonedShotConfig.Cast.DefaultCast.Cost = 0
- clonedShotConfig.Cast.CD = core.Cooldown{}
- clonedShotConfig.ManaCost.BaseCost = 0
- clonedShotConfig.ManaCost.FlatCost = 0
- clonedShotConfig.MetricSplits = 0
- clonedShotConfig.DamageMultiplier *= 0.30
- clonedShotConfig.ExtraCastCondition = func(sim *core.Simulation, target *core.Unit) bool {
- return hunter.DistanceFromTarget >= core.MinRangedAttackDistance
- }
-
- clonedShot := hunter.RegisterSpell(clonedShotConfig)
-
- hunter.RegisterAura(core.Aura{
- Label: "Striker's Pursuit 2P",
- OnInit: func(aura *core.Aura, sim *core.Simulation) {
- hunter.KillShot.CD.FlatModifier -= 6 * time.Second
-
- if !hunter.HasRune(proto.HunterRune_RuneHelmRapidKilling) {
- return
- }
-
- oldApplyEffects := hunter.KillShot.ApplyEffects
- hunter.KillShot.ApplyEffects = func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
- oldApplyEffects(sim, target, spell)
-
- if hunter.RapidFireAura.IsActive() {
- spell.CD.Reset()
-
- for i := 1; i < 4; i++ {
- core.StartDelayedAction(sim, core.DelayedActionOptions{
- DoAt: sim.CurrentTime + time.Duration(i*375)*time.Millisecond,
- OnAction: func(sim *core.Simulation) {
- // Ensure that the cloned shots get any damage amps from the main Kill Shot ability
- clonedShot.DamageMultiplier *= spell.DamageMultiplier
- clonedShot.DamageMultiplierAdditive += spell.DamageMultiplierAdditive - 1
- clonedShot.Cast(sim, target)
- clonedShot.DamageMultiplier /= spell.DamageMultiplier
- clonedShot.DamageMultiplierAdditive -= spell.DamageMultiplierAdditive - 1
- },
- })
- }
- }
- }
- },
- })
- },
- },
-})
diff --git a/sim/hunter/item_sets_pve_phase_4.go b/sim/hunter/item_sets_pve_phase_4.go
new file mode 100644
index 0000000000..c8e659d898
--- /dev/null
+++ b/sim/hunter/item_sets_pve_phase_4.go
@@ -0,0 +1,219 @@
+package hunter
+
+import (
+ "time"
+
+ "github.com/wowsims/sod/sim/core"
+ "github.com/wowsims/sod/sim/core/stats"
+)
+
+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{
+ 2: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyT1Melee2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyT1Melee4PBonus()
+ },
+ 6: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyT1Melee6PBonus()
+ },
+ },
+})
+
+// Your Mongoose Bite also reduces its target's chance to Dodge by 1% and increases your chance to hit by 1% for 30 sec.
+func (hunter *Hunter) applyT1Melee2PBonus() {
+ label := "S03 - Item - T1 - Hunter - Melee 2P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ 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: label,
+ 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
+func (hunter *Hunter) applyT1Melee4PBonus() {
+ label := "S03 - Item - T1 - Hunter - Melee 4P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ hunter.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ // Just adding 3% damage to assume the hunter is tracking their target's type
+ hunter.PseudoStats.DamageDealtMultiplier *= 1.03
+ },
+ })
+}
+
+// Mongoose Bite also activates for 5 sec whenever your target Parries or Blocks or when your melee attack misses.
+func (hunter *Hunter) applyT1Melee6PBonus() {
+ label := "S03 - Item - T1 - Hunter - Melee 6P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ core.MakePermanent(hunter.RegisterAura(core.Aura{
+ Label: label,
+ OnSpellHitDealt: func(_ *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ if spell.ProcMask.Matches(core.ProcMaskMelee) && !result.Landed() {
+ 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) {
+ // Do nothing
+ },
+ 4: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyT1Ranged4PBonus()
+ },
+ 6: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyT1Ranged6PBonus()
+ },
+ },
+})
+
+// While tracking a creature type, you deal 3% increased damage to that creature type.
+// Unsure if this stacks with the Prowess 4p
+func (hunter *Hunter) applyT1Ranged4PBonus() {
+ label := "S03 - Item - T1 - Hunter - Ranged 4P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ hunter.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ // Just adding 3% damage to assume the hunter is tracking their target's type
+ hunter.PseudoStats.DamageDealtMultiplier *= 1.03
+ },
+ })
+}
+
+// Your next Shot ability within 12 sec after Aimed Shot deals 20% more damage.
+func (hunter *Hunter) applyT1Ranged6PBonus() {
+ if !hunter.Talents.AimedShot {
+ return
+ }
+
+ label := "S03 - Item - T1 - Hunter - Ranged 6P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ procAura := hunter.RegisterAura(core.Aura{
+ ActionID: core.ActionID{SpellID: 456382},
+ Label: "Precision",
+ Duration: time.Second * 12,
+ OnGain: func(aura *core.Aura, sim *core.Simulation) {
+ for _, spell := range hunter.Shots {
+ spell.DamageMultiplierAdditive += 0.20
+ }
+ },
+ OnExpire: func(aura *core.Aura, sim *core.Simulation) {
+ for _, spell := range hunter.Shots {
+ spell.DamageMultiplierAdditive -= 0.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: label,
+ OnCastComplete: func(_ *core.Aura, sim *core.Simulation, spell *core.Spell) {
+ if spell.SpellCode == SpellCode_HunterAimedShot {
+ procAura.Activate(sim)
+ }
+ },
+ }))
+}
diff --git a/sim/hunter/item_sets_pve_phase_5.go b/sim/hunter/item_sets_pve_phase_5.go
new file mode 100644
index 0000000000..a27442c394
--- /dev/null
+++ b/sim/hunter/item_sets_pve_phase_5.go
@@ -0,0 +1,294 @@
+package hunter
+
+import (
+ "time"
+
+ "github.com/wowsims/sod/sim/core"
+ "github.com/wowsims/sod/sim/core/stats"
+)
+
+var ItemSetDragonstalkerProwess = core.NewItemSet(core.ItemSet{
+ Name: "Dragonstalker's Prowess",
+ Bonuses: map[int32]core.ApplyEffect{
+ 2: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyT2Melee2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyT2Melee4PBonus()
+ },
+ 6: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyT2Melee6PBonus()
+ },
+ },
+})
+
+// Raptor Strike increases the damage done by your next other melee ability within 5 sec by 20%.
+func (hunter *Hunter) applyT2Melee2PBonus() {
+ label := "S03 - Item - T2 - Hunter - Melee 2P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ 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.DamageMultiplierAdditive += 0.20
+ }
+ },
+ OnExpire: func(aura *core.Aura, sim *core.Simulation) {
+ for spell := range affectedSpells {
+ spell.DamageMultiplierAdditive -= 0.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: label,
+ 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%.
+func (hunter *Hunter) applyT2Melee4PBonus() {
+ label := "S03 - Item - T2 - Hunter - Melee 4P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ hunter.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ for _, spell := range []*core.Spell{hunter.RaptorStrike, hunter.RaptorStrikeMH, hunter.WyvernStrike} {
+ if spell == nil {
+ continue
+ }
+
+ spell.DamageMultiplierAdditive += 0.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.
+func (hunter *Hunter) applyT2Melee6PBonus() {
+ label := "S03 - Item - T2 - Hunter - Melee 6P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ core.MakePermanent(hunter.RegisterAura(core.Aura{
+ ActionID: core.ActionID{SpellID: 467334}, // Tracking in APL
+ Label: label,
+ 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{
+ 2: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyT2Ranged2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyT2Ranged4PBonus()
+ },
+ 6: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyT2Ranged6PBonus()
+ },
+ },
+})
+
+// Your Aimed Shot deals 20% more damage to targets afflicted by one of your trap effects.
+func (hunter *Hunter) applyT2Ranged2PBonus() {
+ if !hunter.Talents.AimedShot {
+ return
+ }
+
+ label := "S03 - Item - T2 - Hunter - Ranged 2P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ hunter.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ oldApplyEffects := hunter.AimedShot.ApplyEffects
+ hunter.AimedShot.ApplyEffects = func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
+ modifier := 0.0
+ if target.HasActiveAuraWithTag("ImmolationTrap") || hunter.HasActiveAuraWithTag("ExplosiveTrap") {
+ modifier += 0.20
+ }
+
+ spell.DamageMultiplierAdditive += modifier
+ oldApplyEffects(sim, target, spell)
+ spell.DamageMultiplierAdditive -= modifier
+ }
+ },
+ })
+}
+
+// Your damaging Shot abilities deal 10% increased damage if the previous damaging Shot used was different than the current one.
+func (hunter *Hunter) applyT2Ranged4PBonus() {
+ label := "S03 - Item - T2 - Hunter - Ranged 4P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ shotSpells := []*core.Spell{}
+ procAura := hunter.RegisterAura(core.Aura{
+ ActionID: core.ActionID{SpellID: 467312},
+ Label: label + " Proc",
+ 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.DamageMultiplierAdditive += 0.10
+ }
+ }
+ },
+ OnExpire: func(aura *core.Aura, sim *core.Simulation) {
+ for _, spell := range shotSpells {
+ if spell.SpellCode != hunter.LastShot.SpellCode {
+ spell.DamageMultiplierAdditive -= 0.10
+ }
+ }
+ },
+ })
+
+ core.MakePermanent(hunter.RegisterAura(core.Aura{
+ Label: label,
+ 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.
+func (hunter *Hunter) applyT2Ranged6PBonus() {
+ label := "S03 - Item - T2 - Hunter - Ranged 6P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ hunter.RegisterAura(core.Aura{
+ Label: label,
+ 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)
+ },
+ 3: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyZGBeastmaster3PBonus()
+ },
+ 5: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyZGBeastmaster5PBonus()
+ },
+ },
+})
+
+// Increases the Attack Power your Beast pet gains from your attributes by 20%.
+func (hunter *Hunter) applyZGBeastmaster3PBonus() {
+ if hunter.pet == nil {
+ return
+ }
+
+ label := "S03 - Item - ZG - Hunter - Beastmaster 3P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ hunter.RegisterAura(core.Aura{
+ Label: label,
+ 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%.
+func (hunter *Hunter) applyZGBeastmaster5PBonus() {
+ if hunter.pet == nil {
+ return
+ }
+
+ label := "S03 - Item - ZG - Hunter - Beastmaster 5P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ hunter.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ hunter.pet.AddFocusRegenMultiplier(0.20)
+ },
+ })
+}
diff --git a/sim/hunter/item_sets_pve_phase_6.go b/sim/hunter/item_sets_pve_phase_6.go
new file mode 100644
index 0000000000..589c437e0c
--- /dev/null
+++ b/sim/hunter/item_sets_pve_phase_6.go
@@ -0,0 +1,190 @@
+package hunter
+
+import (
+ "time"
+
+ "github.com/wowsims/sod/sim/core"
+ "github.com/wowsims/sod/sim/core/proto"
+)
+
+var StrikersProwess = core.NewItemSet(core.ItemSet{
+ Name: "Striker's Prowess",
+ Bonuses: map[int32]core.ApplyEffect{
+ 2: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyTAQMelee2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyTAQMelee4PBonus()
+ },
+ },
+})
+
+// Increases Wyvern Strike DoT by 50% and increases your pet's maximum focus by 50.
+func (hunter *Hunter) applyTAQMelee2PBonus() {
+ label := "S03 - Item - TAQ - Hunter - Melee 2P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ hunter.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ if hunter.WyvernStrike != nil {
+ hunter.WyvernStrike.PeriodicDamageMultiplierAdditive += 0.50
+ }
+
+ if hunter.pet != nil {
+ hunter.pet.IncreaseMaxFocus(50)
+ }
+ },
+ })
+}
+
+// Increases the Impact Damage of Mongoose Bite and all Strikes by 15%
+func (hunter *Hunter) applyTAQMelee4PBonus() {
+ label := "S03 - Item - TAQ - Hunter - Melee 4P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ hunter.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ for _, spell := range hunter.Strikes {
+ spell.ImpactDamageMultiplierAdditive += 0.15
+ }
+ hunter.RaptorStrikeMH.ImpactDamageMultiplierAdditive += 0.15
+ hunter.RaptorStrikeOH.ImpactDamageMultiplierAdditive += 0.15
+ hunter.MongooseBite.ImpactDamageMultiplierAdditive += 0.15
+ },
+ })
+}
+
+var StrikersPursuit = core.NewItemSet(core.ItemSet{
+ Name: "Striker's Pursuit",
+ Bonuses: map[int32]core.ApplyEffect{
+ 2: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyTAQRanged2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyTAQRanged4PBonus()
+ },
+ },
+})
+
+// Increases Kill Shot damage by 50% against non-player targets.
+func (hunter *Hunter) applyTAQRanged2PBonus() {
+ if !hunter.HasRune(proto.HunterRune_RuneLegsKillShot) {
+ return
+ }
+
+ label := "S03 - Item - TAQ - Hunter - Ranged 2P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ hunter.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ hunter.KillShot.DamageMultiplierAdditive += 0.20
+ },
+ })
+}
+
+// Kill Shot's cooldown is reduced by 50%.
+// While Rapid Fire is active with Rapid killing engraved, Kill Shot has no cooldown and fires 3 additional Kill Shots at 30% damage, with a minimum range.
+func (hunter *Hunter) applyTAQRanged4PBonus() {
+ if !hunter.HasRune(proto.HunterRune_RuneLegsKillShot) {
+ return
+ }
+
+ label := "S03 - Item - TAQ - Hunter - Ranged 4P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ clonedShotConfig := hunter.newKillShotConfig()
+ clonedShotConfig.ActionID.Tag = 1
+ clonedShotConfig.Flags |= core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell
+ clonedShotConfig.Flags ^= core.SpellFlagAPL
+ clonedShotConfig.Cast.DefaultCast.GCD = 0
+ clonedShotConfig.Cast.DefaultCast.Cost = 0
+ clonedShotConfig.Cast.CD = core.Cooldown{}
+ clonedShotConfig.ManaCost.BaseCost = 0
+ clonedShotConfig.ManaCost.FlatCost = 0
+ clonedShotConfig.MetricSplits = 0
+ clonedShotConfig.DamageMultiplier *= 0.30
+ clonedShotConfig.ExtraCastCondition = func(sim *core.Simulation, target *core.Unit) bool {
+ return hunter.DistanceFromTarget >= core.MinRangedAttackDistance
+ }
+
+ clonedShot := hunter.RegisterSpell(clonedShotConfig)
+ clonedShot.DamageMultiplierAdditive += 0.20 // Add the 2p bonus 20%
+
+ hunter.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ hunter.KillShot.CD.FlatModifier -= 6 * time.Second
+
+ if !hunter.HasRune(proto.HunterRune_RuneHelmRapidKilling) {
+ return
+ }
+
+ oldApplyEffects := hunter.KillShot.ApplyEffects
+ hunter.KillShot.ApplyEffects = func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
+ oldApplyEffects(sim, target, spell)
+
+ if hunter.RapidFireAura.IsActive() {
+ spell.CD.Reset()
+
+ for i := 1; i < 4; i++ {
+ core.StartDelayedAction(sim, core.DelayedActionOptions{
+ DoAt: sim.CurrentTime + time.Duration(i*375)*time.Millisecond,
+ OnAction: func(sim *core.Simulation) {
+ // Ensure that the cloned shots get any damage amps from the main Kill Shot ability
+ clonedShot.DamageMultiplier *= spell.DamageMultiplier
+ clonedShot.DamageMultiplierAdditive += spell.DamageMultiplierAdditive - 1
+ clonedShot.Cast(sim, target)
+ clonedShot.DamageMultiplier /= spell.DamageMultiplier
+ clonedShot.DamageMultiplierAdditive -= spell.DamageMultiplierAdditive - 1
+ },
+ })
+ }
+ }
+ }
+ },
+ })
+}
+
+var TrappingsOfTheUnseenPath = core.NewItemSet(core.ItemSet{
+ Name: "Trappings of the Unseen Path",
+ Bonuses: map[int32]core.ApplyEffect{
+ 3: func(agent core.Agent) {
+ hunter := agent.(HunterAgent).GetHunter()
+ hunter.applyRAQBeastmastery5PBonus()
+ },
+ },
+})
+
+// Increases the Focus regeneration of your Beast pet by 100%.
+func (hunter *Hunter) applyRAQBeastmastery5PBonus() {
+ if hunter.pet == nil {
+ return
+ }
+
+ label := "S03 - Item - RAQ - Hunter - Beastmastery 5P Bonus"
+ if hunter.HasAura(label) {
+ return
+ }
+
+ hunter.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ hunter.pet.AddFocusRegenMultiplier(1.00)
+ },
+ })
+}
diff --git a/sim/hunter/pet.go b/sim/hunter/pet.go
index b935149df3..a90628cd01 100644
--- a/sim/hunter/pet.go
+++ b/sim/hunter/pet.go
@@ -185,12 +185,7 @@ func (hp *HunterPet) Initialize() {
hp.specialAbility = hp.NewPetAbility(hp.config.SpecialAbility, true)
hp.focusDump = hp.NewPetAbility(hp.config.FocusDump, false)
- maxFocus := core.MaxFocus
- if hp.hunterOwner.HasSetBonus(StrikersProwess, 2) {
- maxFocus += 50
- }
-
- hp.EnableFocusBar(maxFocus, 1, func(sim *core.Simulation) {
+ hp.EnableFocusBar(core.MaxFocus, 1, func(sim *core.Simulation) {
if hp.GCD.IsReady(sim) {
hp.OnGCDReady(sim)
}
diff --git a/sim/hunter/runes.go b/sim/hunter/runes.go
index 15e4bc5b62..0baeba64fc 100644
--- a/sim/hunter/runes.go
+++ b/sim/hunter/runes.go
@@ -10,6 +10,8 @@ import (
)
func (hunter *Hunter) ApplyRunes() {
+ hunter.applyShoulderRuneEffect()
+
if hunter.HasRune(proto.HunterRune_RuneChestLoneWolf) && hunter.pet == nil {
hunter.PseudoStats.DamageDealtMultiplier *= 1.30
}
@@ -52,6 +54,54 @@ func (hunter *Hunter) ApplyRunes() {
hunter.applyResourcefulness()
}
+func (hunter *Hunter) applyShoulderRuneEffect() {
+ if hunter.Equipment.Shoulders().Rune == int32(proto.HunterRune_HunterRuneNone) {
+ return
+ }
+
+ switch hunter.Equipment.Shoulders().Rune {
+ // Melee
+ case int32(proto.HunterRune_RuneShouldersHuntsman):
+ hunter.applyT1Melee2PBonus()
+ case int32(proto.HunterRune_RuneShouldersRetaliator):
+ hunter.applyT1Melee6PBonus()
+ case int32(proto.HunterRune_RuneShouldersEchoes):
+ hunter.applyT2Melee2PBonus()
+ case int32(proto.HunterRune_RuneShouldersLethalLasher):
+ hunter.applyT2Melee4PBonus()
+ case int32(proto.HunterRune_RuneShouldersKineticist):
+ hunter.applyT2Melee6PBonus()
+ case int32(proto.HunterRune_RuneShouldersStrategist):
+ hunter.applyTAQMelee2PBonus()
+ case int32(proto.HunterRune_RuneShouldersDeadlyStriker):
+ hunter.applyTAQMelee4PBonus()
+
+ // Ranged
+ case int32(proto.HunterRune_RuneShouldersPreyseeker):
+ hunter.applyT1Ranged4PBonus()
+ case int32(proto.HunterRune_RuneShouldersSharpshooter):
+ hunter.applyT1Ranged6PBonus()
+ case int32(proto.HunterRune_RuneShouldersHazardHarrier):
+ hunter.applyT2Ranged2PBonus()
+ case int32(proto.HunterRune_RuneShouldersAlternator):
+ hunter.applyT2Ranged4PBonus()
+ case int32(proto.HunterRune_RuneShouldersToxinologist):
+ hunter.applyT2Ranged6PBonus()
+ case int32(proto.HunterRune_RuneShouldersBountyHunter):
+ hunter.applyTAQRanged2PBonus()
+ case int32(proto.HunterRune_RuneShouldersTrickShooter):
+ hunter.applyTAQRanged4PBonus()
+
+ // Beastmaster
+ case int32(proto.HunterRune_RuneShouldersBeastTender):
+ hunter.applyZGBeastmaster3PBonus()
+ case int32(proto.HunterRune_RuneShouldersHoundMaster):
+ hunter.applyZGBeastmaster5PBonus()
+ case int32(proto.HunterRune_RuneshouldersAlphaTamer):
+ hunter.applyRAQBeastmastery5PBonus()
+ }
+}
+
// TODO: 2024-06-13 - Rune seemingly replaced with Wyvern Strike
// func (hunter *Hunter) applyInvigoration() {
// if !hunter.HasRune(proto.HunterRune_RuneBootsInvigoration) || hunter.pet == nil {
diff --git a/tools/database/gen_db/main.go b/tools/database/gen_db/main.go
index aa1177adb6..cee2610bfc 100644
--- a/tools/database/gen_db/main.go
+++ b/tools/database/gen_db/main.go
@@ -167,9 +167,7 @@ func main() {
}
for id, rune := range shoulderRuneTooltips {
- if database.ShoulderRuneClassAllowlist[rune.GetRequiredClasses()[0]] {
- db.AddShoulderRune(id, rune)
- }
+ db.AddShoulderRune(id, rune)
}
db.MergeItems(database.ItemOverrides)
diff --git a/tools/database/rune_overrides.go b/tools/database/rune_overrides.go
index 48ff852a5d..07fa2d9b11 100644
--- a/tools/database/rune_overrides.go
+++ b/tools/database/rune_overrides.go
@@ -46,16 +46,3 @@ var RuneOverrides = []*proto.UIRune{
// As of 2024-06-13 Cobra Slayer is being missed by the scraper because the rune engraving ability is missing "Engrave Rune" in the name
{Id: 458393, Name: "Engrave Gloves - Cobra Slayer", Icon: "spell_nature_guardianward", Type: proto.ItemType_ItemTypeHands, ClassAllowlist: []proto.Class{proto.Class_ClassHunter}},
}
-
-// Classes that have shoulder rune implementations completed to not confuse users
-var ShoulderRuneClassAllowlist = map[proto.Class]bool{
- proto.Class_ClassDruid: true,
- // proto.Class_ClassHunter: true,
- proto.Class_ClassMage: true,
- // proto.Class_ClassPaladin: true,
- proto.Class_ClassPriest: true,
- proto.Class_ClassRogue: true,
- proto.Class_ClassShaman: true,
- proto.Class_ClassWarlock: true,
- proto.Class_ClassWarrior: true,
-}