From dc922c9af8e021ffe9dc737b199520c2eb082920 Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Sat, 30 Nov 2024 12:14:20 -0500 Subject: [PATCH] fix warlock T2.5 Tank 4p --- sim/core/pet.go | 24 ++++ sim/warlock/item_sets_pve.go | 2 +- sim/warlock/pet.go | 41 +++++-- sim/warlock/runes.go | 4 +- sim/warlock/talents.go | 200 +++++++++++++++++++++---------- sim/warlock/warlock.go | 16 +-- ui/core/proto_utils/action_id.ts | 7 ++ 7 files changed, 213 insertions(+), 81 deletions(-) diff --git a/sim/core/pet.go b/sim/core/pet.go index d0e11440f0..2376853ce5 100644 --- a/sim/core/pet.go +++ b/sim/core/pet.go @@ -179,6 +179,18 @@ func (pet *Pet) Enable(sim *Simulation, petAgent PetAgent) { } } +func (pet *Pet) ApplyOnPetEnable(newOnPetEnable OnPetEnable) { + oldOnPetEnable := pet.OnPetEnable + if oldOnPetEnable == nil { + pet.OnPetEnable = oldOnPetEnable + } else { + pet.OnPetEnable = func(sim *Simulation) { + oldOnPetEnable(sim) + newOnPetEnable(sim) + } + } +} + // Helper for enabling a pet that will expire after a certain duration. func (pet *Pet) EnableWithTimeout(sim *Simulation, petAgent PetAgent, petDuration time.Duration) { pet.Enable(sim, petAgent) @@ -260,6 +272,18 @@ func (pet *Pet) Disable(sim *Simulation) { } } +func (pet *Pet) ApplyOnPetDisable(newOnPetDisable OnPetDisable) { + oldOnPetDisable := pet.OnPetDisable + if oldOnPetDisable == nil { + pet.OnPetDisable = oldOnPetDisable + } else { + pet.OnPetDisable = func(sim *Simulation) { + oldOnPetDisable(sim) + newOnPetDisable(sim) + } + } +} + func (pet *Pet) UpdateStatInheritance(newStatInheritance PetStatInheritance) { pet.statInheritance = newStatInheritance } diff --git a/sim/warlock/item_sets_pve.go b/sim/warlock/item_sets_pve.go index de20249c65..06e1fddb75 100644 --- a/sim/warlock/item_sets_pve.go +++ b/sim/warlock/item_sets_pve.go @@ -581,7 +581,7 @@ var ItemSetDoomcallersMalevolence = core.NewItemSet(core.ItemSet{ core.MakePermanent(warlock.RegisterAura(core.Aura{ Label: "S03 - Item - TAQ - Warlock - Tank 4P Bonus", OnInit: func(aura *core.Aura, sim *core.Simulation) { - warlock.disableMasterDemonologistOnSacrifice = false + warlock.maintainBuffsOnSacrifice = true }, })) }, diff --git a/sim/warlock/pet.go b/sim/warlock/pet.go index 5b9fca5dd3..108b96b8e8 100644 --- a/sim/warlock/pet.go +++ b/sim/warlock/pet.go @@ -9,9 +9,13 @@ import ( "github.com/wowsims/sod/sim/core/stats" ) +type OnPetDisable func(sim *core.Simulation, isSacrifice bool) + type WarlockPet struct { core.Pet + OnPetDisable OnPetDisable + owner *Warlock primaryAbility *core.Spell @@ -50,13 +54,13 @@ func (warlock *Warlock) setDefaultActivePet() { } func (warlock *Warlock) changeActivePet(sim *core.Simulation, newPet *WarlockPet, isSacrifice bool) { - hasMasterDemonologist := warlock.MasterDemonologistAura != nil - if warlock.ActivePet != nil { - warlock.ActivePet.Disable(sim) + warlock.ActivePet.Disable(sim, isSacrifice) - // Sacrificed pets lose all buffs if isSacrifice { + warlock.SacrificedPet = warlock.ActivePet + + // Sacrificed pets lose all buffs, but don't remove trigger auras for _, aura := range warlock.ActivePet.GetAuras() { if aura.Duration == core.NeverExpires { continue @@ -65,10 +69,6 @@ func (warlock *Warlock) changeActivePet(sim *core.Simulation, newPet *WarlockPet aura.Deactivate(sim) } } - - if hasMasterDemonologist && (!isSacrifice || warlock.disableMasterDemonologistOnSacrifice) { - warlock.MasterDemonologistAura.Deactivate(sim) - } } warlock.ActivePet = newPet @@ -94,8 +94,9 @@ func (warlock *Warlock) registerPets() { func (warlock *Warlock) makePet(cfg PetConfig, enabledOnStart bool) *WarlockPet { wp := &WarlockPet{ - Pet: core.NewPet(cfg.Name, &warlock.Character, cfg.Stats, warlock.makeStatInheritance(), enabledOnStart, false), - owner: warlock, + Pet: core.NewPet(cfg.Name, &warlock.Character, cfg.Stats, warlock.makeStatInheritance(), enabledOnStart, false), + owner: warlock, + OnPetDisable: func(sim *core.Simulation, isSacrifice bool) {}, } wp.EnableManaBarWithModifier(cfg.PowerModifier) @@ -152,6 +153,26 @@ func (wp *WarlockPet) Reset(_ *core.Simulation) { wp.manaPooling = false } +func (wp *WarlockPet) Disable(sim *core.Simulation, isSacrifice bool) { + wp.Pet.Disable(sim) + + if wp.OnPetDisable != nil { + wp.OnPetDisable(sim, isSacrifice) + } +} + +func (wp *WarlockPet) ApplyOnPetDisable(newOnPetDisable OnPetDisable) { + oldOnPetDisable := wp.OnPetDisable + if oldOnPetDisable == nil { + wp.OnPetDisable = oldOnPetDisable + } else { + wp.OnPetDisable = func(sim *core.Simulation, isSacrifice bool) { + oldOnPetDisable(sim, isSacrifice) + newOnPetDisable(sim, isSacrifice) + } + } +} + func (wp *WarlockPet) ExecuteCustomRotation(sim *core.Simulation) { if !wp.IsEnabled() || wp.primaryAbility == nil { return diff --git a/sim/warlock/runes.go b/sim/warlock/runes.go index 6a0ea10ac8..f9c9d4f272 100644 --- a/sim/warlock/runes.go +++ b/sim/warlock/runes.go @@ -273,8 +273,8 @@ func (warlock *Warlock) applyDemonicKnowledge() { } oldOnPetDisable := pet.OnPetDisable - pet.OnPetDisable = func(sim *core.Simulation) { - oldOnPetDisable(sim) + pet.OnPetDisable = func(sim *core.Simulation, isSacrifice bool) { + oldOnPetDisable(sim, isSacrifice) warlock.DemonicKnowledgeAura.Deactivate(sim) } } diff --git a/sim/warlock/talents.go b/sim/warlock/talents.go index 55949e3938..6c72471473 100644 --- a/sim/warlock/talents.go +++ b/sim/warlock/talents.go @@ -288,8 +288,6 @@ func (warlock *Warlock) applyMasterDemonologist() { return } - warlock.disableMasterDemonologistOnSacrifice = true - hasMetaRune := warlock.HasRune(proto.WarlockRune_RuneHandsMetamorphosis) points := float64(warlock.Talents.MasterDemonologist) @@ -299,70 +297,143 @@ func (warlock *Warlock) applyMasterDemonologist() { threatMultiplier := 1 + (core.TernaryFloat64(hasMetaRune, 0.04*points, -0.04*points) * bonusMultiplier) bonusResistance := 2 * points * bonusMultiplier - masterDemonologistConfig := core.Aura{ - Label: "Master Demonologist", - ActionID: core.ActionID{SpellID: 23825}, + impConfig := core.Aura{ + Label: "Master Demonologist (Imp)", + ActionID: core.ActionID{SpellID: 23825, Tag: 1}, Duration: core.NeverExpires, OnGain: func(aura *core.Aura, sim *core.Simulation) { - if warlock.ActivePet == nil { - return - } + aura.Unit.PseudoStats.ThreatMultiplier *= threatMultiplier + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.PseudoStats.ThreatMultiplier /= threatMultiplier + }, + } - switch warlock.ActivePet { - case warlock.Felguard: - aura.Unit.PseudoStats.DamageDealtMultiplier *= damageDealtMultiplier - aura.Unit.PseudoStats.DamageTakenMultiplier *= damageTakenMultiplier - aura.Unit.PseudoStats.ThreatMultiplier *= threatMultiplier - aura.Unit.AddResistancesDynamic(sim, bonusResistance) - case warlock.Felhunter: - aura.Unit.AddResistancesDynamic(sim, bonusResistance) - case warlock.Imp: - aura.Unit.PseudoStats.ThreatMultiplier *= threatMultiplier - case warlock.Succubus: - aura.Unit.PseudoStats.DamageDealtMultiplier *= damageDealtMultiplier - case warlock.Voidwalker: - aura.Unit.PseudoStats.DamageTakenMultiplier *= damageTakenMultiplier - } + voidwalkerConfig := core.Aura{ + Label: "Master Demonologist (Voidwalker)", + ActionID: core.ActionID{SpellID: 23825, Tag: 2}, + Duration: core.NeverExpires, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.PseudoStats.DamageTakenMultiplier *= damageTakenMultiplier }, OnExpire: func(aura *core.Aura, sim *core.Simulation) { - if warlock.ActivePet == nil { - return + aura.Unit.PseudoStats.DamageTakenMultiplier /= damageTakenMultiplier + }, + } + + succubusConfig := core.Aura{ + Label: "Master Demonologist (Succubus)", + ActionID: core.ActionID{SpellID: 23825, Tag: 3}, + Duration: core.NeverExpires, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.PseudoStats.DamageDealtMultiplier *= damageDealtMultiplier + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.PseudoStats.DamageDealtMultiplier /= damageDealtMultiplier + }, + } + + felhunterConfig := core.Aura{ + Label: "Master Demonologist (Felhunter)", + ActionID: core.ActionID{SpellID: 23825, Tag: 4}, + Duration: core.NeverExpires, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.AddResistancesDynamic(sim, bonusResistance) + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.AddResistancesDynamic(sim, -bonusResistance) + }, + } + + for _, pet := range warlock.BasePets { + pet.ApplyOnPetEnable(func(sim *core.Simulation) { + if warlock.MasterDemonologistAura != nil { + warlock.MasterDemonologistAura.Deactivate(sim) } + }) + } - switch warlock.ActivePet { - case warlock.Felguard: - aura.Unit.PseudoStats.DamageDealtMultiplier /= damageDealtMultiplier - aura.Unit.PseudoStats.DamageTakenMultiplier /= damageTakenMultiplier - aura.Unit.PseudoStats.ThreatMultiplier /= threatMultiplier - aura.Unit.AddResistancesDynamic(sim, -bonusResistance) - case warlock.Felhunter: - aura.Unit.AddResistancesDynamic(sim, -bonusResistance) - case warlock.Imp: + warlockImpAura := warlock.RegisterAura(impConfig) + impAura := warlock.Imp.RegisterAura(impConfig) + warlock.Imp.ApplyOnPetEnable(func(sim *core.Simulation) { + impAura.Activate(sim) + warlock.MasterDemonologistAura = warlockImpAura + }) + warlock.Imp.ApplyOnPetDisable(func(sim *core.Simulation, isSacrifice bool) { + impAura.Deactivate(sim) + }) + + warlockVoidwalkerAura := warlock.RegisterAura(voidwalkerConfig) + voidwalkerAura := warlock.Voidwalker.RegisterAura(voidwalkerConfig) + warlock.Voidwalker.ApplyOnPetEnable(func(sim *core.Simulation) { + voidwalkerAura.Activate(sim) + warlock.MasterDemonologistAura = warlockVoidwalkerAura + }) + warlock.Voidwalker.ApplyOnPetDisable(func(sim *core.Simulation, isSacrifice bool) { + voidwalkerAura.Deactivate(sim) + }) + + warlockSuccubusAura := warlock.RegisterAura(succubusConfig) + succubusAura := warlock.Succubus.RegisterAura(succubusConfig) + warlock.Succubus.ApplyOnPetEnable(func(sim *core.Simulation) { + succubusAura.Activate(sim) + warlock.MasterDemonologistAura = warlockSuccubusAura + }) + warlock.Succubus.ApplyOnPetDisable(func(sim *core.Simulation, isSacrifice bool) { + succubusAura.Deactivate(sim) + }) + + warlockFelhunterAura := warlock.RegisterAura(felhunterConfig) + felhunterAura := warlock.Felhunter.RegisterAura(felhunterConfig) + warlock.Felhunter.ApplyOnPetEnable(func(sim *core.Simulation) { + felhunterAura.Activate(sim) + warlock.MasterDemonologistAura = warlockFelhunterAura + }) + warlock.Felhunter.ApplyOnPetDisable(func(sim *core.Simulation, isSacrifice bool) { + felhunterAura.Deactivate(sim) + }) + + if warlock.HasRune(proto.WarlockRune_RuneBracerSummonFelguard) { + felguardConfig := core.Aura{ + Label: "Master Demonologist (Felguard)", + ActionID: core.ActionID{SpellID: 23825, Tag: 5}, + Duration: core.NeverExpires, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + aura.Unit.PseudoStats.ThreatMultiplier *= threatMultiplier + aura.Unit.PseudoStats.DamageTakenMultiplier *= damageTakenMultiplier + aura.Unit.PseudoStats.DamageDealtMultiplier *= damageDealtMultiplier + aura.Unit.AddResistancesDynamic(sim, bonusResistance) + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { aura.Unit.PseudoStats.ThreatMultiplier /= threatMultiplier - case warlock.Succubus: - aura.Unit.PseudoStats.DamageDealtMultiplier /= damageDealtMultiplier - case warlock.Voidwalker: aura.Unit.PseudoStats.DamageTakenMultiplier /= damageTakenMultiplier - } - }, + aura.Unit.PseudoStats.DamageDealtMultiplier /= damageDealtMultiplier + aura.Unit.AddResistancesDynamic(sim, -bonusResistance) + }, + } + + warlockFelguardAura := warlock.RegisterAura(felguardConfig) + felguardAura := warlock.Felguard.RegisterAura(felguardConfig) + warlock.Felguard.ApplyOnPetEnable(func(sim *core.Simulation) { + felguardAura.Activate(sim) + warlock.MasterDemonologistAura = warlockFelguardAura + }) + warlock.Felguard.ApplyOnPetDisable(func(sim *core.Simulation, isSacrifice bool) { + felguardAura.Deactivate(sim) + }) } - warlock.MasterDemonologistAura = warlock.RegisterAura(masterDemonologistConfig) for _, pet := range warlock.BasePets { - petAura := pet.RegisterAura(masterDemonologistConfig) - - oldOnPetEnable := pet.OnPetEnable - pet.OnPetEnable = func(sim *core.Simulation) { - oldOnPetEnable(sim) + pet.ApplyOnPetEnable(func(sim *core.Simulation) { warlock.MasterDemonologistAura.Activate(sim) - petAura.Activate(sim) - } + }) - oldOnPetDisable := pet.OnPetDisable - pet.OnPetDisable = func(sim *core.Simulation) { - oldOnPetDisable(sim) - petAura.Deactivate(sim) - } + pet.ApplyOnPetDisable(func(sim *core.Simulation, isSacrifice bool) { + if !isSacrifice || !warlock.maintainBuffsOnSacrifice { + warlock.MasterDemonologistAura.Deactivate(sim) + warlock.MasterDemonologistAura = nil + } + }) } } @@ -391,8 +462,8 @@ func (warlock *Warlock) applySoulLink() { pet.SoulLinkAura = pet.RegisterAura(soulLinkConfig) oldOnPetDisable := pet.OnPetDisable - pet.OnPetDisable = func(sim *core.Simulation) { - oldOnPetDisable(sim) + pet.OnPetDisable = func(sim *core.Simulation, isSacrifice bool) { + oldOnPetDisable(sim, isSacrifice) warlock.SoulLinkAura.Deactivate(sim) pet.SoulLinkAura.Deactivate(sim) } @@ -495,14 +566,16 @@ func (warlock *Warlock) applyDemonicSacrifice() { }) dsAuras := []*core.Aura{felhunterAura, impAura, succubusAura, voidwalkerAura} - for _, pet := range warlock.BasePets { - oldOnPetEnable := pet.OnPetEnable - pet.OnPetEnable = func(sim *core.Simulation) { - oldOnPetEnable(sim) - for _, dsAura := range dsAuras { - dsAura.Deactivate(sim) + for idx := range warlock.BasePets { + pet := warlock.BasePets[idx] + + pet.ApplyOnPetEnable(func(sim *core.Simulation) { + if !warlock.maintainBuffsOnSacrifice || pet == warlock.SacrificedPet { + for _, dsAura := range dsAuras { + dsAura.Deactivate(sim) + } } - } + }) } warlock.GetOrRegisterSpell(core.SpellConfig{ @@ -516,6 +589,11 @@ func (warlock *Warlock) applyDemonicSacrifice() { }, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + felhunterAura.Deactivate(sim) + impAura.Deactivate(sim) + succubusAura.Deactivate(sim) + voidwalkerAura.Deactivate(sim) + switch warlock.ActivePet { case warlock.Felguard: felhunterAura.Activate(sim) diff --git a/sim/warlock/warlock.go b/sim/warlock/warlock.go index ed633eafd8..2f8ea67e5e 100644 --- a/sim/warlock/warlock.go +++ b/sim/warlock/warlock.go @@ -49,16 +49,17 @@ type Warlock struct { Options *proto.WarlockOptions BasePets []*WarlockPet - ActivePet *WarlockPet Felhunter *WarlockPet Felguard *WarlockPet Imp *WarlockPet Succubus *WarlockPet Voidwalker *WarlockPet - // Doomguard *DoomguardPet // Infernal *InfernalPet + ActivePet *WarlockPet // The Warlock's current pet + SacrificedPet *WarlockPet // Stored reference to the Warlock's most recently-sacrified pet + ChaosBolt *core.Spell Conflagrate []*core.Spell Corruption []*core.Spell @@ -125,11 +126,11 @@ type Warlock struct { DPSPAggregate float64 // Extra state and logic variables - demonicKnowledgeSp float64 - masterDemonologistBonus float64 // Bonus multiplier applied to the Master Demonologist talent - disableMasterDemonologistOnSacrifice bool // Whether to disable the Master Demonologist buff after Sacrificing a pet. Used by TAQ 4pc - improvedShadowBoltSpellCodes []int32 // List of spells that proc ISB - nightfallProcChance float64 + demonicKnowledgeSp float64 + maintainBuffsOnSacrifice bool // Whether to disable the Master Demonologist and Demonic Sacrifice buffs when sacrificing/summoning pets. Used by TAQ 4pc + masterDemonologistBonus float64 // Bonus multiplier applied to the Master Demonologist talent + improvedShadowBoltSpellCodes []int32 // List of spells that proc ISB + nightfallProcChance float64 // For effects that buff the damage of shadow bolt for each active Warlock effect on the target, e.g. 2pc DPS 6pc shadowBoltActiveEffectModifierPer float64 shadowBoltActiveEffectModifierMax float64 @@ -194,6 +195,7 @@ func (warlock *Warlock) AddRaidBuffs(raidBuffs *proto.RaidBuffs) { func (warlock *Warlock) Reset(sim *core.Simulation) { warlock.setDefaultActivePet() + warlock.SacrificedPet = nil warlock.ActiveCurseAura = make([]*core.Aura, len(sim.Environment.AllUnits)) // warlock.ItemSwap.SwapItems(sim, []proto.ItemSlot{proto.ItemSlot_ItemSlotMainHand, diff --git a/ui/core/proto_utils/action_id.ts b/ui/core/proto_utils/action_id.ts index 8c2f040825..99c7c0513f 100644 --- a/ui/core/proto_utils/action_id.ts +++ b/ui/core/proto_utils/action_id.ts @@ -469,6 +469,13 @@ export class ActionId { case 'Kill Shot': if (this.tag === 1) name = `${name} (Rapid Fire)` break; + case 'Master Demonologist': + if (this.tag === 1) name = `${name} (Imp)` + else if (this.tag === 2) name = `${name} (Voidwalker)` + else if (this.tag === 3) name = `${name} (Succubus)` + else if (this.tag === 4) name = `${name} (Felhunter)` + else if (this.tag === 5) name = `${name} (Felguard)` + break; // Temporary until real debuff is implemented case 'Ice Lance': if (this.tag === 1) name = 'Glaciate'