Skip to content

Commit

Permalink
Merge pull request #1089 from wowsims/feature/mage-simple-apl
Browse files Browse the repository at this point in the history
Add custom APL Mage Combust value
  • Loading branch information
1337LutZ authored Oct 11, 2024
2 parents 50df776 + 2473168 commit 5ca8f7f
Show file tree
Hide file tree
Showing 19 changed files with 765 additions and 646 deletions.
5 changes: 4 additions & 1 deletion proto/apl.proto
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ message APLAction {
}
}

// NextIndex: 77
// NextIndex: 78
message APLValue {
oneof value {
// Operators
Expand Down Expand Up @@ -184,6 +184,7 @@ message APLValue {
APLValueWarlockShouldRecastDrainSoul warlock_should_recast_drain_soul = 59;
APLValueWarlockShouldRefreshCorruption warlock_should_refresh_corruption = 60;
APLValueCurrentEclipsePhase druid_current_eclipse_phase = 70;
APLValueMageCurrentCombustionDotEstimate mage_current_combustion_dot_estimate = 77;
}
}

Expand Down Expand Up @@ -584,3 +585,5 @@ message APLValueWarlockShouldRecastDrainSoul {
message APLValueWarlockShouldRefreshCorruption {
UnitReference target_unit = 1;
}
message APLValueMageCurrentCombustionDotEstimate {
}
13 changes: 9 additions & 4 deletions proto/mage.proto
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,15 @@ message ArcaneMage {

message FireMage {
message Rotation {
// Minimum Ignite threshold to combust at during lust
int32 ignite_combust_threshold = 1;
double ignite_last_moment_lust_percentage = 2;
double ignite_no_lust_percentage = 3;
// Minimum Combustion threshold to cast Combustion at during lust
int32 combust_threshold = 4;
double combust_last_moment_lust_percentage = 5;
double combust_no_lust_percentage = 6;

// deprecated fields
int32 ignite_combust_threshold = 1 [deprecated=true];
double ignite_last_moment_lust_percentage = 2 [deprecated=true];
double ignite_no_lust_percentage = 3 [deprecated=true];
}

message Options {
Expand Down
15 changes: 14 additions & 1 deletion sim/core/dot.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,22 @@ func (dot *Dot) TimeUntilNextTick(sim *Simulation) time.Duration {
return dot.NextTickAt() - sim.CurrentTime
}

func (dot *Dot) calculateHastedTickCount(baseDuration time.Duration, tickPeriod time.Duration) int32 {
return int32(math.Round(float64(baseDuration) / float64(tickPeriod)))
}

// Returns the total amount of ticks with the snapshotted haste
func (dot *Dot) HastedTickCount() int32 {
return int32(math.Round(float64(dot.BaseDuration()) / float64(dot.tickPeriod)))
return dot.calculateHastedTickCount(dot.BaseDuration(), dot.tickPeriod)
}

func (dot *Dot) ExpectedTickCount() int32 {
tickCount := dot.BaseTickCount
if dot.affectedByCastSpeed && !dot.hasteReducesDuration {
tickPeriod := dot.Spell.Unit.ApplyCastSpeedForSpell(dot.BaseTickLength, dot.Spell).Round(time.Millisecond)
tickCount = dot.calculateHastedTickCount(dot.BaseDuration(), tickPeriod)
}
return tickCount
}

func (dot *Dot) RemainingTicks() int32 {
Expand Down
60 changes: 60 additions & 0 deletions sim/mage/apl_values.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package mage

import (
"github.com/wowsims/cata/sim/core"
"github.com/wowsims/cata/sim/core/proto"
)

func (mage *Mage) NewAPLValue(rot *core.APLRotation, config *proto.APLValue) core.APLValue {
switch config.Value.(type) {
case *proto.APLValue_MageCurrentCombustionDotEstimate:
return mage.newValueCurrentCombustionDotEstimate(config.GetMageCurrentCombustionDotEstimate())
default:
return nil
}
}

type APLValueMageCurrentCombustionDotEstimate struct {
core.DefaultAPLValueImpl
mage *Mage
}

func (mage *Mage) newValueCurrentCombustionDotEstimate(_ *proto.APLValueMageCurrentCombustionDotEstimate) core.APLValue {
if !mage.Talents.Combustion {
return nil
}

return &APLValueMageCurrentCombustionDotEstimate{
mage: mage,
}
}
func (value *APLValueMageCurrentCombustionDotEstimate) Type() proto.APLValueType {
return proto.APLValueType_ValueTypeInt
}

func (value *APLValueMageCurrentCombustionDotEstimate) GetInt(sim *core.Simulation) int32 {
mage := value.mage

combustionDotDamage := 0.0
tickCount := int(mage.Combustion.RelatedDotSpell.Dot(mage.CurrentTarget).ExpectedTickCount())

for i := 0; i < tickCount; i++ {
damage := mage.Combustion.RelatedDotSpell.ExpectedTickDamage(sim, mage.CurrentTarget)
combustionDotDamage += damage
}

combustionDotDamageAsInt := int32(combustionDotDamage)

if combustionDotDamageAsInt != mage.previousCombustionDotEstimate {
mage.previousCombustionDotEstimate = int32(combustionDotDamage)
if sim.Log != nil {
mage.Log(sim, "Combustion Dot Estimate: %d", combustionDotDamageAsInt)
}
}

return combustionDotDamageAsInt
}

func (value *APLValueMageCurrentCombustionDotEstimate) String() string {
return "Combustion Dot Estimated Value"
}
50 changes: 33 additions & 17 deletions sim/mage/combustion.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ func (mage *Mage) registerCombustionSpell() {
return
}

actionID := core.ActionID{SpellID: 11129}

mage.Combustion = mage.RegisterSpell(core.SpellConfig{
ActionID: core.ActionID{SpellID: 11129},
ActionID: actionID,
SpellSchool: core.SpellSchoolFire,
ProcMask: core.ProcMaskSpellDamage, // need to check proc mask for impact damage
ClassSpellMask: MageSpellCombustionApplication,
Expand Down Expand Up @@ -47,8 +49,26 @@ func (mage *Mage) registerCombustionSpell() {
MageSpellPyroblastDot: 0.175 * mage.ClassSpellScaling,
}

calculatedDotTick := func(target *core.Unit) float64 {
tickDamage := 0.0
dotSpells := []*core.Spell{mage.LivingBomb, mage.Ignite, mage.Pyroblast.RelatedDotSpell}
for _, spell := range dotSpells {
dot := spell.Dot(target)
if dot.IsActive() {
if spell.ClassSpellMask&(MageSpellLivingBombDot|MageSpellPyroblastDot) != 0 {
dps := dotBase[spell.ClassSpellMask] + dot.BonusCoefficient*dot.Spell.SpellPower()
dps *= spell.DamageMultiplier * spell.DamageMultiplierAdditive
tickDamage += dps / dot.BaseTickLength.Seconds()
} else {
tickDamage += dot.SnapshotBaseDamage / 2
}
}
}
return tickDamage
}

mage.Combustion.RelatedDotSpell = mage.RegisterSpell(core.SpellConfig{
ActionID: core.ActionID{SpellID: 11129}.WithTag(1),
ActionID: actionID.WithTag(1),
SpellSchool: core.SpellSchoolFire,
ProcMask: core.ProcMaskEmpty,
ClassSpellMask: MageSpellCombustion,
Expand All @@ -72,27 +92,23 @@ func (mage *Mage) registerCombustionSpell() {
AffectedByCastSpeed: true,

OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, _ bool) {
combustionDotDamage := 0.0
dotSpells := []*core.Spell{mage.LivingBomb, mage.Ignite, mage.Pyroblast.RelatedDotSpell}
for _, spell := range dotSpells {
dot := spell.Dot(target)
if dot.IsActive() {
if spell.ClassSpellMask&(MageSpellLivingBombDot|MageSpellPyroblastDot) != 0 {
dps := dotBase[spell.ClassSpellMask] + dot.BonusCoefficient*dot.Spell.SpellPower()
dps *= spell.DamageMultiplier * spell.DamageMultiplierAdditive
combustionDotDamage += dps / dot.BaseTickLength.Seconds()
} else {
combustionDotDamage += dot.SnapshotBaseDamage / 2
}
}
}
dot.Snapshot(target, combustionDotDamage)
tickBase := calculatedDotTick(target)
dot.Snapshot(target, tickBase)
},
OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) {
dot.CalcAndDealPeriodicSnapshotDamage(sim, target, dot.OutcomeSnapshotCrit)
},
},
ExpectedTickDamage: func(sim *core.Simulation, target *core.Unit, spell *core.Spell, useSnapshot bool) *core.SpellResult {
tickBase := calculatedDotTick(target)
result := spell.CalcPeriodicDamage(sim, target, tickBase, spell.OutcomeExpectedMagicAlwaysHit)

critChance := spell.SpellCritChance(target)
critMod := (critChance * (spell.CritMultiplier - 1))
result.Damage *= 1 + critMod

return result
},
ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
spell.Dot(target).Apply(sim)
},
Expand Down
Loading

0 comments on commit 5ca8f7f

Please sign in to comment.