-
Notifications
You must be signed in to change notification settings - Fork 30
/
paladin.go
173 lines (138 loc) · 5 KB
/
paladin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package paladin
import (
"time"
"github.com/wowsims/sod/sim/common/vanilla"
"github.com/wowsims/sod/sim/core"
"github.com/wowsims/sod/sim/core/proto"
"github.com/wowsims/sod/sim/core/stats"
)
var TalentTreeSizes = [3]int{14, 15, 15}
const (
SpellCode_PaladinNone = iota
SpellCode_PaladinHolyShock
SpellCode_PaladinJudgementOfCommand
)
type Paladin struct {
core.Character
Talents *proto.PaladinTalents
primarySeal *core.Spell // the seal configured in options, available via "Cast Primary Seal"
currentSeal *core.Aura
currentJudgement *core.Spell
// Active abilities and shared cooldowns that are externally manipulated.
exorcismCooldown *core.Cooldown
holyShockCooldown *core.Cooldown
judgement *core.Spell
// highest rank seal spell if available
sealOfRighteousness *core.Spell
sealOfCommand *core.Spell
sealOfMartyrdom *core.Spell
}
// Implemented by each Paladin spec.
type PaladinAgent interface {
GetPaladin() *Paladin
}
func (paladin *Paladin) GetCharacter() *core.Character {
return &paladin.Character
}
func (paladin *Paladin) GetPaladin() *Paladin {
return paladin
}
func (paladin *Paladin) AddRaidBuffs(_ *proto.RaidBuffs) {
// Buffs are handled explicitly through APLs now
}
func (paladin *Paladin) AddPartyBuffs(_ *proto.PartyBuffs) {
}
func (paladin *Paladin) Initialize() {
// Judgement and Seals
paladin.registerJudgement()
paladin.registerSealOfRighteousness()
paladin.registerSealOfCommand()
paladin.registerSealOfMartyrdom()
paladin.registerSealOfTheCrusader()
// Active abilities
paladin.registerCrusaderStrike()
paladin.registerDivineStorm()
paladin.registerConsecration()
paladin.registerHolyShock()
paladin.registerExorcism()
paladin.registerDivineFavor()
paladin.registerHammerOfWrath()
paladin.registerHolyWrath()
}
func (paladin *Paladin) Reset(_ *core.Simulation) {
}
// maybe need to add stat dependencies
func NewPaladin(character *core.Character, talentsStr string) *Paladin {
paladin := &Paladin{
Character: *character,
Talents: &proto.PaladinTalents{},
}
core.FillTalentsProto(paladin.Talents.ProtoReflect(), talentsStr, TalentTreeSizes)
paladin.PseudoStats.CanParry = true
paladin.EnableManaBar()
paladin.AddStatDependency(stats.Strength, stats.AttackPower, 2.0)
paladin.AddStatDependency(stats.Agility, stats.MeleeCrit, core.CritPerAgiAtLevel[character.Class][int(paladin.Level)]*core.CritRatingPerCritChance)
paladin.AddStatDependency(stats.Intellect, stats.SpellCrit, core.CritPerIntAtLevel[character.Class][int(paladin.Level)]*core.SpellCritRatingPerCritChance)
// Paladins get 1 block value per 20 str
paladin.AddStatDependency(stats.Strength, stats.BlockValue, .05)
// Bonus Armor and Armor are treated identically for Paladins
paladin.AddStatDependency(stats.BonusArmor, stats.Armor, 1)
// Dodge per agi at a given level behaves identically in classic to Crit per agi at a given level.
// paladin.AddStatDependency(stats.Agility, stats.Dodge, core.CritPerAgiAtLevel[character.Class][int(paladin.Level)]*core.DodgeRatingPerDodgeChance)
// The below requires some verification for the prot paladin sim when it is implemented.
// Switch these to AddStat as the PsuedoStats are being removed
// paladin.PseudoStats.BaseDodge += 0.034943
// paladin.PseudoStats.BaseParry += 0.05
vanilla.ConstructEmeralDragonWhelpPets(&paladin.Character)
return paladin
}
func (paladin *Paladin) hasRune(rune proto.PaladinRune) bool {
return paladin.HasRuneById(int32(rune))
}
func (paladin *Paladin) has2hEquipped() bool {
return paladin.MainHand().HandType == proto.HandType_HandTypeTwoHand
}
func (paladin *Paladin) ResetPrimarySeal(primarySeal proto.PaladinSeal) {
paladin.currentSeal = nil
paladin.primarySeal = paladin.getPrimarySealSpell(primarySeal)
}
func (paladin *Paladin) getPrimarySealSpell(primarySeal proto.PaladinSeal) *core.Spell {
// Used in the Cast Primary Seal APLAction to get the max rank spell for the level.
switch primarySeal {
case proto.PaladinSeal_Martyrdom:
return paladin.sealOfMartyrdom
case proto.PaladinSeal_Command:
return paladin.sealOfCommand
default:
return paladin.sealOfRighteousness
}
}
func (paladin *Paladin) applySeal(newSeal *core.Aura, judgement *core.Spell, sim *core.Simulation) {
const lingerDuration = time.Millisecond * 400
if seal := paladin.currentSeal; seal.IsActive() && newSeal != seal {
if seal.RemainingDuration(sim) >= lingerDuration {
seal.UpdateExpires(sim, sim.CurrentTime+lingerDuration)
}
}
paladin.currentSeal = newSeal
paladin.currentJudgement = judgement
paladin.currentSeal.Activate(sim)
}
func (paladin *Paladin) getLibramSealCostReduction() float64 {
if paladin.Ranged().ID == LibramOfBenediction {
return 10
}
if paladin.Ranged().ID == LibramOfHope {
return 20
}
return 0
}
func (paladin *Paladin) holyCrit() float64 {
var holySpellCrit float64
if paladin.HasSetBonus(ItemSetObsessedProphetsPlate, 2) {
holySpellCrit += 3 * core.SpellCritRatingPerCritChance
}
holySpellCrit += paladin.holyPower()
holySpellCrit += paladin.fanaticism()
return holySpellCrit
}