diff --git a/sim/druid/balance/TestBalance.results b/sim/druid/balance/TestBalance.results index 3f42b9c887..04625a4fcb 100644 --- a/sim/druid/balance/TestBalance.results +++ b/sim/druid/balance/TestBalance.results @@ -51,119 +51,119 @@ character_stats_results: { dps_results: { key: "TestBalance-AllItems-BlackfathomElementalist'sHide" value: { - dps: 32.59634 - tps: 33.52338 + dps: 35.20865 + tps: 36.13569 } } dps_results: { key: "TestBalance-AllItems-BlackfathomSlayer'sLeather" value: { - dps: 27.74523 - tps: 28.49407 + dps: 30.74433 + tps: 31.49317 } } dps_results: { key: "TestBalance-AllItems-HyperconductiveMender'sMeditation" value: { - dps: 17.1432 - tps: 18.02404 + dps: 18.70173 + tps: 19.58257 } } dps_results: { key: "TestBalance-AllItems-HyperconductiveWizard'sAttire" value: { - dps: 17.10841 - tps: 18.07505 + dps: 18.23835 + tps: 19.20499 } } dps_results: { key: "TestBalance-AllItems-InsulatedLeathers" value: { - dps: 13.74543 - tps: 14.50747 + dps: 14.87791 + tps: 15.63995 } } dps_results: { key: "TestBalance-AllItems-InsulatedSorceror'sLeathers" value: { - dps: 16.85366 - tps: 17.7873 + dps: 18.29556 + tps: 19.2292 } } dps_results: { key: "TestBalance-AllItems-IrradiatedGarments" value: { - dps: 19.37368 - tps: 20.34032 + dps: 20.37491 + tps: 21.34155 } } dps_results: { key: "TestBalance-AllItems-StormshroudArmor" value: { - dps: 11.30751 - tps: 12.03655 + dps: 12.43377 + tps: 13.16281 } } dps_results: { key: "TestBalance-AllItems-TwilightInvoker'sVestments" value: { - dps: 31.606 - tps: 32.48024 + dps: 34.95225 + tps: 35.82649 } } dps_results: { key: "TestBalance-Average-Default" value: { - dps: 55.39244 - tps: 56.3132 + dps: 61.48214 + tps: 62.4029 } } dps_results: { key: "TestBalance-Settings-Tauren-25-phase_1-Default-phase_1-FullBuffs-Full Consumes-LongMultiTarget" value: { - dps: 56.07227 - tps: 74.48107 + dps: 62.02244 + tps: 80.43124 } } dps_results: { key: "TestBalance-Settings-Tauren-25-phase_1-Default-phase_1-FullBuffs-Full Consumes-LongSingleTarget" value: { - dps: 56.07227 - tps: 56.99271 + dps: 62.02244 + tps: 62.94288 } } dps_results: { key: "TestBalance-Settings-Tauren-25-phase_1-Default-phase_1-FullBuffs-Full Consumes-ShortSingleTarget" value: { - dps: 58.03327 - tps: 62.63547 + dps: 62.34573 + tps: 66.94793 } } dps_results: { key: "TestBalance-Settings-Tauren-25-phase_1-Default-phase_1-NoBuffs-Full Consumes-LongMultiTarget" value: { - dps: 52.58487 - tps: 52.58487 + dps: 59.33107 + tps: 59.33107 } } dps_results: { key: "TestBalance-Settings-Tauren-25-phase_1-Default-phase_1-NoBuffs-Full Consumes-LongSingleTarget" value: { - dps: 52.58487 - tps: 52.58487 + dps: 59.33107 + tps: 59.33107 } } dps_results: { key: "TestBalance-Settings-Tauren-25-phase_1-Default-phase_1-NoBuffs-Full Consumes-ShortSingleTarget" value: { - dps: 56.75419 - tps: 56.75419 + dps: 62.0261 + tps: 62.0261 } } dps_results: { key: "TestBalance-SwitchInFrontOfTarget-Default" value: { - dps: 56.07227 - tps: 56.99271 + dps: 62.02244 + tps: 62.94288 } } diff --git a/sim/druid/balance/balance_test.go b/sim/druid/balance/balance_test.go index 47e7a6bf88..0ac8d19c05 100644 --- a/sim/druid/balance/balance_test.go +++ b/sim/druid/balance/balance_test.go @@ -28,7 +28,7 @@ func TestBalance(t *testing.T) { })) } -var StandardTalents = "50005003021" +var StandardTalents = "5000500302541051" var FullConsumes = core.ConsumesCombo{ Label: "Full Consumes", diff --git a/sim/druid/runes.go b/sim/druid/runes.go index cbdb64c090..ad8e9c5935 100644 --- a/sim/druid/runes.go +++ b/sim/druid/runes.go @@ -4,6 +4,7 @@ import ( "slices" "time" + "github.com/wowsims/sod/sim/common/sod/item_sets" "github.com/wowsims/sod/sim/core" "github.com/wowsims/sod/sim/core/proto" ) @@ -198,7 +199,6 @@ func (druid *Druid) applySunfire() { }) } -// TODO: Classic verify star surge numbers func (druid *Druid) applyStarsurge() { if !druid.HasRune(proto.DruidRune_RuneLegsStarsurge) { return @@ -234,12 +234,17 @@ func (druid *Druid) applyStarsurge() { DamageMultiplier: 1, CritMultiplier: druid.VengeanceCritMultiplier(), + BonusCritRating: core.TernaryFloat64(druid.HasSetBonus(item_sets.ItemSetInsulatedSorcerorLeather, 3), 2, 0) * core.CritRatingPerCritChance, ThreatMultiplier: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { baseDamage := sim.Roll(baseLowDamage, baseHighDamage)*druid.MoonfuryDamageMultiplier() + spell.SpellDamage() result := spell.CalcDamage(sim, target, baseDamage, spell.OutcomeMagicHitAndCrit) + if result.DidCrit() && druid.NaturesGraceProcAura != nil { + druid.NaturesGraceProcAura.Activate(sim) + } + spell.WaitTravelTime(sim, func(sim *core.Simulation) { spell.DealDamage(sim, result) }) diff --git a/sim/druid/talents.go b/sim/druid/talents.go index 69a426f1ac..4112a26ebb 100644 --- a/sim/druid/talents.go +++ b/sim/druid/talents.go @@ -41,12 +41,32 @@ func (druid *Druid) setupNaturesGrace() { return } + ngWrathGCD := time.Millisecond * 1000 + + var wrathSpells []*DruidSpell + druid.NaturesGraceProcAura = druid.RegisterAura(core.Aura{ Label: "Natures Grace Proc", ActionID: core.ActionID{SpellID: 16886}, Duration: time.Second * 15, - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if aura.TimeActive(sim) >= spell.CastTime() { + OnInit: func(aura *core.Aura, sim *core.Simulation) { + wrathSpells = core.FilterSlice( + core.Flatten([][]*DruidSpell{druid.Wrath}), + func(spell *DruidSpell) bool { return spell != nil }, + ) + }, + OnGain: func(aura *core.Aura, sim *core.Simulation) { + core.Each(wrathSpells, func(spell *DruidSpell) { + spell.Spell.DefaultCast.GCD = ngWrathGCD + }) + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + core.Each(wrathSpells, func(spell *DruidSpell) { + spell.Spell.DefaultCast.GCD = core.GCDDefault + }) + }, + OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) { + if spell.CastTime() > 0 && aura.TimeActive(sim) >= spell.CastTime() { aura.Deactivate(sim) } }, @@ -59,10 +79,10 @@ func (druid *Druid) setupNaturesGrace() { aura.Activate(sim) }, OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !result.DidCrit() { - return + // Spells with travel times have their own calculation prior to the travel time to prevent issues with back-to-back casts + if spell.TravelTime() > 0 && result.DidCrit() { + druid.NaturesGraceProcAura.Activate(sim) } - druid.NaturesGraceProcAura.Activate(sim) }, }) } diff --git a/sim/druid/wrath.go b/sim/druid/wrath.go index 0466a9e925..1ace726267 100644 --- a/sim/druid/wrath.go +++ b/sim/druid/wrath.go @@ -68,6 +68,10 @@ func (druid *Druid) newWrathSpellConfig(rank int) core.SpellConfig { baseDamage := sim.Roll(baseDamageLow, baseDamageHigh)*druid.MoonfuryDamageMultiplier() + spellCoeff*spell.SpellDamage() result := spell.CalcDamage(sim, target, baseDamage, spell.OutcomeMagicHitAndCrit) + if result.DidCrit() && druid.NaturesGraceProcAura != nil { + druid.NaturesGraceProcAura.Activate(sim) + } + spell.WaitTravelTime(sim, func(sim *core.Simulation) { spell.DealDamage(sim, result) })