From 06323e7c8fd8e92f6704ed3efb9fddaa71556058 Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sun, 7 Jul 2024 02:37:43 -0400 Subject: [PATCH] phase 4 item effects --- sim/common/sod/item_effects/phase_4.go | 111 +- sim/common/vanilla/item_effects.go | 1669 ++++++++++++++++-------- sim/core/aura.go | 2 +- sim/core/metrics_aggregator.go | 10 + tools/database/overrides.go | 3 + 5 files changed, 1180 insertions(+), 615 deletions(-) diff --git a/sim/common/sod/item_effects/phase_4.go b/sim/common/sod/item_effects/phase_4.go index 2c11782c1d..be1209e588 100644 --- a/sim/common/sod/item_effects/phase_4.go +++ b/sim/common/sod/item_effects/phase_4.go @@ -8,14 +8,13 @@ import ( ) const ( - CraftOfTheShadows = 227280 - SulfurasHandOfRagnaros = 227683 // 17182 - DukesDomain = 227915 - AccursedChalice = 228078 - GerminatingPoisonseed = 228081 - GloamingTreeheart = 228083 - WoodcarvedMoonstalker = 228089 - TheMoltenCore = 228122 + CraftOfTheShadows = 227280 + DukesDomain = 227915 + AccursedChalice = 228078 + GerminatingPoisonseed = 228081 + GloamingTreeheart = 228083 + WoodcarvedMoonstalker = 228089 + TheMoltenCore = 228122 ) func init() { @@ -25,102 +24,6 @@ func init() { // Weapons /////////////////////////////////////////////////////////////////////////// - // https://www.wowhead.com/classic/item=227683/sulfuras-hand-of-ragnaros - // Chance on hit: Hurls a fiery ball that causes 273 to 333 Fire damage and purges the target's soul, increasing Fire and Holy damage taken by up to 30 and dealing an additional 75 damage over 10 sec. - // Equip: 20% chance to deal 25 Fire damage to all nearby enemies when you are struck by a melee attack. (Proc chance: 20%) - core.NewItemEffect(SulfurasHandOfRagnaros, func(agent core.Agent) { - character := agent.GetCharacter() - - immolationSpell := character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 460335}, - SpellSchool: core.SpellSchoolFire, - DefenseType: core.DefenseTypeMagic, - ProcMask: core.ProcMaskEmpty, - - BonusCoefficient: .025, - DamageMultiplier: 1, - ThreatMultiplier: 1, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - spell.CalcAndDealDamage(sim, target, 25, spell.OutcomeAlwaysHit) - }, - }) - - core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ - Name: "Immolation (Hand of Ragnaros)", - Callback: core.CallbackOnSpellHitTaken, - Outcome: core.OutcomeLanded, - ProcMask: core.ProcMaskMelee, - ProcChance: .20, - Handler: func(sim *core.Simulation, _ *core.Spell, _ *core.SpellResult) { - for _, aoeTarget := range sim.Encounter.TargetUnits { - immolationSpell.Cast(sim, aoeTarget) - } - }, - }) - - debuffAuras := character.NewEnemyAuraArray(func(unit *core.Unit, _ int32) *core.Aura { - return unit.GetOrRegisterAura(core.Aura{ - ActionID: core.ActionID{SpellID: 460338}, - Label: "Purged by Fire", - Duration: time.Second * 10, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexFire] += 30 - unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexHoly] += 30 - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexFire] -= 30 - unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexHoly] -= 30 - }, - }) - }) - - purgedByFireSpell := character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 460338}, - SpellSchool: core.SpellSchoolFire, - DefenseType: core.DefenseTypeMagic, - ProcMask: core.ProcMaskEmpty, - - DamageMultiplier: 1, - ThreatMultiplier: 1, - - Dot: core.DotConfig{ - Aura: core.Aura{ - Label: "Purged By Fire", - }, - TickLength: 2 * time.Second, - NumberOfTicks: 5, - - OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) { - dot.Snapshot(target, 15, isRollover) - debuffAuras.Get(target).Activate(sim) - }, - - 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) { - result := spell.CalcAndDealDamage(sim, target, sim.Roll(273, 333), spell.OutcomeMagicHitAndCrit) - if result.Landed() { - spell.Dot(target).Apply(sim) - } - }, - }) - - core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ - Name: "Purged by Fire Trigger", - Callback: core.CallbackOnSpellHitDealt, - Outcome: core.OutcomeLanded, - ProcMask: core.ProcMaskMelee, - PPM: 1, // Estimated based on data from WoW Armaments Discord - Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - purgedByFireSpell.Cast(sim, result.Target) - }, - }) - }) - /////////////////////////////////////////////////////////////////////////// // Trinkets /////////////////////////////////////////////////////////////////////////// diff --git a/sim/common/vanilla/item_effects.go b/sim/common/vanilla/item_effects.go index 423b8db563..7dfe9ec8f3 100644 --- a/sim/common/vanilla/item_effects.go +++ b/sim/common/vanilla/item_effects.go @@ -9,6 +9,7 @@ import ( "github.com/wowsims/sod/sim/core/stats" ) +// Ordered by ID const ( ShortswordOfVengeance = 754 FieryWarAxe = 870 @@ -41,7 +42,6 @@ const ( SatyrsLash = 17752 MarkOfTheChosen = 17774 Thunderfury = 19019 - Nightfall = 19169 DarkmoonCardHeroism = 19287 DarkmoonCardBlueDragon = 19288 DarkmoonCardMaelstrom = 19289 @@ -49,67 +49,228 @@ const ( ScarabBrooch = 21625 MarkOfTheChampionPhys = 23206 MarkOfTheChampionSpell = 23207 - ReavingNightfall = 227843 + SulfurasHandOfRagnaros = 227683 // 17182 + SulfuronHammer = 227684 // 17193 + TemperedBlackAmnesty = 227832 // 19166 + EbonFist = 227842 // 19170 + ReavingNightfall = 227843 // 19169 BurstOfKnowledge = 227972 HandOfJustice = 227989 // 11815 Ironfoe = 227991 // 11684 FiendishMachete = 228056 // 18310 TalismanOfEphemeralPower = 228255 // 18820 + GutgoreRipper = 228267 // 17071 EssenceOfThePureFlame = 228293 // 18815 + PerditionsBlade = 228296 // 18816 + EskhandarsLeftClaw = 228349 // 18202 + EskhandarsRightClaw = 228350 // 18203 + BlazefuryMedallion = 228354 // 17111 EmpyreanDemolisher = 228397 // 17112 + PerditionsBladeMolten = 228511 + Venomspitter = 228573 // 13183 + Chillpike = 228586 // 13148 + FangOfTheCrystalSpider = 228592 // 13218 + BlackhandDoomsaw = 228603 // 12583 + BlackbladeOfShahram = 228606 // 12592 + SeepingWillow = 228666 // 12969 DraconicInfusedEmblem = 228678 // 22268 HandOfJustice2 = 228722 // TODO: Unsure why there's a second version of this item - - // SulfurasHandOfRagnaros = 17182 ) func init() { core.AddEffectsToTest = false + // ! Please keep items ordered alphabetically within a given category ! + /////////////////////////////////////////////////////////////////////////// // Weapons /////////////////////////////////////////////////////////////////////////// - itemhelpers.CreateWeaponProcDamage(ShortswordOfVengeance, "Shortsword of Vengeance", 1.0, 13519, core.SpellSchoolHoly, 30, 0, 0, core.DefenseTypeMagic) + // https://www.wowhead.com/classic/item=228606/blackblade-of-shahram + // Chance on hit: Summons the infernal spirit of Shahram. + // Summons an NPC "Shahram" who has an equal chance to cast one of 6 spells: + // Curse of Shahram: -50% movement speed and -25% attack speed on all enemies within 10 yards of Shahram for 10 seconds. + // Might of Shahram: 5-second stun on all enemies within 10 yards of Shahram. + // Fist of Shahram: +30% Melee Attack Speed for all party members within 30 yards of Shahram for 8 seconds. + // Blessing of Shahram: Restores 50 health and mana every 5 seconds for all party members within 30 yards of Shahram for 20 seconds. The Healing portion of this effect scales at 100% of self-healing buffs such as Amplify Magic. + // Will of Shahram: +50 all stats for yourself for 20 seconds. + // Flames of Shahram: Deals 100-150 Fire damage to all enemies within 10 yards of Shahram. Damage scales at 100% with +spelldmg debuffs placed on enemies such as Flame Buffet. + // + // Implementing this without the guardian as it seems to just cast a spell and depart and guardians are expensive + core.NewItemEffect(BlackbladeOfShahram, func(agent core.Agent) { + character := agent.GetCharacter() - itemhelpers.CreateWeaponProcSpell(FieryWarAxe, "Fiery War Axe", 1.0, func(character *core.Character) *core.Spell { - return character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 18796}, - SpellSchool: core.SpellSchoolFire, + curseOfShahramAuras := character.NewEnemyAuraArray(func(target *core.Unit, _ int32) *core.Aura { + aura := target.GetOrRegisterAura(core.Aura{ + ActionID: core.ActionID{SpellID: 16597}, + Label: "Curse of Shahram", + Duration: time.Second * 10, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.MultiplyAttackSpeed(sim, 1/1.25) + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.MultiplyAttackSpeed(sim, 1.25) + }, + }) + core.AtkSpeedReductionEffect(aura, 1.25) + return aura + }) + curseOfShahram := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 16597}, + SpellSchool: core.SpellSchoolArcane, DefenseType: core.DefenseTypeMagic, ProcMask: core.ProcMaskEmpty, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + curseOfShahramAuras.Get(target).Activate(sim) + }, + }) - DamageMultiplier: 1, - ThreatMultiplier: 1, - - Dot: core.DotConfig{ - Aura: core.Aura{ - Label: "Fiery War Axe Fireball", + mightOfShahramAuras := character.NewEnemyAuraArray(func(target *core.Unit, _ int32) *core.Aura { + return target.GetOrRegisterAura(core.Aura{ + ActionID: core.ActionID{SpellID: 16600}, + Label: "Might of Shahram", + Duration: time.Second * 5, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.PseudoStats.Stunned = true }, - TickLength: 2 * time.Second, - NumberOfTicks: 3, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.PseudoStats.Stunned = false + }, + }) + }) + mightOfShahram := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 16600}, + SpellSchool: core.SpellSchoolArcane, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + for _, aoeTarget := range sim.Encounter.TargetUnits { + mightOfShahramAuras.Get(aoeTarget).Activate(sim) + } + }, + }) - OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) { - dot.Snapshot(target, 8, isRollover) + fistOfShahramAuras := character.NewPartyAuraArray(func(unit *core.Unit) *core.Aura { + return unit.GetOrRegisterAura(core.Aura{ + ActionID: core.ActionID{SpellID: 16601}, + Label: "Fist of Shahram", + Duration: time.Second * 8, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + character.MultiplyAttackSpeed(sim, 1.3) + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + character.MultiplyAttackSpeed(sim, 1/1.3) }, + }) + }) + fistOfShahram := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 16601}, + SpellSchool: core.SpellSchoolArcane, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + for _, aura := range fistOfShahramAuras { + aura.Activate(sim) + } + }, + }) + blessingOfShahramManaMetrics := character.NewPartyManaMetrics(core.ActionID{SpellID: 16599}) + blessingOfShahram := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 16599}, + SpellSchool: core.SpellSchoolArcane, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagIgnoreAttackerModifiers, + Hot: core.DotConfig{ + Aura: core.Aura{ + Label: "Blessing of Shahram", + }, + NumberOfTicks: 4, + TickLength: time.Second * 5, + OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, _ bool) { + dot.SnapshotBaseDamage = 50 + }, OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { - dot.CalcAndDealPeriodicSnapshotDamage(sim, target, dot.OutcomeTick) + dot.CalcAndDealPeriodicSnapshotHealing(sim, target, dot.OutcomeTick) + if target.HasManaBar() { + target.AddMana(sim, 50, blessingOfShahramManaMetrics[target.UnitIndex]) + } }, }, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + for _, agent := range character.Party.PlayersAndPets { + spell.Hot(&agent.GetCharacter().Unit).Apply(sim) + } + }, + }) + + willOfShahramAura := character.GetOrRegisterAura(core.Aura{ + ActionID: core.ActionID{SpellID: 16598}, + Label: "Will of Shahram", + Duration: time.Second * 20, + MaxStacks: 5, + OnStacksChange: func(aura *core.Aura, sim *core.Simulation, oldStacks int32, newStacks int32) { + stats := stats.Stats{ + stats.Agility: 25, + stats.Intellect: 25, + stats.Stamina: 25, + stats.Spirit: 25, + stats.Strength: 25, + } + character.AddStatsDynamic(sim, stats.Multiply(float64(-1*oldStacks))) + character.AddStatsDynamic(sim, stats.Multiply(float64(newStacks))) + }, + }) + willOfShahram := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 16598}, + SpellSchool: core.SpellSchoolArcane, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + willOfShahramAura.Activate(sim) + willOfShahramAura.AddStack(sim) + }, + }) + flamesOfShahram := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 16596}, + SpellSchool: core.SpellSchoolFire, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagIgnoreAttackerModifiers, + DamageMultiplier: 1, + ThreatMultiplier: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - dmg := sim.Roll(155, 197) - result := spell.CalcAndDealDamage(sim, target, dmg, spell.OutcomeMagicHitAndCrit) - if result.Landed() { - spell.Dot(target).Apply(sim) + for _, aoeTarget := range sim.Encounter.TargetUnits { + spell.CalcAndDealDamage(sim, aoeTarget, 90, spell.OutcomeMagicCrit) } }, }) + + castableSpells := []*core.Spell{curseOfShahram, mightOfShahram, fistOfShahram, blessingOfShahram, willOfShahram, flamesOfShahram} + core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: "Summon Shahram", + Callback: core.CallbackOnSpellHitDealt, + Outcome: core.OutcomeLanded, + ProcMask: core.ProcMaskMelee, + PPM: 1, + Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + spellIdx := int32(sim.Roll(0, 6)) + castableSpells[spellIdx].Cast(sim, result.Target) + }, + }) }) + // https://www.wowhead.com/classic/item=228603/blackhand-doomsaw + // Chance on hit: Wounds the target for 324 to 540 damage. + // TODO: Proc rate based on the original item + itemhelpers.CreateWeaponProcDamage(BlackhandDoomsaw, "Blackhand Doomsaw", 0.4, 16549, core.SpellSchoolPhysical, 324, 216, 0, core.DefenseTypeMelee) + + itemhelpers.CreateWeaponProcDamage(BloodletterScalpel, "Bloodletter Scalpel", 1.0, 18081, core.SpellSchoolPhysical, 60, 10, 0, core.DefenseTypeMelee) + itemhelpers.CreateWeaponProcSpell(Bloodrazor, "Bloodrazor", 1.0, func(character *core.Character) *core.Spell { - return character.RegisterSpell(core.SpellConfig{ + return character.GetOrRegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: 17504}, SpellSchool: core.SpellSchoolPhysical, DefenseType: core.DefenseTypeMelee, @@ -135,30 +296,8 @@ func init() { }) }) - itemhelpers.CreateWeaponProcDamage(HammerOfTheNorthernWind, "Hammer of the Northern Wind", 3.5, 13439, core.SpellSchoolFrost, 20, 10, 0, core.DefenseTypeMagic) - - itemhelpers.CreateWeaponProcSpell(FlurryAxe, "Flurry Axe", 1.0, func(character *core.Character) *core.Spell { - return character.GetOrRegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 18797}, - SpellSchool: core.SpellSchoolPhysical, - DefenseType: core.DefenseTypeMelee, - ProcMask: core.ProcMaskEmpty, - DamageMultiplier: 1, - ThreatMultiplier: 1, - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - character.AutoAttacks.ExtraMHAttack(sim, 1, core.ActionID{SpellID: 18797}) - }, - }) - }) - - itemhelpers.CreateWeaponProcDamage(Nightblade, "Nightblade", 1.0, 18211, core.SpellSchoolShadow, 125, 150, 0, core.DefenseTypeMagic) - - itemhelpers.CreateWeaponProcDamage(Shadowblade, "Shadowblade", 1.0, 18138, core.SpellSchoolShadow, 110, 30, 0, core.DefenseTypeMagic) - - itemhelpers.CreateWeaponProcDamage(GutRipper, "Gut Ripper", 1.0, 18107, core.SpellSchoolPhysical, 95, 26, 0, core.DefenseTypeMelee) - itemhelpers.CreateWeaponProcSpell(BowOfSearingArrows, "Bow of Searing Arrows", 3.35, func(character *core.Character) *core.Spell { - return character.RegisterSpell(core.SpellConfig{ + return character.GetOrRegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: 29638}, SpellSchool: core.SpellSchoolFire, DefenseType: core.DefenseTypeRanged, @@ -174,98 +313,76 @@ func init() { }) }) - itemhelpers.CreateWeaponProcSpell(Gutwrencher, "Gutwrencher", 1.0, func(character *core.Character) *core.Spell { - return character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 16406}, - SpellSchool: core.SpellSchoolPhysical, - DefenseType: core.DefenseTypeMelee, - ProcMask: core.ProcMaskEmpty, - DamageMultiplier: 1, - ThreatMultiplier: 1, - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - result := spell.CalcAndDealOutcome(sim, target, spell.OutcomeMeleeSpecialHit) - if result.Landed() { - spell.Dot(target).Apply(sim) - } + // https://www.wowhead.com/classic/item=228586/chillpike + // Chance on hit: Blasts a target for 160 to 250 Frost damage. + // TODO: Proc rate assumed and needs testing + itemhelpers.CreateWeaponProcDamage(Chillpike, "Chillpike", 1.0, 19260, core.SpellSchoolFrost, 160, 90, 0, core.DefenseTypeMagic) + + // https://www.wowhead.com/classic/item=227842/ebon-fist + // Chance on hit: Sends a shadowy bolt at the enemy causing 125 to 275 Shadow damage. + // TODO: Proc rate assumed and needs testing + itemhelpers.CreateWeaponProcDamage(EbonFist, "Ebon Fist", 1.0, 18211, core.SpellSchoolShadow, 125, 150, 0, core.DefenseTypeMagic) + + // https://www.wowhead.com/classic/item=228397/empyrean-demolisher + // Chance on hit: Increases your attack speed by 20% for 10 sec. + itemhelpers.CreateWeaponProcAura(EmpyreanDemolisher, "Empyrean Demolisher", 1.0, func(character *core.Character) *core.Aura { + return character.GetOrRegisterAura(core.Aura{ + Label: "Empyrean Demolisher Haste Aura", + ActionID: core.ActionID{SpellID: 21165}, + Duration: time.Second * 10, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + character.MultiplyAttackSpeed(sim, 1.2) }, - Dot: core.DotConfig{ - NumberOfTicks: 10, - TickLength: time.Second * 3, - Aura: core.Aura{ - Label: "Rend", - }, - OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { - dot.Spell.CalcAndDealPeriodicDamage(sim, target, 8, dot.OutcomeTick) - }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + character.MultiplyAttackSpeed(sim, 1/1.2) }, }) }) - itemhelpers.CreateWeaponProcAura(Ravager, "Ravager", 1.0, func(character *core.Character) *core.Aura { - tickActionID := core.ActionID{SpellID: 9633} - procActionID := core.ActionID{SpellID: 9632} - auraActionID := core.ActionID{SpellID: 433801} - - ravegerBladestormTickSpell := character.GetOrRegisterSpell(core.SpellConfig{ - ActionID: tickActionID, + // https://www.wowhead.com/classic/item=228349/eskhandars-left-claw + // Chance on hit: Slows enemy's movement by 60% and causes them to bleed for 150 damage over 30 sec. + // TODO: Proc rate untested + itemhelpers.CreateWeaponProcSpell(EskhandarsLeftClaw, "Eskhandar's Left Claw", 1.0, func(character *core.Character) *core.Spell { + return character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 22639}, SpellSchool: core.SpellSchoolPhysical, DefenseType: core.DefenseTypeMelee, - ProcMask: core.ProcMaskMeleeMHSpecial, - - DamageMultiplier: 1, - BonusCoefficient: 1, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - damage := 5.0 + spell.Unit.MHNormalizedWeaponDamage(sim, spell.MeleeAttackPower()) - for _, aoeTarget := range sim.Encounter.TargetUnits { - spell.CalcAndDealDamage(sim, aoeTarget, damage, spell.OutcomeMeleeSpecialHitAndCrit) - } - }, - }) - - character.GetOrRegisterSpell(core.SpellConfig{ - SpellSchool: core.SpellSchoolPhysical, - ActionID: procActionID, - ProcMask: core.ProcMaskMeleeMHSpecial, - Flags: core.SpellFlagChanneled, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagPureDot, Dot: core.DotConfig{ - IsAOE: true, Aura: core.Aura{ - Label: "Ravager Whirlwind", + Label: "Eskhandar's Rake", }, - NumberOfTicks: 3, - TickLength: time.Second * 3, - AffectedByCastSpeed: false, - OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { - ravegerBladestormTickSpell.Cast(sim, target) + TickLength: time.Second * 3, + NumberOfTicks: 10, + + OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) { + dot.Snapshot(target, 15, isRollover) }, - }, - }) - return character.GetOrRegisterAura(core.Aura{ - Label: "Ravager Bladestorm", - ActionID: auraActionID, - Duration: time.Second * 9, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - character.AutoAttacks.CancelAutoSwing(sim) - dotSpell := character.GetSpell(procActionID) - dotSpell.AOEDot().Apply(sim) + OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { + dot.CalcAndDealPeriodicSnapshotDamage(sim, target, dot.OutcomeTickCounted) + }, }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - character.AutoAttacks.EnableAutoSwing(sim) - dotSpell := character.GetSpell(procActionID) - dotSpell.AOEDot().Cancel(sim) + DamageMultiplier: 1, + ThreatMultiplier: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + result := spell.CalcAndDealOutcome(sim, target, spell.OutcomeMeleeSpecialHit) + if result.Landed() { + spell.SpellMetrics[result.Target.UnitIndex].Hits-- + spell.Dot(target).Apply(sim) + } }, }) }) - itemhelpers.CreateWeaponProcDamage(HanzoSword, "Hanzo Sword", 1.0, 16405, core.SpellSchoolPhysical, 75, 0, 0, core.DefenseTypeMelee) - - itemhelpers.CreateWeaponProcAura(TheJackhammer, "The Jackhammer", 1.0, func(character *core.Character) *core.Aura { + // https://www.wowhead.com/classic/item=228350/eskhandars-right-claw + // Chance on hit: Increases your attack speed by 30% for 5 sec. + itemhelpers.CreateWeaponProcAura(EskhandarsRightClaw, "Eskhandar's Right Claw", 1.0, func(character *core.Character) *core.Aura { return character.GetOrRegisterAura(core.Aura{ - Label: "The Jackhammer Haste Aura", - ActionID: core.ActionID{SpellID: 13533}, - Duration: time.Second * 9, + Label: "Eskhandar's Rage", + ActionID: core.ActionID{SpellID: 22640}, + Duration: time.Second * 15, OnGain: func(aura *core.Aura, sim *core.Simulation) { character.MultiplyAttackSpeed(sim, 1.3) }, @@ -275,49 +392,100 @@ func init() { }) }) - itemhelpers.CreateWeaponProcDamage(PendulumOfDoom, "Pendulum of Doom", 0.5, 10373, core.SpellSchoolPhysical, 250, 100, 0, core.DefenseTypeMelee) - - itemhelpers.CreateWeaponProcDamage(BloodletterScalpel, "Bloodletter Scalpel", 1.0, 18081, core.SpellSchoolPhysical, 60, 10, 0, core.DefenseTypeMelee) + // https://www.wowhead.com/classic/item=13218/fang-of-the-crystal-spider + // Chance on hit: Slows target enemy's casting speed and increases the time between melee and ranged attacks by 10% for 10 sec. + // TODO: Proc rate assumed and needs testing + core.NewItemEffect(FangOfTheCrystalSpider, func(agent core.Agent) { + character := agent.GetCharacter() - itemhelpers.CreateWeaponProcSpell(TheHandOfAntusul, "The Hand of Antu'sul", 1.0, func(character *core.Character) *core.Spell { debuffAuras := character.NewEnemyAuraArray(func(unit *core.Unit, _ int32) *core.Aura { aura := unit.GetOrRegisterAura(core.Aura{ - Label: "ThunderClap-Antu'sul", - ActionID: core.ActionID{SpellID: 13532}, + ActionID: core.ActionID{SpellID: 17331}, + Label: "Fang of the Crystal Spider", Duration: time.Second * 10, }) - core.AtkSpeedReductionEffect(aura, 1.11) + core.AtkSpeedReductionEffect(aura, 1.10) return aura }) - results := make([]*core.SpellResult, min(4, character.Env.GetNumTargets())) - - return character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 13532}, - SpellSchool: core.SpellSchoolNature, - DefenseType: core.DefenseTypeMagic, - ProcMask: core.ProcMaskEmpty, - DamageMultiplier: 1, - ThreatMultiplier: 1, - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - for idx := range results { - results[idx] = spell.CalcDamage(sim, target, 7, spell.OutcomeMagicHitAndCrit) - target = character.Env.NextTargetUnit(target) - } - for _, result := range results { - spell.DealDamage(sim, result) - if result.Landed() { - debuffAuras.Get(result.Target).Activate(sim) - } - } + procMask := character.GetProcMaskForItem(FangOfTheCrystalSpider) + core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: "Fang of the Crystal Spider Trigger", + Callback: core.CallbackOnSpellHitDealt, + Outcome: core.OutcomeLanded, + ProcMask: procMask, + PPM: 1, + Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + debuffAuras.Get(result.Target).Activate(sim) + }, + }) + }) + + // https://www.wowhead.com/classic/item=12590/felstriker + // Chance on hit: All attacks are guaranteed to land and will be critical strikes for the next 3 sec. + core.NewItemEffect(Felstriker, func(agent core.Agent) { + character := agent.GetCharacter() + + effectAura := character.NewTemporaryStatsAura("Felstriker", core.ActionID{SpellID: 16551}, stats.Stats{stats.MeleeCrit: 100 * core.CritRatingPerCritChance, stats.MeleeHit: 100 * core.MeleeHitRatingPerHitChance}, time.Second*3) + procMask := character.GetProcMaskForItem(Felstriker) + core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: "Felstriker Trigger", + Callback: core.CallbackOnSpellHitDealt, + Outcome: core.OutcomeLanded, + ProcMask: procMask, + PPM: 1, + Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + effectAura.Activate(sim) + }, + }) + }) + + core.NewItemEffect(FiendishMachete, func(agent core.Agent) { + character := agent.GetCharacter() + + if character.CurrentTarget.MobType == proto.MobType_MobTypeElemental { + character.PseudoStats.MobTypeAttackPower += 36 + } + }) + + itemhelpers.CreateWeaponProcSpell(FieryWarAxe, "Fiery War Axe", 1.0, func(character *core.Character) *core.Spell { + return character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 18796}, + SpellSchool: core.SpellSchoolFire, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + + DamageMultiplier: 1, + ThreatMultiplier: 1, + + Dot: core.DotConfig{ + Aura: core.Aura{ + Label: "Fiery War Axe Fireball", + }, + TickLength: 2 * time.Second, + NumberOfTicks: 3, + + OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) { + dot.Snapshot(target, 8, isRollover) + }, + + 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) { + dmg := sim.Roll(155, 197) + result := spell.CalcAndDealDamage(sim, target, dmg, spell.OutcomeMagicHitAndCrit) + if result.Landed() { + spell.Dot(target).Apply(sim) + } }, }) }) - itemhelpers.CreateWeaponProcDamage(GryphonRidersStormhammer, "Gryphon Rider's Stormhammer", 1.0, 18081, core.SpellSchoolNature, 91, 34, 0, core.DefenseTypeMagic) - itemhelpers.CreateWeaponProcSpell(Firebreather, "Firebreather", 1.0, func(character *core.Character) *core.Spell { - return character.RegisterSpell(core.SpellConfig{ + return character.GetOrRegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: 16413}, SpellSchool: core.SpellSchoolFire, DefenseType: core.DefenseTypeMagic, @@ -343,10 +511,102 @@ func init() { }) }) - itemhelpers.CreateWeaponProcDamage(VilerendSlicer, "Vilerend Slicer", 1.0, 16405, core.SpellSchoolPhysical, 75, 0, 0, core.DefenseTypeMelee) + itemhelpers.CreateWeaponProcSpell(FlurryAxe, "Flurry Axe", 1.0, func(character *core.Character) *core.Spell { + return character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 18797}, + SpellSchool: core.SpellSchoolPhysical, + DefenseType: core.DefenseTypeMelee, + ProcMask: core.ProcMaskEmpty, + DamageMultiplier: 1, + ThreatMultiplier: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + character.AutoAttacks.ExtraMHAttack(sim, 1, core.ActionID{SpellID: 18797}) + }, + }) + }) + + itemhelpers.CreateWeaponProcDamage(GryphonRidersStormhammer, "Gryphon Rider's Stormhammer", 1.0, 18081, core.SpellSchoolNature, 91, 34, 0, core.DefenseTypeMagic) + + // https://www.wowhead.com/classic/item=228267/gutgore-ripper + // Chance on hit: Sends a shadowy bolt at the enemy causing 150 Shadow damage and lowering all stats by 25 for 30 sec. + itemhelpers.CreateWeaponProcSpell(GutgoreRipper, "Gutgore Ripper", 1.0, func(character *core.Character) *core.Spell { + procAuras := character.NewEnemyAuraArray(func(target *core.Unit, _ int32) *core.Aura { + return target.GetOrRegisterAura(core.Aura{ + ActionID: core.ActionID{SpellID: 461682}, + Label: "Gutgore Ripper", + Duration: time.Second * 30, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.AddStatsDynamic(sim, stats.Stats{ + stats.Agility: -25, + stats.Intellect: -25, + stats.Stamina: -25, + stats.Spirit: -25, + stats.Strength: -25, + }) + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.AddStatsDynamic(sim, stats.Stats{ + stats.Agility: 25, + stats.Intellect: 25, + stats.Stamina: 25, + stats.Spirit: 25, + stats.Strength: 25, + }) + }, + }) + }) + + return character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 461682}, + SpellSchool: core.SpellSchoolShadow, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + DamageMultiplier: 1, + ThreatMultiplier: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + result := spell.CalcAndDealDamage(sim, target, 150, spell.OutcomeMagicHitAndCrit) + if result.Landed() { + procAuras.Get(target).Activate(sim) + } + }, + }) + }) + + itemhelpers.CreateWeaponProcSpell(Gutwrencher, "Gutwrencher", 1.0, func(character *core.Character) *core.Spell { + return character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 16406}, + SpellSchool: core.SpellSchoolPhysical, + DefenseType: core.DefenseTypeMelee, + ProcMask: core.ProcMaskEmpty, + DamageMultiplier: 1, + ThreatMultiplier: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + result := spell.CalcAndDealOutcome(sim, target, spell.OutcomeMeleeSpecialHit) + if result.Landed() { + spell.Dot(target).Apply(sim) + } + }, + Dot: core.DotConfig{ + NumberOfTicks: 10, + TickLength: time.Second * 3, + Aura: core.Aura{ + Label: "Rend", + }, + OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { + dot.Spell.CalcAndDealPeriodicDamage(sim, target, 8, dot.OutcomeTick) + }, + }, + }) + }) + + itemhelpers.CreateWeaponProcDamage(GutRipper, "Gut Ripper", 1.0, 18107, core.SpellSchoolPhysical, 95, 26, 0, core.DefenseTypeMelee) + + itemhelpers.CreateWeaponProcDamage(HammerOfTheNorthernWind, "Hammer of the Northern Wind", 3.5, 13439, core.SpellSchoolFrost, 20, 10, 0, core.DefenseTypeMagic) + + itemhelpers.CreateWeaponProcDamage(HanzoSword, "Hanzo Sword", 1.0, 16405, core.SpellSchoolPhysical, 75, 0, 0, core.DefenseTypeMelee) itemhelpers.CreateWeaponProcSpell(HookfangShanker, "Hookfang Shanker", 1.0, func(character *core.Character) *core.Spell { - return character.RegisterSpell(core.SpellConfig{ + return character.GetOrRegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: 13526}, SpellSchool: core.SpellSchoolNature, DefenseType: core.DefenseTypeMagic, @@ -379,10 +639,27 @@ func init() { }) }) + itemhelpers.CreateWeaponProcSpell(Ironfoe, "Ironfoe", 1.0, func(character *core.Character) *core.Spell { + return character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 15494}, + SpellSchool: core.SpellSchoolPhysical, + DefenseType: core.DefenseTypeMelee, + ProcMask: core.ProcMaskEmpty, + DamageMultiplier: 1, + ThreatMultiplier: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + character.AutoAttacks.ExtraMHAttack(sim, 2, core.ActionID{SpellID: 15494}) + }, + }) + }) + + itemhelpers.CreateWeaponProcDamage(JoonhosMercy, "Joonho's Mercy", 1.0, 20883, core.SpellSchoolArcane, 70, 0, 0, core.DefenseTypeMagic) + itemhelpers.CreateWeaponProcDamage(LinkensSwordOfMastery, "Linken's Sword of Mastery", 1.0, 18089, core.SpellSchoolNature, 45, 30, 0, core.DefenseTypeMagic) - // TODO Searing Needle adds an "Apply Aura: Mod Damage Done (Fire): 10" aura to the /target/, buffing it; not currently modelled - itemhelpers.CreateWeaponProcDamage(SearingNeedle, "Searing Needle", 1.0, 16454, core.SpellSchoolFire, 60, 0, 0, core.DefenseTypeMagic) + itemhelpers.CreateWeaponProcDamage(Nightblade, "Nightblade", 1.0, 18211, core.SpellSchoolShadow, 125, 150, 0, core.DefenseTypeMagic) + + itemhelpers.CreateWeaponProcDamage(PendulumOfDoom, "Pendulum of Doom", 0.5, 10373, core.SpellSchoolPhysical, 250, 100, 0, core.DefenseTypeMelee) core.NewItemEffect(PipsSkinner, func(agent core.Agent) { character := agent.GetCharacter() @@ -392,74 +669,534 @@ func init() { } }) - itemhelpers.CreateWeaponProcSpell(SerpentSlicer, "Serpent Slicer", 1.0, func(character *core.Character) *core.Spell { - return character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 17511}, - SpellSchool: core.SpellSchoolNature, - DefenseType: core.DefenseTypeMagic, - ProcMask: core.ProcMaskEmpty, - Flags: core.SpellFlagPoison, + // https://www.wowhead.com/classic/item=228296/perditions-blade + // Chance on hit: Blasts a target for 98 to 122 Fire damage. + itemhelpers.CreateWeaponProcDamage(PerditionsBlade, "Perdition's Blade", 2.8, 461695, core.SpellSchoolFire, 98, 24, 0, core.DefenseTypeMagic) + itemhelpers.CreateWeaponProcDamage(PerditionsBladeMolten, "Perdition's Blade", 2.8, 461695, core.SpellSchoolFire, 98, 24, 0, core.DefenseTypeMagic) + + itemhelpers.CreateWeaponProcAura(Ravager, "Ravager", 1.0, func(character *core.Character) *core.Aura { + tickActionID := core.ActionID{SpellID: 9633} + procActionID := core.ActionID{SpellID: 9632} + auraActionID := core.ActionID{SpellID: 433801} + + ravegerBladestormTickSpell := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: tickActionID, + SpellSchool: core.SpellSchoolPhysical, + DefenseType: core.DefenseTypeMelee, + ProcMask: core.ProcMaskMeleeMHSpecial, + DamageMultiplier: 1, - ThreatMultiplier: 1, + BonusCoefficient: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - result := spell.CalcAndDealOutcome(sim, target, spell.OutcomeMagicHit) - if result.Landed() { - spell.Dot(target).Apply(sim) + damage := 5.0 + spell.Unit.MHNormalizedWeaponDamage(sim, spell.MeleeAttackPower()) + for _, aoeTarget := range sim.Encounter.TargetUnits { + spell.CalcAndDealDamage(sim, aoeTarget, damage, spell.OutcomeMeleeSpecialHitAndCrit) } }, + }) + + character.GetOrRegisterSpell(core.SpellConfig{ + SpellSchool: core.SpellSchoolPhysical, + ActionID: procActionID, + ProcMask: core.ProcMaskMeleeMHSpecial, + Flags: core.SpellFlagChanneled, Dot: core.DotConfig{ - NumberOfTicks: 10, - TickLength: time.Second * 2, + IsAOE: true, Aura: core.Aura{ - Label: "Poison", + Label: "Ravager Whirlwind", }, + NumberOfTicks: 3, + TickLength: time.Second * 3, + AffectedByCastSpeed: false, OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { - dot.Spell.CalcAndDealPeriodicDamage(sim, target, 8, dot.OutcomeTick) + ravegerBladestormTickSpell.Cast(sim, target) }, }, }) + + return character.GetOrRegisterAura(core.Aura{ + Label: "Ravager Bladestorm", + ActionID: auraActionID, + Duration: time.Second * 9, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + character.AutoAttacks.CancelAutoSwing(sim) + dotSpell := character.GetSpell(procActionID) + dotSpell.AOEDot().Apply(sim) + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + character.AutoAttacks.EnableAutoSwing(sim) + dotSpell := character.GetSpell(procActionID) + dotSpell.AOEDot().Cancel(sim) + }, + }) }) - itemhelpers.CreateWeaponProcDamage(JoonhosMercy, "Joonho's Mercy", 1.0, 20883, core.SpellSchoolArcane, 70, 0, 0, core.DefenseTypeMagic) + // https://www.wowhead.com/classic/item=227843/reaving-nightfall + // Chance on hit: Spell damage taken by target increased by 15% for 5 sec. + core.NewItemEffect(ReavingNightfall, func(agent core.Agent) { + character := agent.GetCharacter() - itemhelpers.CreateWeaponProcSpell(ThrashBlade, "Thrash Blade", 1.0, func(character *core.Character) *core.Spell { - return character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 21919}, - SpellSchool: core.SpellSchoolPhysical, - DefenseType: core.DefenseTypeMelee, - ProcMask: core.ProcMaskEmpty, - DamageMultiplier: 1, - ThreatMultiplier: 1, - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - character.AutoAttacks.ExtraMHAttack(sim, 1, core.ActionID{SpellID: 21919}) + procAuras := character.NewEnemyAuraArray(func(target *core.Unit, _ int32) *core.Aura { + return target.GetOrRegisterAura(core.Aura{ + Label: "Spell Vulnerability (Nightfall)", + ActionID: core.ActionID{SpellID: 23605}, + Duration: time.Second * 5, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexArcane] *= 1.15 + aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexFire] *= 1.15 + aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexFrost] *= 1.15 + aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexHoly] *= 1.15 + aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexNature] *= 1.15 + aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexShadow] *= 1.15 + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexArcane] /= 1.15 + aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexFire] /= 1.15 + aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexFrost] /= 1.15 + aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexHoly] /= 1.15 + aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexNature] /= 1.15 + aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexShadow] /= 1.15 + }, + }) + }) + + core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: "Nightfall Trigger", + Callback: core.CallbackOnSpellHitDealt, + Outcome: core.OutcomeLanded, + ProcMask: core.ProcMaskMelee, + PPM: 2, + Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + procAuras.Get(result.Target).Activate(sim) + }, + }) + }) + + itemhelpers.CreateWeaponProcDamage(SatyrsLash, "Satyr's Lash", 1.0, 18205, core.SpellSchoolShadow, 55, 30, 0, core.DefenseTypeMagic) + + // TODO Searing Needle adds an "Apply Aura: Mod Damage Done (Fire): 10" aura to the /target/, buffing it; not currently modelled + itemhelpers.CreateWeaponProcDamage(SearingNeedle, "Searing Needle", 1.0, 16454, core.SpellSchoolFire, 60, 0, 0, core.DefenseTypeMagic) + + // https://www.wowhead.com/classic/item=228666/seeping-willow + // Chance on hit: Lowers all stats by 20 and deals 20 Nature damage every 3 sec to all enemies within an 8 yard radius of the caster for 30 sec. + // TODO: Proc rate assumed and needs testing + itemhelpers.CreateWeaponProcSpell(SeepingWillow, "Seeping Willow", 0.5, func(character *core.Character) *core.Spell { + stats := stats.Stats{ + stats.Agility: 20, + stats.Intellect: 20, + stats.Stamina: 20, + stats.Spirit: 20, + stats.Strength: 20, + } + debuffAuras := character.NewEnemyAuraArray(func(unit *core.Unit, _ int32) *core.Aura { + return unit.GetOrRegisterAura(core.Aura{ + ActionID: core.ActionID{SpellID: 17196}, + Label: "Seeping Willow", + Duration: time.Second * 30, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + unit.AddStatsDynamic(sim, stats.Multiply(-1)) + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + unit.AddStatsDynamic(sim, stats) + }, + }) + }) + + return character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 17196}, + SpellSchool: core.SpellSchoolNature, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagPoison | core.SpellFlagPureDot, + Dot: core.DotConfig{ + Aura: core.Aura{ + Label: "Seeping Willow Poison", + }, + NumberOfTicks: 10, + TickLength: time.Second * 3, + OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) { + dot.Snapshot(target, 20, isRollover) + debuffAuras.Get(target).Activate(sim) + }, + OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { + dot.CalcAndDealPeriodicSnapshotDamage(sim, target, dot.OutcomeTickCounted) + }, + }, + DamageMultiplier: 1, + ThreatMultiplier: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + for _, aoeTarget := range sim.Encounter.TargetUnits { + result := spell.CalcAndDealOutcome(sim, aoeTarget, spell.OutcomeMagicHit) + if result.Landed() { + spell.SpellMetrics[result.Target.UnitIndex].Hits-- + spell.Dot(aoeTarget).Apply(sim) + } + } + }, + }) + }) + + itemhelpers.CreateWeaponProcSpell(SerpentSlicer, "Serpent Slicer", 1.0, func(character *core.Character) *core.Spell { + return character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 17511}, + SpellSchool: core.SpellSchoolNature, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagPoison, + DamageMultiplier: 1, + ThreatMultiplier: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + result := spell.CalcAndDealOutcome(sim, target, spell.OutcomeMagicHit) + if result.Landed() { + spell.Dot(target).Apply(sim) + } + }, + Dot: core.DotConfig{ + NumberOfTicks: 10, + TickLength: time.Second * 2, + Aura: core.Aura{ + Label: "Poison", + }, + OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { + dot.Spell.CalcAndDealPeriodicDamage(sim, target, 8, dot.OutcomeTick) + }, + }, + }) + }) + + itemhelpers.CreateWeaponProcDamage(Shadowblade, "Shadowblade", 1.0, 18138, core.SpellSchoolShadow, 110, 30, 0, core.DefenseTypeMagic) + + itemhelpers.CreateWeaponProcDamage(ShortswordOfVengeance, "Shortsword of Vengeance", 1.0, 13519, core.SpellSchoolHoly, 30, 0, 0, core.DefenseTypeMagic) + + // https://www.wowhead.com/classic/item=227683/sulfuras-hand-of-ragnaros + // Chance on hit: Hurls a fiery ball that causes 273 to 333 Fire damage and purges the target's soul, increasing Fire and Holy damage taken by up to 30 and dealing an additional 75 damage over 10 sec. + // Equip: 20% chance to deal 25 Fire damage to all nearby enemies when you are struck by a melee attack. (Proc chance: 20%) + core.NewItemEffect(SulfurasHandOfRagnaros, func(agent core.Agent) { + character := agent.GetCharacter() + + immolationSpell := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 460335}, + SpellSchool: core.SpellSchoolFire, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + + BonusCoefficient: .025, + DamageMultiplier: 1, + ThreatMultiplier: 1, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + spell.CalcAndDealDamage(sim, target, 25, spell.OutcomeAlwaysHit) + }, + }) + + core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: "Immolation (Hand of Ragnaros)", + Callback: core.CallbackOnSpellHitTaken, + Outcome: core.OutcomeLanded, + ProcMask: core.ProcMaskMelee, + ProcChance: .20, + Handler: func(sim *core.Simulation, _ *core.Spell, _ *core.SpellResult) { + for _, aoeTarget := range sim.Encounter.TargetUnits { + immolationSpell.Cast(sim, aoeTarget) + } + }, + }) + + debuffAuras := character.NewEnemyAuraArray(func(unit *core.Unit, _ int32) *core.Aura { + return unit.GetOrRegisterAura(core.Aura{ + ActionID: core.ActionID{SpellID: 460338}, + Label: "Purged by Fire", + Duration: time.Second * 10, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexFire] += 30 + unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexHoly] += 30 + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexFire] -= 30 + unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexHoly] -= 30 + }, + }) + }) + + purgedByFireSpell := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 460338}, + SpellSchool: core.SpellSchoolFire, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + + DamageMultiplier: 1, + ThreatMultiplier: 1, + + Dot: core.DotConfig{ + Aura: core.Aura{ + Label: "Purged By Fire", + }, + TickLength: 2 * time.Second, + NumberOfTicks: 5, + + OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) { + dot.Snapshot(target, 15, isRollover) + debuffAuras.Get(target).Activate(sim) + }, + + 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) { + result := spell.CalcAndDealDamage(sim, target, sim.Roll(273, 333), spell.OutcomeMagicHitAndCrit) + if result.Landed() { + spell.Dot(target).Apply(sim) + } + }, + }) + + core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: "Purged by Fire Trigger", + Callback: core.CallbackOnSpellHitDealt, + Outcome: core.OutcomeLanded, + ProcMask: core.ProcMaskMelee, + PPM: 1, // Estimated based on data from WoW Armaments Discord + Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + purgedByFireSpell.Cast(sim, result.Target) + }, + }) + }) + + // https://www.wowhead.com/classic/item=17182/sulfuras-hand-of-ragnaros + // Chance on hit: Hurls a fiery ball that causes 273 to 333 Fire damage and an additional 75 damage over 10 sec. + // Equip: Deals 5 Fire damage to anyone who strikes you with a melee attack. + // core.NewItemEffect(SulfurasHandOfRagnaros, func(agent core.Agent) { + // character := agent.GetCharacter() + + // immolationActionID := core.ActionID{SpellID: 21142} + + // immolationSpell := character.GetOrRegisterSpell(core.SpellConfig{ + // ActionID: immolationActionID, + // SpellSchool: core.SpellSchoolFire, + // ProcMask: core.ProcMaskEmpty, + + // DamageMultiplier: 1, + // ThreatMultiplier: 1, + + // ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + // spell.CalcAndDealDamage(sim, target, 5, spell.OutcomeMagicHit) + // }, + // }) + + // character.GetOrRegisterAura(core.Aura{ + // ActionID: immolationActionID, + // Label: "Immolation (Hand of Ragnaros)", + // Duration: core.NeverExpires, + // OnReset: func(aura *core.Aura, sim *core.Simulation) { + // aura.Activate(sim) + // }, + // OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + // if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { + // immolationSpell.Cast(sim, spell.Unit) + // } + // }, + // }) + + // fireballSpell := character.GetOrRegisterSpell(core.SpellConfig{ + // ActionID: core.ActionID{SpellID: 21162}, + // SpellSchool: core.SpellSchoolFire, + // DefenseType: core.DefenseTypeMagic, + // ProcMask: core.ProcMaskEmpty, + + // DamageMultiplier: 1, + // ThreatMultiplier: 1, + + // Dot: core.DotConfig{ + // Aura: core.Aura{ + // Label: "Fireball (Hand of Ragnaros)", + // }, + // TickLength: 2 * time.Second, + // NumberOfTicks: 5, + + // OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) { + // dot.Snapshot(target, 15, isRollover) + // }, + + // 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) { + // result := spell.CalcAndDealDamage(sim, target, sim.Roll(273, 333), spell.OutcomeMagicHitAndCrit) + // if result.Landed() { + // spell.Dot(target).Apply(sim) + // } + // }, + // }) + + // core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + // Name: "Hand of Ragnaros Trigger", + // Callback: core.CallbackOnSpellHitDealt, + // Outcome: core.OutcomeLanded, + // ProcMask: core.ProcMaskMelee, + // PPM: 1, // Estimated based on data from WoW Armaments Discord + // Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + // fireballSpell.Cast(sim, result.Target) + // }, + // }) + // }) + + // https://www.wowhead.com/classic/item=227684/sulfuron-hammer + // Chance on hit: Hurls a fiery ball that causes 83 to 101 Fire damage and an additional 16 damage over 8 sec. + // Equip: Deals 5 Fire damage to anyone who strikes you with a melee attack. + core.NewItemEffect(SulfuronHammer, func(agent core.Agent) { + character := agent.GetCharacter() + + immolationSpell := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 21142}, + SpellSchool: core.SpellSchoolFire, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + + DamageMultiplier: 1, + ThreatMultiplier: 1, + + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + spell.CalcAndDealDamage(sim, target, 5, spell.OutcomeAlwaysHit) + }, + }) + + core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: "Immolation (Hand of Ragnaros)", + Callback: core.CallbackOnSpellHitTaken, + Outcome: core.OutcomeLanded, + ProcMask: core.ProcMaskMelee, + Handler: func(sim *core.Simulation, spell *core.Spell, _ *core.SpellResult) { + immolationSpell.Cast(sim, spell.Unit) + }, + }) + + fireballSpell := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 21159}, + SpellSchool: core.SpellSchoolFire, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + + DamageMultiplier: 1, + ThreatMultiplier: 1, + + Dot: core.DotConfig{ + Aura: core.Aura{ + Label: "Fireball (Sulfuron Hammer)", + }, + TickLength: 2 * time.Second, + NumberOfTicks: 4, + + OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) { + dot.Snapshot(target, 4, isRollover) + }, + + 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) { + result := spell.CalcAndDealDamage(sim, target, sim.Roll(83, 101), spell.OutcomeMagicHitAndCrit) + if result.Landed() { + spell.Dot(target).Apply(sim) + } + }, + }) + + core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: "Sulfuron Hammer Trigger", + Callback: core.CallbackOnSpellHitDealt, + Outcome: core.OutcomeLanded, + ProcMask: core.ProcMaskMelee, + PPM: 1, // TODO: Armaments Discord didn't hvae any data on Sulfuron Hammer + Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + fireballSpell.Cast(sim, result.Target) + }, + }) + }) + + // https://www.wowhead.com/classic/item=227832/tempered-black-amnesty + // Chance on hit: Reduce your threat to the current target making them less likely to attack you. + // TODO: Proc rate untested, no way to reduce threat right now + // itemhelpers.CreateWeaponProcSpell(TemperedBlackAmnesty, "Tempered Black Amnesty", 1.0, func(character *core.Character) *core.Spell { + // return character.GetOrRegisterSpell(core.SpellConfig{ + // ActionID: core.ActionID{SpellID: 23604}, + // SpellSchool: core.SpellSchoolPhysical, + // ProcMask: core.ProcMaskEmpty, + // ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + // character.threat + // }, + // }) + // }) + + itemhelpers.CreateWeaponProcSpell(TheHandOfAntusul, "The Hand of Antu'sul", 1.0, func(character *core.Character) *core.Spell { + debuffAuras := character.NewEnemyAuraArray(func(unit *core.Unit, _ int32) *core.Aura { + aura := unit.GetOrRegisterAura(core.Aura{ + Label: "ThunderClap-Antu'sul", + ActionID: core.ActionID{SpellID: 13532}, + Duration: time.Second * 10, + }) + core.AtkSpeedReductionEffect(aura, 1.11) + return aura + }) + + results := make([]*core.SpellResult, min(4, character.Env.GetNumTargets())) + + return character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 13532}, + SpellSchool: core.SpellSchoolNature, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + DamageMultiplier: 1, + ThreatMultiplier: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + for idx := range results { + results[idx] = spell.CalcDamage(sim, target, 7, spell.OutcomeMagicHitAndCrit) + target = character.Env.NextTargetUnit(target) + } + for _, result := range results { + spell.DealDamage(sim, result) + if result.Landed() { + debuffAuras.Get(result.Target).Activate(sim) + } + } + }, + }) + }) + + itemhelpers.CreateWeaponProcAura(TheJackhammer, "The Jackhammer", 1.0, func(character *core.Character) *core.Aura { + return character.GetOrRegisterAura(core.Aura{ + Label: "The Jackhammer Haste Aura", + ActionID: core.ActionID{SpellID: 13533}, + Duration: time.Second * 9, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + character.MultiplyAttackSpeed(sim, 1.3) + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + character.MultiplyAttackSpeed(sim, 1/1.3) }, }) }) - itemhelpers.CreateWeaponProcSpell(Ironfoe, "Ironfoe", 1.0, func(character *core.Character) *core.Spell { - return character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{SpellID: 15494}, + itemhelpers.CreateWeaponProcSpell(ThrashBlade, "Thrash Blade", 1.0, func(character *core.Character) *core.Spell { + return character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 21919}, SpellSchool: core.SpellSchoolPhysical, DefenseType: core.DefenseTypeMelee, ProcMask: core.ProcMaskEmpty, DamageMultiplier: 1, ThreatMultiplier: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - character.AutoAttacks.ExtraMHAttack(sim, 2, core.ActionID{SpellID: 15494}) + character.AutoAttacks.ExtraMHAttack(sim, 1, core.ActionID{SpellID: 21919}) }, }) }) - itemhelpers.CreateWeaponProcDamage(SatyrsLash, "Satyr's Lash", 1.0, 18205, core.SpellSchoolShadow, 55, 30, 0, core.DefenseTypeMagic) - - core.NewItemEffect(FiendishMachete, func(agent core.Agent) { - character := agent.GetCharacter() - - if character.CurrentTarget.MobType == proto.MobType_MobTypeElemental { - character.PseudoStats.MobTypeAttackPower += 36 - } - }) - core.NewItemEffect(Thunderfury, func(agent core.Agent) { character := agent.GetCharacter() @@ -468,7 +1205,7 @@ func init() { procActionID := core.ActionID{SpellID: 21992} - singleTargetSpell := character.RegisterSpell(core.SpellConfig{ + singleTargetSpell := character.GetOrRegisterSpell(core.SpellConfig{ ActionID: procActionID.WithTag(1), SpellSchool: core.SpellSchoolNature, DefenseType: core.DefenseTypeMagic, @@ -498,7 +1235,7 @@ func init() { results := make([]*core.SpellResult, min(5, character.Env.GetNumTargets())) - bounceSpell := character.RegisterSpell(core.SpellConfig{ + bounceSpell := character.GetOrRegisterSpell(core.SpellConfig{ ActionID: procActionID.WithTag(2), SpellSchool: core.SpellSchoolNature, ProcMask: core.ProcMaskEmpty, @@ -520,7 +1257,7 @@ func init() { }, }) - character.RegisterAura(core.Aura{ + character.GetOrRegisterAura(core.Aura{ Label: "Thunderfury", Duration: core.NeverExpires, OnReset: func(aura *core.Aura, sim *core.Simulation) { @@ -539,253 +1276,93 @@ func init() { }) }) - // Chance on hit: Spell damage taken by target increased by 15% for 5 sec. - nightfallEffect := func(agent core.Agent) { - character := agent.GetCharacter() - - procAuras := character.NewEnemyAuraArray(func(target *core.Unit, _ int32) *core.Aura { - return target.GetOrRegisterAura(core.Aura{ - Label: "Spell Vulnerability (Nightfall)", - ActionID: core.ActionID{SpellID: 23605}, - Duration: time.Second * 5, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexArcane] *= 1.15 - aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexFire] *= 1.15 - aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexFrost] *= 1.15 - aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexHoly] *= 1.15 - aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexNature] *= 1.15 - aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexShadow] *= 1.15 - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexArcane] /= 1.15 - aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexFire] /= 1.15 - aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexFrost] /= 1.15 - aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexHoly] /= 1.15 - aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexNature] /= 1.15 - aura.Unit.PseudoStats.SchoolBonusDamageTaken[stats.SchoolIndexShadow] /= 1.15 + // https://www.wowhead.com/classic/item=13183/venomspitter + // Chance on hit: Poisons target for 7 Nature damage every 2 sec for 30 sec. + // TODO: Proc rate assumed and needs testing + itemhelpers.CreateWeaponProcSpell(Venomspitter, "Venomspitter", 1.0, func(character *core.Character) *core.Spell { + return character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 18203}, + SpellSchool: core.SpellSchoolNature, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagPoison | core.SpellFlagPureDot, + Dot: core.DotConfig{ + Aura: core.Aura{ + Label: "Poison (Venomspitter)", }, - }) - }) - - core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ - Name: "Nightfall Trigger", - Callback: core.CallbackOnSpellHitDealt, - Outcome: core.OutcomeLanded, - ProcMask: core.ProcMaskMelee, - PPM: 2, - Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - procAuras.Get(result.Target).Activate(sim) - }, - }) - } - // https://www.wowhead.com/classic/item=19169/nightfall - core.NewItemEffect(Nightfall, nightfallEffect) - // https://www.wowhead.com/classic/item=227843/reaving-nightfall - core.NewItemEffect(ReavingNightfall, nightfallEffect) - - // https://www.wowhead.com/classic/item=12590/felstriker - // Chance on hit: All attacks are guaranteed to land and will be critical strikes for the next 3 sec. - core.NewItemEffect(Felstriker, func(agent core.Agent) { - character := agent.GetCharacter() + TickLength: time.Second * 2, + NumberOfTicks: 15, - effectAura := character.NewTemporaryStatsAura("Felstriker", core.ActionID{SpellID: 16551}, stats.Stats{stats.MeleeCrit: 100 * core.CritRatingPerCritChance, stats.MeleeHit: 100 * core.MeleeHitRatingPerHitChance}, time.Second*3) - procMask := character.GetProcMaskForItem(Felstriker) - core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ - Name: "Felstriker Trigger", - Callback: core.CallbackOnSpellHitDealt, - Outcome: core.OutcomeLanded, - ProcMask: procMask, - PPM: 1, - Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - effectAura.Activate(sim) - }, - }) - }) + OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) { + dot.Snapshot(target, 7, isRollover) + }, - // https://www.wowhead.com/classic/item=228397/empyrean-demolisher - // Chance on hit: Increases your attack speed by 20% for 10 sec. - itemhelpers.CreateWeaponProcAura(EmpyreanDemolisher, "Empyrean Demolisher", 1.0, func(character *core.Character) *core.Aura { - return character.GetOrRegisterAura(core.Aura{ - Label: "Empyrean Demolisher Haste Aura", - ActionID: core.ActionID{SpellID: 21165}, - Duration: time.Second * 10, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - character.MultiplyAttackSpeed(sim, 1.2) + OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { + dot.CalcAndDealPeriodicSnapshotDamage(sim, target, dot.OutcomeTickCounted) + }, }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - character.MultiplyAttackSpeed(sim, 1/1.2) + DamageMultiplier: 1, + ThreatMultiplier: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + result := spell.CalcAndDealOutcome(sim, target, spell.OutcomeMagicHit) + if result.Landed() { + spell.SpellMetrics[result.Target.UnitIndex].Hits-- + spell.Dot(target).Apply(sim) + } }, }) }) - // https://www.wowhead.com/classic/item=17182/sulfuras-hand-of-ragnaros - // Chance on hit: Hurls a fiery ball that causes 273 to 333 Fire damage and an additional 75 damage over 10 sec. - // Equip: Deals 5 Fire damage to anyone who strikes you with a melee attack. - // core.NewItemEffect(SulfurasHandOfRagnaros, func(agent core.Agent) { - // character := agent.GetCharacter() - - // immolationActionID := core.ActionID{SpellID: 21142} - - // immolationSpell := character.RegisterSpell(core.SpellConfig{ - // ActionID: immolationActionID, - // SpellSchool: core.SpellSchoolFire, - // ProcMask: core.ProcMaskEmpty, - - // DamageMultiplier: 1, - // ThreatMultiplier: 1, - - // ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - // spell.CalcAndDealDamage(sim, target, 5, spell.OutcomeMagicHit) - // }, - // }) - - // character.RegisterAura(core.Aura{ - // ActionID: immolationActionID, - // Label: "Immolation (Hand of Ragnaros)", - // Duration: core.NeverExpires, - // OnReset: func(aura *core.Aura, sim *core.Simulation) { - // aura.Activate(sim) - // }, - // OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - // if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) { - // immolationSpell.Cast(sim, spell.Unit) - // } - // }, - // }) - - // fireballSpell := character.RegisterSpell(core.SpellConfig{ - // ActionID: core.ActionID{SpellID: 21162}, - // SpellSchool: core.SpellSchoolFire, - // DefenseType: core.DefenseTypeMagic, - // ProcMask: core.ProcMaskEmpty, - - // DamageMultiplier: 1, - // ThreatMultiplier: 1, - - // Dot: core.DotConfig{ - // Aura: core.Aura{ - // Label: "Fireball (Hand of Ragnaros)", - // }, - // TickLength: 2 * time.Second, - // NumberOfTicks: 5, - - // OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) { - // dot.Snapshot(target, 15, isRollover) - // }, - - // 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) { - // result := spell.CalcAndDealDamage(sim, target, sim.Roll(273, 333), spell.OutcomeMagicHitAndCrit) - // if result.Landed() { - // spell.Dot(target).Apply(sim) - // } - // }, - // }) - - // core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ - // Name: "Hand of Ragnaros Trigger", - // Callback: core.CallbackOnSpellHitDealt, - // Outcome: core.OutcomeLanded, - // ProcMask: core.ProcMaskMelee, - // PPM: 1, // Estimated based on data from WoW Armaments Discord - // Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - // fireballSpell.Cast(sim, result.Target) - // }, - // }) - // }) + itemhelpers.CreateWeaponProcDamage(VilerendSlicer, "Vilerend Slicer", 1.0, 16405, core.SpellSchoolPhysical, 75, 0, 0, core.DefenseTypeMelee) /////////////////////////////////////////////////////////////////////////// // Trinkets /////////////////////////////////////////////////////////////////////////// - // https://www.wowhead.com/classic/item=13209/seal-of-the-dawn - // Equip: +81 Attack Power when fighting Undead. - core.NewItemEffect(SealOfTheDawn, func(agent core.Agent) { - character := agent.GetCharacter() - - if character.CurrentTarget.MobType == proto.MobType_MobTypeUndead { - character.AddStat(stats.AttackPower, 81) - character.AddStat(stats.AttackPower, 81) - } - }) - - // https://www.wowhead.com/classic/item=19812/rune-of-the-dawn - // Equip: Increases damage done to Undead by magical spells and effects by up to 48. - core.NewItemEffect(RuneOfTheDawn, func(agent core.Agent) { - character := agent.GetCharacter() - - if character.CurrentTarget.MobType == proto.MobType_MobTypeUndead { - character.AddStat(stats.SpellDamage, 48) - } - }) - - core.NewItemEffect(HandOfJustice, func(agent core.Agent) { + // https://www.wowhead.com/classic/item=227972/burst-of-knowledge + // Use: Reduces mana cost of all spells by 100 for 10 sec. (5 Min Cooldown) + core.NewItemEffect(BurstOfKnowledge, func(agent core.Agent) { character := agent.GetCharacter() - if !character.AutoAttacks.AutoSwingMelee { - return - } - - icd := core.Cooldown{ - Timer: character.NewTimer(), - Duration: time.Second * 2, - } - character.RegisterAura(core.Aura{ - Label: "Hand of Justice", - Duration: core.NeverExpires, - OnReset: func(aura *core.Aura, sim *core.Simulation) { - aura.Activate(sim) + aura := character.GetOrRegisterAura(core.Aura{ + ActionID: core.ActionID{ItemID: BurstOfKnowledge}, + Label: "Burst of Knowledge", + Duration: time.Second * 10, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + character.PseudoStats.CostMultiplier -= 1 }, - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !result.Landed() || !spell.ProcMask.Matches(core.ProcMaskMelee) { - return - } - - if !icd.IsReady(sim) { - return - } - - if sim.RandomFloat("HandOfJustice") < 0.02 { - icd.Use(sim) - aura.Unit.AutoAttacks.ExtraMHAttack(sim, 1, core.ActionID{SpellID: 15600}) - } + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + character.PseudoStats.CostMultiplier += 1 }, }) - }) - // https://www.wowhead.com/classic/item=19287/darkmoon-card-heroism - // Equip: Sometimes heals bearer of 120 to 180 damage when damaging an enemy in melee. - core.NewItemEffect(DarkmoonCardHeroism, func(agent core.Agent) { - character := agent.GetCharacter() + spell := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{ItemID: BurstOfKnowledge}, + ProcMask: core.ProcMaskEmpty, + Flags: core.SpellFlagNoOnCastComplete, - actionID := core.ActionID{SpellID: 23689} - healthMetrics := character.NewHealthMetrics(actionID) + Cast: core.CastConfig{ + CD: core.Cooldown{ + Timer: character.NewTimer(), + Duration: time.Minute * 5, + }, + }, - procSpell := character.RegisterSpell(core.SpellConfig{ - ActionID: actionID, - SpellSchool: core.SpellSchoolHoly, - ProcMask: core.ProcMaskEmpty, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - character.GainHealth(sim, sim.Roll(120, 180), healthMetrics) + aura.Activate(sim) }, }) - core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ - Name: "Heroism Trigger", - Callback: core.CallbackOnSpellHitDealt, - Outcome: core.OutcomeLanded, - ProcMask: core.ProcMaskMelee, - PPM: 2, - Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - procSpell.Cast(sim, spell.Unit) - }, + character.AddMajorCooldown(core.MajorCooldown{ + Type: core.CooldownTypeMana, + Spell: spell, }) }) + // https://www.wowhead.com/classic/item=228678/draconic-infused-emblem + // Use: Increases your spell damage by up to 100 and your healing by up to 190 for 15 sec. (1 Min, 30 Sec Cooldown) + core.NewSimpleStatOffensiveTrinketEffect(DraconicInfusedEmblem, stats.Stats{stats.SpellDamage: 100, stats.HealingPower: 190}, time.Second*15, time.Second*90) + // https://www.wowhead.com/classic/item=19288/darkmoon-card-blue-dragon // Equip: 2% chance on successful spellcast to allow 100% of your Mana regeneration to continue while casting for 15 sec. (Proc chance: 2%) core.NewItemEffect(DarkmoonCardBlueDragon, func(agent core.Agent) { @@ -793,7 +1370,7 @@ func init() { actionID := core.ActionID{SpellID: 23684} - procAura := character.RegisterAura(core.Aura{ + procAura := character.GetOrRegisterAura(core.Aura{ Label: "Aura of the Blue Dragon", ActionID: actionID, Duration: time.Second * 15, @@ -816,6 +1393,35 @@ func init() { }) }) + // https://www.wowhead.com/classic/item=19287/darkmoon-card-heroism + // Equip: Sometimes heals bearer of 120 to 180 damage when damaging an enemy in melee. + core.NewItemEffect(DarkmoonCardHeroism, func(agent core.Agent) { + character := agent.GetCharacter() + + actionID := core.ActionID{SpellID: 23689} + healthMetrics := character.NewHealthMetrics(actionID) + + procSpell := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: actionID, + SpellSchool: core.SpellSchoolHoly, + ProcMask: core.ProcMaskEmpty, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + character.GainHealth(sim, sim.Roll(120, 180), healthMetrics) + }, + }) + + core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: "Heroism Trigger", + Callback: core.CallbackOnSpellHitDealt, + Outcome: core.OutcomeLanded, + ProcMask: core.ProcMaskMelee, + PPM: 2, + Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + procSpell.Cast(sim, spell.Unit) + }, + }) + }) + // https://www.wowhead.com/classic/item=19289/darkmoon-card-maelstrom // Equip: Chance to strike your melee target with lightning for 200 to 300 Nature damage. core.NewItemEffect(DarkmoonCardMaelstrom, func(agent core.Agent) { @@ -823,7 +1429,7 @@ func init() { actionID := core.ActionID{SpellID: 23687} - procSpell := character.RegisterSpell(core.SpellConfig{ + procSpell := character.GetOrRegisterSpell(core.SpellConfig{ ActionID: actionID, SpellSchool: core.SpellSchoolNature, DefenseType: core.DefenseTypeMagic, @@ -849,56 +1455,12 @@ func init() { }) }) - // https://www.wowhead.com/classic/item=227972/burst-of-knowledge - // Use: Reduces mana cost of all spells by 100 for 10 sec. (5 Min Cooldown) - core.NewItemEffect(BurstOfKnowledge, func(agent core.Agent) { - character := agent.GetCharacter() - - aura := character.RegisterAura(core.Aura{ - ActionID: core.ActionID{ItemID: BurstOfKnowledge}, - Label: "Burst of Knowledge", - Duration: time.Second * 10, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - character.PseudoStats.CostMultiplier -= 1 - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - character.PseudoStats.CostMultiplier += 1 - }, - }) - - spell := character.RegisterSpell(core.SpellConfig{ - ActionID: core.ActionID{ItemID: BurstOfKnowledge}, - ProcMask: core.ProcMaskEmpty, - Flags: core.SpellFlagNoOnCastComplete, - - Cast: core.CastConfig{ - CD: core.Cooldown{ - Timer: character.NewTimer(), - Duration: time.Minute * 5, - }, - }, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - aura.Activate(sim) - }, - }) - - character.AddMajorCooldown(core.MajorCooldown{ - Type: core.CooldownTypeMana, - Spell: spell, - }) - }) - - // https://www.wowhead.com/classic/item=228255/talisman-of-ephemeral-power - // Use: Increases damage and healing done by magical spells and effects by up to 184 for 15 sec. (1 Min, 30 Sec Cooldown) - core.NewSimpleStatOffensiveTrinketEffect(TalismanOfEphemeralPower, stats.Stats{stats.SpellPower: 184}, time.Second*15, time.Second*90) - // https://www.wowhead.com/classic/item=228293/essence-of-the-pure-flame // Equip: When struck in combat inflicts 50 Fire damage to the attacker. core.NewItemEffect(EssenceOfThePureFlame, func(agent core.Agent) { character := agent.GetCharacter() - procSpell := character.RegisterSpell(core.SpellConfig{ + procSpell := character.GetOrRegisterSpell(core.SpellConfig{ ActionID: core.ActionID{SpellID: 461694}, SpellSchool: core.SpellSchoolFire, DefenseType: core.DefenseTypeMagic, @@ -923,9 +1485,105 @@ func init() { }) }) - // https://www.wowhead.com/classic/item=228678/draconic-infused-emblem - // Use: Increases your spell damage by up to 100 and your healing by up to 190 for 15 sec. (1 Min, 30 Sec Cooldown) - core.NewSimpleStatOffensiveTrinketEffect(DraconicInfusedEmblem, stats.Stats{stats.SpellDamage: 100, stats.HealingPower: 190}, time.Second*15, time.Second*90) + core.NewItemEffect(HandOfJustice, func(agent core.Agent) { + character := agent.GetCharacter() + if !character.AutoAttacks.AutoSwingMelee { + return + } + + icd := core.Cooldown{ + Timer: character.NewTimer(), + Duration: time.Second * 2, + } + + character.GetOrRegisterAura(core.Aura{ + Label: "Hand of Justice", + 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 !result.Landed() || !spell.ProcMask.Matches(core.ProcMaskMelee) { + return + } + + if !icd.IsReady(sim) { + return + } + + if sim.RandomFloat("HandOfJustice") < 0.02 { + icd.Use(sim) + aura.Unit.AutoAttacks.ExtraMHAttack(sim, 1, core.ActionID{SpellID: 15600}) + } + }, + }) + }) + + core.NewItemEffect(MarkOfTheChampionPhys, func(agent core.Agent) { + character := agent.GetCharacter() + + if character.CurrentTarget.MobType == proto.MobType_MobTypeUndead || character.CurrentTarget.MobType == proto.MobType_MobTypeDemon { + character.PseudoStats.MobTypeAttackPower += 150 + } + }) + + core.NewItemEffect(MarkOfTheChampionSpell, func(agent core.Agent) { + character := agent.GetCharacter() + + if character.CurrentTarget.MobType == proto.MobType_MobTypeUndead || character.CurrentTarget.MobType == proto.MobType_MobTypeDemon { + character.PseudoStats.MobTypeSpellPower += 85 + } + }) + + core.NewItemEffect(MarkOfTheChosen, func(agent core.Agent) { + character := agent.GetCharacter() + statIncrease := float64(25) + markProcChance := 0.02 + + procAura := character.GetOrRegisterAura(core.Aura{ + Label: "Mark of the Chosen Effect", + ActionID: core.ActionID{SpellID: 21970}, + Duration: time.Minute, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + character.AddStatsDynamic(sim, stats.Stats{ + stats.Stamina: statIncrease, + stats.Agility: statIncrease, + stats.Strength: statIncrease, + stats.Intellect: statIncrease, + stats.Spirit: statIncrease, + }) + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + character.AddStatsDynamic(sim, stats.Stats{ + stats.Stamina: -statIncrease, + stats.Agility: -statIncrease, + stats.Strength: -statIncrease, + stats.Intellect: -statIncrease, + stats.Spirit: -statIncrease, + }) + }, + }) + + core.MakePermanent(character.GetOrRegisterAura(core.Aura{ + Label: "Mark of the Chosen", + ActionID: core.ActionID{SpellID: 21969}, + OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) && sim.RandomFloat("Mark of the Chosen") < markProcChance { + procAura.Activate(sim) + } + }, + })) + }) + + // https://www.wowhead.com/classic/item=19812/rune-of-the-dawn + // Equip: Increases damage done to Undead by magical spells and effects by up to 48. + core.NewItemEffect(RuneOfTheDawn, func(agent core.Agent) { + character := agent.GetCharacter() + + if character.CurrentTarget.MobType == proto.MobType_MobTypeUndead { + character.AddStat(stats.SpellDamage, 48) + } + }) core.NewItemEffect(ScarabBrooch, func(agent core.Agent) { character := agent.GetCharacter() @@ -957,7 +1615,7 @@ func init() { }, }) - spell := character.RegisterSpell(core.SpellConfig{ + spell := character.GetOrRegisterSpell(core.SpellConfig{ ActionID: actionID, SpellSchool: core.SpellSchoolPhysical, ProcMask: core.ProcMaskEmpty, @@ -981,60 +1639,51 @@ func init() { }) }) - core.NewItemEffect(MarkOfTheChampionPhys, func(agent core.Agent) { + // https://www.wowhead.com/classic/item=13209/seal-of-the-dawn + // Equip: +81 Attack Power when fighting Undead. + core.NewItemEffect(SealOfTheDawn, func(agent core.Agent) { character := agent.GetCharacter() - if character.CurrentTarget.MobType == proto.MobType_MobTypeUndead || character.CurrentTarget.MobType == proto.MobType_MobTypeDemon { - character.PseudoStats.MobTypeAttackPower += 150 + if character.CurrentTarget.MobType == proto.MobType_MobTypeUndead { + character.AddStat(stats.AttackPower, 81) + character.AddStat(stats.AttackPower, 81) } }) - core.NewItemEffect(MarkOfTheChampionSpell, func(agent core.Agent) { - character := agent.GetCharacter() + // https://www.wowhead.com/classic/item=228255/talisman-of-ephemeral-power + // Use: Increases damage and healing done by magical spells and effects by up to 184 for 15 sec. (1 Min, 30 Sec Cooldown) + core.NewSimpleStatOffensiveTrinketEffect(TalismanOfEphemeralPower, stats.Stats{stats.SpellPower: 184}, time.Second*15, time.Second*90) - if character.CurrentTarget.MobType == proto.MobType_MobTypeUndead || character.CurrentTarget.MobType == proto.MobType_MobTypeDemon { - character.PseudoStats.MobTypeSpellPower += 85 - } - }) + /////////////////////////////////////////////////////////////////////////// + // Other + /////////////////////////////////////////////////////////////////////////// - core.NewItemEffect(MarkOfTheChosen, func(agent core.Agent) { + // https://www.wowhead.com/classic/item=228354/blazefury-medallion + // Equip: Adds 2 fire damage to your melee attacks. + core.NewItemEffect(BlazefuryMedallion, func(agent core.Agent) { character := agent.GetCharacter() - statIncrease := float64(25) - markProcChance := 0.02 - procAura := character.RegisterAura(core.Aura{ - Label: "Mark of the Chosen Effect", - ActionID: core.ActionID{SpellID: 21970}, - Duration: time.Minute, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - character.AddStatsDynamic(sim, stats.Stats{ - stats.Stamina: statIncrease, - stats.Agility: statIncrease, - stats.Strength: statIncrease, - stats.Intellect: statIncrease, - stats.Spirit: statIncrease, - }) - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - character.AddStatsDynamic(sim, stats.Stats{ - stats.Stamina: -statIncrease, - stats.Agility: -statIncrease, - stats.Strength: -statIncrease, - stats.Intellect: -statIncrease, - stats.Spirit: -statIncrease, - }) + procSpell := character.GetOrRegisterSpell(core.SpellConfig{ + ActionID: core.ActionID{SpellID: 7712}, + SpellSchool: core.SpellSchoolFire, + DefenseType: core.DefenseTypeMagic, + ProcMask: core.ProcMaskEmpty, + DamageMultiplier: 1, + ThreatMultiplier: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + spell.CalcAndDealDamage(sim, target, 2, spell.OutcomeMagicCrit) }, }) - core.MakePermanent(character.RegisterAura(core.Aura{ - Label: "Mark of the Chosen", - ActionID: core.ActionID{SpellID: 21969}, - OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if result.Landed() && spell.ProcMask.Matches(core.ProcMaskMelee) && sim.RandomFloat("Mark of the Chosen") < markProcChance { - procAura.Activate(sim) - } + core.MakeProcTriggerAura(&character.Unit, core.ProcTrigger{ + Name: "Blazefury Medallion Trigger", + Callback: core.CallbackOnSpellHitDealt, + Outcome: core.OutcomeLanded, + ProcMask: core.ProcMaskMelee, + Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { + procSpell.Cast(sim, result.Target) }, - })) + }) }) core.AddEffectsToTest = true diff --git a/sim/core/aura.go b/sim/core/aura.go index 36eddcc7cb..b864f564e8 100644 --- a/sim/core/aura.go +++ b/sim/core/aura.go @@ -900,7 +900,7 @@ func (caster *Unit) NewPartyAuraArray(makeAura func(*Unit) *Aura) AuraArray { party := caster.Env.Raid.GetPlayerParty(caster) auras := make([]*Aura, len(party.Players)) - for partyIndex, player := range party.Players { + for partyIndex, player := range party.PlayersAndPets { target := &player.GetCharacter().Unit if target.Type != EnemyUnit { auras[partyIndex] = makeAura(target) diff --git a/sim/core/metrics_aggregator.go b/sim/core/metrics_aggregator.go index 708e68c561..305aa58bd5 100644 --- a/sim/core/metrics_aggregator.go +++ b/sim/core/metrics_aggregator.go @@ -265,6 +265,16 @@ func (unit *Unit) NewHealthMetrics(actionID ActionID) *ResourceMetrics { func (unit *Unit) NewManaMetrics(actionID ActionID) *ResourceMetrics { return unit.Metrics.NewResourceMetrics(actionID, proto.ResourceType_ResourceTypeMana) } +func (unit *Unit) NewPartyManaMetrics(actionID ActionID) map[int32]*ResourceMetrics { + party := unit.Env.Raid.GetPlayerParty(unit) + metrics := make(map[int32]*ResourceMetrics, 0) + for _, agent := range party.PlayersAndPets { + if c := agent.GetCharacter(); c.HasManaBar() { + metrics[c.UnitIndex] = c.NewManaMetrics(actionID) + } + } + return metrics +} func (unit *Unit) NewRageMetrics(actionID ActionID) *ResourceMetrics { return unit.Metrics.NewResourceMetrics(actionID, proto.ResourceType_ResourceTypeRage) } diff --git a/tools/database/overrides.go b/tools/database/overrides.go index 3b5d9a2cdd..789226d75d 100644 --- a/tools/database/overrides.go +++ b/tools/database/overrides.go @@ -111,6 +111,9 @@ var ItemDenyList = map[int32]struct{}{ 17802: {}, // Deprecated version of Thunderfury 18820: {}, // Talisman of Ephemeral Power 19147: {}, // Ring of Spell Power + 19166: {}, // Black Amnesty (replaced by Tempered Black Amnesty) + 19169: {}, // Nightfall (replaced by Reaving Nightfall + 19170: {}, // Ebon Hand (replaced by Ebon Fist) 20522: {}, // Feral Staff 22736: {}, // Andonisus, Reaper of Souls 34576: {}, // Battlemaster's Cruelty