Skip to content

Commit

Permalink
fire aoe: add blast wave, dragonsbreath, FS downrank, firestarter (#3764
Browse files Browse the repository at this point in the history
)

* add blast wave, dragonsbreath, FS downrank, firestarter

* flamestrike is a 2s cast

* fix rank8 firestarter logic
  • Loading branch information
lime-green authored Sep 28, 2023
1 parent 28f44a0 commit 2980120
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 52 deletions.
24 changes: 12 additions & 12 deletions sim/mage/TestFire.results
Original file line number Diff line number Diff line change
Expand Up @@ -770,43 +770,43 @@ dps_results: {
dps_results: {
key: "TestFire-Settings-Troll-P1Fire-Fire-AOE-FullBuffs-LongMultiTarget"
value: {
dps: 22320.73055
tps: 23758.55456
dps: 40063.21859
tps: 37491.36433
}
}
dps_results: {
key: "TestFire-Settings-Troll-P1Fire-Fire-AOE-FullBuffs-LongSingleTarget"
value: {
dps: 1824.28082
tps: 1601.47193
dps: 1837.11212
tps: 1458.2075
}
}
dps_results: {
key: "TestFire-Settings-Troll-P1Fire-Fire-AOE-FullBuffs-ShortSingleTarget"
value: {
dps: 2556.05555
tps: 2144.17213
dps: 3102.48924
tps: 2446.7825
}
}
dps_results: {
key: "TestFire-Settings-Troll-P1Fire-Fire-AOE-NoBuffs-LongMultiTarget"
value: {
dps: 14231.19584
tps: 15899.21555
dps: 18989.72266
tps: 18769.31008
}
}
dps_results: {
key: "TestFire-Settings-Troll-P1Fire-Fire-AOE-NoBuffs-LongSingleTarget"
value: {
dps: 824.56344
tps: 719.57012
dps: 789.41494
tps: 637.71944
}
}
dps_results: {
key: "TestFire-Settings-Troll-P1Fire-Fire-AOE-NoBuffs-ShortSingleTarget"
value: {
dps: 1443.67748
tps: 1175.26449
dps: 1587.14525
tps: 1268.80011
}
}
dps_results: {
Expand Down
44 changes: 44 additions & 0 deletions sim/mage/blast_wave.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package mage

import (
"github.com/wowsims/wotlk/sim/core"
"time"
)

func (mage *Mage) registerBlastWaveSpell() {
if !mage.Talents.BlastWave {
return
}

mage.BlastWave = mage.RegisterSpell(core.SpellConfig{
ActionID: core.ActionID{SpellID: 42945},
SpellSchool: core.SpellSchoolFire,
ProcMask: core.ProcMaskSpellDamage,
Flags: SpellFlagMage | core.SpellFlagAPL,
ManaCost: core.ManaCostOptions{
BaseCost: 0.07,
},
Cast: core.CastConfig{
DefaultCast: core.Cast{
GCD: core.GCDDefault,
},
CD: core.Cooldown{
Timer: mage.NewTimer(),
Duration: time.Second * 30,
},
},
BonusCritRating: float64(mage.Talents.CriticalMass+mage.Talents.WorldInFlames) * 2 * core.CritRatingPerCritChance,
DamageMultiplierAdditive: 1 +
.02*float64(mage.Talents.SpellImpact) +
.02*float64(mage.Talents.FirePower),
CritMultiplier: mage.SpellCritMultiplier(1, mage.bonusCritDamage),
ThreatMultiplier: 1 - 0.1*float64(mage.Talents.BurningSoul),
ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
for _, aoeTarget := range sim.Encounter.TargetUnits {
baseDamage := sim.Roll(1047, 1233) + 0.193*spell.SpellPower()
baseDamage *= sim.Encounter.AOECapMultiplier()
spell.CalcAndDealDamage(sim, aoeTarget, baseDamage, spell.OutcomeMagicHitAndCrit)
}
},
})
}
42 changes: 42 additions & 0 deletions sim/mage/dragons_breath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package mage

import (
"github.com/wowsims/wotlk/sim/core"
"time"
)

func (mage *Mage) registerDragonsBreathSpell() {
if !mage.Talents.DragonsBreath {
return
}

mage.DragonsBreath = mage.RegisterSpell(core.SpellConfig{
ActionID: core.ActionID{SpellID: 42950},
SpellSchool: core.SpellSchoolFire,
ProcMask: core.ProcMaskSpellDamage,
Flags: SpellFlagMage | core.SpellFlagAPL,
ManaCost: core.ManaCostOptions{
BaseCost: 0.07,
},
Cast: core.CastConfig{
DefaultCast: core.Cast{
GCD: core.GCDDefault,
},
CD: core.Cooldown{
Timer: mage.NewTimer(),
Duration: time.Second * 20,
},
},
BonusCritRating: float64(mage.Talents.CriticalMass+mage.Talents.WorldInFlames) * 2 * core.CritRatingPerCritChance,
DamageMultiplierAdditive: 1 + .02*float64(mage.Talents.FirePower),
CritMultiplier: mage.SpellCritMultiplier(1, mage.bonusCritDamage),
ThreatMultiplier: 1 - 0.1*float64(mage.Talents.BurningSoul),
ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
for _, aoeTarget := range sim.Encounter.TargetUnits {
baseDamage := sim.Roll(1101, 1279) + 0.193*spell.SpellPower()
baseDamage *= sim.Encounter.AOECapMultiplier()
spell.CalcAndDealDamage(sim, aoeTarget, baseDamage, spell.OutcomeMagicHitAndCrit)
}
},
})
}
40 changes: 28 additions & 12 deletions sim/mage/flamestrike.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,22 @@ import (
"github.com/wowsims/wotlk/sim/core"
)

