Skip to content

Commit

Permalink
Merge pull request #27 from wowsims/fix_misery
Browse files Browse the repository at this point in the history
Fix misery
  • Loading branch information
lologarithm authored Nov 12, 2021
2 parents 515cac6 + 1ad32ff commit df405f8
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 69 deletions.
37 changes: 18 additions & 19 deletions sim/common/lightning_capacitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,21 @@ func init() {
}

var TheLightningCapacitorAuraID = core.NewAuraID()

func ApplyTheLightningCapacitor(agent core.Agent) {
spellObj := core.DirectCastAction{}

character := agent.GetCharacter()
character.AddPermanentAura(func(sim *core.Simulation) core.Aura {
lightningCapacitorCastTemplate := newLightningCapacitorCastTemplate(sim, character)
castGenerator := newLightningCapacitorCastGenerator(sim, character)
charges := 0

const icdDur = time.Millisecond * 2500
icd := core.NewICD()

return core.Aura{
ID: TheLightningCapacitorAuraID,
Name: "The Lightning Capacitor",
ID: TheLightningCapacitorAuraID,
Name: "The Lightning Capacitor",
OnSpellHit: func(sim *core.Simulation, cast *core.Cast, result *core.DirectCastDamageResult) {
if icd.IsOnCD(sim) {
return
Expand All @@ -41,7 +42,7 @@ func ApplyTheLightningCapacitor(agent core.Agent) {
charges = 0

castAction := &spellObj
*castAction = lightningCapacitorCastTemplate
*castAction = castGenerator()
castAction.HitInputs[0].Target = result.Target
castAction.Init(sim)
castAction.Act(sim)
Expand All @@ -52,31 +53,29 @@ func ApplyTheLightningCapacitor(agent core.Agent) {
}

// Returns a cast object for a Lightning Capacitor cast with as many fields precomputed as possible.
func newLightningCapacitorCastTemplate(sim *core.Simulation, character *core.Character) core.DirectCastAction {
return core.DirectCastAction{
func newLightningCapacitorCastGenerator(sim *core.Simulation, character *core.Character) core.DirectCastGenerator {
return core.NewDirectCastGenerator(core.DirectCastAction{
Cast: core.Cast{
Name: "Lightning Capacitor",
ActionID: core.ActionID{
ItemID: core.ItemIDTheLightningCapacitor,
},
Character: character,
SpellSchool: stats.NatureSpellPower,
Character: character,
SpellSchool: stats.NatureSpellPower,
IgnoreCooldowns: true,
IgnoreManaCost: true,
CritMultiplier: 1.5,
OnCastComplete: func(sim *core.Simulation, cast *core.Cast) {},
IgnoreManaCost: true,
CritMultiplier: 1.5,
OnCastComplete: func(sim *core.Simulation, cast *core.Cast) {},
},
HitInputs: []core.DirectCastDamageInput{
core.DirectCastDamageInput{
MinBaseDamage: 694,
MaxBaseDamage: 807,
{
MinBaseDamage: 694,
MaxBaseDamage: 807,
DamageMultiplier: 1,
},
},
HitResults: []core.DirectCastDamageResult{
core.DirectCastDamageResult{},
},
OnSpellHit: func(sim *core.Simulation, cast *core.Cast, result *core.DirectCastDamageResult) {},
HitResults: []core.DirectCastDamageResult{{}},
OnSpellHit: func(sim *core.Simulation, cast *core.Cast, result *core.DirectCastDamageResult) {},
OnSpellMiss: func(sim *core.Simulation, cast *core.Cast) {},
}
})
}
21 changes: 17 additions & 4 deletions sim/core/direct_cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ type DirectCastAction struct {
HitResults []DirectCastDamageResult

// Callbacks for providing additional custom behavior.
OnSpellHit OnSpellHit
OnSpellMiss OnSpellMiss
OnSpellHit OnSpellHit
OnSpellMiss OnSpellMiss
}

func (action *DirectCastAction) GetActionID() ActionID {
Expand Down Expand Up @@ -164,15 +164,15 @@ func (action *DirectCastAction) calculateDirectCastDamage(sim *Simulation, damag

character := action.Cast.Character

hit := 0.83 + (character.GetStat(stats.SpellHit) + damageInput.BonusSpellHitRating)/(SpellHitRatingPerHitChance*100)
hit := 0.83 + (character.GetStat(stats.SpellHit)+damageInput.BonusSpellHitRating)/(SpellHitRatingPerHitChance*100)
hit = MinFloat(hit, 0.99) // can't get away from the 1% miss

if sim.RandomFloat("DirectCast Hit") >= hit { // Miss
return result
}
result.Hit = true

baseDamage := damageInput.MinBaseDamage + sim.RandomFloat("DirectCast Base Damage")*(damageInput.MaxBaseDamage - damageInput.MinBaseDamage)
baseDamage := damageInput.MinBaseDamage + sim.RandomFloat("DirectCast Base Damage")*(damageInput.MaxBaseDamage-damageInput.MinBaseDamage)
totalSpellPower := character.GetStat(stats.SpellPower) + character.GetStat(action.Cast.SpellSchool) + damageInput.BonusSpellPower
damageFromSpellPower := (totalSpellPower * damageInput.SpellCoefficient)
damage := baseDamage + damageFromSpellPower
Expand Down Expand Up @@ -208,3 +208,16 @@ func (action *DirectCastAction) calculateDirectCastDamage(sim *Simulation, damag

return result
}

type DirectCastGenerator func() DirectCastAction

// NewDirectCastGenerator will take in a cast template and create a generator so you dont have to manually manage hit inputs.
func NewDirectCastGenerator(template DirectCastAction) DirectCastGenerator {
hitInput := make([]DirectCastDamageInput, len(template.HitInputs))
return func() DirectCastAction {
newAction := template
newAction.HitInputs = hitInput
copy(newAction.HitInputs, template.HitInputs)
return newAction
}
}
25 changes: 11 additions & 14 deletions sim/shaman/chain_lightning.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,25 @@ import (
)

const SpellIDCL6 int32 = 25442

var ChainLightningCooldownID = core.NewCooldownID()

// Returns a cast object for Chain Lightning with as many fields precomputed as possible.
func (shaman *Shaman) newChainLightningTemplate(sim *core.Simulation, isLightningOverload bool) core.DirectCastAction {
// newChainLightningGenerator returns a cast generator for Chain Lightning with as many fields precomputed as possible.
func (shaman *Shaman) newChainLightningGenerator(sim *core.Simulation, isLightningOverload bool) core.DirectCastGenerator {
spellTemplate := shaman.newElectricSpellTemplate(
"Chain Lightning",
core.ActionID{
SpellID: SpellIDCL6,
SpellID: SpellIDCL6,
CooldownID: ChainLightningCooldownID,
},
760.0,
time.Millisecond*2000,
isLightningOverload)
spellTemplate.Cast.Cooldown = time.Second*6
spellTemplate.Cast.Cooldown = time.Second * 6

hitInput := core.DirectCastDamageInput{
MinBaseDamage: 734,
MaxBaseDamage: 838,
MinBaseDamage: 734,
MaxBaseDamage: 838,
SpellCoefficient: 0.651,
DamageMultiplier: 1,
}
Expand All @@ -34,7 +35,7 @@ func (shaman *Shaman) newChainLightningTemplate(sim *core.Simulation, isLightnin
hitInputs := make([]core.DirectCastDamageInput, 0, numHits)
hitInputs = append(hitInputs, hitInput)
for i := int32(1); i < numHits; i++ {
bounceHit := hitInputs[i - 1] // Makes a copy
bounceHit := hitInputs[i-1] // Makes a copy

if shaman.HasAura(Tidefury2PcAuraID) {
bounceHit.DamageMultiplier *= 0.83
Expand Down Expand Up @@ -68,7 +69,7 @@ func (shaman *Shaman) newChainLightningTemplate(sim *core.Simulation, isLightnin
}
}

return spellTemplate
return core.NewDirectCastGenerator(spellTemplate)
}

func (shaman *Shaman) NewChainLightning(sim *core.Simulation, target *core.Target, isLightningOverload bool) *core.DirectCastAction {
Expand All @@ -77,14 +78,10 @@ func (shaman *Shaman) NewChainLightning(sim *core.Simulation, target *core.Targe
// Initialize cast from precomputed template.
if isLightningOverload {
spell = &shaman.electricSpellLO
*spell = shaman.chainLightningLOTemplate
spell.HitInputs = shaman.clHitInputs
copy(spell.HitInputs, shaman.chainLightningLOTemplate.HitInputs)
*spell = shaman.chainLightningLOCastGenerator()
} else {
spell = &shaman.electricSpell
*spell = shaman.chainLightningTemplate
spell.HitInputs = shaman.clHitInputs
copy(spell.HitInputs, shaman.chainLightningTemplate.HitInputs)
*spell = shaman.chainLightningCastGenerator()
}

// Set dynamic fields, i.e. the stuff we couldn't precompute.
Expand Down
20 changes: 8 additions & 12 deletions sim/shaman/lightning_bolt.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (

const SpellIDLB12 int32 = 25449

// Returns a cast object for Lightning Bolt with as many fields precomputed as possible.
func (shaman *Shaman) newLightningBoltTemplate(sim *core.Simulation, isLightningOverload bool) core.DirectCastAction {
// newLightningBoltGenerator returns a cast generator for Lightning Bolt with as many fields precomputed as possible.
func (shaman *Shaman) newLightningBoltGenerator(sim *core.Simulation, isLightningOverload bool) core.DirectCastGenerator {
baseManaCost := 300.0
if shaman.Equip[items.ItemSlotRanged].ID == TotemOfThePulsingEarth {
baseManaCost -= 27.0
Expand All @@ -26,15 +26,15 @@ func (shaman *Shaman) newLightningBoltTemplate(sim *core.Simulation, isLightning
isLightningOverload)

hitInput := core.DirectCastDamageInput{
MinBaseDamage: 571,
MaxBaseDamage: 652,
MinBaseDamage: 571,
MaxBaseDamage: 652,
SpellCoefficient: 0.794,
DamageMultiplier: 1,
}
shaman.applyElectricSpellHitInputModifiers(&hitInput, isLightningOverload)

spellTemplate.HitInputs = []core.DirectCastDamageInput{hitInput}
spellTemplate.HitResults = []core.DirectCastDamageResult{core.DirectCastDamageResult{}}
spellTemplate.HitResults = []core.DirectCastDamageResult{{}}

if !isLightningOverload && shaman.Talents.LightningOverload > 0 {
lightningOverloadChance := float64(shaman.Talents.LightningOverload) * 0.04
Expand All @@ -56,7 +56,7 @@ func (shaman *Shaman) newLightningBoltTemplate(sim *core.Simulation, isLightning
}
}

return spellTemplate
return core.NewDirectCastGenerator(spellTemplate)
}

func (shaman *Shaman) NewLightningBolt(sim *core.Simulation, target *core.Target, isLightningOverload bool) *core.DirectCastAction {
Expand All @@ -65,14 +65,10 @@ func (shaman *Shaman) NewLightningBolt(sim *core.Simulation, target *core.Target
// Initialize cast from precomputed template.
if isLightningOverload {
spell = &shaman.electricSpellLO
*spell = shaman.lightningBoltLOTemplate
spell.HitInputs = shaman.singleHitInputs
spell.HitInputs[0] = shaman.lightningBoltLOTemplate.HitInputs[0]
*spell = shaman.lightningBoltLOCastGenerator()
} else {
spell = &shaman.electricSpell
*spell = shaman.lightningBoltTemplate
spell.HitInputs = shaman.singleHitInputs
spell.HitInputs[0] = shaman.lightningBoltTemplate.HitInputs[0]
*spell = shaman.lightningBoltCastGenerator()
}

// Set dynamic fields, i.e. the stuff we couldn't precompute.
Expand Down
29 changes: 9 additions & 20 deletions sim/shaman/shaman.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,12 @@ type Shaman struct {
electricSpell core.DirectCastAction
electricSpellLO core.DirectCastAction

// Temporary hitInput slices to be used in object pool casts to avoid modifying
// templates directly (because slices are copied by reference).
singleHitInputs []core.DirectCastDamageInput
clHitInputs []core.DirectCastDamageInput
// Precomputed templated cast generator for quickly resetting cast fields.
lightningBoltCastGenerator core.DirectCastGenerator
lightningBoltLOCastGenerator core.DirectCastGenerator

// Precomputed template cast objects for quickly resetting cast fields.
lightningBoltTemplate core.DirectCastAction
lightningBoltLOTemplate core.DirectCastAction

chainLightningTemplate core.DirectCastAction
chainLightningLOTemplate core.DirectCastAction
chainLightningCastGenerator core.DirectCastGenerator
chainLightningLOCastGenerator core.DirectCastGenerator
}

// Implemented by each Shaman spec.
Expand Down Expand Up @@ -117,16 +112,10 @@ func (shaman *Shaman) AddPartyBuffs(partyBuffs *proto.PartyBuffs) {

func (shaman *Shaman) Init(sim *core.Simulation) {
// Precompute all the spell templates.
shaman.lightningBoltTemplate = shaman.newLightningBoltTemplate(sim, false)
shaman.lightningBoltLOTemplate = shaman.newLightningBoltTemplate(sim, true)
shaman.chainLightningTemplate = shaman.newChainLightningTemplate(sim, false)
shaman.chainLightningLOTemplate = shaman.newChainLightningTemplate(sim, true)

// Need to allocate separate hit input slices so we can avoid modifying the template versions.
shaman.singleHitInputs = []core.DirectCastDamageInput{
core.DirectCastDamageInput{},
}
shaman.clHitInputs = make([]core.DirectCastDamageInput, len(shaman.chainLightningTemplate.HitInputs))
shaman.lightningBoltCastGenerator = shaman.newLightningBoltGenerator(sim, false)
shaman.lightningBoltLOCastGenerator = shaman.newLightningBoltGenerator(sim, true)
shaman.chainLightningCastGenerator = shaman.newChainLightningGenerator(sim, false)
shaman.chainLightningLOCastGenerator = shaman.newChainLightningGenerator(sim, true)
}

func (shaman *Shaman) Reset(sim *core.Simulation) {
Expand Down

0 comments on commit df405f8

Please sign in to comment.