Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sim/core/debuffs.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ func MangleAura(target *Target) *Aura {
return target.GetOrRegisterAura(Aura{
Label: "Mangle",
ActionID: ActionID{SpellID: 33876},
Duration: time.Second * 12,
OnGain: func(aura *Aura, sim *Simulation) {
aura.Unit.PseudoStats.PeriodicPhysicalDamageTakenMultiplier *= 1.3
},
Expand Down
37 changes: 29 additions & 8 deletions sim/druid/druid.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Druid struct {

RebirthUsed bool
CatForm bool
Wolfshead bool

FaerieFire *core.Spell
Hurricane *core.Spell
Expand All @@ -24,6 +25,7 @@ type Druid struct {
Starfire6 *core.Spell
Starfire8 *core.Spell
Wrath *core.Spell
Powershift *core.Spell

InsectSwarmDot *core.Dot
MoonfireDot *core.Dot
Expand Down Expand Up @@ -51,6 +53,7 @@ func (druid *Druid) AddRaidBuffs(raidBuffs *proto.RaidBuffs) {
}

const ravenGoddessItemID = 32387
const wolfsheadItemID = 8345

func (druid *Druid) AddPartyBuffs(partyBuffs *proto.PartyBuffs) {
if druid.Talents.MoonkinForm { // assume if you have moonkin talent you are using it.
Expand All @@ -74,14 +77,23 @@ func (druid *Druid) AddPartyBuffs(partyBuffs *proto.PartyBuffs) {
}

func (druid *Druid) Init(sim *core.Simulation) {
druid.registerFaerieFireSpell(sim)
druid.registerHurricaneSpell(sim)
druid.registerInsectSwarmSpell(sim)
druid.registerMoonfireSpell(sim)
druid.registerRebirthSpell(sim)
druid.Starfire8 = druid.newStarfireSpell(sim, 8)
druid.Starfire6 = druid.newStarfireSpell(sim, 6)
druid.registerWrathSpell(sim)
if druid.CatForm {
druid.registerPowershiftSpell(sim)
druid.registerRipSpell(sim)
} else {
druid.registerFaerieFireSpell(sim)
druid.registerHurricaneSpell(sim)
druid.registerInsectSwarmSpell(sim)
druid.registerMoonfireSpell(sim)
druid.registerRebirthSpell(sim)
druid.Starfire8 = druid.newStarfireSpell(sim, 8)
druid.Starfire6 = druid.newStarfireSpell(sim, 6)
druid.registerWrathSpell(sim)
}

if druid.CatForm {
druid.DelayCooldownsForArmorDebuffs(sim)
}
}

func (druid *Druid) Reset(sim *core.Simulation) {
Expand All @@ -99,6 +111,7 @@ func New(char core.Character, selfBuffs SelfBuffs, talents proto.DruidTalents) *
Talents: talents,
RebirthUsed: false,
CatForm: false,
Wolfshead: false,
}
druid.EnableManaBar()

Expand Down Expand Up @@ -126,6 +139,14 @@ func New(char core.Character, selfBuffs SelfBuffs, talents proto.DruidTalents) *
},
})

// Check if Wolfshead Helm is equipped
for _, e := range druid.Equip {
if e.ID == wolfsheadItemID {
druid.Wolfshead = true
break
}
}

return druid
}

Expand Down
14 changes: 12 additions & 2 deletions sim/druid/feral/feral.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ func NewFeralDruid(character core.Character, options proto.Player) *FeralDruid {

// Set up base paw weapon. Assume that Predatory Instincts is a primary rather than secondary modifier for now, but this needs to confirmed!
primaryModifier := 1 + 0.02*float64(cat.Talents.PredatoryInstincts)
critMultiplier := cat.MeleeCritMultiplier(primaryModifier, 0)
cat.critMultiplier = cat.MeleeCritMultiplier(primaryModifier, 0)
basePaw := core.Weapon{
BaseDamageMin: 43.5,
BaseDamageMax: 66.5,
SwingSpeed: 1.0,
NormalizedSwingSpeed: 1.0,
SwingDuration: time.Duration(1.0 * float64(time.Second)),
CritMultiplier: critMultiplier,
CritMultiplier: cat.critMultiplier,
}
cat.EnableAutoAttacks(cat, core.AutoAttackOptions{
MainHand: basePaw,
Expand Down Expand Up @@ -93,9 +93,19 @@ type FeralDruid struct {
*druid.Druid

Rotation proto.FeralDruid_Rotation

readyToShift bool
waitingForTick bool
critMultiplier float64
}

// GetDruid is to implement druid.Agent (supports nordrassil set bonus)
func (cat *FeralDruid) GetDruid() *druid.Druid {
return cat.Druid
}

func (cat *FeralDruid) Reset(sim *core.Simulation) {
cat.Druid.Reset(sim)
cat.readyToShift = false
cat.waitingForTick = false
}
53 changes: 45 additions & 8 deletions sim/druid/feral/rotation.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,49 @@
package feral

import (
"github.com/wowsims/tbc/sim/core"
)
import (
"github.com/wowsims/tbc/sim/core"
"github.com/wowsims/tbc/sim/core/proto"
)

func (cat *FeralDruid) OnGCDReady(sim *core.Simulation) {
cat.doRotation(sim)
}
func (cat *FeralDruid) OnGCDReady(sim *core.Simulation) {
cat.doRotation(sim)
}

func (cat *FeralDruid) doRotation(sim *core.Simulation) {
}
func (cat *FeralDruid) doRotation(sim *core.Simulation) {
// If we're out of form because we just cast Innervate, always shift
if !cat.CatForm {
cat.Powershift.Cast(sim, nil)
return
}

// If we previously decided to shift, then execute the shift now once the input delay is over.
if cat.readyToShift {
cat.innervateOrShift(sim)
return
}

// Get current Energy and CP
energy := cat.CurrentEnergy()
comboPoints := cat.ComboPoints()

// Decide whether to cast Rip as our next special
ripNow := cat.ShouldCastRip(sim, cat.Rotation)

// Decide whether to cast Mangle as our next special
mangleNow := !ripNow && !cat.MangleAura.IsActive()
}

func (cat *FeralDruid) innervateOrShift(sim *core.Simulation) {
cat.waitingForTick = false

// If we have just now decided to shift, then we do not execute the shift immediately, but instead trigger an input delay for realism.
if !cat.readyToShift {
cat.readyToShift = true
return
}

cat.readyToShift = false

// Logic for Innervate and Haste Pot usage will go here. For now we just execute simple powershifts without bundling any caster form CDs.
cat.Powershift.Cast(sim, nil)
}
55 changes: 55 additions & 0 deletions sim/druid/ferocious_bite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package druid

import (
"time"

"github.com/wowsims/tbc/sim/core"
"github.com/wowsims/tbc/sim/core/stats"
)

var FerociousBiteActionID = core.ActionID{SpellID: 24248}
var FerociousBiteEnergyCost = 35.0

func (druid *Druid) registerFerociousBiteSpell(sim *core.Simulation) {
druid.FerociousBite = druid.RegisterSpell(core.SpellConfig{
ActionID: FerociousBiteActionID,
SpellSchool: core.SpellSchoolPhysical,
SpellExtras: core.SpellExtrasMeleeMetrics,

ResourceType: stats.Energy,
BaseCost: FerociousBiteEnergyCost,

Cast: core.CastConfig{
DefaultCast: core.Cast{
Cost: FerociousBiteEnergyCost,
GCD: time.Second,
},
IgnoreHaste: true,
},

ApplyEffects: core.ApplyEffectFuncDirectDamage(core.SpellEffect{
ProcMask: core.ProcMaskMeleeMHSpecial,
DamageMultiplier: 1 + 0.03*float64(druid.Talents.FeralAggression),
ThreatMultiplier: 1,
BaseDamage: core.BaseDamageConfig{
Calculator: func(sim *core.Simulation, hitEffect *core.SpellEffect, spell *core.Spell) float64 {
comboPoints := float64(druid.ComboPoints())
excessEnergy := druid.CurrentEnergy() - FerociousBiteEnergyCost
base := 57.0 + 169.0*comboPoints + 4.1*excessEnergy
roll := sim.RandomFloat("Ferocious Bite") * 66.0
return base + roll + hitEffect.MeleeAttackPower(spell.Character)*0.05*comboPoints
},
TargetSpellCoefficient: 1,
},
OutcomeApplier: core.OutcomeFuncMeleeSpecialHitAndCrit(druid.critMultiplier),
OnSpellHit: func(sim *core.Simulation, spell *core.Spell, spellEffect *core.SpellEffect) {
if spellEffect.Landed() {
druid.SpendComboPoints(sim, spell.ActionID)
}
},
}),
})
}

func (druid *Druid) ShouldCastBite(sim *core.Simulation, rotation proto.FeralDruid_Rotation) bool {
}
13 changes: 13 additions & 0 deletions sim/druid/items.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func init() {
core.AddItemSet(&ItemSetMalorne)
core.AddItemSet(&ItemSetNordrassil)
core.AddItemSet(&ItemSetThunderheart)
core.AddItemSet(&ItemSetThunderheartFeral)
}

var ItemSetMalorne = core.ItemSet{
Expand Down Expand Up @@ -69,6 +70,18 @@ var ItemSetThunderheart = core.ItemSet{
},
}

var ItemSetThunderheartFeral = core.ItemSet{
Name: "Thunderheart Harness",
Bonuses: map[int32]core.ApplyEffect{
2: func(agent core.Agent) {
// handled in mangle.go in template construction
},
4: func(agent core.Agent) {
// handled in rip.go and bite.go in template construction
},
},
}

func ApplyLivingRootoftheWildheart(agent core.Agent) {
druidAgent := agent.(Agent)
druid := druidAgent.GetDruid()
Expand Down
53 changes: 53 additions & 0 deletions sim/druid/mangle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package druid

import (
"github.com/wowsims/tbc/sim/core"
"github.com/wowsims/tbc/sim/core/proto"
"github.com/wowsims/tbc/sim/core/stats"
)

var MangleActionID = core.ActionID{SpellID: 33983}

func (druid *Druid) registerMangleSpell(sim *core.Simulation) {
druid.MangleAura = core.MangleAura(sim.GetPrimaryTarget())

if druid.Rotation.MangleBot {
druid.MangleAura = core.MakePermanent(druid.MangleAura)
}

energyCost := 45.0 - float64(druid.Talents.Ferocity) - core.TernaryFloat64(ItemSetThunderheartFeral.CharacterHasSetBonus(&druid.Character, 2), 5.0, 0)
refundAmount := energyCost * 0.8

druid.Mangle = druid.RegisterSpell(core.SpellConfig{
ActionID: MangleActionID,
SpellSchool: core.SpellSchoolPhysical,
SpellExtras: core.SpellExtrasMeleeMetrics,

ResourceType: stats.Energy,
BaseCost: energyCost,

Cast: core.CastConfig{
DefaultCast: core.Cast{
Cost: energyCost,
GCD: time.Second,
},
IgnoreHaste: true,
},

ApplyEffects: core.ApplyEffectFuncDirectDamage(core.SpellEffect{
ProcMask: core.ProcMaskMeleeMHSpecial,
DamageMultiplier: 1 + 0.1*float64(druid.Talents.SavageFury),
ThreatMultiplier: 1,
BaseDamage: core.BaseDamageConfigMeleeWeapon(core.MainHand, false, 264.0, 1.6, true),
OutcomeApplier: core.OutcomeFuncMeleeSpecialHitAndCrit(druid.critMultiplier),
OnSpellHit: func(sim *core.Simulation, spell *core.Spell, spellEffect *core.SpellEffect) {
if spellEffect.Landed() {
druid.AddComboPoints(sim, 1, MangleActionID)
druid.MangleAura.Activate()
} else {
druid.AddEnergy(sim, refundAmount, core.ActionID{OtherID: proto.OtherAction_OtherActionRefund})
}
},
}),
})
}
39 changes: 39 additions & 0 deletions sim/druid/powershift.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package druid

import (
"time"

"github.com/wowsims/tbc/sim/core"
"github.com/wowsims/tbc/sim/core/stats"
)

var PowershiftActionID = core.ActionID{SpellID: 768}

func (druid *Druid) registerPowershiftSpell(sim *core.Simulation) {
baseCost := 830.0
finalEnergy := 40.0

if druid.Wolfshead {
finalEnergy += 20.0
}

druid.Powershift = druid.RegisterSpell(core.SpellConfig{
ActionID: PowershiftActionID,

ResourceType: stats.Mana,
BaseCost: baseCost,

Cast: core.CastConfig{
DefaultCast: core.Cast{
Cost: baseCost * (1 - 0.1*float64(druid.Talents.NaturalShapeshifter)),
GCD: core.GCDDefault,
},
},

ApplyEffects: func(sim *core.Simulation, _ *core.Target, spell *core.Spell) {
currentEnergy := druid.CurrentEnergy()
druid.AddEnergy(sim, finalEnergy - currentEnergy, PowershiftActionID)
druid.CatForm = true
},
})
}
Loading