diff --git a/assets/database/db.bin b/assets/database/db.bin
index 93c7a127ad..6578a92c15 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 33b8ac614e..6f83a5b020 100644
--- a/assets/database/db.json
+++ b/assets/database/db.json
@@ -10594,8 +10594,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":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":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":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},
@@ -10670,8 +10670,8 @@
{"effectId":929,"itemId":16217,"spellId":20069,"name":"Enchant Shield - Greater Stamina","type":13,"enchantType":2,"stats":[0,0,7,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":930,"spellId":13947,"name":"Enchant Gloves - Riding Skill","type":7,"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":2},
{"effectId":931,"spellId":13948,"name":"Enchant Gloves - Minor Haste","type":7,"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":943,"spellId":13693,"name":"Enchant Weapon - 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":943,"spellId":13529,"name":"Enchant 2H Weapon - Lesser 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":943,"spellId":13693,"name":"Enchant Weapon - 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":963,"spellId":13937,"name":"Enchant 2H Weapon - Greater 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":1483,"itemId":11622,"spellId":15340,"name":"Lesser Arcanum of Rumination","type":1,"extraTypes":[9],"enchantType":3,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":2},
{"effectId":1503,"itemId":11642,"spellId":15389,"name":"Lesser Arcanum of Constitution","type":1,"extraTypes":[9],"enchantType":3,"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,100,0,0,0,0,0,0,0,0,0],"quality":2},
@@ -10697,8 +10697,8 @@
{"effectId":1893,"spellId":20028,"name":"Enchant Chest - Major 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,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":2},
{"effectId":1894,"spellId":20029,"name":"Enchant Weapon - Icy Chill","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":2},
{"effectId":1896,"spellId":20030,"name":"Enchant 2H Weapon - Superior 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":2},
-{"effectId":1897,"spellId":13695,"name":"Enchant 2H Weapon - 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":1897,"spellId":20031,"name":"Enchant Weapon - Superior 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":2},
+{"effectId":1897,"spellId":13695,"name":"Enchant 2H Weapon - 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":1898,"spellId":20032,"name":"Enchant Weapon - Lifestealing","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":3},
{"effectId":1899,"spellId":20033,"name":"Enchant Weapon - Unholy Weapon","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":2},
{"effectId":1900,"spellId":20034,"name":"Enchant Weapon - Crusader","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":2},
@@ -11017,31 +11017,64 @@
{"id":440870,"name":"Engrave Boots - Decimation","icon":"spell_fire_fireball02","type":10,"classAllowlist":[8]},
{"id":440882,"name":"Engrave Cloak - Infernal Armor","icon":"achievement_boss_kiljaedan","type":4,"classAllowlist":[8]},
{"id":440892,"name":"Engrave Cloak - Mark of Chaos","icon":"spell_shadow_unstableaffliction_1","type":4,"classAllowlist":[8]},
-{"id":442813,"name":"Engrave Ring - Sword Specialization","icon":"ability_meleedamage","type":11,"classAllowlist":[0,9,4,2,6,3,8]},
-{"id":442876,"name":"Engrave Ring - Axe Specialization","icon":"inv_axe_03","type":11,"classAllowlist":[0,9,4,2,7]},
-{"id":442881,"name":"Engrave Ring - Mace Specialization","icon":"inv_hammer_01","type":11,"classAllowlist":[0,9,4,6,5,7,1]},
-{"id":442887,"name":"Engrave Ring - Dagger Specialization","icon":"inv_weapon_shortblade_05","type":11,"classAllowlist":[0,9,2,6,5,7,3,8,1]},
-{"id":442890,"name":"Engrave Ring - Fist Weapon Specialization","icon":"inv_misc_desecrated_plategloves","type":11,"classAllowlist":[0,9,2,6,7,1]},
-{"id":442891,"name":"Engrave Ring - Ranged Weapon Specialization","icon":"inv_weapon_bow_02","type":11,"classAllowlist":[0,9,2,6]},
-{"id":442892,"name":"Engrave Ring - Pole Weapon Specialization","icon":"inv_staff_08","type":11,"classAllowlist":[0,9,4,2,5,7,3,8,1]},
-{"id":442893,"name":"Engrave Ring - Arcane Specialization","icon":"inv_elemental_primal_mana","type":11,"classAllowlist":[0,1,3,2]},
-{"id":442894,"name":"Engrave Ring - Fire Specialization","icon":"inv_elemental_primal_fire","type":11,"classAllowlist":[0,7,3,8,2,5]},
-{"id":442895,"name":"Engrave Ring - Frost Specialization","icon":"inv_elemental_primal_water","type":11,"classAllowlist":[0,7,3]},
-{"id":442896,"name":"Engrave Ring - Nature Specialization","icon":"inv_elemental_primal_life","type":11,"classAllowlist":[0,6,7,1]},
-{"id":442897,"name":"Engrave Ring - Shadow Specialization","icon":"inv_elemental_primal_shadow","type":11,"classAllowlist":[0,5,8]},
-{"id":442898,"name":"Engrave Ring - Holy Specialization","icon":"spell_holy_aspiration","type":11,"classAllowlist":[0,4,5]},
+{"id":442813,"name":"Engrave Ring - Sword Specialization","icon":"ability_meleedamage","type":11,"classAllowlist":[9,4,2,6,3,8]},
+{"id":442876,"name":"Engrave Ring - Axe Specialization","icon":"inv_axe_03","type":11,"classAllowlist":[9,4,2,7]},
+{"id":442881,"name":"Engrave Ring - Mace Specialization","icon":"inv_hammer_01","type":11,"classAllowlist":[9,4,6,5,7,1]},
+{"id":442887,"name":"Engrave Ring - Dagger Specialization","icon":"inv_weapon_shortblade_05","type":11,"classAllowlist":[9,2,6,5,7,3,8,1]},
+{"id":442890,"name":"Engrave Ring - Fist Weapon Specialization","icon":"inv_misc_desecrated_plategloves","type":11,"classAllowlist":[9,2,6,7,1]},
+{"id":442891,"name":"Engrave Ring - Ranged Weapon Specialization","icon":"inv_weapon_bow_02","type":11,"classAllowlist":[9,2,6]},
+{"id":442892,"name":"Engrave Ring - Pole Weapon Specialization","icon":"inv_staff_08","type":11,"classAllowlist":[9,4,2,5,7,3,8,1]},
+{"id":442893,"name":"Engrave Ring - Arcane Specialization","icon":"inv_elemental_primal_mana","type":11,"classAllowlist":[1,3,2]},
+{"id":442894,"name":"Engrave Ring - Fire Specialization","icon":"inv_elemental_primal_fire","type":11,"classAllowlist":[7,3,8,2,5]},
+{"id":442895,"name":"Engrave Ring - Frost Specialization","icon":"inv_elemental_primal_water","type":11,"classAllowlist":[7,3]},
+{"id":442896,"name":"Engrave Ring - Nature Specialization","icon":"inv_elemental_primal_life","type":11,"classAllowlist":[6,7,1]},
+{"id":442897,"name":"Engrave Ring - Shadow Specialization","icon":"inv_elemental_primal_shadow","type":11,"classAllowlist":[5,8]},
+{"id":442898,"name":"Engrave Ring - Holy Specialization","icon":"spell_holy_aspiration","type":11,"classAllowlist":[4,5]},
{"id":453622,"name":"Engrave Ring - Feral Combat Specialization","icon":"ability_druid_catformattack","type":11,"classAllowlist":[1]},
{"id":458287,"name":"Engrave Chest - Hallowed Ground","icon":"spell_holy_crusade","type":5,"classAllowlist":[4]},
{"id":458318,"name":"Engrave Belt - Malleable Protection","icon":"spell_holy_restoration","type":8,"classAllowlist":[4]},
{"id":458393,"name":"Engrave Gloves - Cobra Slayer","icon":"spell_nature_guardianward","type":7,"classAllowlist":[2]},
{"id":458479,"name":"Engrave Boots - Wyvern Strike","icon":"inv_spear_02","type":10,"classAllowlist":[2]},
{"id":458856,"name":"Engrave Cloak - Divine Light","icon":"spell_holy_surgeoflight","type":4,"classAllowlist":[4]},
-{"id":459312,"name":"Engrave Ring - Defense Specialization","icon":"inv_shield_06","type":11,"classAllowlist":[0,9,4,6,7,8,1]},
+{"id":459312,"name":"Engrave Ring - Defense Specialization","icon":"inv_shield_06","type":11,"classAllowlist":[9,4,6,7,8,1]},
{"id":462708,"name":"Engrave Gloves - Cutthroat","icon":"ability_rogue_slaughterfromtheshadows","type":7,"classAllowlist":[6]},
{"id":462834,"name":"Engrave Cloak - Shock and Awe","icon":"ability_vehicle_sonicshockwave","type":4,"classAllowlist":[4]},
{"id":462853,"name":"Engrave Pants - Hand of Sacrifice","icon":"spell_holy_sealofsacrifice","type":9,"classAllowlist":[4]},
-{"id":468758,"name":"Engrave Ring - Healing Specialization","icon":"spell_holy_greaterheal","type":11,"classAllowlist":[0,1,3,4,5,7]},
-{"id":468762,"name":"Engrave Ring - Meditation Specialization","icon":"spell_holy_greaterheal","type":11,"classAllowlist":[0,1,2,3,4,5,7,8]}
+{"id":468758,"name":"Engrave Ring - Healing Specialization","icon":"spell_holy_greaterheal","type":11,"classAllowlist":[1,3,4,5,7]},
+{"id":468762,"name":"Engrave Ring - Meditation Specialization","icon":"spell_holy_greaterheal","type":11,"classAllowlist":[1,2,3,4,5,7,8]},
+{"id":1220034,"name":"Soul of the Refined","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"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]},
+{"id":1220238,"name":"Soul of the Shock-Absorber","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220240,"name":"Soul of the Spiritual Bulwark","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220242,"name":"Soul of the Maelstrombringer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220244,"name":"Soul of the Lavawalker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220246,"name":"Soul of the True Alpha","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220248,"name":"Soul of the Totemkeeper","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220250,"name":"Soul of the Ancestors","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220252,"name":"Soul of the Spiritweaver","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220254,"name":"Soul of the Waterwalker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220256,"name":"Soul of the Stormtender","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220258,"name":"Soul of the Elemental Seer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220260,"name":"Soul of the Vitalist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220262,"name":"Soul of the Spirithealer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220266,"name":"Soul of the Chieftain","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220268,"name":"Soul of the Furycharged","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220270,"name":"Soul of the Stormbreaker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220272,"name":"Soul of the Tempest","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220274,"name":"Soul of the Seismic Smasher","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220276,"name":"Soul of the Flamebringer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220278,"name":"Soul of the Volcano","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220279,"name":"Soul of the Raging Flame","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220282,"name":"Soul of the Elemental Master","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220284,"name":"Soul of the Tribesman","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220286,"name":"Soul of the Spirit Guide","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220288,"name":"Soul of the Elder","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220291,"name":"Soul of the Elements","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220293,"name":"Soul of the Lava Sage","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[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]}
],
"zones":[
{"id":209,"name":"Shadowfang Keep","expansion":1},
@@ -15538,7 +15571,6 @@
{"id":453696,"name":"Engrave Ring - Fire Specialization","icon":"inv_jewelry_ring_31","requiresLevel":1},
{"id":453697,"name":"Engrave Ring - Frost Specialization","icon":"inv_jewelry_ring_31","requiresLevel":1},
{"id":457437,"name":"Vanish","icon":"ability_vanish","requiresLevel":1,"hasBuff":true},
-{"id":457544,"name":"S03 - Item - T1 - Shaman - Tank 6P Bonus","icon":"ability_ghoulfrenzy","requiresLevel":1},
{"id":458436,"name":"Wyvern Strike","icon":"inv_spear_02","requiresLevel":1,"hasBuff":true},
{"id":458481,"name":"Wyvern Strike","icon":"inv_spear_02","requiresLevel":1,"hasBuff":true},
{"id":458482,"name":"Wyvern Strike","icon":"inv_spear_02","requiresLevel":1,"hasBuff":true},
diff --git a/assets/database/leftover_db.bin b/assets/database/leftover_db.bin
index e8e708bdbc..38ed5a452e 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 04baf7c889..1bbb831ebd 100644
--- a/assets/database/leftover_db.json
+++ b/assets/database/leftover_db.json
@@ -1482,8 +1482,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},
@@ -1558,8 +1558,8 @@
{"effectId":929,"itemId":16217,"spellId":20069,"name":"Enchant Shield - Greater Stamina","type":13,"enchantType":2,"stats":[0,0,7,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":930,"spellId":13947,"name":"Enchant Gloves - Riding Skill","type":7,"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":2},
{"effectId":931,"spellId":13948,"name":"Enchant Gloves - Minor Haste","type":7,"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":943,"spellId":13529,"name":"Enchant 2H Weapon - Lesser 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":943,"spellId":13693,"name":"Enchant Weapon - 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":943,"spellId":13529,"name":"Enchant 2H Weapon - Lesser 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":963,"spellId":13937,"name":"Enchant 2H Weapon - Greater 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":1483,"itemId":11622,"spellId":15340,"name":"Lesser Arcanum of Rumination","type":1,"extraTypes":[9],"enchantType":3,"stats":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"quality":2},
{"effectId":1503,"itemId":11642,"spellId":15389,"name":"Lesser Arcanum of Constitution","type":1,"extraTypes":[9],"enchantType":3,"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,100,0,0,0,0,0,0,0,0,0],"quality":2},
@@ -1905,31 +1905,64 @@
{"id":440870,"name":"Engrave Boots - Decimation","icon":"spell_fire_fireball02","type":10,"classAllowlist":[8]},
{"id":440882,"name":"Engrave Cloak - Infernal Armor","icon":"achievement_boss_kiljaedan","type":4,"classAllowlist":[8]},
{"id":440892,"name":"Engrave Cloak - Mark of Chaos","icon":"spell_shadow_unstableaffliction_1","type":4,"classAllowlist":[8]},
-{"id":442813,"name":"Engrave Ring - Sword Specialization","icon":"ability_meleedamage","type":11,"classAllowlist":[0,9,4,2,6,3,8]},
-{"id":442876,"name":"Engrave Ring - Axe Specialization","icon":"inv_axe_03","type":11,"classAllowlist":[0,9,4,2,7]},
-{"id":442881,"name":"Engrave Ring - Mace Specialization","icon":"inv_hammer_01","type":11,"classAllowlist":[0,9,4,6,5,7,1]},
-{"id":442887,"name":"Engrave Ring - Dagger Specialization","icon":"inv_weapon_shortblade_05","type":11,"classAllowlist":[0,9,2,6,5,7,3,8,1]},
-{"id":442890,"name":"Engrave Ring - Fist Weapon Specialization","icon":"inv_misc_desecrated_plategloves","type":11,"classAllowlist":[0,9,2,6,7,1]},
-{"id":442891,"name":"Engrave Ring - Ranged Weapon Specialization","icon":"inv_weapon_bow_02","type":11,"classAllowlist":[0,9,2,6]},
-{"id":442892,"name":"Engrave Ring - Pole Weapon Specialization","icon":"inv_staff_08","type":11,"classAllowlist":[0,9,4,2,5,7,3,8,1]},
-{"id":442893,"name":"Engrave Ring - Arcane Specialization","icon":"inv_elemental_primal_mana","type":11,"classAllowlist":[0,1,3,2]},
-{"id":442894,"name":"Engrave Ring - Fire Specialization","icon":"inv_elemental_primal_fire","type":11,"classAllowlist":[0,7,3,8,2,5]},
-{"id":442895,"name":"Engrave Ring - Frost Specialization","icon":"inv_elemental_primal_water","type":11,"classAllowlist":[0,7,3]},
-{"id":442896,"name":"Engrave Ring - Nature Specialization","icon":"inv_elemental_primal_life","type":11,"classAllowlist":[0,6,7,1]},
-{"id":442897,"name":"Engrave Ring - Shadow Specialization","icon":"inv_elemental_primal_shadow","type":11,"classAllowlist":[0,5,8]},
-{"id":442898,"name":"Engrave Ring - Holy Specialization","icon":"spell_holy_aspiration","type":11,"classAllowlist":[0,4,5]},
+{"id":442813,"name":"Engrave Ring - Sword Specialization","icon":"ability_meleedamage","type":11,"classAllowlist":[9,4,2,6,3,8]},
+{"id":442876,"name":"Engrave Ring - Axe Specialization","icon":"inv_axe_03","type":11,"classAllowlist":[9,4,2,7]},
+{"id":442881,"name":"Engrave Ring - Mace Specialization","icon":"inv_hammer_01","type":11,"classAllowlist":[9,4,6,5,7,1]},
+{"id":442887,"name":"Engrave Ring - Dagger Specialization","icon":"inv_weapon_shortblade_05","type":11,"classAllowlist":[9,2,6,5,7,3,8,1]},
+{"id":442890,"name":"Engrave Ring - Fist Weapon Specialization","icon":"inv_misc_desecrated_plategloves","type":11,"classAllowlist":[9,2,6,7,1]},
+{"id":442891,"name":"Engrave Ring - Ranged Weapon Specialization","icon":"inv_weapon_bow_02","type":11,"classAllowlist":[9,2,6]},
+{"id":442892,"name":"Engrave Ring - Pole Weapon Specialization","icon":"inv_staff_08","type":11,"classAllowlist":[9,4,2,5,7,3,8,1]},
+{"id":442893,"name":"Engrave Ring - Arcane Specialization","icon":"inv_elemental_primal_mana","type":11,"classAllowlist":[1,3,2]},
+{"id":442894,"name":"Engrave Ring - Fire Specialization","icon":"inv_elemental_primal_fire","type":11,"classAllowlist":[7,3,8,2,5]},
+{"id":442895,"name":"Engrave Ring - Frost Specialization","icon":"inv_elemental_primal_water","type":11,"classAllowlist":[7,3]},
+{"id":442896,"name":"Engrave Ring - Nature Specialization","icon":"inv_elemental_primal_life","type":11,"classAllowlist":[6,7,1]},
+{"id":442897,"name":"Engrave Ring - Shadow Specialization","icon":"inv_elemental_primal_shadow","type":11,"classAllowlist":[5,8]},
+{"id":442898,"name":"Engrave Ring - Holy Specialization","icon":"spell_holy_aspiration","type":11,"classAllowlist":[4,5]},
{"id":453622,"name":"Engrave Ring - Feral Combat Specialization","icon":"ability_druid_catformattack","type":11,"classAllowlist":[1]},
{"id":458287,"name":"Engrave Chest - Hallowed Ground","icon":"spell_holy_crusade","type":5,"classAllowlist":[4]},
{"id":458318,"name":"Engrave Belt - Malleable Protection","icon":"spell_holy_restoration","type":8,"classAllowlist":[4]},
{"id":458393,"name":"Engrave Gloves - Cobra Slayer","icon":"spell_nature_guardianward","type":7,"classAllowlist":[2]},
{"id":458479,"name":"Engrave Boots - Wyvern Strike","icon":"inv_spear_02","type":10,"classAllowlist":[2]},
{"id":458856,"name":"Engrave Cloak - Divine Light","icon":"spell_holy_surgeoflight","type":4,"classAllowlist":[4]},
-{"id":459312,"name":"Engrave Ring - Defense Specialization","icon":"inv_shield_06","type":11,"classAllowlist":[0,9,4,6,7,8,1]},
+{"id":459312,"name":"Engrave Ring - Defense Specialization","icon":"inv_shield_06","type":11,"classAllowlist":[9,4,6,7,8,1]},
{"id":462708,"name":"Engrave Gloves - Cutthroat","icon":"ability_rogue_slaughterfromtheshadows","type":7,"classAllowlist":[6]},
{"id":462834,"name":"Engrave Cloak - Shock and Awe","icon":"ability_vehicle_sonicshockwave","type":4,"classAllowlist":[4]},
{"id":462853,"name":"Engrave Pants - Hand of Sacrifice","icon":"spell_holy_sealofsacrifice","type":9,"classAllowlist":[4]},
-{"id":468758,"name":"Engrave Ring - Healing Specialization","icon":"spell_holy_greaterheal","type":11,"classAllowlist":[0,1,3,4,5,7]},
-{"id":468762,"name":"Engrave Ring - Meditation Specialization","icon":"spell_holy_greaterheal","type":11,"classAllowlist":[0,1,2,3,4,5,7,8]}
+{"id":468758,"name":"Engrave Ring - Healing Specialization","icon":"spell_holy_greaterheal","type":11,"classAllowlist":[1,3,4,5,7]},
+{"id":468762,"name":"Engrave Ring - Meditation Specialization","icon":"spell_holy_greaterheal","type":11,"classAllowlist":[1,2,3,4,5,7,8]},
+{"id":1220034,"name":"Soul of the Refined","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"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]},
+{"id":1220238,"name":"Soul of the Shock-Absorber","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220240,"name":"Soul of the Spiritual Bulwark","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220242,"name":"Soul of the Maelstrombringer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220244,"name":"Soul of the Lavawalker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220246,"name":"Soul of the True Alpha","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220248,"name":"Soul of the Totemkeeper","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220250,"name":"Soul of the Ancestors","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220252,"name":"Soul of the Spiritweaver","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220254,"name":"Soul of the Waterwalker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220256,"name":"Soul of the Stormtender","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220258,"name":"Soul of the Elemental Seer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220260,"name":"Soul of the Vitalist","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220262,"name":"Soul of the Spirithealer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220266,"name":"Soul of the Chieftain","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220268,"name":"Soul of the Furycharged","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220270,"name":"Soul of the Stormbreaker","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220272,"name":"Soul of the Tempest","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220274,"name":"Soul of the Seismic Smasher","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220276,"name":"Soul of the Flamebringer","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220278,"name":"Soul of the Volcano","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220279,"name":"Soul of the Raging Flame","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220282,"name":"Soul of the Elemental Master","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220284,"name":"Soul of the Tribesman","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220286,"name":"Soul of the Spirit Guide","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220288,"name":"Soul of the Elder","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220291,"name":"Soul of the Elements","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[7]},
+{"id":1220293,"name":"Soul of the Lava Sage","icon":"spell_holy_divinespirit","type":3,"classAllowlist":[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]}
],
"zones":[
],
diff --git a/assets/db_inputs/wowhead_shoulder_rune_tooltips.csv b/assets/db_inputs/wowhead_shoulder_rune_tooltips.csv
new file mode 100644
index 0000000000..5d4291b591
--- /dev/null
+++ b/assets/db_inputs/wowhead_shoulder_rune_tooltips.csv
@@ -0,0 +1,206 @@
+1220142,{"name":"Soul of the Reverberant","quality":3,"icon":"spell_holy_divinespirit","tooltip":"
Soul of the Reverberant Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220050,{"name":"Soul of the Demonlord","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Demonlord Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220350,{"name":"Soul of the Lunatic","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Lunatic Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220042,{"name":"Soul of the Shadowmancer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Shadowmancer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220364,{"name":"Soul of the Cometcaller","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Cometcaller Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220307,{"name":"Soul of the Beast","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Beast Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220328,{"name":"Soul of the Benevolet Seer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Benevolet Seer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219956,{"name":"Soul of the Tactician","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Tactician Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219964,{"name":"Soul of the Titan","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Titan Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219976,{"name":"Soul of Enmity","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of Enmity Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220072,{"name":"Soul of the Preyseeker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Preyseeker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219960,{"name":"Soul of the Battle Forecaster","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Battle Forecaster Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220305,{"name":"Soul of the Territorial","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Territorial Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220090,{"name":"Soul of the Alpha Tamer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Alpha Tamer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220102,{"name":"Soul of the Strategist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Strategist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220040,{"name":"Soul of the Malevolent","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Malevolent Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220232,{"name":"Soul of the Windwalker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Windwalker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219992,{"name":"Soul of the Aftershock","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Aftershock Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220299,{"name":"Soul of the Gentle Paw","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Gentle Paw Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220214,{"name":"Soul of the Sealbearer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Sealbearer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220088,{"name":"Soul of the Hound Master","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Hound Master Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220150,{"name":"Soul of the Arcanist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Arcanist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220354,{"name":"Soul of the Night","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Night Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220208,{"name":"Soul of the Vindicator","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Vindicator Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220060,{"name":"Soul of the Abyssal","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Abyssal Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220234,{"name":"Soul of the Shield Master","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Shield Master Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220340,{"name":"Soul of the Prideful","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Prideful Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220362,{"name":"Soul of the Astral Ascendant","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Astral Ascendant Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220358,{"name":"Soul of the Wrathful","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Wrathful Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220035,{"name":"Soul of the Decimator","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Decimator Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220274,{"name":"Soul of the Seismic Smasher","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Seismic Smasher Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220318,{"name":"Soul of the Nurturer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Nurturer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220034,{"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":[]}
+1220336,{"name":"Soul of the Ripper","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Ripper Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220346,{"name":"Soul of the Exsanguinator","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Exsanguinator Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220366,{"name":"Soul of the Forest","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Forest Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219966,{"name":"Soul of the Destroyer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Destroyer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219972,{"name":"Soul of the Pristine Blocker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Pristine Blocker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220080,{"name":"Soul of the Toxinologist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Toxinologist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220182,{"name":"Soul of the Lightwarden","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Lightwarden Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219970,{"name":"Soul of the Sanguinist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Sanguinist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219988,{"name":"Soul of the Southpaw","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Southpaw Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220004,{"name":"Soul of the Phantom","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Phantom Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220048,{"name":"Soul of the Infernal Shepherd","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Infernal Shepherd Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220240,{"name":"Soul of the Spiritual Bulwark","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Spiritual Bulwark Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220244,{"name":"Soul of the Lavawalker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Lavawalker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220140,{"name":"Soul of the Plaguebringer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Plaguebringer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220160,{"name":"Soul of the Cryomancer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Cryomancer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219980,{"name":"Soul of the Revenger","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Revenger Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220056,{"name":"Soul of the Flamewraith","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Flamewraith Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220070,{"name":"Soul of the Misleader","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Misleader Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220132,{"name":"Soul of the Spirit Font","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Spirit Font Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220268,{"name":"Soul of the Furycharged","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Furycharged Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220270,{"name":"Soul of the Stormbreaker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Stormbreaker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220316,{"name":"Soul of the Innervator","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Innervator Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219958,{"name":"Soul of the War Veteran","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the War Veteran Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219986,{"name":"Soul of the Sentinel","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Sentinel Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220064,{"name":"Soul of the Umbral Blade","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Umbral Blade Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220078,{"name":"Soul of the Alternator","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Alternator Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220138,{"name":"Soul of the Contemnor","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Contemnor Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220184,{"name":"Soul of the Radiant Defender","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Radiant Defender Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220186,{"name":"Soul of the Shieldbearer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Shieldbearer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220200,{"name":"Soul of the Exemplar","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Exemplar Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219982,{"name":"Soul of the Incessant","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Incessant Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220076,{"name":"Soul of the Hazard Harrier","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Hazard Harrier Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220192,{"name":"Soul of the Ironclad","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Ironclad Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220194,{"name":"Soul of the Guardian","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Guardian Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220202,{"name":"Soul of the Inquisitor","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Inquisitor Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220218,{"name":"Soul of the Judicator","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Judicator Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220228,{"name":"Soul of the Exile","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Exile Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220312,{"name":"Soul of the Furious","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Furious Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220338,{"name":"Soul of the Claw","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Claw Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220104,{"name":"Soul of the Deadly Striker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Deadly Striker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220166,{"name":"Soul of the Magical Armorer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Magical Armorer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220230,{"name":"Soul of the Templar","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Templar Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220291,{"name":"Soul of the Elements","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Elements Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220314,{"name":"Soul of the Mangler","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Mangler Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220124,{"name":"Soul of the Soul Warder","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Soul Warder Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220126,{"name":"Soul of the Twilight Walker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Twilight Walker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220236,{"name":"Soul of the Totemic Protector","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Totemic Protector Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220008,{"name":"Soul of the Thrill Seeker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Thrill Seeker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220054,{"name":"Soul of the Pained","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Pained Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220226,{"name":"Soul of the Lightbringer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Lightbringer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220266,{"name":"Soul of the Chieftain","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Chieftain Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220272,{"name":"Soul of the Tempest","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Tempest Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220332,{"name":"Soul of the Illuminator","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Illuminator Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220360,{"name":"Soul of the Graceful","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Graceful Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219962,{"name":"Soul of the Bloodseeker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Bloodseeker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220094,{"name":"Soul of the Retaliator","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Retaliator Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220120,{"name":"Soul of the Archbishop","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Archbishop Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220176,{"name":"Soul of the Igniter","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Igniter Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220242,{"name":"Soul of the Maelstrombringer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Maelstrombringer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220330,{"name":"Soul of the Lifeweaver","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Lifeweaver Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220344,{"name":"Soul of the Frenetic","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Frenetic Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220348,{"name":"Soul of the Thornkeeper","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Thornkeeper Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220052,{"name":"Soul of the Demonic Exorcist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Demonic Exorcist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220092,{"name":"Soul of the Huntsman","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Huntsman Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220110,{"name":"Soul of the Celebrant","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Celebrant Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220136,{"name":"Soul of the Unwavering Defiler","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Unwavering Defiler Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220172,{"name":"Soul of the Perpetual Blaze","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Perpetual Blaze Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219968,{"name":"Soul of the Deathbound","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Deathbound Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219974,{"name":"Soul of the Savage","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Savage Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220066,{"name":"Soul of the Ritualist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Ritualist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220112,{"name":"Soul of the Faithful","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Faithful Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219996,{"name":"Soul of the Toxicologist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Toxicologist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220038,{"name":"Soul of the Rotbringer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Rotbringer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220128,{"name":"Soul of the Mind Breaker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Mind Breaker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220152,{"name":"Soul of the Eternal Caretaker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Eternal Caretaker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220216,{"name":"Soul of the Justicar","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Justicar Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220238,{"name":"Soul of the Shock-Absorber","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Shock-Absorber Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220032,{"name":"Soul of the Transfusionist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Transfusionist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220144,{"name":"Soul of the Chronohealer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Chronohealer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220220,{"name":"Soul of the Ascendant","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Ascendant Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220224,{"name":"Soul of the Excommunicator","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Excommunicator Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220368,{"name":"Soul of Animalistic Expertise","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of Animalistic Expertise Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219990,{"name":"Soul of the Gladiator","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Gladiator Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220014,{"name":"Soul of the Efficient","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Efficient Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220022,{"name":"Soul of the Poised Brawler","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Poised Brawler Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220058,{"name":"Soul of the Fleshfeaster","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Fleshfeaster Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220034,{"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":[]}
+1220118,{"name":"Soul of the Devotee","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Devotee Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220122,{"name":"Soul of the Penitent","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Penitent Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220134,{"name":"Soul of the Zealot","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Zealot Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220276,{"name":"Soul of the Flamebringer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Flamebringer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220326,{"name":"Soul of the Feathered Sage","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Feathered Sage Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220098,{"name":"Soul of the Lethal Lasher","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Lethal Lasher Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220106,{"name":"Soul of the Hastened Healer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Hastened Healer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220114,{"name":"Soul of the Serendipitous","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Serendipitous Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220297,{"name":"Soul of the Corrupt","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Corrupt Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220324,{"name":"Soul of the Grove Tender","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Grove Tender Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220334,{"name":"Soul of Predatory Instincts","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of Predatory Instincts Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219998,{"name":"Soul of the Executioner","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Executioner Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220016,{"name":"Soul of the Knife Juggler","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Knife Juggler Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220030,{"name":"Soul of the Bloodthirsty","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Bloodthirsty Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220062,{"name":"Soul of the Voidborne","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Voidborne Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220100,{"name":"Soul of the Kineticist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Kineticist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220158,{"name":"Soul of Winter's Grasp","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of Winter's Grasp Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220162,{"name":"Soul of the Evoker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Evoker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220190,{"name":"Soul of the Reckoner","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Reckoner Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220002,{"name":"Soul of the Butcher","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Butcher Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220026,{"name":"Soul of the Fencer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Fencer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220044,{"name":"Soul of the Chaos Harbinger","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Chaos Harbinger Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220046,{"name":"Soul of the Arsonist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Arsonist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220074,{"name":"Soul of the Sharpshooter","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Sharpshooter Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220130,{"name":"Soul of the Deathdealer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Deathdealer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219972,{"name":"Soul of the Pristine Blocker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Pristine Blocker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220000,{"name":"Soul of the Opportunist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Opportunist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220010,{"name":"Soul of the Shiv Savant","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Shiv Savant Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220020,{"name":"Soul of the Equilibrist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Equilibrist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220084,{"name":"Soul of the Trick Shooter","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Trick Shooter Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220116,{"name":"Soul of the Resonant","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Resonant Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220164,{"name":"Soul of the Elementalist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Elementalist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220174,{"name":"Soul of the Pyromaniac","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Pyromaniac Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220034,{"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":[]}
+1220204,{"name":"Soul of the Sovereign","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Sovereign Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220250,{"name":"Soul of the Ancestors","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Ancestors Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220342,{"name":"Soul of the Barbaric","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Barbaric Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220352,{"name":"Soul of the Starcaller","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Starcaller Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219994,{"name":"Soul of the Avoidant","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Avoidant Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220006,{"name":"Soul of the Scoundrel","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Scoundrel Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220023,{"name":"Soul of the Black Belt","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Black Belt Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220178,{"name":"Soul of the Torcher","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Torcher Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220248,{"name":"Soul of the Totemkeeper","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Totemkeeper Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220258,{"name":"Soul of the Elemental Seer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Elemental Seer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220286,{"name":"Soul of the Spirit Guide","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Spirit Guide Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220288,{"name":"Soul of the Elder","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Elder Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220303,{"name":"Soul of the Shifter","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Shifter Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220086,{"name":"Soul of the Beast Tender","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Beast Tender Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220210,{"name":"Soul of the Altruist","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Altruist Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220278,{"name":"Soul of the Volcano","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Volcano Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220279,{"name":"Soul of the Raging Flame","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Raging Flame Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220320,{"name":"Soul of the Tranquil","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Tranquil Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220322,{"name":"Soul of the Dreamwalker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Dreamwalker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219978,{"name":"Soul of the Deflective","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Deflective Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1219984,{"name":"Soul of the Thunderbringer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Thunderbringer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220146,{"name":"Soul of Temporal Longing","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of Temporal Longing Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220156,{"name":"Soul of the Wardshaper","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Wardshaper Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220188,{"name":"Soul of the Bastion","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Bastion Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220196,{"name":"Soul of the Peacekeeper","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Peacekeeper Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220212,{"name":"Soul of the Arbiter","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Arbiter Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220284,{"name":"Soul of the Tribesman","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Tribesman Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220012,{"name":"Soul of the Stalker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Stalker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220028,{"name":"Soul of the Swashbuckler","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Swashbuckler Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220148,{"name":"Soul of the Precognitive","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Precognitive Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220154,{"name":"Soul of the Spellbider","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Spellbider Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220206,{"name":"Soul of the Dominus","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Dominus Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220295,{"name":"Soul of the Ancestral Warden","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Ancestral Warden Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220356,{"name":"Soul of the Keepers","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Keepers Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220018,{"name":"Soul of the Shadow Master","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Shadow Master Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220082,{"name":"Soul of the Bounty Hunter","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Bounty Hunter Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220168,{"name":"Soul of the Kindler","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Kindler Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220222,{"name":"Soul of the Retributor","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Retributor Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220246,{"name":"Soul of the True Alpha","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the True Alpha Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220256,{"name":"Soul of the Stormtender","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Stormtender Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220262,{"name":"Soul of the Spirithealer","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Spirithealer Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220034,{"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":[]}
+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":[]}
+1220310,{"name":"Soul of the Lacerator","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Lacerator Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220252,{"name":"Soul of the Spiritweaver","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Spiritweaver Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220254,{"name":"Soul of the Waterwalker","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Waterwalker Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
+1220293,{"name":"Soul of the Lava Sage","quality":3,"icon":"spell_holy_divinespirit","tooltip":"Soul of the Lava Sage Item Level 55 Binds when picked up Unique (20) |
","spells":[]}
\ No newline at end of file
diff --git a/proto/shaman.proto b/proto/shaman.proto
index 0892e4fb35..09c2136869 100644
--- a/proto/shaman.proto
+++ b/proto/shaman.proto
@@ -4,211 +4,240 @@ package proto;
option go_package = "./proto";
message ShamanTalents {
- // Elemental
- int32 convection = 1;
- int32 concussion = 2;
- int32 earths_grasp = 3;
- int32 elemental_warding = 4;
- int32 call_of_flame = 5;
- bool elemental_focus = 6;
- int32 reverberation = 7;
- int32 call_of_thunder = 8;
- int32 improved_fire_totems = 9;
- int32 eye_of_the_storm = 10;
- int32 elemental_devastation = 11;
- int32 storm_reach = 12;
- bool elemental_fury = 13;
- int32 lightning_mastery = 14;
- bool elemental_mastery = 15;
-
- // Enhancement
- int32 ancestral_knowledge = 16;
- int32 shield_specialization = 17;
- int32 guardian_totems = 18;
- int32 thundering_strikes = 19;
- int32 improved_ghost_wolf = 20;
- int32 improved_lightning_shield = 21;
- int32 enhancing_totems = 22;
- bool two_handed_axes_and_maces = 23;
- int32 anticipation = 24;
- int32 flurry = 25;
- int32 toughness = 26;
- int32 improved_weapon_totems = 27;
- int32 elemental_weapons = 28;
- bool parry = 29;
- int32 weapon_mastery = 30;
- bool stormstrike = 31;
-
- // Restoration
- int32 improved_healing_wave = 32;
- int32 tidal_focus = 33;
- int32 improved_reincarnation = 34;
- int32 ancestral_healing = 35;
- int32 totemic_focus = 36;
- int32 natures_guidance = 37;
- int32 healing_focus = 38;
- bool totemic_mastery = 39;
- int32 healing_grace = 40;
- int32 restorative_totems = 41;
- int32 tidal_mastery = 42;
- int32 healing_way = 43;
- bool natures_swiftness = 44;
- int32 purification = 45;
- bool mana_tide_totem = 46;
+ // Elemental
+ int32 convection = 1;
+ int32 concussion = 2;
+ int32 earths_grasp = 3;
+ int32 elemental_warding = 4;
+ int32 call_of_flame = 5;
+ bool elemental_focus = 6;
+ int32 reverberation = 7;
+ int32 call_of_thunder = 8;
+ int32 improved_fire_totems = 9;
+ int32 eye_of_the_storm = 10;
+ int32 elemental_devastation = 11;
+ int32 storm_reach = 12;
+ bool elemental_fury = 13;
+ int32 lightning_mastery = 14;
+ bool elemental_mastery = 15;
+
+ // Enhancement
+ int32 ancestral_knowledge = 16;
+ int32 shield_specialization = 17;
+ int32 guardian_totems = 18;
+ int32 thundering_strikes = 19;
+ int32 improved_ghost_wolf = 20;
+ int32 improved_lightning_shield = 21;
+ int32 enhancing_totems = 22;
+ bool two_handed_axes_and_maces = 23;
+ int32 anticipation = 24;
+ int32 flurry = 25;
+ int32 toughness = 26;
+ int32 improved_weapon_totems = 27;
+ int32 elemental_weapons = 28;
+ bool parry = 29;
+ int32 weapon_mastery = 30;
+ bool stormstrike = 31;
+
+ // Restoration
+ int32 improved_healing_wave = 32;
+ int32 tidal_focus = 33;
+ int32 improved_reincarnation = 34;
+ int32 ancestral_healing = 35;
+ int32 totemic_focus = 36;
+ int32 natures_guidance = 37;
+ int32 healing_focus = 38;
+ bool totemic_mastery = 39;
+ int32 healing_grace = 40;
+ int32 restorative_totems = 41;
+ int32 tidal_mastery = 42;
+ int32 healing_way = 43;
+ bool natures_swiftness = 44;
+ int32 purification = 45;
+ bool mana_tide_totem = 46;
}
enum ShamanRune {
- RuneNone = 0;
-
- RuneHelmBurn = 415231;
- RuneHelmMentalDexterity = 415140;
- RuneHelmTidalWaves = 432042;
-
- RuneCloakCoherence = 415096;
- RuneCloakStormEarthAndFire = 440569;
- RuneCloakFeralSpirit = 440580;
-
- RuneChestDualWieldSpec = 408496;
- RuneChestHealingRain = 415236;
- RuneChestOverload = 408438;
- RuneChestShieldMastery = 408524;
- RuneChestTwoHandedMastery = 436364;
-
- RuneBracersOvercharged = 432140;
- RuneBracersRiptide = 408521;
- RuneBracersRollingThunder = 432056;
- RuneBracersStaticShock = 432134;
-
- RuneHandsLavaBurst = 408490;
- RuneHandsLavaLash = 408507;
- RuneHandsMoltenBlast = 425339;
- RuneHandsWaterShield = 408510;
-
- RuneWaistFireNova = 408339;
- RuneWaistMaelstromWeapon = 408498;
- RuneWaistPowerSurge = 415100;
-
- RuneLegsAncestralGuidance = 409324;
- RuneLegsEarthShield = 408514;
- RuneLegsWayOfEarth = 408531;
- RuneLegsGreaterGhostWolf = 415813;
-
- RuneFeetAncestralAwakening = 425858;
- RuneFeetDecoyTotem = 425874;
- RuneFeetSpiritOfTheAlpha = 408696;
+ RuneNone = 0;
+
+ RuneHelmBurn = 415231;
+ RuneHelmMentalDexterity = 415140;
+ RuneHelmTidalWaves = 432042;
+
+ RuneShouldersAncestralWarden = 1220295;
+ RuneShouldersChieftain = 1220266;
+ RuneShouldersCorrupt = 1220297;
+ RuneShouldersElder = 1220288;
+ RuneShouldersElementalMaster = 1220282;
+ RuneShouldersElementalSeer = 1220258;
+ RuneShouldersElements = 1220291;
+ RuneShouldersFlamebringer = 1220276;
+ RuneShouldersFurycharged = 1220268;
+ RuneShouldersLavaSage = 1220293;
+ RuneShouldersLavaWalker = 1220244;
+ RuneShouldersMaelstrombringer = 1220242;
+ RuneShouldersRagingFlame = 1220279;
+ RuneShouldersRefined = 1220034;
+ RuneShouldersSeismicSmasher = 1220274;
+ RuneShouldersShieldMaster = 1220234;
+ RuneShouldersShockAbsorber = 1220238;
+ RuneShouldersSpiritualBulwark = 1220240;
+ RuneShouldersSpiritGuide = 1220286;
+ RuneShouldersStormbreaker = 1220270;
+ RuneShouldersStormtender = 1220256;
+ RuneShouldersTempest = 1220272;
+ RuneShouldersTotemicProtector = 1220236;
+ RuneShouldersTribesman = 1220284;
+ RuneShouldersTrueAlpha = 1220246;
+ RuneShouldersVolcano = 1220278;
+ RuneShouldersWaterWalker = 1220254;
+ RuneShouldersWindwalker = 1220232;
+
+ RuneCloakCoherence = 415096;
+ RuneCloakStormEarthAndFire = 440569;
+ RuneCloakFeralSpirit = 440580;
+
+ RuneChestDualWieldSpec = 408496;
+ RuneChestHealingRain = 415236;
+ RuneChestOverload = 408438;
+ RuneChestShieldMastery = 408524;
+ RuneChestTwoHandedMastery = 436364;
+
+ RuneBracersOvercharged = 432140;
+ RuneBracersRiptide = 408521;
+ RuneBracersRollingThunder = 432056;
+ RuneBracersStaticShock = 432134;
+
+ RuneHandsLavaBurst = 408490;
+ RuneHandsLavaLash = 408507;
+ RuneHandsMoltenBlast = 425339;
+ RuneHandsWaterShield = 408510;
+
+ RuneWaistFireNova = 408339;
+ RuneWaistMaelstromWeapon = 408498;
+ RuneWaistPowerSurge = 415100;
+
+ RuneLegsAncestralGuidance = 409324;
+ RuneLegsEarthShield = 408514;
+ RuneLegsWayOfEarth = 408531;
+ RuneLegsGreaterGhostWolf = 415813;
+
+ RuneFeetAncestralAwakening = 425858;
+ RuneFeetDecoyTotem = 425874;
+ RuneFeetSpiritOfTheAlpha = 408696;
}
enum EarthTotem {
- NoEarthTotem = 0;
- StrengthOfEarthTotem = 1;
- TremorTotem = 2;
- StoneskinTotem = 3;
+ NoEarthTotem = 0;
+ StrengthOfEarthTotem = 1;
+ TremorTotem = 2;
+ StoneskinTotem = 3;
}
enum AirTotem {
- NoAirTotem = 0;
- WindfuryTotem = 1;
- GraceOfAirTotem = 2;
+ NoAirTotem = 0;
+ WindfuryTotem = 1;
+ GraceOfAirTotem = 2;
}
enum FireTotem {
- NoFireTotem = 0;
- MagmaTotem = 1;
- SearingTotem = 2;
- FireNovaTotem = 3;
+ NoFireTotem = 0;
+ MagmaTotem = 1;
+ SearingTotem = 2;
+ FireNovaTotem = 3;
}
enum WaterTotem {
- NoWaterTotem = 0;
- ManaSpringTotem = 1;
- HealingStreamTotem = 2;
+ NoWaterTotem = 0;
+ ManaSpringTotem = 1;
+ HealingStreamTotem = 2;
}
// Deprecated on 2024-02-08. Use APL totems instead
message ShamanTotems {
- // Not used here, but used by APL
- enum TotemType {
- TypeUnknown = 0;
- Earth = 1;
- Air = 2;
- Fire = 3;
- Water = 4;
- }
-
- EarthTotem earth = 1;
- AirTotem air = 2;
- FireTotem fire = 3;
- WaterTotem water = 4;
-
- // If set, will use mana tide when appropriate.
- bool use_mana_tide = 5;
-
- // If set, any time a 2-minute totem is about to expire, will recall and
- // replace all totems.
- bool recall_totems = 8;
-
- // If set will use fire totems as an MCD instead of manually controlling when to place them.
- bool use_fire_mcd = 9;
-
- // Bonus spell power for fire elemental snapshotting.
- int32 bonus_spellpower = 10;
-
- // Snapshot fire elemental using Tier 10 4 set bonus.
- bool enh_tier_ten_bonus = 11;
+ // Not used here, but used by APL
+ enum TotemType {
+ TypeUnknown = 0;
+ Earth = 1;
+ Air = 2;
+ Fire = 3;
+ Water = 4;
+ }
+
+ EarthTotem earth = 1;
+ AirTotem air = 2;
+ FireTotem fire = 3;
+ WaterTotem water = 4;
+
+ // If set, will use mana tide when appropriate.
+ bool use_mana_tide = 5;
+
+ // If set, any time a 2-minute totem is about to expire, will recall and
+ // replace all totems.
+ bool recall_totems = 8;
+
+ // If set will use fire totems as an MCD instead of manually controlling when to place them.
+ bool use_fire_mcd = 9;
+
+ // Bonus spell power for fire elemental snapshotting.
+ int32 bonus_spellpower = 10;
+
+ // Snapshot fire elemental using Tier 10 4 set bonus.
+ bool enh_tier_ten_bonus = 11;
}
enum ShamanSyncType {
NoSync = 0;
SyncMainhandOffhandSwings = 1;
DelayOffhandSwings = 2;
- Auto = 3;
+ Auto = 3;
}
message ElementalShaman {
- message Rotation {
- }
+ message Rotation {
+ }
- // NextIndex: 6
- message Options {
- }
- Options options = 3;
+ // NextIndex: 6
+ message Options {
+ }
+ Options options = 3;
}
message EnhancementShaman {
- message Rotation {
- }
-
- // NextIndex: 7
- message Options {
- ShamanSyncType sync_type = 3;
- }
- Options options = 3;
+ message Rotation {
+ }
+
+ // NextIndex: 7
+ message Options {
+ ShamanSyncType sync_type = 3;
+ }
+ Options options = 3;
}
enum ShamanHealSpell {
- AutoHeal = 0;
- HealingWave = 1;
- LesserHealingWave = 2;
- ChainHeal = 3;
+ AutoHeal = 0;
+ HealingWave = 1;
+ LesserHealingWave = 2;
+ ChainHeal = 3;
}
message RestorationShaman {
- message Rotation {
- }
-
- // NextIndex: 8
- message Options {
- int32 earth_shield_p_p_m = 5;
- ShamanTotems totems = 6 [deprecated=true];
- }
- Options options = 3;
+ message Rotation {
+ }
+
+ // NextIndex: 8
+ message Options {
+ int32 earth_shield_p_p_m = 5;
+ ShamanTotems totems = 6 [deprecated=true];
+ }
+ Options options = 3;
}
message WardenShaman {
- message Rotation {
- }
+ message Rotation {
+ }
- message Options {
- }
- Options options = 1;
+ message Options {
+ }
+ Options options = 1;
}
diff --git a/sim/core/aura.go b/sim/core/aura.go
index 7ffb003c2c..93c0c17ce7 100644
--- a/sim/core/aura.go
+++ b/sim/core/aura.go
@@ -18,6 +18,7 @@ type OnInit func(aura *Aura, sim *Simulation)
type OnReset func(aura *Aura, sim *Simulation)
type OnDoneIteration func(aura *Aura, sim *Simulation)
type OnGain func(aura *Aura, sim *Simulation)
+type OnRefresh func(aura *Aura, sim *Simulation)
type OnExpire func(aura *Aura, sim *Simulation)
type OnStacksChange func(aura *Aura, sim *Simulation, oldStacks int32, newStacks int32)
type OnStatsChange func(aura *Aura, sim *Simulation, oldStats stats.Stats, newStats stats.Stats)
@@ -89,6 +90,7 @@ type Aura struct {
OnReset OnReset
OnDoneIteration OnDoneIteration
OnGain OnGain
+ OnRefresh OnRefresh
OnExpire OnExpire
OnStacksChange OnStacksChange // Invoked when the number of stacks of this aura changes.
OnStatsChange OnStatsChange // Invoked when the stats of this aura owner changes.
@@ -179,6 +181,10 @@ func (aura *Aura) Refresh(sim *Simulation) {
if sim.Log != nil && aura.IsActive() && !aura.ActionID.IsEmptyAction() {
aura.Unit.Log(sim, "Aura refreshed: %s", aura.ActionID)
}
+
+ if aura.OnRefresh != nil {
+ aura.OnRefresh(aura, sim)
+ }
}
func (aura *Aura) GetStacks() int32 {
@@ -276,6 +282,19 @@ func (aura *Aura) ApplyOnGain(newOnGain OnGain) {
}
}
+// Adds a handler to be called OnRefresh, in addition to any current handlers.
+func (aura *Aura) ApplyOnRefresh(newOnRefresh OnRefresh) {
+ oldOnRefresh := aura.OnRefresh
+ if oldOnRefresh == nil {
+ aura.OnRefresh = newOnRefresh
+ } else {
+ aura.OnRefresh = func(aura *Aura, sim *Simulation) {
+ oldOnRefresh(aura, sim)
+ newOnRefresh(aura, sim)
+ }
+ }
+}
+
// Adds a handler to be called OnExpire, in addition to any current handlers.
func (aura *Aura) ApplyOnExpire(newOnExpire OnExpire) {
oldOnExpire := aura.OnExpire
diff --git a/sim/core/database.go b/sim/core/database.go
index 5c20b4156d..319af4020c 100644
--- a/sim/core/database.go
+++ b/sim/core/database.go
@@ -175,6 +175,10 @@ func (equipment *Equipment) Head() *Item {
return &equipment[proto.ItemSlot_ItemSlotHead]
}
+func (equipment *Equipment) Shoulders() *Item {
+ return &equipment[proto.ItemSlot_ItemSlotShoulder]
+}
+
func (equipment *Equipment) Hands() *Item {
return &equipment[proto.ItemSlot_ItemSlotHands]
}
diff --git a/sim/shaman/air_totems.go b/sim/shaman/air_totems.go
index 3fe8ea07fb..24189dd741 100644
--- a/sim/shaman/air_totems.go
+++ b/sim/shaman/air_totems.go
@@ -127,16 +127,12 @@ func (shaman *Shaman) registerWindwallTotemSpell() {
}
func (shaman *Shaman) newWindwallTotemSpellConfig(rank int) core.SpellConfig {
- has6PEarthfuryResolve := shaman.HasSetBonus(ItemSetEarthfuryResolve, 6)
-
spellId := WindwallTotemSpellId[rank]
manaCost := WindwallTotemManaCost[rank]
level := WindwallTotemLevel[rank]
duration := time.Second * 120
- windwallTotemAura := core.ImprovedWindwallTotemAura(&shaman.Unit)
-
spell := shaman.newTotemSpellConfig(manaCost, spellId)
spell.RequiredLevel = level
spell.Rank = rank
@@ -147,9 +143,6 @@ func (shaman *Shaman) newWindwallTotemSpellConfig(rank int) core.SpellConfig {
// We don't have a separation of Melee vs Ranged bonus physical damage at the moment
// but it's probably fine because bosses generally don't have ranged physical attacks.
// core.WindwallTotemAura(&shaman.Unit, shaman.Level, shaman.Talents.GuardianTotems).Activate(sim)
- if has6PEarthfuryResolve {
- windwallTotemAura.Activate(sim)
- }
}
return spell
}
diff --git a/sim/shaman/earth_totems.go b/sim/shaman/earth_totems.go
index 9ddd1be9a2..e2e4ae7802 100644
--- a/sim/shaman/earth_totems.go
+++ b/sim/shaman/earth_totems.go
@@ -83,8 +83,6 @@ func (shaman *Shaman) registerStoneskinTotemSpell() {
}
func (shaman *Shaman) newStoneskinTotemSpellConfig(rank int) core.SpellConfig {
- has6PEarthfuryResolve := shaman.HasSetBonus(ItemSetEarthfuryResolve, 6)
-
spellId := StoneskinTotemSpellId[rank]
manaCost := StoneskinTotemManaCost[rank]
level := StoneskinTotemLevel[rank]
@@ -99,9 +97,6 @@ func (shaman *Shaman) newStoneskinTotemSpellConfig(rank int) core.SpellConfig {
shaman.ActiveTotems[EarthTotem] = spell
core.StoneskinTotemAura(&shaman.Unit, shaman.Talents.GuardianTotems).Activate(sim)
- if has6PEarthfuryResolve {
- core.ImprovedStoneskinTotemAura(&shaman.Unit).Activate(sim)
- }
}
return spell
}
diff --git a/sim/shaman/item_sets_pve.go b/sim/shaman/item_sets_pve.go
index ec45fc2e57..12ba0e3fd1 100644
--- a/sim/shaman/item_sets_pve.go
+++ b/sim/shaman/item_sets_pve.go
@@ -1,11 +1,9 @@
package shaman
import (
- "slices"
"time"
"github.com/wowsims/sod/sim/core"
- "github.com/wowsims/sod/sim/core/proto"
"github.com/wowsims/sod/sim/core/stats"
)
@@ -113,698 +111,3 @@ var ItemSetEmeraldLadenChain = core.NewItemSet(core.ItemSet{
},
},
})
-
-///////////////////////////////////////////////////////////////////////////
-// SoD Phase 4 Item Sets
-///////////////////////////////////////////////////////////////////////////
-
-var ItemSetTheFiveThunders = core.NewItemSet(core.ItemSet{
- Name: "The Five Thunders",
- Bonuses: map[int32]core.ApplyEffect{
- // +40 Attack Power, up to 23 increased damage from spells, and up to 44 increased healing from spells.
- 2: func(agent core.Agent) {
- c := agent.GetCharacter()
- c.AddStats(stats.Stats{
- stats.AttackPower: 40,
- stats.RangedAttackPower: 40,
- stats.SpellDamage: 23,
- stats.HealingPower: 44,
- })
- },
- // 6% chance on mainhand autoattack and 4% chance on spellcast to increase your damage and healing done by magical spells and effects by up to 95 for 10 sec.
- 4: func(agent core.Agent) {
- c := agent.GetCharacter()
-
- procAura := c.NewTemporaryStatsAura("The Furious Storm", core.ActionID{SpellID: 27775}, stats.Stats{stats.SpellPower: 95}, time.Second*10)
- handler := func(sim *core.Simulation, spell *core.Spell, _ *core.SpellResult) {
- procAura.Activate(sim)
- }
-
- core.MakeProcTriggerAura(&c.Unit, core.ProcTrigger{
- Name: "Item - The Furious Storm Proc (MH Auto)",
- Callback: core.CallbackOnSpellHitDealt,
- Outcome: core.OutcomeLanded,
- ProcMask: core.ProcMaskMeleeMHAuto,
- ProcChance: 0.06,
- Handler: handler,
- })
- core.MakeProcTriggerAura(&c.Unit, core.ProcTrigger{
- Name: "Item - The Furious Storm Proc (Spell Cast)",
- Callback: core.CallbackOnCastComplete,
- ProcMask: core.ProcMaskSpellDamage | core.ProcMaskSpellHealing,
- ProcChance: 0.04,
- Handler: handler,
- })
- },
- // +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 ItemSetEarthfuryEruption = core.NewItemSet(core.ItemSet{
- Name: "Earthfury Eruption",
- Bonuses: map[int32]core.ApplyEffect{
- // The radius of your totems that affect friendly targets is increased to 40 yd.
- 2: func(agent core.Agent) {
- // Nothing to do
- },
- // Your Lightning Bolt critical strikes have a 35% chance to reset the cooldown on Lava Burst and Chain Lightning and make the next Lava Burst, Chain Heal, or Chain Lightning within 10 sec instant.
- 4: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- shaman.GetOrRegisterAura(core.Aura{
- Label: "S03 - Item - T1 - Shaman - Elemental 4P Bonus",
- 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.SpellCode == SpellCode_ShamanLightningBolt && spell.ProcMask.Matches(core.ProcMaskSpellDamage) && result.DidCrit() && sim.Proc(.35, "Power Surge") {
- shaman.PowerSurgeDamageAura.Activate(sim)
- }
- },
- })
- },
- // Lava Burst now also refreshes the duration of Flame Shock on your target back to 12 sec.
- 6: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- core.MakePermanent(shaman.GetOrRegisterAura(core.Aura{
- Label: "S03 - Item - T1 - Shaman - Elemental 6P Bonus",
- OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) {
- if spell.SpellCode == SpellCode_ShamanLavaBurst {
- for _, spell := range shaman.FlameShock {
- if spell == nil {
- continue
- }
-
- if dot := spell.Dot(shaman.CurrentTarget); dot.IsActive() {
- dot.NumberOfTicks = dot.OriginalNumberOfTicks
- dot.Rollover(sim)
- }
- }
- }
- },
- }))
- },
- },
-})
-
-var ItemSetEarthfuryRelief = core.NewItemSet(core.ItemSet{
- Name: "Earthfury Relief",
- Bonuses: map[int32]core.ApplyEffect{
- // The radius of your totems that affect friendly targets is increased to 40 yd.
- 2: func(agent core.Agent) {
- // Nothing to do
- },
- // After casting your Healing Wave, Lesser Healing Wave, or Riptide spell, gives you a 25% chance to gain Mana equal to 35% of the base cost of the spell.
- 4: func(agent core.Agent) {
- // Not implementing for now
- },
- // Your Healing Wave will now jump to additional nearby targets. Each jump reduces the effectiveness of the heal by 80%, and the spell will jump to up to 2 additional targets.
- 6: func(agent core.Agent) {
- // Not implementing for now
- },
- },
-})
-
-var ItemSetEarthfuryImpact = core.NewItemSet(core.ItemSet{
- Name: "Earthfury Impact",
- Bonuses: map[int32]core.ApplyEffect{
- // The radius of your totems that affect friendly targets is increased to 40 yd.
- 2: func(agent core.Agent) {
- // Nothing to do
- },
- // Increases your critical strike chance with spells and attacks by 2%.
- 4: func(agent core.Agent) {
- c := agent.GetCharacter()
- c.AddStats(stats.Stats{
- stats.MeleeCrit: 2 * core.CritRatingPerCritChance,
- stats.SpellCrit: 2 * core.CritRatingPerCritChance,
- })
- },
- // Your Flurry talent grants an additional 10% increase to your attack speed.
- 6: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - T1 - Shaman - Enhancement 6P Bonus",
- OnInit: func(aura *core.Aura, sim *core.Simulation) {
- shaman.bonusFlurrySpeed += .10
- },
- })
- },
- },
-})
-
-var ItemSetEarthfuryResolve = core.NewItemSet(core.ItemSet{
- Name: "Earthfury Resolve",
- Bonuses: map[int32]core.ApplyEffect{
- // Increases your attack speed by 30% for your next 3 swings after you parry, dodge, or block.
- 2: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
-
- flurryAura := shaman.makeFlurryAura(5)
- // The consumption trigger may not exist if the Shaman doesn't talent into Flurry
- shaman.makeFlurryConsumptionTrigger(flurryAura)
-
- core.MakePermanent(shaman.GetOrRegisterAura(core.Aura{
- Label: "S03 - Item - T1 - Shaman - Tank 2P Bonus",
- OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
- if result.DidParry() || result.DidDodge() || result.DidBlock() {
- flurryAura.Activate(sim)
- flurryAura.SetStacks(sim, 3)
- }
- },
- }))
- },
- // Your parries and dodges also activate your Shield Mastery rune ability.
- 4: func(agent core.Agent) {
- // Implemented in runes.go
- },
- // Your Stoneskin Totem also reduces Physical damage taken by 5% and your Windwall Totem also reduces Magical damage taken by 5%.
- 6: func(agent core.Agent) {
- // Debuffs implemented in core/buffs.go, activated with a raid buff setting or in earth_totems.go and air_totems.go
- },
- },
-})
-
-///////////////////////////////////////////////////////////////////////////
-// SoD Phase 5 Item Sets
-///////////////////////////////////////////////////////////////////////////
-
-var ItemSetEruptionOfTheTenStorms = core.NewItemSet(core.ItemSet{
- Name: "Eruption of the Ten Storms",
- Bonuses: map[int32]core.ApplyEffect{
- // Your spell critical strikes now have a 100% chance trigger your Elemental Focus talent.
- 2: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- if !shaman.Talents.ElementalFocus {
- return
- }
-
- core.MakePermanent(shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - T2 - Shaman - Elemental 2P Bonus",
- OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
- if shaman.isShamanDamagingSpell(spell) && result.DidCrit() {
- shaman.ClearcastingAura.Activate(sim)
- shaman.ClearcastingAura.SetStacks(sim, shaman.ClearcastingAura.MaxStacks)
- }
- },
- }))
- },
- // Loyal Beta from your Spirit of the Alpha ability now also increases Fire, Frost, and Nature damage by 5%.
- 4: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- if !shaman.HasRune(proto.ShamanRune_RuneFeetSpiritOfTheAlpha) {
- return
- }
-
- core.MakePermanent(shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - T2 - Shaman - Elemental 4P Bonus",
- OnInit: func(aura *core.Aura, sim *core.Simulation) {
- oldOnGain := shaman.LoyalBetaAura.OnGain
- shaman.LoyalBetaAura.OnGain = func(aura *core.Aura, sim *core.Simulation) {
- oldOnGain(aura, sim)
- shaman.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexFrost] *= 1.05
- shaman.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexFire] *= 1.05
- shaman.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexNature] *= 1.05
- }
- },
- }))
- },
- // While Clearcasting is active, you deal 15% more non-Physical damage.
- 6: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- if !shaman.Talents.ElementalFocus {
- return
- }
-
- core.MakePermanent(shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - T2 - Shaman - Elemental 6P Bonus",
- OnInit: func(aura *core.Aura, sim *core.Simulation) {
- oldOnGain := shaman.ClearcastingAura.OnGain
- shaman.ClearcastingAura.OnGain = func(aura *core.Aura, sim *core.Simulation) {
- oldOnGain(aura, sim)
- shaman.PseudoStats.SchoolDamageDealtMultiplier.MultiplyMagicSchools(1.15)
- }
-
- oldOnExpire := shaman.ClearcastingAura.OnExpire
- shaman.ClearcastingAura.OnExpire = func(aura *core.Aura, sim *core.Simulation) {
- oldOnExpire(aura, sim)
- shaman.PseudoStats.SchoolDamageDealtMultiplier.MultiplyMagicSchools(1 / 1.15)
- }
- },
- }))
- },
- },
-})
-
-var ItemSetResolveOfTheTenStorms = core.NewItemSet(core.ItemSet{
- Name: "Resolve of the Ten Storms",
- Bonuses: map[int32]core.ApplyEffect{
- // Your Flame Shock also grants 30% increased chance to Block for 5 sec or until you Block an attack.
- 2: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
-
- shieldBlockAura := shaman.RegisterAura(core.Aura{
- ActionID: core.ActionID{SpellID: 467891},
- Label: "Shield Block",
- Duration: time.Second * 5,
- OnGain: func(aura *core.Aura, sim *core.Simulation) {
- shaman.AddStatDynamic(sim, stats.Block, 30*core.BlockRatingPerBlockChance)
- },
- OnExpire: func(aura *core.Aura, sim *core.Simulation) {
- shaman.AddStatDynamic(sim, stats.Block, -30*core.BlockRatingPerBlockChance)
- },
- OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, _ *core.Spell, result *core.SpellResult) {
- if result.DidBlock() {
- aura.Deactivate(sim)
- }
- },
- })
-
- core.MakePermanent(shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - T2 - Shaman - Tank 2P Bonus",
- OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
- if spell.SpellCode == SpellCode_ShamanFlameShock {
- shieldBlockAura.Activate(sim)
- }
- },
- }))
- },
- // Each time you Block, your Block amount is increased by 10% of your Spell Damage for 6 sec, stacking up to 3 times.
- 4: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
-
- statDeps := []*stats.StatDependency{
- nil,
- shaman.NewDynamicMultiplyStat(stats.BlockValue, 1.10),
- shaman.NewDynamicMultiplyStat(stats.BlockValue, 1.20),
- shaman.NewDynamicMultiplyStat(stats.BlockValue, 1.30),
- }
-
- // Couldn't find a separate spell for this
- blockAura := shaman.RegisterAura(core.Aura{
- ActionID: core.ActionID{SpellID: 467909},
- Label: "S03 - Item - T2 - Shaman - Tank 4P Bonus",
- Duration: time.Second * 6,
- MaxStacks: 3,
- OnStacksChange: func(aura *core.Aura, sim *core.Simulation, oldStacks, newStacks int32) {
- if oldStacks != 0 {
- shaman.DisableDynamicStatDep(sim, statDeps[oldStacks])
- }
- if newStacks != 0 {
- shaman.EnableDynamicStatDep(sim, statDeps[newStacks])
- }
- },
- })
-
- core.MakePermanent(shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - T2 - Shaman - Tank 4P Bonus Trigger",
- OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
- if result.DidBlock() {
- blockAura.Activate(sim)
- blockAura.AddStack(sim)
- }
- },
- }))
- },
- // Each time you Block an attack, you have a 50% chance to trigger your Maelstrom Weapon rune.
- 6: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- if !shaman.HasRune(proto.ShamanRune_RuneWaistMaelstromWeapon) {
- return
- }
-
- core.MakePermanent(shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - T2 - Shaman - Tank 6P Bonus",
- OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
- if result.DidBlock() && sim.Proc(0.50, "T2 6P Proc Maelstrom Weapon") {
- shaman.MaelstromWeaponAura.Activate(sim)
- shaman.MaelstromWeaponAura.AddStack(sim)
- }
- },
- }))
- },
- },
-})
-
-var ItemSetImpactOfTheTenStorms = core.NewItemSet(core.ItemSet{
- Name: "Impact of the Ten Storms",
- Bonuses: map[int32]core.ApplyEffect{
- // Your chance to trigger Static Shock is increased by 12% (6% while dual-wielding)
- 2: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- if !shaman.HasRune(proto.ShamanRune_RuneBracersStaticShock) {
- return
- }
-
- core.MakePermanent(shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - T2 - Shaman - Enhancement 2P Bonus",
- OnInit: func(aura *core.Aura, sim *core.Simulation) {
- shaman.staticSHocksProcChance += 0.06
- },
- }))
- },
- // Main-hand Stormstrike now deals 50% more damage.
- 4: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- if !shaman.Talents.Stormstrike {
- return
- }
-
- shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - T2 - Shaman - Enhancement 4P Bonus",
- OnInit: func(aura *core.Aura, sim *core.Simulation) {
- shaman.StormstrikeMH.DamageMultiplier += 0.50
- },
- })
- },
- // Your Lightning Shield now gains a charge each time you hit a target with Lightning Bolt or Chain Lightning, up to a maximum of 9 charges.
- // In addition, your Lightning Shield can now deal critical damage.
- // Note: Only works with Static Shock
- 6: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- if !shaman.HasRune(proto.ShamanRune_RuneBracersStaticShock) {
- return
- }
-
- affectedSpellCodes := []int32{SpellCode_ShamanLightningBolt, SpellCode_ShamanChainLightning}
- core.MakePermanent(shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - T2 - Shaman - Enhancement 6P Bonus",
- OnInit: func(t26pAura *core.Aura, sim *core.Simulation) {
- for _, aura := range shaman.LightningShieldAuras {
- if aura == nil {
- continue
- }
-
- oldOnGain := aura.OnGain
- aura.OnGain = func(aura *core.Aura, sim *core.Simulation) {
- oldOnGain(aura, sim)
- t26pAura.Activate(sim)
- }
-
- oldOnExpire := aura.OnExpire
- aura.OnExpire = func(aura *core.Aura, sim *core.Simulation) {
- oldOnExpire(aura, sim)
- t26pAura.Deactivate(sim)
- }
- }
-
- shaman.lightningShieldCanCrit = true
- },
- OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
- // Tested and it doesn't proc from overloads
- if slices.Contains(affectedSpellCodes, spell.SpellCode) && spell.ActionID.Tag != CastTagOverload && result.Landed() {
- shaman.ActiveShieldAura.AddStack(sim)
- }
- },
- }))
- },
- },
-})
-
-var ItemSetReliefOfTheTenStorms = core.NewItemSet(core.ItemSet{
- Name: "Relief of the Ten Storms",
- Bonuses: map[int32]core.ApplyEffect{
- // Your damaging and healing critical strikes now have a 100% chance to trigger your Water Shield, but do not consume a charge or trigger its cooldown.
- 2: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- if !shaman.HasRune(proto.ShamanRune_RuneHandsWaterShield) {
- return
- }
-
- core.MakePermanent(shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - T2 - Shaman - Restoration 2P Bonus",
- OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
- if spell.ProcMask.Matches(core.ProcMaskSpellDamage) && result.DidCrit() {
- shaman.WaterShieldRestore.Cast(sim, aura.Unit)
- }
- },
- }))
- },
- // Your Chain Lightning now also heals the target of your Earth Shield for 100% of the damage done.
- 4: func(agent core.Agent) {
- // TODO: Implement Earth Shield
- shaman := agent.(ShamanAgent).GetShaman()
- if !shaman.HasRune(proto.ShamanRune_RuneLegsEarthShield) {
- return
- }
-
- // core.MakePermanent(shaman.RegisterAura())
- },
- // Increases the healing of Chain Heal and the damage of Chain Lightning by 20%.
- 6: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
-
- shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - T2 - Shaman - Restoration 6P Bonus",
- OnInit: func(aura *core.Aura, sim *core.Simulation) {
- spells := core.FilterSlice(
- core.Flatten([][]*core.Spell{
- shaman.ChainHeal,
- shaman.ChainHealOverload,
- shaman.ChainLightning,
- shaman.ChainLightningOverload,
- }), func(spell *core.Spell) bool { return spell != nil },
- )
-
- for _, spell := range spells {
- spell.DamageMultiplierAdditive += 0.20
- }
- },
- })
- },
- },
-})
-
-var ItemSetAugursRegalia = core.NewItemSet(core.ItemSet{
- Name: "Augur's Regalia",
- Bonuses: map[int32]core.ApplyEffect{
- // Increased Defense +7.
- 2: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- shaman.AddStat(stats.Defense, 7)
- },
- // Increases your chance to block attacks with a shield by 10%.
- 3: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- shaman.AddStat(stats.Block, 10*core.BlockRatingPerBlockChance)
- },
- // Increases the chance to trigger your Power Surge rune by an additional 5%.
- 5: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- if !shaman.HasRune(proto.ShamanRune_RuneWaistPowerSurge) {
- return
- }
-
- shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - ZG - Shaman - Tank 5P Bonus",
- OnInit: func(aura *core.Aura, sim *core.Simulation) {
- shaman.powerSurgeProcChance += .05
- },
- })
- },
- },
-})
-
-///////////////////////////////////////////////////////////////////////////
-// SoD Phase 6 Item Sets
-///////////////////////////////////////////////////////////////////////////
-
-var ItemSetStormcallersEruption = core.NewItemSet(core.ItemSet{
- Name: "Stormcaller's Eruption",
- Bonuses: map[int32]core.ApplyEffect{
- // You have a 70% chance to avoid interruption caused by damage while casting Lightning Bolt, Chain Lightning, or Lava Burst, and a 10% increased chance to trigger your Elemental Focus talent.
- 2: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
-
- shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - TAQ - Shaman - Elemental 2P Bonus",
- OnInit: func(aura *core.Aura, sim *core.Simulation) {
- affectedPushbackSpells := core.FilterSlice(
- core.Flatten(
- [][]*core.Spell{
- shaman.LightningBolt,
- shaman.ChainLightning,
- {shaman.LavaBurst},
- },
- ),
- func(spell *core.Spell) bool { return spell != nil },
- )
-
- for _, spell := range affectedPushbackSpells {
- spell.PushbackReduction += .70
- }
-
- shaman.elementalFocusProcChance += .10
- },
- })
- },
- // Increases the critical strike damage bonus of your Fire, Frost, and Nature spells by 60%.
- 4: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- shaman.OnSpellRegistered(func(spell *core.Spell) {
- if (spell.Flags.Matches(SpellFlagShaman) || spell.Flags.Matches(SpellFlagTotem)) && spell.DefenseType == core.DefenseTypeMagic {
- spell.CritDamageBonus += 0.60
- }
- })
- },
- },
-})
-
-var ItemSetStormcallersResolve = core.NewItemSet(core.ItemSet{
- Name: "Stormcaller's Resolve",
- Bonuses: map[int32]core.ApplyEffect{
- // Damaging a target with Stormstrike, Lava Burst, or Molten Blast also reduces all damage you take by 10% for 10 sec.
- 2: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
-
- affectedSpellCodess := []int32{SpellCode_ShamanStormstrike, SpellCode_ShamanLavaBurst, SpellCode_ShamanMoltenBlast}
-
- buffAura := shaman.RegisterAura(core.Aura{
- ActionID: core.ActionID{SpellID: 1213934},
- Label: "Stormbraced",
- Duration: time.Second * 10,
- OnGain: func(aura *core.Aura, sim *core.Simulation) {
- aura.Unit.PseudoStats.DamageTakenMultiplier *= 0.90
- },
- OnExpire: func(aura *core.Aura, sim *core.Simulation) {
- aura.Unit.PseudoStats.DamageTakenMultiplier /= 0.90
- },
- })
-
- core.MakePermanent(shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - TAQ - Shaman - Elemental 2P Bonus",
- OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
- if result.Landed() && slices.Contains(affectedSpellCodess, spell.SpellCode) {
- buffAura.Activate(sim)
- }
- },
- }))
- },
- // Your Spirit of the Alpha also increases your health by 10%, threat by 20%, and damage by 10% when cast on self.
- 4: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- // Do a simple multiply stat because we assume that a tank shaman is using Alpha on theirself
- if shaman.HasRune(proto.ShamanRune_RuneFeetSpiritOfTheAlpha) && shaman.IsTanking() {
- shaman.PseudoStats.DamageDealtMultiplier *= 1.10
- shaman.PseudoStats.ThreatMultiplier *= 1.20
- shaman.MultiplyStat(stats.Health, 1.10)
- }
- },
- },
-})
-
-var ItemSetStormcallersRelief = core.NewItemSet(core.ItemSet{
- Name: "Stormcaller's Relief",
- Bonuses: map[int32]core.ApplyEffect{
- // Your Riptide increases the amount healed by Chain Heal by an additional 25%.
- 2: func(agent core.Agent) {
- },
- // Reduces the cast time of Chain Heal by 0.5 sec.
- 4: func(agent core.Agent) {
- },
- },
-})
-
-var ItemSetStormcallersImpact = core.NewItemSet(core.ItemSet{
- Name: "Stormcaller's Impact",
- Bonuses: map[int32]core.ApplyEffect{
- // Increases Stormstrike and Lava Lash damage by 50%. Stormstrike's damage is increased by an additional 50% when using a Two-handed weapon.
- 2: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - TAQ - Shaman - Enhancement 2P Bonus",
- OnInit: func(aura *core.Aura, sim *core.Simulation) {
- if shaman.StormstrikeMH != nil {
- shaman.StormstrikeMH.DamageMultiplierAdditive += core.TernaryFloat64(shaman.HasRune(proto.ShamanRune_RuneChestTwoHandedMastery), 1.00, 0.50)
- }
-
- if shaman.StormstrikeOH != nil {
- shaman.StormstrikeOH.DamageMultiplierAdditive += 0.50
- }
-
- if shaman.LavaLash != nil {
- shaman.LavaLash.DamageMultiplierAdditive += 0.50
- }
- },
- })
- },
- // Your Stormstrike, Lava Lash, and Lava Burst critical strikes cause your target to burn for 30% of the damage done over 4 sec.
- 4: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
-
- // This is the spell used for the burn proc.
- // https://www.wowhead.com/classic/spell=1213915/burning
- burnSpell := shaman.RegisterSpell(core.SpellConfig{
- ActionID: core.ActionID{SpellID: 1213915},
- SpellSchool: core.SpellSchoolFire,
- DefenseType: core.DefenseTypeMagic,
- ProcMask: core.ProcMaskEmpty,
- Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell,
-
- DamageMultiplier: 1,
- ThreatMultiplier: 1,
- BonusCoefficient: 1,
-
- Dot: core.DotConfig{
- Aura: core.Aura{
- Label: "Burning",
- },
- NumberOfTicks: 2,
- TickLength: time.Second * 2,
- OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) {
- dot.CalcAndDealPeriodicSnapshotDamage(sim, target, dot.OutcomeTick)
- },
- },
-
- ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
- spell.Dot(target).ApplyOrRefresh(sim)
- spell.CalcAndDealOutcome(sim, target, spell.OutcomeAlwaysHitNoHitCounter)
- },
- })
-
- core.MakePermanent(shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - TAQ - Shaman - Enhancement 4P Bonus",
- OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
- if !result.Outcome.Matches(core.OutcomeCrit) || !(spell == shaman.StormstrikeMH || spell == shaman.StormstrikeOH || spell == shaman.LavaLash || spell == shaman.LavaBurst) {
- return
- }
-
- dot := burnSpell.Dot(result.Target)
- dotDamage := result.Damage * 0.3
- if dot.IsActive() {
- dotDamage += dot.SnapshotBaseDamage * float64(dot.MaxTicksRemaining())
- }
- dot.SnapshotBaseDamage = dotDamage / float64(dot.NumberOfTicks)
- dot.SnapshotAttackerMultiplier = 1
-
- burnSpell.Cast(sim, result.Target)
- },
- }))
- },
- },
-})
-
-var ItemSetGiftOfTheGatheringStorm = core.NewItemSet(core.ItemSet{
- Name: "Gift of the Gathering Storm",
- Bonuses: map[int32]core.ApplyEffect{
- // Your Lava Burst deals increased damage equal to its critical strike chance.
- 3: func(agent core.Agent) {
- shaman := agent.(ShamanAgent).GetShaman()
- shaman.RegisterAura(core.Aura{
- Label: "S03 - Item - RAQ - Shaman - Elemental 3P Bonus",
- OnInit: func(aura *core.Aura, sim *core.Simulation) {
- shaman.useLavaBurstCritScaling = true
- },
- })
- },
- },
-})
diff --git a/sim/shaman/item_sets_pve_phase_4.go b/sim/shaman/item_sets_pve_phase_4.go
new file mode 100644
index 0000000000..dfec53ae82
--- /dev/null
+++ b/sim/shaman/item_sets_pve_phase_4.go
@@ -0,0 +1,307 @@
+package shaman
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/wowsims/sod/sim/core"
+ "github.com/wowsims/sod/sim/core/stats"
+)
+
+var ItemSetTheFiveThunders = core.NewItemSet(core.ItemSet{
+ Name: "The Five Thunders",
+ Bonuses: map[int32]core.ApplyEffect{
+ // +40 Attack Power, up to 23 increased damage from spells, and up to 44 increased healing from spells.
+ 2: func(agent core.Agent) {
+ c := agent.GetCharacter()
+ c.AddStats(stats.Stats{
+ stats.AttackPower: 40,
+ stats.RangedAttackPower: 40,
+ stats.SpellDamage: 23,
+ stats.HealingPower: 44,
+ })
+ },
+ // 6% chance on mainhand autoattack and 4% chance on spellcast to increase your damage and healing done by magical spells and effects by up to 95 for 10 sec.
+ 4: func(agent core.Agent) {
+ c := agent.GetCharacter()
+
+ procAura := c.NewTemporaryStatsAura("The Furious Storm", core.ActionID{SpellID: 27775}, stats.Stats{stats.SpellPower: 95}, time.Second*10)
+ handler := func(sim *core.Simulation, spell *core.Spell, _ *core.SpellResult) {
+ procAura.Activate(sim)
+ }
+
+ core.MakeProcTriggerAura(&c.Unit, core.ProcTrigger{
+ Name: "Item - The Furious Storm Proc (MH Auto)",
+ Callback: core.CallbackOnSpellHitDealt,
+ Outcome: core.OutcomeLanded,
+ ProcMask: core.ProcMaskMeleeMHAuto,
+ ProcChance: 0.06,
+ Handler: handler,
+ })
+ core.MakeProcTriggerAura(&c.Unit, core.ProcTrigger{
+ Name: "Item - The Furious Storm Proc (Spell Cast)",
+ Callback: core.CallbackOnCastComplete,
+ ProcMask: core.ProcMaskSpellDamage | core.ProcMaskSpellHealing,
+ ProcChance: 0.04,
+ Handler: handler,
+ })
+ },
+ // +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 ItemSetEarthfuryEruption = core.NewItemSet(core.ItemSet{
+ Name: "Earthfury Eruption",
+ Bonuses: map[int32]core.ApplyEffect{
+ // The radius of your totems that affect friendly targets is increased to 40 yd.
+ 2: func(agent core.Agent) {
+ // Nothing to do
+ },
+ 4: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT1Elemental4PBonus()
+ },
+ 6: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT1Elemental6PBonus()
+ },
+ },
+})
+
+// Your Lightning Bolt critical strikes have a 35% chance to reset the cooldown on Lava Burst and Chain Lightning and make the next Lava Burst, Chain Heal, or Chain Lightning within 10 sec instant.
+func (shaman *Shaman) applyT1Elemental4PBonus() {
+ label := "S03 - Item - T1 - Shaman - Elemental 4P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ if spell.SpellCode == SpellCode_ShamanLightningBolt && spell.ProcMask.Matches(core.ProcMaskSpellDamage) && result.DidCrit() && sim.Proc(.35, "Power Surge") {
+ shaman.PowerSurgeDamageAura.Activate(sim)
+ }
+ },
+ }))
+}
+
+// Lava Burst now also refreshes the duration of Flame Shock on your target back to 12 sec.
+func (shaman *Shaman) applyT1Elemental6PBonus() {
+ label := "S03 - Item - T1 - Shaman - Elemental 6P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ var flameShockSpells []*core.Spell
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ flameShockSpells = core.FilterSlice(shaman.FlameShock, func(spell *core.Spell) bool { return spell != nil })
+ },
+ OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) {
+ if spell.SpellCode != SpellCode_ShamanLavaBurst {
+ return
+ }
+
+ for _, spell := range flameShockSpells {
+ if dot := spell.Dot(shaman.CurrentTarget); dot.IsActive() {
+ dot.NumberOfTicks = dot.OriginalNumberOfTicks
+ dot.Rollover(sim)
+ }
+ }
+ },
+ }))
+}
+
+var ItemSetEarthfuryRelief = core.NewItemSet(core.ItemSet{
+ Name: "Earthfury Relief",
+ Bonuses: map[int32]core.ApplyEffect{
+ // The radius of your totems that affect friendly targets is increased to 40 yd.
+ 2: func(agent core.Agent) {
+ // Nothing to do
+ },
+ // After casting your Healing Wave, Lesser Healing Wave, or Riptide spell, gives you a 25% chance to gain Mana equal to 35% of the base cost of the spell.
+ 4: func(agent core.Agent) {
+ // Not implementing for now
+ },
+ // Your Healing Wave will now jump to additional nearby targets. Each jump reduces the effectiveness of the heal by 80%, and the spell will jump to up to 2 additional targets.
+ 6: func(agent core.Agent) {
+ // Not implementing for now
+ },
+ },
+})
+
+var ItemSetEarthfuryImpact = core.NewItemSet(core.ItemSet{
+ Name: "Earthfury Impact",
+ Bonuses: map[int32]core.ApplyEffect{
+ // The radius of your totems that affect friendly targets is increased to 40 yd.
+ 2: func(agent core.Agent) {
+ // Nothing to do
+ },
+ 4: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT1Enhancement4PBonus()
+ },
+ 6: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT1Enhancement6PBonus()
+ },
+ },
+})
+
+// Increases your critical strike chance with spells and attacks by 2%.
+func (shaman *Shaman) applyT1Enhancement4PBonus() {
+ label := "S03 - Item - T1 - Shaman - Enhancement 4P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ bonusStats := stats.Stats{
+ stats.MeleeCrit: 2 * core.CritRatingPerCritChance,
+ stats.SpellCrit: 2 * core.CritRatingPerCritChance,
+ }
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ BuildPhase: core.CharacterBuildPhaseBuffs,
+ OnGain: func(aura *core.Aura, sim *core.Simulation) {
+ if aura.Unit.Env.MeasuringStats && aura.Unit.Env.State != core.Finalized {
+ aura.Unit.AddStats(bonusStats)
+ } else {
+ aura.Unit.AddStatsDynamic(sim, bonusStats)
+ }
+ },
+ OnExpire: func(aura *core.Aura, sim *core.Simulation) {
+ if aura.Unit.Env.MeasuringStats && aura.Unit.Env.State != core.Finalized {
+ aura.Unit.AddStats(bonusStats.Invert())
+ } else {
+ aura.Unit.AddStatsDynamic(sim, bonusStats.Invert())
+ }
+ },
+ }))
+}
+
+// Your Flurry talent grants an additional 10% increase to your attack speed.
+func (shaman *Shaman) applyT1Enhancement6PBonus() {
+ label := "S03 - Item - T1 - Shaman - Enhancement 6P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ shaman.bonusFlurrySpeed += .10
+ },
+ })
+}
+
+var ItemSetEarthfuryResolve = core.NewItemSet(core.ItemSet{
+ Name: "Earthfury Resolve",
+ Bonuses: map[int32]core.ApplyEffect{
+ 2: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT1Tank2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT1Tank4PBonus()
+ },
+
+ 6: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT1Tank6PBonus()
+ },
+ },
+})
+
+// Increases your attack speed by 30% for your next 3 swings after you parry, dodge, or block.
+func (shaman *Shaman) applyT1Tank2PBonus() {
+ label := "S03 - Item - T1 - Shaman - Tank 2P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ flurryAura := shaman.makeFlurryAura(5)
+ // The consumption trigger may not exist if the Shaman doesn't talent into Flurry
+ shaman.makeFlurryConsumptionTrigger(flurryAura)
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ if result.DidParry() || result.DidDodge() || result.DidBlock() {
+ flurryAura.Activate(sim)
+ flurryAura.SetStacks(sim, 3)
+ }
+ },
+ }))
+}
+
+// Your parries and dodges also activate your Shield Mastery rune ability.
+func (shaman *Shaman) applyT1Tank4PBonus() {
+ label := "S03 - Item - T1 - Shaman - Tank 4P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ if result.DidParry() || result.DidDodge() {
+ fmt.Println("good")
+ shaman.ShieldMasteryAura.Activate(sim)
+ shaman.ShieldMasteryAura.AddStack(sim)
+ }
+ },
+ }))
+}
+
+// Your Stoneskin Totem also reduces Physical damage taken by 5% and your Windwall Totem also reduces Magical damage taken by 5%.
+func (shaman *Shaman) applyT1Tank6PBonus() {
+ label := "S03 - Item - T1 - Shaman - Tank 6P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ improvedStoneskinAura := core.ImprovedStoneskinTotemAura(&shaman.Unit)
+ improvedWindwallAura := core.ImprovedWindwallTotemAura(&shaman.Unit)
+
+ shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ for _, spell := range shaman.StoneskinTotem {
+ if spell == nil {
+ continue
+ }
+
+ oldApplyEffects := spell.ApplyEffects
+ spell.ApplyEffects = func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
+ oldApplyEffects(sim, target, spell)
+ improvedStoneskinAura.Activate(sim)
+ }
+ }
+
+ for _, spell := range shaman.WindwallTotem {
+ if spell == nil {
+ continue
+ }
+
+ oldApplyEffects := spell.ApplyEffects
+ spell.ApplyEffects = func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
+ oldApplyEffects(sim, target, spell)
+ improvedWindwallAura.Activate(sim)
+ }
+ }
+ },
+ })
+}
diff --git a/sim/shaman/item_sets_pve_phase_5.go b/sim/shaman/item_sets_pve_phase_5.go
new file mode 100644
index 0000000000..20512204f4
--- /dev/null
+++ b/sim/shaman/item_sets_pve_phase_5.go
@@ -0,0 +1,486 @@
+package shaman
+
+import (
+ "slices"
+ "time"
+
+ "github.com/wowsims/sod/sim/core"
+ "github.com/wowsims/sod/sim/core/proto"
+ "github.com/wowsims/sod/sim/core/stats"
+)
+
+///////////////////////////////////////////////////////////////////////////
+// SoD Phase 5 Item Sets
+///////////////////////////////////////////////////////////////////////////
+
+var ItemSetEruptionOfTheTenStorms = core.NewItemSet(core.ItemSet{
+ Name: "Eruption of the Ten Storms",
+ Bonuses: map[int32]core.ApplyEffect{
+ 2: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT2Elemental2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT2Elemental4PBonus()
+ },
+ 6: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT2Elemental6PBonus()
+ },
+ },
+})
+
+// Your spell critical strikes now have a 100% chance trigger your Elemental Focus talent.
+func (shaman *Shaman) applyT2Elemental2PBonus() {
+ if !shaman.Talents.ElementalFocus {
+ return
+ }
+
+ label := "S03 - Item - T2 - Shaman - Elemental 2P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ if shaman.isShamanDamagingSpell(spell) && result.DidCrit() {
+ shaman.ClearcastingAura.Activate(sim)
+ shaman.ClearcastingAura.SetStacks(sim, shaman.ClearcastingAura.MaxStacks)
+ }
+ },
+ }))
+}
+
+// Loyal Beta from your Spirit of the Alpha ability now also increases Fire, Frost, and Nature damage by 5%.
+func (shaman *Shaman) applyT2Elemental4PBonus() {
+ if !shaman.HasRune(proto.ShamanRune_RuneFeetSpiritOfTheAlpha) || shaman.IsTanking() {
+ return
+ }
+
+ label := "S03 - Item - T2 - Shaman - Elemental 4P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ oldOnGain := shaman.LoyalBetaAura.OnGain
+ shaman.LoyalBetaAura.OnGain = func(aura *core.Aura, sim *core.Simulation) {
+ oldOnGain(aura, sim)
+ shaman.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexFrost] *= 1.05
+ shaman.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexFire] *= 1.05
+ shaman.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexNature] *= 1.05
+ }
+ },
+ }))
+}
+
+// While Clearcasting is active, you deal 15% more non-Physical damage.
+func (shaman *Shaman) applyT2Elemental6PBonus() {
+ if !shaman.Talents.ElementalFocus {
+ return
+ }
+
+ label := "S03 - Item - T2 - Shaman - Elemental 6P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ oldOnGain := shaman.ClearcastingAura.OnGain
+ shaman.ClearcastingAura.OnGain = func(aura *core.Aura, sim *core.Simulation) {
+ oldOnGain(aura, sim)
+ shaman.PseudoStats.SchoolDamageDealtMultiplier.MultiplyMagicSchools(1.15)
+ }
+
+ oldOnExpire := shaman.ClearcastingAura.OnExpire
+ shaman.ClearcastingAura.OnExpire = func(aura *core.Aura, sim *core.Simulation) {
+ oldOnExpire(aura, sim)
+ shaman.PseudoStats.SchoolDamageDealtMultiplier.MultiplyMagicSchools(1 / 1.15)
+ }
+ },
+ }))
+}
+
+var ItemSetResolveOfTheTenStorms = core.NewItemSet(core.ItemSet{
+ Name: "Resolve of the Ten Storms",
+ Bonuses: map[int32]core.ApplyEffect{
+ 2: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT2Tank2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT2Tank4PBonus()
+ },
+ 6: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT2Tank6PBonus()
+ },
+ },
+})
+
+// Your Flame Shock also grants 30% increased chance to Block for 5 sec or until you Block an attack.
+func (shaman *Shaman) applyT2Tank2PBonus() {
+ label := "S03 - Item - T2 - Shaman - Tank 2P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ shieldBlockAura := shaman.RegisterAura(core.Aura{
+ ActionID: core.ActionID{SpellID: 467891},
+ Label: "Shield Block",
+ Duration: time.Second * 5,
+ OnGain: func(aura *core.Aura, sim *core.Simulation) {
+ shaman.AddStatDynamic(sim, stats.Block, 30*core.BlockRatingPerBlockChance)
+ },
+ OnExpire: func(aura *core.Aura, sim *core.Simulation) {
+ shaman.AddStatDynamic(sim, stats.Block, -30*core.BlockRatingPerBlockChance)
+ },
+ OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, _ *core.Spell, result *core.SpellResult) {
+ if result.DidBlock() {
+ aura.Deactivate(sim)
+ }
+ },
+ })
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ if spell.SpellCode == SpellCode_ShamanFlameShock {
+ shieldBlockAura.Activate(sim)
+ }
+ },
+ }))
+}
+
+// Each time you Block, your Block amount is increased by 10% of your Spell Damage for 6 sec, stacking up to 3 times.
+func (shaman *Shaman) applyT2Tank4PBonus() {
+ label := "S03 - Item - T2 - Shaman - Tank 4P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ statDeps := []*stats.StatDependency{
+ nil,
+ shaman.NewDynamicMultiplyStat(stats.BlockValue, 1.10),
+ shaman.NewDynamicMultiplyStat(stats.BlockValue, 1.20),
+ shaman.NewDynamicMultiplyStat(stats.BlockValue, 1.30),
+ }
+
+ // Couldn't find a separate spell for this
+ blockAura := shaman.RegisterAura(core.Aura{
+ ActionID: core.ActionID{SpellID: 467909},
+ Label: "S03 - Item - T2 - Shaman - Tank 4P Bonus Proc",
+ Duration: time.Second * 6,
+ MaxStacks: 3,
+ OnStacksChange: func(aura *core.Aura, sim *core.Simulation, oldStacks, newStacks int32) {
+ if oldStacks != 0 {
+ shaman.DisableDynamicStatDep(sim, statDeps[oldStacks])
+ }
+ if newStacks != 0 {
+ shaman.EnableDynamicStatDep(sim, statDeps[newStacks])
+ }
+ },
+ })
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ if result.DidBlock() {
+ blockAura.Activate(sim)
+ blockAura.AddStack(sim)
+ }
+ },
+ }))
+}
+
+// Each time you Block an attack, you have a 50% chance to trigger your Maelstrom Weapon rune.
+func (shaman *Shaman) applyT2Tank6PBonus() {
+ if !shaman.HasRune(proto.ShamanRune_RuneWaistMaelstromWeapon) {
+ return
+ }
+
+ label := "S03 - Item - T2 - Shaman - Tank 6P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ if result.DidBlock() && sim.Proc(0.50, "T2 6P Proc Maelstrom Weapon") {
+ shaman.MaelstromWeaponAura.Activate(sim)
+ shaman.MaelstromWeaponAura.AddStack(sim)
+ }
+ },
+ }))
+}
+
+var ItemSetImpactOfTheTenStorms = core.NewItemSet(core.ItemSet{
+ Name: "Impact of the Ten Storms",
+ Bonuses: map[int32]core.ApplyEffect{
+ 2: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT2Enhancement2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT2Enhancement4PBonus()
+ },
+ 6: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT2Enhancement6PBonus()
+ },
+ },
+})
+
+// Your chance to trigger Static Shock is increased by 12% (6% while dual-wielding)
+func (shaman *Shaman) applyT2Enhancement2PBonus() {
+ if !shaman.HasRune(proto.ShamanRune_RuneBracersStaticShock) {
+ return
+ }
+
+ label := "S03 - Item - T2 - Shaman - Enhancement 2P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ shaman.staticSHocksProcChance += 0.06
+ },
+ }))
+}
+
+// Main-hand Stormstrike now deals 50% more damage.
+func (shaman *Shaman) applyT2Enhancement4PBonus() {
+ if !shaman.Talents.Stormstrike {
+ return
+ }
+
+ label := "S03 - Item - T2 - Shaman - Enhancement 4P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ shaman.StormstrikeMH.DamageMultiplier += 0.50
+ },
+ })
+}
+
+// While Static Shock is engraved, your Lightning Shield now gains a charge each time you hit a target with Lightning Bolt or Chain Lightning, up to a maximum of 9 charges.
+// In addition, while Static Shock is engraved, your Lightning Shield can now deal critical damage.
+func (shaman *Shaman) applyT2Enhancement6PBonus() {
+ if !shaman.HasRune(proto.ShamanRune_RuneBracersStaticShock) {
+ return
+ }
+
+ label := "S03 - Item - T2 - Shaman - Enhancement 6P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ affectedSpellCodes := []int32{SpellCode_ShamanLightningBolt, SpellCode_ShamanChainLightning}
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(t26pAura *core.Aura, sim *core.Simulation) {
+ for _, aura := range shaman.LightningShieldAuras {
+ if aura == nil {
+ continue
+ }
+
+ oldOnGain := aura.OnGain
+ aura.OnGain = func(aura *core.Aura, sim *core.Simulation) {
+ oldOnGain(aura, sim)
+ t26pAura.Activate(sim)
+ }
+
+ oldOnExpire := aura.OnExpire
+ aura.OnExpire = func(aura *core.Aura, sim *core.Simulation) {
+ oldOnExpire(aura, sim)
+ t26pAura.Deactivate(sim)
+ }
+ }
+
+ shaman.lightningShieldCanCrit = true
+ },
+ OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ // Tested and it doesn't proc from overloads
+ if slices.Contains(affectedSpellCodes, spell.SpellCode) && spell.ActionID.Tag != CastTagOverload && result.Landed() {
+ shaman.ActiveShieldAura.AddStack(sim)
+ }
+ },
+ }))
+}
+
+var ItemSetReliefOfTheTenStorms = core.NewItemSet(core.ItemSet{
+ Name: "Relief of the Ten Storms",
+ Bonuses: map[int32]core.ApplyEffect{
+ 2: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT2Restoration2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT2Restoration4PBonus()
+ },
+ 6: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyT2Restoration6PBonus()
+ },
+ },
+})
+
+// Your damaging and healing critical strikes now have a 100% chance to trigger your Water Shield, but do not consume a charge or trigger its cooldown.
+func (shaman *Shaman) applyT2Restoration2PBonus() {
+ if !shaman.HasRune(proto.ShamanRune_RuneHandsWaterShield) {
+ return
+ }
+
+ label := "S03 - Item - T2 - Shaman - Restoration 2P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ if spell.ProcMask.Matches(core.ProcMaskSpellDamage) && result.DidCrit() {
+ shaman.WaterShieldRestore.Cast(sim, aura.Unit)
+ }
+ },
+ }))
+}
+
+// Your Chain Lightning now also heals the target of your Earth Shield for 100% of the damage done.
+func (shaman *Shaman) applyT2Restoration4PBonus() {
+ if !shaman.HasRune(proto.ShamanRune_RuneHandsWaterShield) {
+ return
+ }
+
+ label := "S03 - Item - T2 - Shaman - Restoration 4P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ healthMetrics := shaman.NewHealthMetrics(core.ActionID{SpellID: 467809})
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ if spell.SpellCode == SpellCode_ShamanChainLightning {
+ shaman.GainHealth(sim, result.Damage, healthMetrics)
+ }
+ },
+ }))
+}
+
+// Increases the healing of Chain Heal and the damage of Chain Lightning by 20%.
+func (shaman *Shaman) applyT2Restoration6PBonus() {
+ if !shaman.HasRune(proto.ShamanRune_RuneHandsWaterShield) {
+ return
+ }
+
+ label := "S03 - Item - T2 - Shaman - Restoration 6P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ spells := core.FilterSlice(
+ core.Flatten([][]*core.Spell{
+ shaman.ChainHeal,
+ shaman.ChainHealOverload,
+ shaman.ChainLightning,
+ shaman.ChainLightningOverload,
+ }), func(spell *core.Spell) bool { return spell != nil },
+ )
+
+ for _, spell := range spells {
+ spell.DamageMultiplierAdditive += 0.20
+ }
+ },
+ })
+}
+
+var ItemSetAugursRegalia = core.NewItemSet(core.ItemSet{
+ Name: "Augur's Regalia",
+ Bonuses: map[int32]core.ApplyEffect{
+ // Increased Defense +7.
+ 2: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ // No corresponding Soul found so leaving this simple
+ shaman.AddStat(stats.Defense, 7)
+ },
+
+ 3: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyZGTank3PBonus()
+ },
+
+ 5: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyZGTank5PBonus()
+ },
+ },
+})
+
+// Increases your chance to block attacks with a shield by 10%.
+func (shaman *Shaman) applyZGTank3PBonus() {
+ label := "S03 - Item - ZG - Shaman - Tank 3P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ bonusStats := stats.Stats{stats.Block: 10 * core.BlockRatingPerBlockChance}
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ BuildPhase: core.CharacterBuildPhaseBuffs,
+ OnGain: func(aura *core.Aura, sim *core.Simulation) {
+ if aura.Unit.Env.MeasuringStats && aura.Unit.Env.State != core.Finalized {
+ aura.Unit.AddStats(bonusStats)
+ } else {
+ aura.Unit.AddStatsDynamic(sim, bonusStats)
+ }
+ },
+ OnExpire: func(aura *core.Aura, sim *core.Simulation) {
+ if aura.Unit.Env.MeasuringStats && aura.Unit.Env.State != core.Finalized {
+ aura.Unit.AddStats(bonusStats.Invert())
+ } else {
+ aura.Unit.AddStatsDynamic(sim, bonusStats.Invert())
+ }
+ },
+ }))
+}
+
+// Increases the chance to trigger your Power Surge rune by an additional 5%.
+func (shaman *Shaman) applyZGTank5PBonus() {
+ if !shaman.HasRune(proto.ShamanRune_RuneWaistPowerSurge) {
+ return
+ }
+
+ label := "S03 - Item - ZG - Shaman - Tank 5P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ shaman.powerSurgeProcChance += .05
+ },
+ })
+}
diff --git a/sim/shaman/item_sets_pve_phase_6.go b/sim/shaman/item_sets_pve_phase_6.go
new file mode 100644
index 0000000000..2cd7a983a9
--- /dev/null
+++ b/sim/shaman/item_sets_pve_phase_6.go
@@ -0,0 +1,311 @@
+package shaman
+
+import (
+ "slices"
+ "time"
+
+ "github.com/wowsims/sod/sim/core"
+ "github.com/wowsims/sod/sim/core/proto"
+ "github.com/wowsims/sod/sim/core/stats"
+)
+
+///////////////////////////////////////////////////////////////////////////
+// SoD Phase 6 Item Sets
+///////////////////////////////////////////////////////////////////////////
+
+var ItemSetStormcallersEruption = core.NewItemSet(core.ItemSet{
+ Name: "Stormcaller's Eruption",
+ Bonuses: map[int32]core.ApplyEffect{
+ 2: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyTAQElemental2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyTAQElemental4PBonus()
+ },
+ },
+})
+
+// You have a 70% chance to avoid interruption caused by damage while casting Lightning Bolt, Chain Lightning, or Lava Burst, and a 10% increased chance to trigger your Elemental Focus talent.
+func (shaman *Shaman) applyTAQElemental2PBonus() {
+ label := "S03 - Item - TAQ - Shaman - Elemental 2P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ affectedPushbackSpells := core.FilterSlice(
+ core.Flatten(
+ [][]*core.Spell{
+ shaman.LightningBolt,
+ shaman.ChainLightning,
+ {shaman.LavaBurst},
+ },
+ ),
+ func(spell *core.Spell) bool { return spell != nil },
+ )
+
+ for _, spell := range affectedPushbackSpells {
+ spell.PushbackReduction += .70
+ }
+
+ if shaman.Talents.ElementalFocus {
+ shaman.elementalFocusProcChance += .10
+ }
+ },
+ })
+}
+
+// Increases the critical strike damage bonus of your Fire, Frost, and Nature spells by 60%.
+func (shaman *Shaman) applyTAQElemental4PBonus() {
+ label := "S03 - Item - TAQ - Shaman - Elemental 4P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ for _, spell := range shaman.Spellbook {
+ if spell.Flags.Matches(SpellFlagShaman) && spell.DefenseType == core.DefenseTypeMagic {
+ spell.CritDamageBonus += 0.60
+ }
+ }
+ },
+ })
+}
+
+var ItemSetStormcallersResolve = core.NewItemSet(core.ItemSet{
+ Name: "Stormcaller's Resolve",
+ Bonuses: map[int32]core.ApplyEffect{
+ 2: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyTAQTank2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyTAQTank4PBonus()
+ },
+ },
+})
+
+// Damaging a target with Stormstrike, Lava Burst, or Molten Blast also reduces all damage you take by 10% for 10 sec.
+func (shaman *Shaman) applyTAQTank2PBonus() {
+ if !shaman.Talents.Stormstrike && !shaman.HasRune(proto.ShamanRune_RuneHandsLavaBurst) && !shaman.HasRune(proto.ShamanRune_RuneHandsMoltenBlast) {
+ return
+ }
+
+ label := "S03 - Item - TAQ - Shaman - Tank 2P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ affectedSpellCodess := []int32{SpellCode_ShamanStormstrike, SpellCode_ShamanLavaBurst, SpellCode_ShamanMoltenBlast}
+
+ buffAura := shaman.RegisterAura(core.Aura{
+ ActionID: core.ActionID{SpellID: 1213934},
+ Label: "Stormbraced",
+ Duration: time.Second * 10,
+ OnGain: func(aura *core.Aura, sim *core.Simulation) {
+ aura.Unit.PseudoStats.DamageTakenMultiplier *= 0.90
+ },
+ OnExpire: func(aura *core.Aura, sim *core.Simulation) {
+ aura.Unit.PseudoStats.DamageTakenMultiplier /= 0.90
+ },
+ })
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ if result.Landed() && slices.Contains(affectedSpellCodess, spell.SpellCode) {
+ buffAura.Activate(sim)
+ }
+ },
+ }))
+}
+
+// Your Spirit of the Alpha also increases your health by 10%, threat by 20%, and damage by 10% when cast on self.
+func (shaman *Shaman) applyTAQTank4PBonus() {
+ if !shaman.HasRune(proto.ShamanRune_RuneFeetSpiritOfTheAlpha) || !shaman.IsTanking() {
+ return
+ }
+
+ label := "S03 - Item - TAQ - Shaman - Tank 4P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ damageMultiplier := 1.10
+ threatMultiplier := 1.20
+ healthMultiplier := 1.10
+ statDep := shaman.NewDynamicMultiplyStat(stats.Health, healthMultiplier)
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ BuildPhase: core.CharacterBuildPhaseBuffs,
+ OnGain: func(aura *core.Aura, sim *core.Simulation) {
+ if aura.Unit.Env.MeasuringStats && aura.Unit.Env.State != core.Finalized {
+ aura.Unit.MultiplyStat(stats.Health, healthMultiplier)
+ } else {
+ aura.Unit.EnableDynamicStatDep(sim, statDep)
+ }
+
+ shaman.PseudoStats.DamageDealtMultiplier *= damageMultiplier
+ shaman.PseudoStats.ThreatMultiplier *= threatMultiplier
+ },
+ OnExpire: func(aura *core.Aura, sim *core.Simulation) {
+ if aura.Unit.Env.MeasuringStats && aura.Unit.Env.State != core.Finalized {
+ aura.Unit.MultiplyStat(stats.Health, 1/healthMultiplier)
+ } else {
+ aura.Unit.DisableDynamicStatDep(sim, statDep)
+ }
+
+ shaman.PseudoStats.DamageDealtMultiplier /= damageMultiplier
+ shaman.PseudoStats.ThreatMultiplier /= threatMultiplier
+ },
+ }))
+}
+
+var ItemSetStormcallersRelief = core.NewItemSet(core.ItemSet{
+ Name: "Stormcaller's Relief",
+ Bonuses: map[int32]core.ApplyEffect{
+ // Your Riptide increases the amount healed by Chain Heal by an additional 25%.
+ 2: func(agent core.Agent) {
+ },
+ // Reduces the cast time of Chain Heal by 0.5 sec.
+ 4: func(agent core.Agent) {
+ },
+ },
+})
+
+var ItemSetStormcallersImpact = core.NewItemSet(core.ItemSet{
+ Name: "Stormcaller's Impact",
+ Bonuses: map[int32]core.ApplyEffect{
+ 2: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyTAQEnhancement2PBonus()
+ },
+ 4: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyTAQEnhancement4PBonus()
+ },
+ },
+})
+
+// Increases Stormstrike and Lava Lash damage by 50%. Stormstrike's damage is increased by an additional 50% when using a Two-handed weapon.
+func (shaman *Shaman) applyTAQEnhancement2PBonus() {
+ if !shaman.Talents.Stormstrike && !shaman.HasRune(proto.ShamanRune_RuneHandsLavaLash) {
+ return
+ }
+
+ label := "S03 - Item - TAQ - Shaman - Enhancement 2P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ if shaman.StormstrikeMH != nil {
+ shaman.StormstrikeMH.DamageMultiplierAdditive += core.TernaryFloat64(shaman.HasRune(proto.ShamanRune_RuneChestTwoHandedMastery), 1.00, 0.50)
+ }
+
+ if shaman.StormstrikeOH != nil {
+ shaman.StormstrikeOH.DamageMultiplierAdditive += 0.50
+ }
+
+ if shaman.LavaLash != nil {
+ shaman.LavaLash.DamageMultiplierAdditive += 0.50
+ }
+ },
+ })
+}
+
+// Your Stormstrike, Lava Lash, and Lava Burst critical strikes cause your target to burn for 30% of the damage done over 4 sec.
+func (shaman *Shaman) applyTAQEnhancement4PBonus() {
+ if !shaman.Talents.Stormstrike && !shaman.HasRune(proto.ShamanRune_RuneHandsLavaLash) && !shaman.HasRune(proto.ShamanRune_RuneHandsLavaBurst) {
+ return
+ }
+
+ label := "S03 - Item - TAQ - Shaman - Enhancement 4P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ // This is the spell used for the burn proc.
+ // https://www.wowhead.com/classic/spell=1213915/burning
+ burnSpell := shaman.RegisterSpell(core.SpellConfig{
+ ActionID: core.ActionID{SpellID: 1213915},
+ SpellSchool: core.SpellSchoolFire,
+ DefenseType: core.DefenseTypeMagic,
+ ProcMask: core.ProcMaskEmpty,
+ Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell,
+
+ DamageMultiplier: 1,
+ ThreatMultiplier: 1,
+ BonusCoefficient: 1,
+
+ Dot: core.DotConfig{
+ Aura: core.Aura{
+ Label: "Burning",
+ },
+ NumberOfTicks: 2,
+ TickLength: time.Second * 2,
+ OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) {
+ dot.CalcAndDealPeriodicSnapshotDamage(sim, target, dot.OutcomeTick)
+ },
+ },
+
+ ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
+ spell.Dot(target).ApplyOrRefresh(sim)
+ spell.CalcAndDealOutcome(sim, target, spell.OutcomeAlwaysHitNoHitCounter)
+ },
+ })
+
+ core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
+ if !result.Outcome.Matches(core.OutcomeCrit) || !(spell == shaman.StormstrikeMH || spell == shaman.StormstrikeOH || spell == shaman.LavaLash || spell == shaman.LavaBurst) {
+ return
+ }
+
+ dot := burnSpell.Dot(result.Target)
+ dotDamage := result.Damage * 0.3
+ if dot.IsActive() {
+ dotDamage += dot.SnapshotBaseDamage * float64(dot.MaxTicksRemaining())
+ }
+ dot.SnapshotBaseDamage = dotDamage / float64(dot.NumberOfTicks)
+ dot.SnapshotAttackerMultiplier = 1
+
+ burnSpell.Cast(sim, result.Target)
+ },
+ }))
+}
+
+var ItemSetGiftOfTheGatheringStorm = core.NewItemSet(core.ItemSet{
+ Name: "Gift of the Gathering Storm",
+ Bonuses: map[int32]core.ApplyEffect{
+ 3: func(agent core.Agent) {
+ shaman := agent.(ShamanAgent).GetShaman()
+ shaman.applyRAQElemental3PBonus()
+ },
+ },
+})
+
+// Your Lava Burst deals increased damage equal to its critical strike chance.
+func (shaman *Shaman) applyRAQElemental3PBonus() {
+ label := "S03 - Item - RAQ - Shaman - Elemental 3P Bonus"
+ if shaman.HasAura(label) {
+ return
+ }
+
+ shaman.RegisterAura(core.Aura{
+ Label: label,
+ OnInit: func(aura *core.Aura, sim *core.Simulation) {
+ shaman.useLavaBurstCritScaling = true
+ },
+ })
+}
diff --git a/sim/shaman/runes.go b/sim/shaman/runes.go
index f21f86b8d4..2c1ec97550 100644
--- a/sim/shaman/runes.go
+++ b/sim/shaman/runes.go
@@ -14,6 +14,9 @@ func (shaman *Shaman) ApplyRunes() {
shaman.applyBurn()
shaman.applyMentalDexterity()
+ // Shoulder
+ shaman.applyShoulderRuneEffect()
+
// Cloak
shaman.registerFeralSpiritCD()
@@ -48,6 +51,78 @@ func (shaman *Shaman) ApplyRunes() {
shaman.applySpiritOfTheAlpha()
}
+func (shaman *Shaman) applyShoulderRuneEffect() {
+ if shaman.Equipment.Shoulders().Rune == int32(proto.ShamanRune_RuneNone) {
+ return
+ }
+
+ switch shaman.Equipment.Shoulders().Rune {
+ // Elemental
+ case int32(proto.ShamanRune_RuneShouldersVolcano):
+ shaman.applyT1Elemental4PBonus()
+ case int32(proto.ShamanRune_RuneShouldersRagingFlame):
+ shaman.applyT1Elemental6PBonus()
+ case int32(proto.ShamanRune_RuneShouldersElementalMaster):
+ shaman.applyT2Elemental2PBonus()
+ case int32(proto.ShamanRune_RuneShouldersTribesman):
+ shaman.applyT2Elemental4PBonus()
+ case int32(proto.ShamanRune_RuneShouldersSpiritGuide):
+ shaman.applyT2Elemental6PBonus()
+ case int32(proto.ShamanRune_RuneShouldersElder):
+ shaman.applyTAQElemental2PBonus()
+ case int32(proto.ShamanRune_RuneShouldersElements):
+ shaman.applyTAQElemental4PBonus()
+ case int32(proto.ShamanRune_RuneShouldersLavaSage):
+ shaman.applyRAQElemental3PBonus()
+
+ // Enhancement
+ case int32(proto.ShamanRune_RuneShouldersRefined):
+ shaman.applyT1Enhancement4PBonus()
+ case int32(proto.ShamanRune_RuneShouldersChieftain):
+ shaman.applyT1Enhancement6PBonus()
+ case int32(proto.ShamanRune_RuneShouldersFurycharged):
+ shaman.applyT2Enhancement2PBonus()
+ case int32(proto.ShamanRune_RuneShouldersStormbreaker):
+ shaman.applyT2Enhancement4PBonus()
+ case int32(proto.ShamanRune_RuneShouldersTempest):
+ shaman.applyT2Enhancement6PBonus()
+ case int32(proto.ShamanRune_RuneShouldersSeismicSmasher):
+ shaman.applyTAQEnhancement2PBonus()
+ case int32(proto.ShamanRune_RuneShouldersFlamebringer):
+ shaman.applyTAQEnhancement4PBonus()
+
+ // Restoration
+ case int32(proto.ShamanRune_RuneShouldersWaterWalker):
+ shaman.applyT2Restoration2PBonus()
+ case int32(proto.ShamanRune_RuneShouldersStormtender):
+ shaman.applyT2Restoration4PBonus()
+ case int32(proto.ShamanRune_RuneShouldersElementalSeer):
+ shaman.applyT2Restoration6PBonus()
+
+ // Tank
+ case int32(proto.ShamanRune_RuneShouldersWindwalker):
+ shaman.applyT1Tank2PBonus()
+ case int32(proto.ShamanRune_RuneShouldersShieldMaster):
+ shaman.applyT1Tank4PBonus()
+ case int32(proto.ShamanRune_RuneShouldersTotemicProtector):
+ shaman.applyT1Tank6PBonus()
+ case int32(proto.ShamanRune_RuneShouldersShockAbsorber):
+ shaman.applyT2Tank2PBonus()
+ case int32(proto.ShamanRune_RuneShouldersSpiritualBulwark):
+ shaman.applyT2Tank4PBonus()
+ case int32(proto.ShamanRune_RuneShouldersMaelstrombringer):
+ shaman.applyT2Tank6PBonus()
+ case int32(proto.ShamanRune_RuneShouldersAncestralWarden):
+ shaman.applyZGTank3PBonus()
+ case int32(proto.ShamanRune_RuneShouldersCorrupt):
+ shaman.applyZGTank5PBonus()
+ case int32(proto.ShamanRune_RuneShouldersLavaWalker):
+ shaman.applyTAQTank2PBonus()
+ case int32(proto.ShamanRune_RuneShouldersTrueAlpha):
+ shaman.applyTAQTank4PBonus()
+ }
+}
+
var BurnFlameShockTargetCount = int32(5)
var BurnFlameShockDamageBonus = 1.0
var BurnFlameShockBonusTicks = int32(2)
@@ -159,8 +234,6 @@ func (shaman *Shaman) applyShieldMastery() {
defendersResolveAura := core.DefendersResolveSpellDamage(shaman.GetCharacter(), 4)
- has4PEarthfuryResolve := shaman.HasSetBonus(ItemSetEarthfuryResolve, 4)
-
shaman.AddStat(stats.Block, 10)
shaman.PseudoStats.BlockValueMultiplier = 1.15
@@ -169,11 +242,14 @@ func (shaman *Shaman) applyShieldMastery() {
procManaReturn := 0.08
armorPerStack := shaman.Equipment.OffHand().Stats[stats.Armor] * 0.3
- blockProcAura := shaman.RegisterAura(core.Aura{
+ shaman.ShieldMasteryAura = shaman.RegisterAura(core.Aura{
Label: "Shield Mastery Block",
ActionID: core.ActionID{SpellID: 408525},
Duration: time.Second * 15,
MaxStacks: 5,
+ OnRefresh: func(aura *core.Aura, sim *core.Simulation) {
+ shaman.AddMana(sim, shaman.MaxMana()*procManaReturn, manaMetrics)
+ },
OnStacksChange: func(aura *core.Aura, sim *core.Simulation, oldStacks, newStacks int32) {
shaman.AddStatDynamic(sim, stats.Armor, armorPerStack*float64(newStacks-oldStacks))
},
@@ -183,10 +259,9 @@ func (shaman *Shaman) applyShieldMastery() {
core.MakePermanent(shaman.RegisterAura(core.Aura{
Label: "Shield Mastery Trigger",
OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
- if result.DidBlock() || (has4PEarthfuryResolve && (result.DidParry() || result.DidDodge())) {
- shaman.AddMana(sim, shaman.MaxMana()*procManaReturn, manaMetrics)
- blockProcAura.Activate(sim)
- blockProcAura.AddStack(sim)
+ if result.DidBlock() {
+ shaman.ShieldMasteryAura.Activate(sim)
+ shaman.ShieldMasteryAura.AddStack(sim)
}
},
OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
diff --git a/sim/shaman/shaman.go b/sim/shaman/shaman.go
index ae37c9d470..b23a4fda97 100644
--- a/sim/shaman/shaman.go
+++ b/sim/shaman/shaman.go
@@ -150,6 +150,7 @@ type Shaman struct {
MaelstromWeaponAura *core.Aura
PowerSurgeDamageAura *core.Aura
PowerSurgeHealAura *core.Aura
+ ShieldMasteryAura *core.Aura
SpiritOfTheAlphaAura *core.Aura
WaterShieldAura *core.Aura
diff --git a/sim/shaman/talents.go b/sim/shaman/talents.go
index d59471edd7..e681976aed 100644
--- a/sim/shaman/talents.go
+++ b/sim/shaman/talents.go
@@ -401,9 +401,14 @@ func (shaman *Shaman) makeFlurryAura(points int32) *core.Aura {
spellID := []int32{16257, 16277, 16278, 16279, 16280}[points-1]
attackSpeed := []float64{1.1, 1.15, 1.2, 1.25, 1.3}[points-1]
+ label := fmt.Sprintf("Flurry Proc (%d)", spellID)
- aura := shaman.GetOrRegisterAura(core.Aura{
- Label: fmt.Sprintf("Flurry Proc (%d)", spellID),
+ if aura := shaman.GetAura(label); aura != nil {
+ return aura
+ }
+
+ aura := shaman.RegisterAura(core.Aura{
+ Label: label,
ActionID: core.ActionID{SpellID: spellID},
Duration: core.NeverExpires,
MaxStacks: 3,
@@ -425,12 +430,18 @@ func (shaman *Shaman) makeFlurryAura(points int32) *core.Aura {
// With the Warden T1 2pc it's possible to have 2 different Flurry auras if using less than 5/5 points in Flurry.
// The two different buffs don't stack whatsoever. Instead the stronger aura takes precedence and each one is only refreshed by the corresponding triggers.
func (shaman *Shaman) makeFlurryConsumptionTrigger(flurryAura *core.Aura) *core.Aura {
+ label := fmt.Sprintf("Flurry Consume Trigger - %d", flurryAura.ActionID.SpellID)
+ if aura := shaman.GetAura(label); aura != nil {
+ return aura
+ }
+
icd := core.Cooldown{
Timer: shaman.NewTimer(),
Duration: time.Millisecond * 500,
}
- return core.MakePermanent(shaman.GetOrRegisterAura(core.Aura{
- Label: fmt.Sprintf("Flurry Consume Trigger - %d", flurryAura.ActionID.SpellID),
+
+ return core.MakePermanent(shaman.RegisterAura(core.Aura{
+ Label: label,
OnSpellHitDealt: func(_ *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
// Remove a stack.
if flurryAura.IsActive() && spell.ProcMask.Matches(core.ProcMaskMeleeWhiteHit) && icd.IsReady(sim) {
diff --git a/tools/database/database.go b/tools/database/database.go
index b7a918110b..1abe0191ac 100644
--- a/tools/database/database.go
+++ b/tools/database/database.go
@@ -191,11 +191,28 @@ func (db *WowDatabase) AddRune(id int32, tooltip WowheadItemResponse) {
Id: id,
Name: tooltip.GetName(),
Icon: tooltip.GetIcon(),
- ClassAllowlist: []proto.Class{tooltip.GetRequiredClass()},
+ ClassAllowlist: tooltip.GetRequiredClasses(),
Type: tooltip.GetRequiredItemSlot(),
}
}
+func (db *WowDatabase) AddShoulderRune(id int32, tooltip WowheadItemResponse) {
+ if tooltip.GetName() == "" || tooltip.GetIcon() == "" {
+ return
+ }
+ if runeOverrideNames[tooltip.GetName()] != nil {
+ return
+ }
+
+ db.Runes[id] = &proto.UIRune{
+ Id: id,
+ Name: tooltip.GetName(),
+ Icon: tooltip.GetIcon(),
+ ClassAllowlist: tooltip.GetRequiredClasses(),
+ Type: proto.ItemType_ItemTypeShoulder,
+ }
+}
+
func (db *WowDatabase) AddItemIcon(id int32, tooltips map[int32]WowheadItemResponse) {
if tooltip, ok := tooltips[id]; ok {
if tooltip.GetName() == "" || tooltip.GetIcon() == "" {
diff --git a/tools/database/gen_db/main.go b/tools/database/gen_db/main.go
index 5ec7b38631..aa1177adb6 100644
--- a/tools/database/gen_db/main.go
+++ b/tools/database/gen_db/main.go
@@ -26,6 +26,7 @@ import (
// go run ./tools/database/gen_db -outDir=assets -gen=wowhead-gearplannerdb
// go run ./tools/database/gen_db -outDir=assets -gen=wago-db2-items
// python3 tools/scrape_runes.py assets/db_inputs/wowhead_rune_tooltips.csv
+// python3 tools/scrape_shoulder_runes.py assets/db_inputs/wowhead_shoulder_rune_tooltips.csv
// Lastly run the following to generate db.json (ensure to delete cached versions and/or rebuild for copying of assets during local development)
// Note: This does not make network requests, only regenerates core db binary and json files from existing inputs
@@ -75,6 +76,7 @@ func main() {
itemTooltips := database.NewWowheadItemTooltipManager(fmt.Sprintf("%s/wowhead_item_tooltips.csv", inputsDir)).Read()
spellTooltips := database.NewWowheadSpellTooltipManager(fmt.Sprintf("%s/wowhead_spell_tooltips.csv", inputsDir)).Read()
runeTooltips := database.NewWowheadSpellTooltipManager(fmt.Sprintf("%s/wowhead_rune_tooltips.csv", inputsDir)).Read()
+ shoulderRuneTooltips := database.NewWowheadSpellTooltipManager(fmt.Sprintf("%s/wowhead_shoulder_rune_tooltips.csv", inputsDir)).Read()
wowheadDB := database.ParseWowheadDB(tools.ReadFile(fmt.Sprintf("%s/wowhead_gearplannerdb.txt", inputsDir)))
atlaslootDB := database.ReadDatabaseFromJson(tools.ReadFile(fmt.Sprintf("%s/atlasloot_db.json", inputsDir)))
wagoItems := database.ParseWagoDB(tools.ReadFile(fmt.Sprintf("%s/wago_db2_items.csv", inputsDir)))
@@ -164,6 +166,12 @@ func main() {
db.AddRune(id, rune)
}
+ for id, rune := range shoulderRuneTooltips {
+ if database.ShoulderRuneClassAllowlist[rune.GetRequiredClasses()[0]] {
+ db.AddShoulderRune(id, rune)
+ }
+ }
+
db.MergeItems(database.ItemOverrides)
db.MergeEnchants(database.EnchantOverrides)
db.MergeRunes(database.RuneOverrides)
diff --git a/tools/database/rune_overrides.go b/tools/database/rune_overrides.go
index 8cb2a46da6..782e2103ea 100644
--- a/tools/database/rune_overrides.go
+++ b/tools/database/rune_overrides.go
@@ -45,33 +45,9 @@ var RuneOverrides = []*proto.UIRune{
// Hunter
// 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}},
+}
- // Special should pseudo-runes
-
- // Druid
-
- // Hunter
-
- // Mage
- // {Id: 1220158, Name: "Soul of Winter's Grasp", Icon: "spell_holy_divinespirit", Type: proto.ItemType_ItemTypeShoulder, ClassAllowlist: []proto.Class{proto.Class_ClassMage}},
-
- // Paladin
-
- // Priest
- // {Id: 1220134, Name: "Soul of the Zealot", Icon: "spell_holy_divinespirit", Type: proto.ItemType_ItemTypeShoulder, ClassAllowlist: []proto.Class{proto.Class_ClassPriest}},
-
- // Rogue
-
- // Shaman
- // {Id: 1220232, Name: "Soul of the Windwalker", Icon: "spell_holy_divinespirit", Type: proto.ItemType_ItemTypeShoulder, ClassAllowlist: []proto.Class{proto.Class_ClassShaman}},
- // {Id: 1220234, Name: "Soul of the Shield Master", Icon: "spell_holy_divinespirit", Type: proto.ItemType_ItemTypeShoulder, ClassAllowlist: []proto.Class{proto.Class_ClassShaman}},
- // {Id: 1220236, Name: "Soul of the Totemic Protector", Icon: "spell_holy_divinespirit", Type: proto.ItemType_ItemTypeShoulder, ClassAllowlist: []proto.Class{proto.Class_ClassShaman}},
- // {Id: 1220238, Name: "Soul of the Shock-Absorber", Icon: "spell_holy_divinespirit", Type: proto.ItemType_ItemTypeShoulder, ClassAllowlist: []proto.Class{proto.Class_ClassShaman}},
- // {Id: 1220240, Name: "Soul of the Spiritual Bulwark", Icon: "spell_holy_divinespirit", Type: proto.ItemType_ItemTypeShoulder, ClassAllowlist: []proto.Class{proto.Class_ClassShaman}},
- // {Id: 1220242, Name: "Soul of the Maelstrombringer", Icon: "spell_holy_divinespirit", Type: proto.ItemType_ItemTypeShoulder, ClassAllowlist: []proto.Class{proto.Class_ClassShaman}},
- // {Id: 1220244, Name: "Soul of the Lavawalker", Icon: "spell_holy_divinespirit", Type: proto.ItemType_ItemTypeShoulder, ClassAllowlist: []proto.Class{proto.Class_ClassShaman}},
-
- // Warlock
-
- // Warrior
+// Classes that have shoulder rune implementations completed to not confuse users
+var ShoulderRuneClassAllowlist = map[proto.Class]bool{
+ proto.Class_ClassShaman: true,
}
diff --git a/tools/database/wowhead_tooltips.go b/tools/database/wowhead_tooltips.go
index 45535f0724..c0bc1a6e6f 100644
--- a/tools/database/wowhead_tooltips.go
+++ b/tools/database/wowhead_tooltips.go
@@ -334,17 +334,26 @@ func (item WowheadItemResponse) GetRequiresLevel() int {
return level
}
-var reqClassRegex = regexp.MustCompile(`Requires (Druid|Hunter|Mage|Paladin|Priest|Rogue|Shaman|Warlock|Warrior)`)
-
-func (item WowheadItemResponse) GetRequiredClass() proto.Class {
- class := item.GetTooltipRegexString(reqClassRegex, 1)
+var allClassesRegex = regexp.MustCompile(`(Druid|Hunter|Mage|Paladin|Priest|Rogue|Shaman|Warlock|Warrior)`)
+var reqClassRegex = regexp.MustCompile(`Requires\s(Druid|Hunter|Mage|Paladin|Priest|Rogue|Shaman|Warlock|Warrior)`)
+var reqClassesRegex = regexp.MustCompile(`Classes:\s((?:.+?(Druid|Hunter|Mage|Paladin|Priest|Rogue|Shaman|Warlock|Warrior)<\/a>(?:,\s)?)+)`)
+
+func (item WowheadItemResponse) GetRequiredClasses() []proto.Class {
+ if singleClass := item.GetTooltipRegexString(reqClassRegex, 1); singleClass != "" {
+ className := "Class" + singleClass
+ return []proto.Class{proto.Class(proto.Class_value[className])}
+ }
- if class == "" {
- return proto.Class_ClassUnknown
+ if multiClasses := item.GetTooltipRegexString(reqClassesRegex, 1); multiClasses != "" {
+ classes := []proto.Class{}
+ for _, classMatch := range allClassesRegex.FindAllStringSubmatch(item.TooltipWithoutSetBonus(), -1) {
+ className := classMatch[1]
+ classes = append(classes, proto.Class(proto.Class_value["Class"+className]))
+ }
+ return classes
}
- className := "Class" + class
- return proto.Class(proto.Class_value[className])
+ return []proto.Class{}
}
var reqSlotRegex = regexp.MustCompile(`Requires (Back|Belt|Bracer|Chest|Cloak|Boots|Feet|Gloves|Hands|Head|Helm|Legs|Pants|Ring|Shoulder|Trinket|Waist|Wrist)`)
diff --git a/tools/scrape_shoulder_runes.py b/tools/scrape_shoulder_runes.py
new file mode 100644
index 0000000000..8134e668df
--- /dev/null
+++ b/tools/scrape_shoulder_runes.py
@@ -0,0 +1,121 @@
+#!/usr/bin/python
+
+# This tool generates the classic SoD runes data
+
+import sys
+import requests
+import math
+import re
+
+from typing import List
+
+from selenium import webdriver
+from selenium.common.exceptions import NoSuchElementException
+from selenium.webdriver.chrome.service import Service
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.common.by import By
+from webdriver_manager.chrome import ChromeDriverManager
+from selenium.webdriver.chrome.options import Options
+
+if len(sys.argv) < 2:
+ raise Exception("Missing arguments, expected output_file_path")
+
+output_file_path = sys.argv[1]
+
+# Added these options so that chrome would run in a docker container
+chrome_options = Options()
+chrome_options.add_argument("--headless")
+chrome_options.add_argument("--no-sandbox")
+chrome_options.add_argument("--disable-dev-shm-usage")
+
+driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
+wait = WebDriverWait(driver, 10)
+element_locator = (By.ID, "data-tree-switcher")
+
+def _get_id_from_link(link):
+ return int(link.split("/")[-2].split("=")[-1])
+
+
+def get_item_ids() -> List[int]:
+ driver.get(f"https://www.wowhead.com/classic/items/name:%22Soul+of+the%22?filter=82:142;2:0;11506:spell_holy_divinespirit")
+ wait.until(EC.presence_of_element_located(element_locator))
+
+ listview = driver.find_element(By.ID, "lv-items")
+ pages = int(listview.find_element(By.CLASS_NAME, "listview-nav").find_element(By.CSS_SELECTOR, 'b:last-child').text)/50
+ pages = math.ceil(pages)
+ all_ids = []
+
+ for page in range(pages):
+ print(f'Loading page {page} for runes...')
+ driver.get(f"https://www.wowhead.com/classic/items/name:%22Soul+of+the%22?filter=82:142;2:0;11506:spell_holy_divinespirit#{page*50}")
+ driver.refresh()
+ wait.until(EC.presence_of_element_located(element_locator))
+ listview = driver.find_element(By.ID, "lv-items")
+ rows = listview.find_elements(By.CLASS_NAME, "listview-row")
+ all_ids.extend([_get_id_from_link(row.find_element(By.CLASS_NAME, "listview-cleartext").get_attribute("href"))
+ for row in rows])
+
+ return all_ids
+
+def get_tooltips_response(id):
+ # Get the underlying item ID from the engraving ID
+ url = f"https://nether.wowhead.com/classic/tooltip/item/{id}"
+ result = requests.get(url)
+
+ if result.status_code == 200:
+ response_json = result.text
+ return response_json
+ else:
+ print(f"Request for id {id} failed with status code: {result.status_code}")
+ return None
+
+
+# id, tooltip_json
+to_export = []
+
+item_ids = get_item_ids()
+
+print(f"Export Count ({len(item_ids)}) {item_ids}")
+
+to_export = []
+
+# This sole spell is missing a related spell to reference
+mismatchedIds = {
+ "1219819": 1220096
+}
+
+for id in item_ids:
+ item_response = get_tooltips_response(id)
+ spell_ids = re.findall(r'\/spell=(\d+)', item_response)
+
+ if len(spell_ids) >= 2:
+ # The base spell is different from what we typically use but the spell we actually want appears as the first related spell in the "See also" tab
+ enchant_spell_id = spell_ids[0]
+ try:
+ driver.get(f"https://www.wowhead.com/classic/spell={enchant_spell_id}#see-also-other")
+ driver.refresh()
+ wait.until(EC.presence_of_element_located(element_locator))
+
+ see_also_tab = driver.find_element(By.ID, "tab-see-also-other")
+ rows = see_also_tab.find_elements(By.CLASS_NAME, "listview-row")
+
+ if len(rows) > 0:
+ actual_spell_id = _get_id_from_link(rows[0].find_element(By.CLASS_NAME, "listview-cleartext").get_attribute("href"))
+ # Use the Spell ID of the effect but keep the item tooltip for class matching and display
+ to_export.append([actual_spell_id, item_response])
+ else:
+ print(f"No related spell IDs found for spell ID {enchant_spell_id}")
+ except NoSuchElementException:
+ if mismatchedIds[enchant_spell_id]:
+ to_export.append([actual_spell_id, item_response])
+ else:
+ print(f"No related spell IDs found for spell ID {enchant_spell_id}")
+ else:
+ print(f"Less than 2 spell IDs found for item ID {id}")
+
+driver.quit()
+output_string = '\n'.join([str(','.join([str(inner_elem) for inner_elem in elem])) for elem in to_export])
+
+with open(output_file_path, "w") as outfile:
+ outfile.write(output_string)