Skip to content

Commit

Permalink
Merge pull request #110 from wowsims/feral
Browse files Browse the repository at this point in the history
Finished Feral tree talent implementations
  • Loading branch information
NerdEgghead authored Apr 18, 2024
2 parents c9be0c6 + f90605b commit 5b048a1
Show file tree
Hide file tree
Showing 18 changed files with 265 additions and 137 deletions.
13 changes: 13 additions & 0 deletions sim/core/energy.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,19 @@ func (eb *energyBar) ResetEnergyTick(sim *Simulation) {
sim.RescheduleTask(eb.nextEnergyTick)
}

// Used for dynamic updates to maximum Energy, such as from the Druid Primal Madness talent
func (eb *energyBar) UpdateMaxEnergy(sim *Simulation, newMaxEnergy float64, metrics *ResourceMetrics) {
oldMaxEnergy := eb.maxEnergy
eb.maxEnergy = newMaxEnergy
energyDelta := newMaxEnergy - oldMaxEnergy

if energyDelta >= 0 {
eb.AddEnergy(sim, energyDelta, metrics)
} else {
eb.SpendEnergy(sim, min(-energyDelta, eb.currentEnergy), metrics)
}
}

func (eb *energyBar) AddComboPoints(sim *Simulation, pointsToAdd int32, metrics *ResourceMetrics) {
newComboPoints := min(eb.comboPoints+pointsToAdd, 5)
metrics.AddEvent(float64(pointsToAdd), float64(newComboPoints-eb.comboPoints))
Expand Down
11 changes: 11 additions & 0 deletions sim/core/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ func (hb *healthBar) RemoveHealth(sim *Simulation, amount float64) {
hb.currentHealth = newHealth
}

// Used for dynamic updates to maximum health from "Last Stand" effects
func (hb *healthBar) UpdateMaxHealth(sim *Simulation, bonusHealth float64, metrics *ResourceMetrics) {
hb.unit.AddStatsDynamic(sim, stats.Stats{stats.Health: bonusHealth})

if bonusHealth >= 0 {
hb.GainHealth(sim, bonusHealth, metrics)
} else {
hb.RemoveHealth(sim, min(-bonusHealth, hb.currentHealth - 1)) // Last Stand effects always leave the player with at least 1 HP when they expire
}
}

var ChanceOfDeathAuraLabel = "Chance of Death"

func (character *Character) trackChanceOfDeath(healingModel *proto.HealingModel) {
Expand Down
5 changes: 2 additions & 3 deletions sim/death_knight/_vampiric_blood.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,12 @@ func (dk *DeathKnight) registerVampiricBloodSpell() {
Duration: time.Second*10 + core.TernaryDuration(dk.HasMajorGlyph(proto.DeathKnightMajorGlyph_GlyphOfVampiricBlood), 5*time.Second, 0),
OnGain: func(aura *core.Aura, sim *core.Simulation) {
bonusHealth = dk.MaxHealth() * 0.15
dk.AddStatsDynamic(sim, stats.Stats{stats.Health: bonusHealth})
dk.GainHealth(sim, bonusHealth, healthMetrics)
dk.UpdateMaxHealth(sim, bonusHealth, healthMetrics)
dk.PseudoStats.HealingTakenMultiplier *= 1.35

},
OnExpire: func(aura *core.Aura, sim *core.Simulation) {
dk.AddStatsDynamic(sim, stats.Stats{stats.Health: -bonusHealth})
dk.UpdateMaxHealth(sim, -bonusHealth, healthMetrics)
dk.PseudoStats.HealingTakenMultiplier /= 1.35
},
})
Expand Down
18 changes: 14 additions & 4 deletions sim/druid/_berserk.go → sim/druid/berserk.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ func (druid *Druid) registerBerserkCD() {
}

actionId := core.ActionID{SpellID: 50334}
glyphBonus := core.TernaryDuration(druid.HasMajorGlyph(proto.DruidMajorGlyph_GlyphOfBerserk), time.Second*5.0, 0.0)
glyphBonus := core.TernaryDuration(druid.HasPrimeGlyph(proto.DruidPrimeGlyph_GlyphOfBerserk), time.Second*10.0, 0.0)
primalMadnessRage := 6.0 * float64(druid.Talents.PrimalMadness)
var affectedSpells []*DruidSpell

druid.BerserkAura = druid.RegisterAura(core.Aura{
Expand All @@ -35,11 +36,19 @@ func (druid *Druid) registerBerserkCD() {
for _, spell := range affectedSpells {
spell.CostMultiplier -= 0.5
}

if druid.PrimalMadnessAura != nil {
druid.PrimalMadnessAura.Activate(sim)
}
},
OnExpire: func(aura *core.Aura, sim *core.Simulation) {
for _, spell := range affectedSpells {
spell.CostMultiplier += 0.5
}

if druid.PrimalMadnessAura.IsActive() && !druid.TigersFuryAura.IsActive() {
druid.PrimalMadnessAura.Deactivate(sim)
}
},
})

Expand All @@ -48,9 +57,6 @@ func (druid *Druid) registerBerserkCD() {
Flags: core.SpellFlagAPL,

Cast: core.CastConfig{
DefaultCast: core.Cast{
GCD: time.Second,
},
CD: core.Cooldown{
Timer: druid.NewTimer(),
Duration: time.Minute * 3,
Expand All @@ -59,6 +65,10 @@ func (druid *Druid) registerBerserkCD() {
},
ApplyEffects: func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) {
druid.BerserkAura.Activate(sim)

if (primalMadnessRage > 0) && druid.InForm(Bear) {
druid.AddRage(sim, primalMadnessRage, druid.PrimalMadnessRageMetrics)
}
},
})

Expand Down
13 changes: 7 additions & 6 deletions sim/druid/druid.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type Druid struct {
Maul *DruidSpell
MaulQueueSpell *DruidSpell
Moonfire *DruidSpell
Pulverize *DruidSpell
Rebirth *DruidSpell
Rake *DruidSpell
Rip *DruidSpell
Expand Down Expand Up @@ -79,6 +80,8 @@ type Druid struct {
MoonkinT84PCAura *core.Aura
NaturesGraceProcAura *core.Aura
PredatoryInstinctsAura *core.Aura
PrimalMadnessAura *core.Aura
PulverizeAura *core.Aura
SavageDefenseAura *core.Aura
SurvivalInstinctsAura *core.Aura
TigersFuryAura *core.Aura
Expand All @@ -89,6 +92,7 @@ type Druid struct {

BleedCategories core.ExclusiveCategoryArray

PrimalMadnessRageMetrics *core.ResourceMetrics
PrimalPrecisionRecoveryMetrics *core.ResourceMetrics
SavageRoarDurationTable [6]time.Duration

Expand Down Expand Up @@ -131,12 +135,9 @@ func (druid *Druid) AddRaidBuffs(raidBuffs *proto.RaidBuffs) {
// raidBuffs.MoonkinAura = proto.TristateEffect_TristateEffectImproved
// }
// }
// if druid.InForm(Cat|Bear) && druid.Talents.LeaderOfThePack {
// raidBuffs.LeaderOfThePack = max(raidBuffs.LeaderOfThePack, proto.TristateEffect_TristateEffectRegular)
// if druid.Talents.ImprovedLeaderOfThePack > 0 {
// raidBuffs.LeaderOfThePack = proto.TristateEffect_TristateEffectImproved
// }
// }
if druid.InForm(Cat|Bear) && druid.Talents.LeaderOfThePack {
raidBuffs.LeaderOfThePack = true
}
}

// func (druid *Druid) BalanceCritMultiplier() float64 {
Expand Down
23 changes: 8 additions & 15 deletions sim/druid/_enrage.go → sim/druid/enrage.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,20 @@ func (druid *Druid) registerEnrageSpell() {
actionID := core.ActionID{SpellID: 5229}
rageMetrics := druid.NewRageMetrics(actionID)

instantRage := []float64{20, 24, 27, 30}[druid.Talents.Intensity]
instantRage := 20.0
primalMadnessRage := 6.0 * float64(druid.Talents.PrimalMadness)

dmgBonus := 0.05 * float64(druid.Talents.KingOfTheJungle)

t10_4p := druid.HasSetBonus(ItemSetLasherweaveBattlegear, 4)

druid.EnrageAura = druid.RegisterAura(core.Aura{
Label: "Enrage Aura",
ActionID: actionID,
Duration: 10 * time.Second,
OnGain: func(aura *core.Aura, sim *core.Simulation) {
druid.PseudoStats.DamageDealtMultiplier *= 1.0 + dmgBonus
if !t10_4p {
druid.ApplyDynamicEquipScaling(sim, stats.Armor, 0.84)
} else {
druid.PseudoStats.DamageTakenMultiplier *= 0.88
}
druid.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexPhysical] *= 1.0 + dmgBonus
},
OnExpire: func(aura *core.Aura, sim *core.Simulation) {
druid.PseudoStats.DamageDealtMultiplier /= 1.0 + dmgBonus
if !t10_4p {
druid.RemoveDynamicEquipScaling(sim, stats.Armor, 0.84)
} else {
druid.PseudoStats.DamageTakenMultiplier /= 0.88
}
druid.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexPhysical] /= 1.0 + dmgBonus
},
})

Expand Down Expand Up @@ -65,6 +54,10 @@ func (druid *Druid) registerEnrageSpell() {
})

druid.EnrageAura.Activate(sim)

if primalMadnessRage > 0 {
druid.AddRage(sim, primalMadnessRage, druid.PrimalMadnessRageMetrics)
}
},
})