func (mage *Mage) registerFlamestrikeSpell() {
mage.Flamestrike = mage.RegisterSpell(core.SpellConfig{
ActionID: core.ActionID{SpellID: 42926},
func (mage *Mage) registerFlamestrikeSpell(rank8 bool) *core.Spell {
actionID := core.ActionID{SpellID: 42926}.WithTag(9)
dotDamage := 780.0 / 4
minDamage := 876.0
maxDamage := 1071.0
label := "Flamestrike (Rank 9)"
if rank8 {
actionID = core.ActionID{SpellID: 42925}.WithTag(8)
dotDamage = 620.0 / 4
minDamage = 699.0
maxDamage = 854.0
label = "Flamestrike (Rank 8)"
}

return mage.RegisterSpell(core.SpellConfig{
ActionID: actionID,
SpellSchool: core.SpellSchoolFire,
ProcMask: core.ProcMaskSpellDamage,
Flags: SpellFlagMage | core.SpellFlagAPL,
Expand All @@ -19,13 +32,11 @@ func (mage *Mage) registerFlamestrikeSpell() {
Cast: core.CastConfig{
DefaultCast: core.Cast{
GCD: core.GCDDefault,
CastTime: time.Second * 3,
CastTime: time.Second * 2,
},
},

BonusCritRating: 0 +
float64(mage.Talents.CriticalMass)*2*core.CritRatingPerCritChance +
float64(mage.Talents.Pyromaniac)*1*core.CritRatingPerCritChance,
BonusCritRating: float64(mage.Talents.CriticalMass+mage.Talents.WorldInFlames) * 2 * core.CritRatingPerCritChance,
DamageMultiplierAdditive: 1 +
.02*float64(mage.Talents.SpellImpact) +
.02*float64(mage.Talents.FirePower),
Expand All @@ -35,13 +46,13 @@ func (mage *Mage) registerFlamestrikeSpell() {
Dot: core.DotConfig{
IsAOE: true,
Aura: core.Aura{
Label: "Flamestrike",
Label: label,
},
NumberOfTicks: 4,
TickLength: time.Second * 2,
OnSnapshot: func(sim *core.Simulation, _ *core.Unit, dot *core.Dot, _ bool) {
target := mage.CurrentTarget
dot.SnapshotBaseDamage = 780.0/4 + 0.122*dot.Spell.SpellPower()
dot.SnapshotBaseDamage = dotDamage + 0.122*dot.Spell.SpellPower()
dot.SnapshotAttackerMultiplier = dot.Spell.AttackerDamageMultiplier(dot.Spell.Unit.AttackTables[target.UnitIndex])
},
OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) {
Expand All @@ -52,13 +63,18 @@ func (mage *Mage) registerFlamestrikeSpell() {
},

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
dmgFromSP := 0.2357 * spell.SpellPower()
dmgFromSP := 0.243 * spell.SpellPower()
for _, aoeTarget := range sim.Encounter.TargetUnits {
baseDamage := sim.Roll(876, 1071) + dmgFromSP
baseDamage := sim.Roll(minDamage, maxDamage) + dmgFromSP
baseDamage *= sim.Encounter.AOECapMultiplier()
spell.CalcAndDealDamage(sim, aoeTarget, baseDamage, spell.OutcomeMagicHitAndCrit)
}
mage.Flamestrike.AOEDot().Apply(sim)
spell.AOEDot().Apply(sim)
},
})
}

func (mage *Mage) registerFlamestrikeSpells() {
mage.Flamestrike = mage.registerFlamestrikeSpell(false)
mage.FlamestrikeRank8 = mage.registerFlamestrikeSpell(true)
}
41 changes: 23 additions & 18 deletions sim/mage/mage.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,26 @@ type Mage struct {
// Cached values for a few mechanics.
bonusCritDamage float64

ArcaneBarrage *core.Spell
ArcaneBlast *core.Spell
ArcaneExplosion *core.Spell
ArcaneMissiles *core.Spell
Blizzard *core.Spell
DeepFreeze *core.Spell
Ignite *core.Spell
LivingBomb *core.Spell
Fireball *core.Spell
FireBlast *core.Spell
Flamestrike *core.Spell
Frostbolt *core.Spell
FrostfireBolt *core.Spell
IceLance *core.Spell
Pyroblast *core.Spell
Scorch *core.Spell
MirrorImage *core.Spell
ArcaneBarrage *core.Spell
ArcaneBlast *core.Spell
ArcaneExplosion *core.Spell
ArcaneMissiles *core.Spell
Blizzard *core.Spell
DeepFreeze *core.Spell
Ignite *core.Spell
LivingBomb *core.Spell
Fireball *core.Spell
FireBlast *core.Spell
Flamestrike *core.Spell
FlamestrikeRank8 *core.Spell
Frostbolt *core.Spell
FrostfireBolt *core.Spell
IceLance *core.Spell
Pyroblast *core.Spell
Scorch *core.Spell
MirrorImage *core.Spell
BlastWave *core.Spell
DragonsBreath *core.Spell

IcyVeins *core.Spell
SummonWaterElemental *core.Spell
Expand Down Expand Up @@ -124,7 +127,7 @@ func (mage *Mage) Initialize() {
mage.registerDeepFreezeSpell()
mage.registerFireballSpell()
mage.registerFireBlastSpell()
mage.registerFlamestrikeSpell()
mage.registerFlamestrikeSpells()
mage.registerFrostboltSpell()
mage.registerIceLanceSpell()
mage.registerPyroblastSpell()
Expand All @@ -134,6 +137,8 @@ func (mage *Mage) Initialize() {
mage.registerEvocation()
mage.registerManaGemsCD()
mage.registerMirrorImageCD()
mage.registerBlastWaveSpell()
mage.registerDragonsBreathSpell()

if mirrorImageMCD := mage.GetMajorCooldownIgnoreTag(mage.MirrorImage.ActionID); mirrorImageMCD != nil {
if len(mirrorImageMCD.GetTimings()) == 0 {
Expand Down
15 changes: 10 additions & 5 deletions sim/mage/mage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,17 @@ var FireRotation = core.APLRotationFromJsonString(`{

var FireAOERotation = core.APLRotationFromJsonString(`{
"type": "TypeAPL",
"prepullActions": [
{"action":{"castSpell":{"spellId":{"otherId":"OtherActionPotion"}}},"doAtValue":{"const":{"val":"-1s"}}}
],
"prepullActions": [],
"priorityList": [
{"action":{"autocastOtherCooldowns":{}}},
{"action":{"castSpell":{"spellId":{"spellId":42926}}}}
{"action":{"condition":{"cmp":{"op":"OpGe","lhs":{"remainingTime":{}},"rhs":{"const":{"val":"12s"}}}},"multidot":{"spellId":{"spellId":55360},"maxDots":10,"maxOverlap":{"const":{"val":"0ms"}}}}},
{"action":{"condition":{"and":{"vals":[{"auraIsActive":{"auraId":{"spellId":54741}}},{"not":{"val":{"dotIsActive":{"spellId":{"spellId":42926,"tag":9}}}}}]}},"castSpell":{"spellId":{"spellId":42926,"tag":9}}}},
{"action":{"condition":{"and":{"vals":[{"auraIsActive":{"auraId":{"spellId":54741}}},{"not":{"val":{"dotIsActive":{"spellId":{"spellId":42925,"tag":8}}}}}]}},"castSpell":{"spellId":{"spellId":42925,"tag":8}}}},
{"action":{"condition":{"or":{"vals":[{"not":{"val":{"dotIsActive":{"spellId":{"spellId":42926,"tag":9}}}}},{"not":{"val":{"dotIsActive":{"spellId":{"spellId":42925,"tag":8}}}}}]}},"castSpell":{"spellId":{"spellId":42950}}}},
{"action":{"condition":{"or":{"vals":[{"not":{"val":{"dotIsActive":{"spellId":{"spellId":42926,"tag":9}}}}},{"not":{"val":{"dotIsActive":{"spellId":{"spellId":42925,"tag":8}}}}}]}},"castSpell":{"spellId":{"spellId":42945}}}},
{"action":{"condition":{"not":{"val":{"dotIsActive":{"spellId":{"spellId":42926,"tag":9}}}}},"castSpell":{"spellId":{"spellId":42926,"tag":9}}}},
{"action":{"condition":{"not":{"val":{"dotIsActive":{"spellId":{"spellId":42926,"tag":9}}}}},"castSpell":{"spellId":{"spellId":42925,"tag":8}}}},
{"action":{"condition":{"auraIsActiveWithReactionTime":{"auraId":{"spellId":44448}}},"castSpell":{"spellId":{"spellId":42891}}}},
{"action":{"castSpell":{"spellId":{"spellId":42921}}}}
]
}`)

Expand Down
48 changes: 48 additions & 0 deletions sim/mage/talents.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ func (mage *Mage) ApplyTalents() {
mage.applyHotStreak()
mage.applyFingersOfFrost()
mage.applyBrainFreeze()
mage.applyFireStarter()

mage.registerArcanePowerCD()
mage.registerPresenceOfMindCD()
mage.registerCombustionCD()
Expand Down Expand Up @@ -771,3 +773,49 @@ func (mage *Mage) applyWintersChill() {
},
})
}

func (mage *Mage) applyFireStarter() {
if mage.Talents.Firestarter == 0 {
return
}

firestarterAura := mage.RegisterAura(core.Aura{
Label: "Firestarter",
ActionID: core.ActionID{SpellID: 54741},
Duration: 10 * time.Second,
OnGain: func(aura *core.Aura, sim *core.Simulation) {
mage.Flamestrike.CostMultiplier -= 100
mage.Flamestrike.CastTimeMultiplier -= 1
mage.FlamestrikeRank8.CostMultiplier -= 100
mage.FlamestrikeRank8.CastTimeMultiplier -= 1
},
OnExpire: func(aura *core.Aura, sim *core.Simulation) {
mage.Flamestrike.CostMultiplier += 100
mage.Flamestrike.CastTimeMultiplier += 1
mage.FlamestrikeRank8.CostMultiplier += 100
mage.FlamestrikeRank8.CastTimeMultiplier += 1
},
OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) {
if spell == mage.Flamestrike || spell == mage.FlamestrikeRank8 {
aura.Deactivate(sim)
}
},
})

mage.RegisterAura(core.Aura{
Label: "Firestarter talent",
Duration: core.NeverExpires,
OnReset: func(aura *core.Aura, sim *core.Simulation) {
aura.Activate(sim)
},
OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
if !result.Landed() {
return
}

if spell == mage.BlastWave || spell == mage.DragonsBreath {
firestarterAura.Activate(sim)
}
},
})
}
6 changes: 6 additions & 0 deletions ui/core/proto_utils/action_id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,12 @@ export class ActionId {
break;
case 'Fireball':
case 'Flamestrike':
if (this.tag == 8) {
name += ' (Rank 8)';
} else if (this.tag == 9) {
name += ' (Rank 9)';
}
break;
case 'Pyroblast':
if (this.tag) name += ' (DoT)';
break;
Expand Down
Loading

0 comments on commit 2980120

Please sign in to comment.