From 5ac6ce36025ec2eb7b6eb69c131637e4cf0e9fb2 Mon Sep 17 00:00:00 2001 From: Kayla Glick Date: Thu, 1 Feb 2024 00:03:29 -0500 Subject: [PATCH] fix LvB, LB, FS all working --- sim/shaman/flame_shock.go | 87 +++++++++++++ sim/shaman/lava_burst.go | 4 +- sim/shaman/lightning_bolt.go | 10 +- sim/shaman/shaman.go | 2 +- sim/shaman/shocks.go | 147 +++++++--------------- ui/elemental_shaman/apls/default.apl.json | 5 - ui/elemental_shaman/apls/phase_1.apl.json | 11 ++ ui/elemental_shaman/presets.ts | 6 +- ui/elemental_shaman/sim.ts | 4 +- 9 files changed, 153 insertions(+), 123 deletions(-) create mode 100644 sim/shaman/flame_shock.go delete mode 100644 ui/elemental_shaman/apls/default.apl.json create mode 100644 ui/elemental_shaman/apls/phase_1.apl.json diff --git a/sim/shaman/flame_shock.go b/sim/shaman/flame_shock.go new file mode 100644 index 0000000000..c79b930fb0 --- /dev/null +++ b/sim/shaman/flame_shock.go @@ -0,0 +1,87 @@ +package shaman + +import ( + "time" + + "github.com/wowsims/sod/sim/core" +) + +const FlameShockRanks = 6 + +// First entry is the base spell ID, second entry is the overload's spell ID +var FlameShockSpellId = [FlameShockRanks + 1]int32{0, 8050, 8052, 8053, 10447, 10448, 29228} +var FlameShockBaseDamage = [FlameShockRanks + 1]float64{0, 25, 51, 95, 164, 245, 292} +var FlameShockBaseDotDamage = [FlameShockRanks + 1]float64{0, 28, 48, 96, 168, 256, 320} +var FlameShockBaseSpellCoef = [FlameShockRanks + 1]float64{0, .134, .198, .214, .214, .214, .214} +var FlameShockDotSpellCoef = [FlameShockRanks + 1]float64{0, .134, .198, .214, .214, .214, .214} +var FlameShockManaCost = [FlameShockRanks + 1]float64{0, 55, 95, 160, 250, 345, 410} +var FlameShockLevel = [FlameShockRanks + 1]int{0, 10, 18, 28, 40, 52, 60} + +func (shaman *Shaman) registerFlameShockSpell(shockTimer *core.Timer) { + for i := 1; i <= FlameShockRanks; i++ { + config := shaman.newFlameShockSpellConfig(shockTimer, i) + + if config.RequiredLevel <= int(shaman.Level) { + shaman.FlameShock = shaman.RegisterSpell(config) + } + } +} + +func (shaman *Shaman) newFlameShockSpellConfig(shockTimer *core.Timer, rank int) core.SpellConfig { + spellId := FlameShockSpellId[rank] + baseDamage := FlameShockBaseDamage[rank] + baseDotDamage := FlameShockBaseDotDamage[rank] + baseSpellCoeff := FlameShockBaseSpellCoef[rank] + dotSpellCoeff := FlameShockDotSpellCoef[rank] + manaCost := FlameShockManaCost[rank] + level := FlameShockLevel[rank] + + numTicks := 4 + tickDuration := time.Second * 3 + + spell := shaman.newShockSpellConfig( + core.ActionID{SpellID: spellId}, + core.SpellSchoolFire, + manaCost, + shockTimer, + ) + + spell.RequiredLevel = level + spell.Rank = rank + + spell.CritMultiplier = shaman.ElementalCritMultiplier(0) + spell.ApplyEffects = func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { + damage := baseDamage + baseSpellCoeff*spell.SpellPower() + result := spell.CalcDamage(sim, target, damage, spell.OutcomeMagicHitAndCrit) + if result.Landed() { + spell.Dot(target).NumberOfTicks = int32(numTicks) + spell.Dot(target).Apply(sim) + } + spell.DealDamage(sim, result) + } + + spell.Dot = core.DotConfig{ + Aura: core.Aura{ + Label: "FlameShock", + OnGain: func(aura *core.Aura, sim *core.Simulation) { + shaman.LavaBurst.BonusCritRating += 100 * core.CritRatingPerCritChance + }, + OnExpire: func(aura *core.Aura, sim *core.Simulation) { + shaman.LavaBurst.BonusCritRating -= 100 * core.CritRatingPerCritChance + }, + }, + NumberOfTicks: int32(numTicks), + TickLength: tickDuration, + + OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, _ bool) { + dot.SnapshotBaseDamage = (baseDotDamage / float64(numTicks)) + dotSpellCoeff*dot.Spell.SpellPower() + dot.SnapshotCritChance = dot.Spell.SpellCritChance(target) + dot.SnapshotAttackerMultiplier = dot.Spell.AttackerDamageMultiplier(dot.Spell.Unit.AttackTables[target.UnitIndex]) + }, + OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { + dot.CalcAndDealPeriodicSnapshotDamage(sim, target, dot.OutcomeSnapshotCrit) + }, + } + + return spell +} diff --git a/sim/shaman/lava_burst.go b/sim/shaman/lava_burst.go index c2b720eebf..1d515fb5fe 100644 --- a/sim/shaman/lava_burst.go +++ b/sim/shaman/lava_burst.go @@ -20,7 +20,7 @@ func (shaman *Shaman) applyLavaBurst() { } func (shaman *Shaman) newLavaBurstSpellConfig(isOverload bool) core.SpellConfig { - level := float64(shaman.GetCharacter().Level) + level := float64(shaman.Level) spellId := core.TernaryInt32(isOverload, 408491, 408490) baseCalc := 7.583798 + 0.471881*level + 0.036599*level*level baseLowDamage := baseCalc * 4.69 @@ -60,13 +60,13 @@ func (shaman *Shaman) newLavaBurstSpellConfig(isOverload bool) core.SpellConfig // Concussion does not currently apply to Lava Burst in SoD // DamageMultiplier: 1 + 0.01*float64(shaman.Talents.Concussion) + DamageMultiplier: 1, CritMultiplier: shaman.ElementalCritMultiplier(0), ThreatMultiplier: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { baseDamage := sim.Roll(baseLowDamage, baseHighDamage) + spellCoeff*spell.SpellPower() result := spell.CalcDamage(sim, target, baseDamage, spell.OutcomeMagicHitAndCrit) - if canOverload && result.Landed() && sim.RandomFloat("LvB Overload") < shaman.OverloadChance { shaman.LavaBurstOverload.Cast(sim, target) } diff --git a/sim/shaman/lightning_bolt.go b/sim/shaman/lightning_bolt.go index aaf65d7b94..9081b71f2a 100644 --- a/sim/shaman/lightning_bolt.go +++ b/sim/shaman/lightning_bolt.go @@ -34,7 +34,6 @@ func (shaman *Shaman) registerLightningBoltSpell() { } func (shaman *Shaman) newLightningBoltSpellConfig(rank int, isOverload bool) core.SpellConfig { - // First entry is the base spell ID, second entry is the overload's spell ID spellId := LightningBoltSpellId[rank][core.TernaryInt32(isOverload, 1, 0)] baseDamageLow := LightningBoltBaseDamage[rank][0] baseDamageHigh := LightningBoltBaseDamage[rank][1] @@ -43,6 +42,9 @@ func (shaman *Shaman) newLightningBoltSpellConfig(rank int, isOverload bool) cor manaCost := LightningBoltManaCost[rank] level := LightningBoltLevel[rank] + dmgBonus := shaman.electricSpellBonusDamage(spellCoeff) + canOverload := !isOverload && shaman.OverloadAura != nil + spell := shaman.newElectricSpellConfig( core.ActionID{SpellID: spellId}, manaCost, @@ -50,16 +52,12 @@ func (shaman *Shaman) newLightningBoltSpellConfig(rank int, isOverload bool) cor isOverload, ) - dmgBonus := shaman.electricSpellBonusDamage(spellCoeff) - - canOverload := !isOverload && shaman.OverloadAura != nil - spell.RequiredLevel = level spell.Rank = rank + spell.ApplyEffects = func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { baseDamage := dmgBonus + sim.Roll(baseDamageLow, baseDamageHigh) + spellCoeff*spell.SpellPower() result := spell.CalcDamage(sim, target, baseDamage, spell.OutcomeMagicHitAndCrit) - if canOverload && result.Landed() && sim.RandomFloat("LB Overload") < shaman.OverloadChance { shaman.LightningBoltOverload.Cast(sim, target) } diff --git a/sim/shaman/shaman.go b/sim/shaman/shaman.go index ebb97b6e70..190e4fd9b9 100644 --- a/sim/shaman/shaman.go +++ b/sim/shaman/shaman.go @@ -235,7 +235,7 @@ func (shaman *Shaman) Initialize() { // shaman.registerManaSpringTotemSpell() // shaman.registerHealingStreamTotemSpell() // shaman.registerSearingTotemSpell() - // shaman.registerShocks() + shaman.registerShocks() // shaman.registerStormstrikeSpell() // shaman.registerStrengthOfEarthTotemSpell() // shaman.registerThunderstormSpell() diff --git a/sim/shaman/shocks.go b/sim/shaman/shocks.go index 29fb3b0aad..4d2ef23dd4 100644 --- a/sim/shaman/shocks.go +++ b/sim/shaman/shocks.go @@ -1,51 +1,41 @@ package shaman -// import ( -// "time" - -// "github.com/wowsims/sod/sim/core" -// ) - -// func (shaman *Shaman) ShockCD() time.Duration { -// return time.Second*6 - time.Millisecond*200*time.Duration(shaman.Talents.Reverberation) -// } - -// // Shared logic for all shocks. -// func (shaman *Shaman) newShockSpellConfig(spellID int32, spellSchool core.SpellSchool, baseCost float64, shockTimer *core.Timer) core.SpellConfig { -// actionID := core.ActionID{SpellID: spellID} - -// return core.SpellConfig{ -// ActionID: actionID, -// SpellSchool: spellSchool, -// ProcMask: core.ProcMaskSpellDamage, -// Flags: SpellFlagShock | core.SpellFlagAPL, - -// ManaCost: core.ManaCostOptions{ -// BaseCost: baseCost, -// Multiplier: 1 - -// core.TernaryFloat64(shaman.Talents.ShamanisticFocus, 0.45, 0) - -// 0.02*float64(shaman.Talents.Convection) - -// 0.02*float64(shaman.Talents.MentalQuickness) - -// core.TernaryFloat64(shaman.HasSetBonus(ItemSetSkyshatterHarness, 2), 0.1, 0), -// }, -// Cast: core.CastConfig{ -// DefaultCast: core.Cast{ -// GCD: core.GCDDefault, -// }, -// CD: core.Cooldown{ -// Timer: shockTimer, -// Duration: shaman.ShockCD(), -// }, -// }, - -// BonusHitRating: float64(shaman.Talents.ElementalPrecision) * core.SpellHitRatingPerHitChance, -// DamageMultiplier: 1 + -// 0.01*float64(shaman.Talents.Concussion) + -// core.TernaryFloat64(shaman.HasSetBonus(ItemSetThrallsBattlegear, 4), 0.25, 0), -// CritMultiplier: shaman.ElementalCritMultiplier(0), -// ThreatMultiplier: shaman.spellThreatMultiplier(), -// } -// } +import ( + "time" + + "github.com/wowsims/sod/sim/core" +) + +func (shaman *Shaman) ShockCD() time.Duration { + return time.Second*6 - time.Millisecond*200*time.Duration(shaman.Talents.Reverberation) +} + +// Shared logic for all shocks. +func (shaman *Shaman) newShockSpellConfig(actionId core.ActionID, spellSchool core.SpellSchool, baseCost float64, shockTimer *core.Timer) core.SpellConfig { + return core.SpellConfig{ + ActionID: actionId, + SpellSchool: spellSchool, + ProcMask: core.ProcMaskSpellDamage, + Flags: SpellFlagShock | core.SpellFlagAPL, + + ManaCost: core.ManaCostOptions{ + FlatCost: baseCost, + Multiplier: 1 - 0.02*float64(shaman.Talents.Convection), + }, + Cast: core.CastConfig{ + DefaultCast: core.Cast{ + GCD: core.GCDDefault, + }, + CD: core.Cooldown{ + Timer: shockTimer, + Duration: shaman.ShockCD(), + }, + }, + + DamageMultiplier: 1 + 0.01*float64(shaman.Talents.Concussion), + CritMultiplier: shaman.ElementalCritMultiplier(0), + } +} // func (shaman *Shaman) registerEarthShockSpell(shockTimer *core.Timer) { // config := shaman.newShockSpellConfig(49231, core.SpellSchoolNature, 0.18, shockTimer) @@ -57,59 +47,6 @@ package shaman // shaman.EarthShock = shaman.RegisterSpell(config) // } -// func (shaman *Shaman) registerFlameShockSpell(shockTimer *core.Timer) { -// config := shaman.newShockSpellConfig(49233, core.SpellSchoolFire, 0.17, shockTimer) - -// config.Cast.CD.Duration -= time.Duration(shaman.Talents.BoomingEchoes) * time.Second -// config.CritMultiplier = shaman.ElementalCritMultiplier(0) -// config.DamageMultiplier += 0.1 * float64(shaman.Talents.BoomingEchoes) - -// flameShockBaseNumberOfTicks := 6 + core.TernaryInt32(shaman.HasSetBonus(ItemSetThrallsRegalia, 2), 3, 0) -// config.ApplyEffects = func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { -// baseDamage := 500 + 0.214*spell.SpellPower() -// result := spell.CalcDamage(sim, target, baseDamage, spell.OutcomeMagicHitAndCrit) -// if result.Landed() { -// spell.Dot(target).NumberOfTicks = flameShockBaseNumberOfTicks -// spell.Dot(target).Apply(sim) -// } -// spell.DealDamage(sim, result) -// } - -// bonusPeriodicDamageMultiplier := 0 + -// 0.2*float64(shaman.Talents.StormEarthAndFire) + -// core.TernaryFloat64(shaman.HasSetBonus(ItemSetWorldbreakerGarb, 2), 0.2, 0) - -// 0.1*float64(shaman.Talents.BoomingEchoes) - -// config.Dot = core.DotConfig{ -// Aura: core.Aura{ -// Label: "FlameShock", -// OnGain: func(aura *core.Aura, sim *core.Simulation) { -// shaman.LavaBurst.BonusCritRating += 100 * core.CritRatingPerCritChance -// }, -// OnExpire: func(aura *core.Aura, sim *core.Simulation) { -// shaman.LavaBurst.BonusCritRating -= 100 * core.CritRatingPerCritChance -// }, -// }, -// NumberOfTicks: flameShockBaseNumberOfTicks, -// TickLength: time.Second * 3, -// AffectedByCastSpeed: true, - -// OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, _ bool) { -// dot.SnapshotBaseDamage = 834/6 + 0.1*dot.Spell.SpellPower() -// dot.SnapshotCritChance = dot.Spell.SpellCritChance(target) - -// dot.Spell.DamageMultiplierAdditive += bonusPeriodicDamageMultiplier -// dot.SnapshotAttackerMultiplier = dot.Spell.AttackerDamageMultiplier(dot.Spell.Unit.AttackTables[target.UnitIndex]) -// dot.Spell.DamageMultiplierAdditive -= bonusPeriodicDamageMultiplier -// }, -// OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { -// dot.CalcAndDealPeriodicSnapshotDamage(sim, target, dot.OutcomeSnapshotCrit) -// }, -// } - -// shaman.FlameShock = shaman.RegisterSpell(config) -// } - // func (shaman *Shaman) registerFrostShockSpell(shockTimer *core.Timer) { // config := shaman.newShockSpellConfig(49236, core.SpellSchoolFrost, 0.18, shockTimer) // config.Cast.CD.Duration -= time.Duration(shaman.Talents.BoomingEchoes) * time.Second @@ -123,9 +60,9 @@ package shaman // shaman.FrostShock = shaman.RegisterSpell(config) // } -// func (shaman *Shaman) registerShocks() { -// shockTimer := shaman.NewTimer() -// shaman.registerEarthShockSpell(shockTimer) -// shaman.registerFlameShockSpell(shockTimer) -// shaman.registerFrostShockSpell(shockTimer) -// } +func (shaman *Shaman) registerShocks() { + shockTimer := shaman.NewTimer() + // shaman.registerEarthShockSpell(shockTimer) + shaman.registerFlameShockSpell(shockTimer) + // shaman.registerFrostShockSpell(shockTimer) +} diff --git a/ui/elemental_shaman/apls/default.apl.json b/ui/elemental_shaman/apls/default.apl.json deleted file mode 100644 index a44aeabe40..0000000000 --- a/ui/elemental_shaman/apls/default.apl.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "type": "TypeAPL", - "prepullActions": [], - "priorityList": [] - } \ No newline at end of file diff --git a/ui/elemental_shaman/apls/phase_1.apl.json b/ui/elemental_shaman/apls/phase_1.apl.json new file mode 100644 index 0000000000..19088ee435 --- /dev/null +++ b/ui/elemental_shaman/apls/phase_1.apl.json @@ -0,0 +1,11 @@ +{ + "type": "TypeAPL", + "prepullActions": [ + {"action":{"castSpell":{"spellId":{"spellId":915,"rank":4}}},"doAtValue":{"const":{"val":"3.05"}}} + ], + "priorityList": [ + {"action":{"condition":{"cmp":{"op":"OpLe","lhs":{"dotRemainingTime":{"spellId":{"spellId":8052,"rank":2}}},"rhs":{"const":{"val":"2"}}}},"castSpell":{"spellId":{"spellId":8052,"rank":2}}}}, + {"action":{"castSpell":{"spellId":{"spellId":408490}}}}, + {"action":{"condition":{"cmp":{"op":"OpGt","lhs":{"dotRemainingTime":{"spellId":{"spellId":8052,"rank":2}}},"rhs":{"spellCastTime":{"spellId":{"spellId":915,"rank":4}}}}},"castSpell":{"spellId":{"spellId":915,"rank":4}}}} + ] +} diff --git a/ui/elemental_shaman/presets.ts b/ui/elemental_shaman/presets.ts index 033f0caed7..b79a995c19 100644 --- a/ui/elemental_shaman/presets.ts +++ b/ui/elemental_shaman/presets.ts @@ -21,7 +21,7 @@ import * as PresetUtils from '../core/preset_utils.js'; import BlankGear from './gear_sets/blank.gear.json'; import Phase1Gear from './gear_sets/phase_1.gear.json'; -import DefaultApl from './apls/default.apl.json'; +import Phase1APL from './apls/phase_1.apl.json'; // Preset options for this spec. // Eventually we will import these values for the raid sim too, so its good to @@ -32,7 +32,9 @@ export const Phase1PresetGear = PresetUtils.makePresetGear('Phase 1', Phase1Gear export const DefaultGear = Phase1PresetGear; -export const ROTATION_PRESET_DEFAULT = PresetUtils.makePresetAPLRotation('Default', DefaultApl); +export const Phase1PresetAPL = PresetUtils.makePresetAPLRotation('Phase 1', Phase1APL); + +export const DefaultAPL = Phase1PresetAPL // Default talents. Uses the wowhead calculator format, make the talents on // https://wowhead.com/classic/talent-calc and copy the numbers in the url. diff --git a/ui/elemental_shaman/sim.ts b/ui/elemental_shaman/sim.ts index 0e67efe992..8e52774492 100644 --- a/ui/elemental_shaman/sim.ts +++ b/ui/elemental_shaman/sim.ts @@ -138,7 +138,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, { ], // Preset rotations that the user can quickly select. rotations: [ - Presets.ROTATION_PRESET_DEFAULT, + Presets.Phase1PresetAPL, ], // Preset gear configurations that the user can quickly select. gear: [ @@ -148,7 +148,7 @@ const SPEC_CONFIG = registerSpecConfig(Spec.SpecElementalShaman, { }, autoRotation: (_: Player): APLRotation => { - return Presets.ROTATION_PRESET_DEFAULT.rotation.rotation!; + return Presets.DefaultAPL.rotation.rotation!; }, raidSimPresets: [