Skip to content

Commit

Permalink
Merge pull request #816 from psiven/master
Browse files Browse the repository at this point in the history
Prot Pal Alpha
  • Loading branch information
psiven authored Jul 4, 2024
2 parents bedb61b + ebae43e commit 251fcdd
Show file tree
Hide file tree
Showing 13 changed files with 228 additions and 170 deletions.
73 changes: 0 additions & 73 deletions sim/paladin/_shield_of_righteousness.go

This file was deleted.

16 changes: 10 additions & 6 deletions sim/paladin/paladin.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ const (
SpellMaskHammerOfTheRighteousMelee
SpellMaskHammerOfTheRighteousAoe
SpellMaskHandOfReckoning
SpellMaskShieldOfRighteousness
SpellMaskAvengersShield
SpellMaskDivinePlea
SpellMaskDivineProtection
Expand Down Expand Up @@ -79,7 +78,8 @@ const SpellMaskCanTriggerSealOfTruth = SpellMaskCrusaderStrike |
SpellMaskExorcism |
SpellMaskHammerOfWrath |
SpellMaskJudgement |
SpellMaskHammerOfTheRighteousMelee
SpellMaskHammerOfTheRighteousMelee |
SpellMaskShieldOfTheRighteous

const SpellMaskCanTriggerAncientPower = SpellMaskCanTriggerSealOfTruth |
SpellMaskHolyWrath
Expand Down Expand Up @@ -156,11 +156,12 @@ type Paladin struct {
AvengingWrathAura *core.Aura
DivineProtectionAura *core.Aura
ForbearanceAura *core.Aura
VengeanceAura *core.Aura
ZealotryAura *core.Aura
InquisitionAura *core.Aura
DivinePurposeAura *core.Aura
JudgementsOfThePureAura *core.Aura
GrandCrusaderAura *core.Aura
SacredDutyAura *core.Aura

SpiritualAttunementMetrics *core.ResourceMetrics
}
Expand Down Expand Up @@ -284,15 +285,18 @@ func NewPaladin(character *core.Character, talentsStr string, options *proto.Pal

paladin.AddStatDependency(stats.Strength, stats.AttackPower, 2)
paladin.AddStatDependency(stats.Agility, stats.MeleeCrit, core.CritPerAgiMaxLevel[character.Class]*core.CritRatingPerCritChance)

paladin.AddStat(stats.Parry, -paladin.GetBaseStats()[stats.Strength]*0.27) // Does not apply to base Strength
paladin.AddStatDependency(stats.Strength, stats.Parry, 0.27)

paladin.PseudoStats.BaseDodge += 0.034943
paladin.PseudoStats.BaseParry += 0.05
// TODO: figure out the exact tanking stat dependencies for prot pala
// // Paladins get 0.0167 dodge per agi. ~1% per 59.88
// paladin.AddStatDependency(stats.Agility, stats.Dodge, (1.0/59.88)*core.DodgeRatingPerDodgeChance)
// // Paladins get more melee haste from haste than other classes
// paladin.PseudoStats.MeleeHasteRatingPerHastePercent /= 1.3
// // Base dodge is unaffected by Diminishing Returns
// paladin.PseudoStats.BaseDodge += 0.034943
// paladin.PseudoStats.BaseParry += 0.05


// Bonus Armor and Armor are treated identically for Paladins
paladin.AddStatDependency(stats.BonusArmor, stats.Armor, 1)
Expand Down
9 changes: 8 additions & 1 deletion sim/paladin/protection/avengers_shield.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
)

func (prot *ProtectionPaladin) registerAvengersShieldSpell() {
actionId := core.ActionID{SpellID: 31935}
hpMetrics := prot.NewHolyPowerMetrics(actionId)
asMinDamage, asMaxDamage := core.CalcScalingSpellEffectVarianceMinMax(proto.Class_ClassPaladin, 3.024, 0.2)
glyphedSingleTargetAS := prot.HasMajorGlyph(proto.PaladinMajorGlyph_GlyphOfFocusedShield)

Expand All @@ -16,7 +18,7 @@ func (prot *ProtectionPaladin) registerAvengersShieldSpell() {
results := make([]*core.SpellResult, numTargets)

prot.AvengersShield = prot.RegisterSpell(core.SpellConfig{
ActionID: core.ActionID{SpellID: 31935},
ActionID: actionId,
SpellSchool: core.SpellSchoolHoly,
ProcMask: core.ProcMaskMeleeMHSpecial,
Flags: core.SpellFlagMeleeMetrics | core.SpellFlagAPL,
Expand Down Expand Up @@ -55,6 +57,11 @@ func (prot *ProtectionPaladin) registerAvengersShieldSpell() {
for idx := int32(0); idx < numTargets; idx++ {
spell.DealDamage(sim, results[idx])
}

if prot.GrandCrusaderAura.IsActive() {
prot.GainHolyPower(sim, 1, hpMetrics)
}

},
})
}
24 changes: 15 additions & 9 deletions sim/paladin/protection/protection.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ func NewProtectionPaladin(character *core.Character, options *proto.Player) *Pro
prot := &ProtectionPaladin{
Paladin: paladin.NewPaladin(character, options.TalentsString, protOptions.Options.ClassOptions),
Options: protOptions.Options,
vengeance: &core.VengeanceTracker{},
}

// healingModel := options.HealingModel
// if healingModel != nil {
// if healingModel.InspirationUptime > 0.0 {
// core.ApplyInspiration(prot.GetCharacter(), healingModel.InspirationUptime)
// }
// }
healingModel := options.HealingModel
if healingModel != nil {
if healingModel.InspirationUptime > 0.0 {
core.ApplyInspiration(&prot.Unit, healingModel.InspirationUptime)
}
}

return prot
}
Expand All @@ -50,7 +51,7 @@ type ProtectionPaladin struct {

Options *proto.ProtectionPaladin_Options

core.VengeanceTracker
vengeance *core.VengeanceTracker
}

func (prot *ProtectionPaladin) GetPaladin() *paladin.Paladin {
Expand All @@ -61,6 +62,7 @@ func (prot *ProtectionPaladin) Initialize() {
prot.Paladin.Initialize()
prot.ActivateRighteousFury()
prot.registerAvengersShieldSpell()
prot.RegisterSpecializationEffects()
}

func (prot *ProtectionPaladin) ApplyTalents() {
Expand All @@ -81,12 +83,16 @@ func (prot *ProtectionPaladin) RegisterSpecializationEffects() {
prot.AddStatDependency(stats.Strength, stats.SpellPower, 0.6)
prot.AddStat(stats.SpellHit, core.SpellHitRatingPerHitChance*8)
prot.MultiplyStat(stats.Stamina, 1.15)
core.MakePermanent(prot.GetOrRegisterAura(core.Aura{
Label: "Touched by the Light",
ActionID: core.ActionID{SpellID: 53592},
}))

// Judgements of the Wise
prot.ApplyJudgementsOfTheWise()

// Vengeance
core.ApplyVengeanceEffect(prot.GetCharacter(), &prot.VengeanceTracker, 84839)
core.ApplyVengeanceEffect(&prot.Character, prot.vengeance, 84839)
}

func (prot *ProtectionPaladin) RegisterMastery() {
Expand All @@ -108,7 +114,7 @@ func (prot *ProtectionPaladin) ApplyJudgementsOfTheWise() {
manaMetrics := prot.NewManaMetrics(actionID)

// It's 30% of base mana over 10 seconds, with haste adding ticks.
manaPerTick := math.Round(0.30 * prot.BaseMana)
manaPerTick := math.Round(0.030 * prot.BaseMana)

jotw := prot.RegisterSpell(core.SpellConfig{
ActionID: actionID,
Expand Down
2 changes: 1 addition & 1 deletion sim/paladin/retribution/TestRetribution.results
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ character_stats_results: {
final_stats: 209
final_stats: 0
final_stats: 0
final_stats: 0
final_stats: 1610.4123
final_stats: 0
final_stats: 141397.4
final_stats: 0
Expand Down
2 changes: 1 addition & 1 deletion sim/paladin/seal_of_truth.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func (paladin *Paladin) registerSealOfTruth() {
return
}

// SoT only procs on white hits, CS, TV, Exo, Judge and HoW
// SoT only procs on white hits, CS, TV, Exo, Judge, HoW, HotR, ShoR
if spell.ProcMask&core.ProcMaskMeleeWhiteHit == 0 &&
spell.ClassSpellMask&SpellMaskCanTriggerSealOfTruth == 0 {
return
Expand Down
97 changes: 97 additions & 0 deletions sim/paladin/talents_protection.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ func (paladin *Paladin) applyProtectionTalents() {
paladin.applyReckoning()
paladin.applyShieldOfTheRighteous()
paladin.applyShieldOfTheTemplar()
paladin.applyGrandCrusader()
paladin.applySacredDuty()
}

func (paladin *Paladin) applySealsOfThePure() {
Expand Down Expand Up @@ -222,6 +224,7 @@ func (paladin *Paladin) applyReckoning() {
OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
if spell == paladin.AutoAttacks.MHAuto() {
reckoningSpell.Cast(sim, result.Target)
aura.RemoveStack(sim)
}
},
})
Expand All @@ -230,6 +233,7 @@ func (paladin *Paladin) applyReckoning() {
Name: "Reckoning",
ProcMask: core.ProcMaskMelee,
ProcChance: procChance,
Callback: core.CallbackOnSpellHitTaken,
Outcome: core.OutcomeBlock,

Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
Expand Down Expand Up @@ -289,6 +293,9 @@ func (paladin *Paladin) applyShieldOfTheTemplar() {
return
}

actionId := core.ActionID{SpellID: 84854}
hpMetrics := paladin.NewHolyPowerMetrics(actionId)

paladin.AddStaticMod(core.SpellModConfig{
ClassMask: SpellMaskGuardianOfAncientKings,
Kind: core.SpellMod_Cooldown_Flat,
Expand All @@ -300,4 +307,94 @@ func (paladin *Paladin) applyShieldOfTheTemplar() {
Kind: core.SpellMod_Cooldown_Flat,
TimeValue: -(time.Second * time.Duration(20*paladin.Talents.ShieldOfTheTemplar)),
})

core.MakeProcTriggerAura(&paladin.Unit, core.ProcTrigger{
Name: "Divine Plea Templar Effect",
ActionID: actionId,
Callback: core.CallbackOnCastComplete,
ClassSpellMask: SpellMaskDivinePlea,
ProcChance: 1,
Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
paladin.GainHolyPower(sim, 3, hpMetrics)
},
})

}

func (paladin *Paladin) applyGrandCrusader() {
if paladin.Talents.GrandCrusader == 0 {
return
}

paladin.GrandCrusaderAura = paladin.RegisterAura(core.Aura{
Label: "Grand Crusader (Proc)",
ActionID: core.ActionID{SpellID: 85043},
Duration: time.Second * 6,

// Dummy effect. Implemented in avengers_shield.go

OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) {
if spell.ClassSpellMask&SpellMaskAvengersShield != 0 {
paladin.GrandCrusaderAura.Deactivate(sim)
}
},
})

core.MakeProcTriggerAura(&paladin.Unit, core.ProcTrigger{
Name: "Grand Crusader",
ActionID: core.ActionID{SpellID: 85416},
Callback: core.CallbackOnSpellHitDealt,
Outcome: core.OutcomeLanded,
ClassSpellMask: SpellMaskBuilder,
ProcChance: []float64{0, 0.05, 0.10}[paladin.Talents.GrandCrusader],
Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
paladin.AvengersShield.CD.Reset()
paladin.GrandCrusaderAura.Activate(sim)
},
})
}

// 25/50% chance on Judgement/AS to apply 100% crit to next SotR
func (paladin *Paladin) applySacredDuty() {
if paladin.Talents.SacredDuty == 0 {
return
}

critMod := paladin.AddDynamicMod(core.SpellModConfig{
ClassMask: SpellMaskShieldOfTheRighteous,
Kind: core.SpellMod_BonusCrit_Rating,
FloatValue: 100 * core.CritRatingPerCritChance,
})

paladin.SacredDutyAura = paladin.RegisterAura(core.Aura{
Label: "Sacred Duty (Proc)",
ActionID: core.ActionID{SpellID: 85433},
Duration: time.Second * 10,

OnGain: func(aura *core.Aura, sim *core.Simulation) {
critMod.Activate()
},

OnExpire: func(aura *core.Aura, sim *core.Simulation) {
critMod.Deactivate()
},

OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
if spell.ClassSpellMask&SpellMaskShieldOfTheRighteous != 0 && result.DidCrit() {
paladin.SacredDutyAura.Deactivate(sim)
}
},
})

core.MakeProcTriggerAura(&paladin.Unit, core.ProcTrigger{
Name: "Sacred Duty",
ActionID: core.ActionID{SpellID: 53710},
Callback: core.CallbackOnSpellHitDealt,
Outcome: core.OutcomeLanded,
ClassSpellMask: SpellMaskAvengersShield | SpellMaskJudgement,
ProcChance: []float64{0, 0.25, 0.50}[paladin.Talents.SacredDuty],
Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
paladin.SacredDutyAura.Activate(sim)
},
})
}
2 changes: 1 addition & 1 deletion ui/core/launched_sims.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export const simLaunchStatuses: Record<Spec, SimStatus> = {
},
[Spec.SpecProtectionPaladin]: {
phase: Phase.Phase1,
status: LaunchStatus.Unlaunched,
status: LaunchStatus.Alpha,
},
[Spec.SpecRetributionPaladin]: {
phase: Phase.Phase1,
Expand Down
Loading

0 comments on commit 251fcdd

Please sign in to comment.