Skip to content
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

Open
wants to merge 62 commits into
base: master
Choose a base branch
from

Conversation

emsimpson92
Copy link
Contributor

No description provided.

@emsimpson92 emsimpson92 mentioned this pull request Nov 28, 2024
21 tasks
@emsimpson92 emsimpson92 changed the base branch from master to bearly-holding-on November 30, 2024 01:19
@emsimpson92 emsimpson92 changed the title Miscellaneous Bear Tank Updates Implementing the bear sim Nov 30, 2024
Copy link

@balor89 balor89 left a 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.

sim/druid/forms.go Show resolved Hide resolved
sim/druid/frenzied_regeneration.go Show resolved Hide resolved
sim/druid/item_sets_pve.go Outdated Show resolved Hide resolved
sim/druid/item_sets_pve.go Outdated Show resolved Hide resolved
sim/druid/lacerate.go Outdated Show resolved Hide resolved
sim/druid/lacerate.go Outdated Show resolved Hide resolved
sim/druid/mangle.go Outdated Show resolved Hide resolved
sim/druid/mangle.go Show resolved Hide resolved
sim/druid/mangle.go Outdated Show resolved Hide resolved
sim/druid/swipe.go Outdated Show resolved Hide resolved
@emsimpson92 emsimpson92 changed the base branch from bearly-holding-on to master January 12, 2025 18:18
Comment on lines +2385 to +2398
// 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")
})
Copy link
Collaborator

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

Comment on lines +36 to +37
phase: Phase.Phase5,
status: LaunchStatus.Alpha,
Copy link
Collaborator

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?

Comment on lines +79 to +82
queuedRealismICD := &core.Cooldown{
Timer: bear.NewTimer(),
Duration: core.SpellBatchWindow * 10,
}
Copy link
Collaborator

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?

Comment on lines -11 to -132

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)
},
},
})
Copy link
Collaborator

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
Copy link
Collaborator

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

Suggested change
druid.Enrage.CD.Duration -= time.Second * 30
druid.Enrage.CD.FlatModifier -= time.Second * 30

Copy link
Collaborator

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
spell.CD.Duration -= 1500 * time.Millisecond
spell.CD.FlatModifier-= 1500 * time.Millisecond

Copy link
Collaborator

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

Comment on lines -186 to -189
label := "S03 - Item - TAQ - Druid - Guardian 2P Bonus"
if druid.HasAura(label) {
return
}
Copy link
Collaborator

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

Comment on lines +133 to +134
Tank2PieceAqAura *core.Aura
Tank2PieceAqProcAura *core.Aura
Copy link
Collaborator

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
}
Copy link
Collaborator

@kayla-glick kayla-glick Jan 14, 2025

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
}
Copy link
Collaborator

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)

Comment on lines -255 to -269
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
},
})
Copy link
Collaborator

@kayla-glick kayla-glick Jan 14, 2025

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
  }
}

Copy link
Collaborator

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

Comment on lines -274 to -288
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
},
})
Copy link
Collaborator

@kayla-glick kayla-glick Jan 14, 2025

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)
Copy link
Collaborator

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

Comment on lines +197 to 214
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)
}
},
})
Copy link
Collaborator

@kayla-glick kayla-glick Jan 14, 2025

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

Suggested change
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
Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

@kayla-glick kayla-glick Jan 14, 2025

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants