-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementing the bear sim #1185
base: master
Are you sure you want to change the base?
Conversation
This reverts commit 9317beb.
This reverts commit 6860e9c.
Uncomment/rename most of the disabled Feral Tank Druid implementation. Fix most of the errors associated with the baseline Feral Tank Druid implementation, making it at least possible to run.
Co-authored-by: Kayla Glick <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't a review of the code correctness or quality, just of the accuracy around in-game numbers and mechanics.
I had two stand alone comments before I started making them as part of a review, so check those out too.
// https://www.wowhead.com/classic/item=228293/essence-of-the-pure-flame | ||
// Equip: When struck in combat inflicts 100 Nature damage to the attacker. Causes twice as much threat as damage dealt. | ||
core.NewItemEffect(RazorbrambleLeathers, func(agent core.Agent) { | ||
DamageShieldWithThreatMod(agent.GetCharacter(), 1213813, 100, 2, "Damage Shield Razorbramble Leathers") | ||
}) | ||
core.NewItemEffect(RazorbrambleShoulderpads, func(agent core.Agent) { | ||
DamageShieldWithThreatMod(agent.GetCharacter(), 1213816, 80, 2, "Damage Shield Razorbramble Shoulderpads") | ||
}) | ||
core.NewItemEffect(RazorbrambleCowl, func(agent core.Agent) { | ||
DamageShieldWithThreatMod(agent.GetCharacter(), 1213813, 100, 2, "Damage Shield Razorbramble Cowl") | ||
}) | ||
core.NewItemEffect(LodestoneofRetaliation, func(agent core.Agent) { | ||
DamageShieldWithThreatMod(agent.GetCharacter(), 1213816, 80, 2, "Damage Shield Lodestone of Retaliation") | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't realize these were included here but I already implemented these in https://github.com/wowsims/sod/blob/master/sim/common/sod/item_effects/phase_6.go the other day while adding in all of the missing Thorns effects
phase: Phase.Phase5, | ||
status: LaunchStatus.Alpha, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we go ahead and update this to Phase 6? Also in index.html
?
queuedRealismICD := &core.Cooldown{ | ||
Timer: bear.NewTimer(), | ||
Duration: core.SpellBatchWindow * 10, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ Since this is only use for Maul should we just initialize this inside of the Maul code?
|
||
var ItemSetCoagulateBloodguardsLeathers = core.NewItemSet(core.ItemSet{ | ||
Name: "Coagulate Bloodguard's Leathers", | ||
Bonuses: map[int32]core.ApplyEffect{ | ||
2: func(agent core.Agent) { | ||
c := agent.GetCharacter() | ||
c.AddStat(stats.Strength, 10) | ||
}, | ||
3: func(agent core.Agent) { | ||
druid := agent.(DruidAgent).GetDruid() | ||
|
||
// Power Shredder | ||
procAura := druid.GetOrRegisterAura(core.Aura{ | ||
Label: "Power Shredder Proc", | ||
ActionID: core.ActionID{SpellID: 449925}, | ||
Duration: time.Second * 10, | ||
OnGain: func(aura *core.Aura, sim *core.Simulation) { | ||
druid.CatForm.Cost.Multiplier -= 30 | ||
//druid.BearForm.CostMultiplier -= 0.3 | ||
}, | ||
OnExpire: func(aura *core.Aura, sim *core.Simulation) { | ||
druid.CatForm.Cost.Multiplier += 30 | ||
//druid.BearForm.CostMultiplier += 0.3 | ||
}, | ||
OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) { | ||
if spell == druid.CatForm.Spell /* || spell == druid.BearForm.Spell */ { | ||
aura.Deactivate(sim) | ||
} | ||
}, | ||
}) | ||
|
||
core.MakeProcTriggerAura(&druid.Unit, core.ProcTrigger{ | ||
Name: "Power Shredder", | ||
Callback: core.CallbackOnCastComplete, | ||
Handler: func(sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { | ||
if spell.SpellCode == SpellCode_DruidShred { | ||
procAura.Activate(sim) | ||
} | ||
}, | ||
}) | ||
|
||
// Precise Claws should be implemented in the bear form spells when those get added back | ||
// Adds 2% hit while in bear/dire bear forms | ||
}, | ||
}, | ||
}) | ||
|
||
var ItemSetExiledProphetsRaiment = core.NewItemSet(core.ItemSet{ | ||
Name: "Exiled Prophet's Raiment", | ||
Bonuses: map[int32]core.ApplyEffect{ | ||
2: func(agent core.Agent) { | ||
c := agent.GetCharacter() | ||
c.AddStat(stats.MP5, 4) | ||
}, | ||
3: func(agent core.Agent) { | ||
druid := agent.(DruidAgent).GetDruid() | ||
// TODO: Not tested because Druid doesn't have healing spells implemented at the moment | ||
if druid.HasRune(proto.DruidRune_RuneFeetDreamstate) { | ||
core.MakeProcTriggerAura(&druid.Unit, core.ProcTrigger{ | ||
Name: "Exiled Dreamer", | ||
Callback: core.CallbackOnHealDealt, | ||
ProcMask: core.ProcMaskSpellHealing, | ||
Outcome: core.OutcomeCrit, | ||
ProcChance: 0.5, | ||
Handler: func(sim *core.Simulation, _ *core.Spell, _ *core.SpellResult) { | ||
druid.DreamstateManaRegenAura.Activate(sim) | ||
}, | ||
}) | ||
} | ||
}, | ||
}, | ||
}) | ||
|
||
var ItemSetEmeraldWatcherVestments = core.NewItemSet(core.ItemSet{ | ||
Name: "Emerald Watcher Vestments", | ||
Bonuses: map[int32]core.ApplyEffect{ | ||
3: func(agent core.Agent) { | ||
c := agent.GetCharacter() | ||
c.AddStat(stats.Stamina, 10) | ||
}, | ||
6: func(agent core.Agent) { | ||
c := agent.GetCharacter() | ||
c.AddStat(stats.SpellPower, 12) | ||
}, | ||
}, | ||
}) | ||
|
||
var ItemSetEmeraldDreamkeeperGarb = core.NewItemSet(core.ItemSet{ | ||
Name: "Emerald Dreamkeeper Garb", | ||
Bonuses: map[int32]core.ApplyEffect{ | ||
3: func(agent core.Agent) { | ||
c := agent.GetCharacter() | ||
c.AddStat(stats.Stamina, 10) | ||
}, | ||
6: func(agent core.Agent) { | ||
c := agent.GetCharacter() | ||
c.AddStat(stats.HealingPower, 22) | ||
}, | ||
}, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Were these removed by mistake?
@@ -225,7 +226,8 @@ var ItemSetCenarionRage = core.NewItemSet(core.ItemSet{ | |||
}, | |||
// Reduces the cooldown of Enrage by 30 sec and it no longer reduces your armor. | |||
4: func(agent core.Agent) { | |||
// TODO: Enrage | |||
druid := agent.(DruidAgent).GetDruid() | |||
druid.Enrage.CD.Duration -= time.Second * 30 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changed since you put up the PR but we should modify the new CD FlatModifier
field for effects like this now. CDs store the original Duration, a cumulative flat modifier like this one, and then a cumulative multiplier, then the sim calculates the real duration from those like the game does
druid.Enrage.CD.Duration -= time.Second * 30 | |
druid.Enrage.CD.FlatModifier -= time.Second * 30 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh also since this is also a rune, we'll want to follow the pattern of creating a function that does the aura check / creates an aura to handle the set bonus. Following the other example, create the aura, then in the OnInit
you can make the change to Enrage's CD FlatModifier
}, | ||
druid.OnSpellRegistered(func(spell *core.Spell) { | ||
if spell.SpellCode == SpellCode_DruidMangleBear { | ||
spell.CD.Duration -= 1500 * time.Millisecond |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spell.CD.Duration -= 1500 * time.Millisecond | |
spell.CD.FlatModifier-= 1500 * time.Millisecond |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh and also this should go inside of the aura's OnInit
function to work properly with the set bonus / shoulder rune
label := "S03 - Item - TAQ - Druid - Guardian 2P Bonus" | ||
if druid.HasAura(label) { | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will end up breaking if using the set bonus + corresponding should rune with this change. We should leave the HasAura
check to prevent that
Tank2PieceAqAura *core.Aura | ||
Tank2PieceAqProcAura *core.Aura |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these two auras used outside of the set bonus code? If not you should be able to remove these and just store them as locals
// TODO | ||
}, | ||
}) | ||
druid.CenarionRageThreatBonus = .2 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will double dip without the aura code here. What you can do for this bonus is in the OnInit
, grab the old OnGain
and OnExpire
for bear form, then override them, calling the old ones and then also adding the threat mod here:
OnInit: func(aura *core.Aura, sim *core.Simulation) {
oldOnGain := druid.BearFormAura.OnGain
druid.BearFormAura.OnGain = func(aura *core.Aura, sim *core.Simulation) {
oldOnGain(aura, sim)
druid.Pseudostats.ThreatMultiplier += 0.20
}
oldOnExpire := druid.BearFormAura.OnExpire
druid.BearFormAura.OnExpire = func(aura *core.Aura, sim *core.Simulation) {
oldOnExpire(aura, sim)
druid.Pseudostats.ThreatMultiplier -= 0.20
}
}
@@ -237,55 +236,76 @@ var ItemSetFuryOfStormrage = core.NewItemSet(core.ItemSet{ | |||
|
|||
// Swipe(Bear) also causes your Maul to hit 1 additional target for the next 6 sec. | |||
func (druid *Druid) applyT2Guardian2PBonus() { | |||
label := "S03 - Item - T2 - Druid - Guardian 2P Bonus" | |||
if druid.HasAura(label) { | |||
if druid.Env.GetNumTargets() == 1 { | |||
return | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll also want to keep this aura check but it can go after the target count check, that way we don't double dip (I think it would just error from the same spell registered twice)
if !druid.HasRune(proto.DruidRune_RuneLegsLacerate) { | ||
return | ||
} | ||
|
||
label := "S03 - Item - T2 - Druid - Guardian 4P Bonus" | ||
if druid.HasAura(label) { | ||
return | ||
} | ||
|
||
druid.RegisterAura(core.Aura{ | ||
Label: label, | ||
OnInit: func(aura *core.Aura, sim *core.Simulation) { | ||
// TODO | ||
}, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also needs to have the aura stuff here because of the tier bonus + shoulder runes, so you could move this bonus crit rating into the OnInit
here and that should be fine.
Another way we could do this is to override the ApplyEffects
of the affected spells so that before calling the old one we check if lacerate is active then add 5% bonus crit to the spell, call the original ApplyEffects
, then remove the bonus crit. Basically what you did in the spell code but all in one place.
You would also want to add a check in this function that returns if the druid doesn't have the Lacerate rune
// This is all inside of the aura's OnInit
for _, spell := range []*DruidSpell{druid.MangleBear, druid.SwipeBear, druid.Maul, druid.Lacerate} {
if spell == nil {
continue
}
oldApplyEffects := spell.ApplyEffects
spell.ApplyEffects = func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
bonusCrit := 0.0
if druid.LacerateBleed.Dot(target).GetStacks() > 0 {
bonusCrit = 5 * core.CritRatingPerCritChance
}
spell.BonusCritRating += bonusCrit
oldApplyEffects(sim, target, spell)
spell.BonusCritRating -= bonusCrit
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you implemented this here then you would want to remove the similar code from the mangle, swipe, lacerate, and maul spells
if !druid.HasRune(proto.DruidRune_RuneLegsLacerate) { | ||
return | ||
} | ||
|
||
label := "S03 - Item - T2 - Druid - Guardian 6P Bonus" | ||
if druid.HasAura(label) { | ||
return | ||
} | ||
|
||
druid.RegisterAura(core.Aura{ | ||
Label: label, | ||
OnInit: func(aura *core.Aura, sim *core.Simulation) { | ||
// TODO | ||
}, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This stuff also needs to come back but bumping this set bonus as unfinished
Duration: time.Second * 10, | ||
MaxStacks: 5, | ||
OnStacksChange: func(aura *core.Aura, sim *core.Simulation, oldStacks, newStacks int32) { | ||
druid.MangleBear.DamageMultiplierAdditive += 0.1 * float64(newStacks-oldStacks) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would need a guard checking if druid.MangleBear != nil
to prevent erroring if not using the Mangle rune
druid.Tank2PieceAqAura = druid.RegisterAura(core.Aura{ | ||
Label: "S03 - Item - TAQ - Druid - Guardian 2P Bonus", | ||
Duration: core.NeverExpires, | ||
OnReset: func(aura *core.Aura, sim *core.Simulation) { | ||
aura.Activate(sim) | ||
}, | ||
OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { | ||
if druid.form == Bear && spell.ProcMask.Matches(core.ProcMaskMelee) && result.Outcome.Matches(core.OutcomeDodge) { | ||
druid.Tank2PieceAqProcAura.Activate(sim) | ||
druid.Tank2PieceAqProcAura.AddStack(sim) | ||
} | ||
}, | ||
OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { | ||
if spell.SpellCode == SpellCode_DruidMangleBear || spell.SpellCode == SpellCode_DruidSwipeBear { | ||
druid.Tank2PieceAqProcAura.SetStacks(sim, 0) | ||
} | ||
}, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just simplifying with the MakePermanent
helper
druid.Tank2PieceAqAura = druid.RegisterAura(core.Aura{ | |
Label: "S03 - Item - TAQ - Druid - Guardian 2P Bonus", | |
Duration: core.NeverExpires, | |
OnReset: func(aura *core.Aura, sim *core.Simulation) { | |
aura.Activate(sim) | |
}, | |
OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { | |
if druid.form == Bear && spell.ProcMask.Matches(core.ProcMaskMelee) && result.Outcome.Matches(core.OutcomeDodge) { | |
druid.Tank2PieceAqProcAura.Activate(sim) | |
druid.Tank2PieceAqProcAura.AddStack(sim) | |
} | |
}, | |
OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { | |
if spell.SpellCode == SpellCode_DruidMangleBear || spell.SpellCode == SpellCode_DruidSwipeBear { | |
druid.Tank2PieceAqProcAura.SetStacks(sim, 0) | |
} | |
}, | |
}) | |
druid.Tank2PieceAqAura = core.MakePermanent(druid.RegisterAura(core.Aura{ | |
Label: "S03 - Item - TAQ - Druid - Guardian 2P Bonus", | |
OnSpellHitTaken: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { | |
if druid.form == Bear && spell.ProcMask.Matches(core.ProcMaskMelee) && result.Outcome.Matches(core.OutcomeDodge) { | |
druid.Tank2PieceAqProcAura.Activate(sim) | |
druid.Tank2PieceAqProcAura.AddStack(sim) | |
} | |
}, | |
OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { | |
if spell.SpellCode == SpellCode_DruidMangleBear || spell.SpellCode == SpellCode_DruidSwipeBear { | |
druid.Tank2PieceAqProcAura.SetStacks(sim, 0) | |
} | |
}, | |
})) |
@@ -15,6 +15,7 @@ const ( | |||
IdolOfFerocity = 22397 | |||
IdolOfTheMoon = 23197 | |||
IdolOfBrutality = 23198 | |||
IdolOfCruelty = 232424 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be implemented still?
|
||
DamageMultiplier: 1, | ||
ThreatMultiplier: 3.33, | ||
// TODO: Berserk 3 target lacerate cleave - Saeyon |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My suggestion for this would be to bake a target count into lacerate's code and default it to. Maybe you can use an attribute on the druid to track the current target count that lacerate should hit. Sounds like there's two different effects that make lacerate affect multiple targets?
}, | ||
}, | ||
// TODO: Berserk 3 target mangle cleave - Saeyon |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above with lacerate ^ bake a target count in by default, start with the current target then N
times use the next target unit to apply the effects too
No description provided.