Expand Down
3 changes: 1 addition & 2 deletions sim/druid/feral/feral.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ func NewFeralDruid(character *core.Character, options *proto.Player) *FeralDruid
// //cat.maxRipTicks = cat.MaxRipTicks()

cat.EnableEnergyBar(100.0)

// cat.EnableRageBar(core.RageBarOptions{RageMultiplier: 1, MHSwingSpeed: 2.5})
cat.EnableRageBar(core.RageBarOptions{RageMultiplier: 1, MHSwingSpeed: 2.5})

cat.EnableAutoAttacks(cat, core.AutoAttackOptions{
// Base paw weapon.
Expand Down
10 changes: 9 additions & 1 deletion sim/druid/ferocious_bite.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

func (druid *Druid) registerFerociousBiteSpell() {
dmgPerComboPoint := 290.0 + core.TernaryFloat64(druid.Ranged().ID == 25667, 14, 0)
ripRefreshChance := 0.5 * float64(druid.Talents.BloodInTheWater)

druid.FerociousBite = druid.RegisterSpell(Cat, core.SpellConfig{
ActionID: core.ActionID{SpellID: 48577},
Expand All @@ -31,7 +32,7 @@ func (druid *Druid) registerFerociousBiteSpell() {
},

BonusCritRating: 0 +
core.TernaryFloat64(druid.AssumeBleedActive, 25.0/3.0*float64(druid.Talents.RendAndTear)*core.CritRatingPerCritChance, 0),
core.TernaryFloat64(druid.AssumeBleedActive, []float64{0.0, 8.0, 17.0, 25.0}[druid.Talents.RendAndTear] * core.CritRatingPerCritChance, 0),
DamageMultiplier: 1 + 0.05*float64(druid.Talents.FeralAggression),
CritMultiplier: druid.DefaultMeleeCritMultiplier(),
ThreatMultiplier: 1,
Expand All @@ -52,6 +53,13 @@ func (druid *Druid) registerFerociousBiteSpell() {
if result.Landed() {
druid.SpendEnergy(sim, excessEnergy, spell.Cost.(*core.EnergyCost).ResourceMetrics)
druid.SpendComboPoints(sim, spell.ComboPointMetrics())

// Blood in the Water
ripDot := druid.Rip.Dot(target)

if sim.IsExecutePhase25() && ripDot.IsActive() && sim.Proc(ripRefreshChance, "Blood in the Water") {
ripDot.Apply(sim)
}
} else {
spell.IssueRefund(sim)
}
Expand Down
9 changes: 5 additions & 4 deletions sim/druid/forms.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ func (druid *Druid) registerCatFormSpell() {
Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagAPL,

ManaCost: core.ManaCostOptions{
BaseCost: 0.35,
Multiplier: (1 - 0.2*float64(druid.Talents.KingOfTheJungle)) * (1 - 0.1*float64(druid.Talents.NaturalShapeshifter)),
BaseCost: 0.05,
Multiplier: 1 - 0.1*float64(druid.Talents.NaturalShapeshifter),
},
Cast: core.CastConfig{
DefaultCast: core.Cast{
Expand Down Expand Up @@ -279,6 +279,7 @@ func (druid *Druid) registerBearFormSpell() {
druid.UpdateManaRegenRates()
druid.EnrageAura.Deactivate(sim)
druid.MaulQueueAura.Deactivate(sim)
druid.PulverizeAura.Deactivate(sim)
}
},
})
Expand All @@ -292,8 +293,8 @@ func (druid *Druid) registerBearFormSpell() {
Flags: core.SpellFlagNoOnCastComplete | core.SpellFlagAPL,

ManaCost: core.ManaCostOptions{
BaseCost: 0.35,
Multiplier: (1 - 0.2*float64(druid.Talents.KingOfTheJungle)) * (1 - 0.1*float64(druid.Talents.NaturalShapeshifter)),
BaseCost: 0.05,
Multiplier: 1 - 0.1*float64(druid.Talents.NaturalShapeshifter),
},
Cast: core.CastConfig{
DefaultCast: core.Cast{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,45 @@ func (druid *Druid) registerFrenziedRegenerationCD() {

cdTimer := druid.NewTimer()
cd := time.Minute * 3
healingMulti := core.TernaryFloat64(druid.HasMajorGlyph(proto.DruidMajorGlyph_GlyphOfFrenziedRegeneration), 1.2, 1.0)
isGlyphed := druid.HasMajorGlyph(proto.DruidMajorGlyph_GlyphOfFrenziedRegeneration)
healingMulti := core.TernaryFloat64(isGlyphed, 1.3, 1.0)

var bonusHealth float64
druid.FrenziedRegenerationAura = druid.RegisterAura(core.Aura{
Label: "Frenzied Regeneration",
ActionID: actionID,
Duration: time.Second * 10,
Duration: time.Second * 20,
OnGain: func(aura *core.Aura, sim *core.Simulation) {
druid.PseudoStats.HealingTakenMultiplier *= healingMulti
bonusHealth = druid.MaxHealth() * 0.3
druid.UpdateMaxHealth(sim, bonusHealth, healthMetrics)
},

OnExpire: func(aura *core.Aura, sim *core.Simulation) {
druid.PseudoStats.HealingTakenMultiplier /= healingMulti
druid.UpdateMaxHealth(sim, -bonusHealth, healthMetrics)
},
})

druid.FrenziedRegeneration = druid.RegisterSpell(Bear, core.SpellConfig{
ActionID: actionID,
Cast: core.CastConfig{
DefaultCast: core.Cast{
GCD: core.GCDDefault,
},
CD: core.Cooldown{
Timer: cdTimer,
Duration: cd,
},
IgnoreHaste: true,
},
ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
druid.FrenziedRegenerationAura.Activate(sim)

if isGlyphed {
return
}

core.StartPeriodicAction(sim, core.PeriodicActionOptions{
NumTicks: 10,
NumTicks: 20,
Period: time.Second * 1,
Priority: core.ActionPriorityDOT,
OnAction: func(sim *core.Simulation) {
rageDumped := min(druid.CurrentRage(), 10.0)
healthGained := rageDumped * 0.3 / 100 * druid.MaxHealth() * druid.PseudoStats.HealingTakenMultiplier
Expand All @@ -55,8 +63,6 @@ func (druid *Druid) registerFrenziedRegenerationCD() {
}
},
})

druid.FrenziedRegenerationAura.Activate(sim)
},
})

Expand Down
13 changes: 7 additions & 6 deletions sim/druid/_maul.go → sim/druid/maul.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ func (druid *Druid) registerMaulSpell() {
}

numHits := core.TernaryInt32(druid.HasMajorGlyph(proto.DruidMajorGlyph_GlyphOfMaul) && druid.Env.GetNumTargets() > 1, 2, 1)
rendAndTearMod := []float64{1.0, 1.07, 1.13, 1.2}[druid.Talents.RendAndTear]

druid.Maul = druid.RegisterSpell(Bear, core.SpellConfig{
ActionID: core.ActionID{SpellID: 48480},
Expand All @@ -22,14 +23,15 @@ func (druid *Druid) registerMaulSpell() {
Flags: core.SpellFlagMeleeMetrics | core.SpellFlagIncludeTargetBonusDamage | core.SpellFlagNoOnCastComplete,

RageCost: core.RageCostOptions{
Cost: 15 - float64(druid.Talents.Ferocity),
Cost: 30,
Refund: 0.8,
},

DamageMultiplier: 1 + 0.1*float64(druid.Talents.SavageFury),
CritMultiplier: druid.MeleeCritMultiplier(Bear),
DamageMultiplier: 1,
CritMultiplier: druid.DefaultMeleeCritMultiplier(),
ThreatMultiplier: 1,
FlatThreatBonus: 424,
BonusCoefficient: 1,

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
// Need to specially deactivate CC here in case maul is cast simultaneously with another spell.
Expand All @@ -42,14 +44,13 @@ func (druid *Druid) registerMaulSpell() {
modifier += .3
}
if druid.AssumeBleedActive || druid.Rip.Dot(target).IsActive() || druid.Rake.Dot(target).IsActive() || druid.Lacerate.Dot(target).IsActive() {
modifier *= 1.0 + (0.04 * float64(druid.Talents.RendAndTear))
modifier *= rendAndTearMod
}

curTarget := target
for hitIndex := int32(0); hitIndex < numHits; hitIndex++ {
baseDamage := flatBaseDamage +
spell.Unit.MHWeaponDamage(sim, spell.MeleeAttackPower()) +
spell.BonusWeaponDamage()
spell.Unit.MHWeaponDamage(sim, spell.MeleeAttackPower())
baseDamage *= modifier

result := spell.CalcAndDealDamage(sim, curTarget, baseDamage, spell.OutcomeMeleeSpecialHitAndCrit)
Expand Down
Loading

0 comments on commit 5b048a1

Please sign in to comment.