diff --git a/proto/apl.proto b/proto/apl.proto index e99562fd07..f4410a1000 100644 --- a/proto/apl.proto +++ b/proto/apl.proto @@ -53,7 +53,7 @@ message APLAction { } } -// NextIndex: 52 +// NextIndex: 53 message APLValue { oneof value { // Operators @@ -128,6 +128,7 @@ message APLValue { // Class or Spec-specific values APLValueTotemRemainingTime totem_remaining_time = 49; + APLValueCatExcessEnergy cat_excess_energy = 52; } } @@ -400,4 +401,6 @@ message APLValueSequenceTimeToReady { message APLValueTotemRemainingTime { ShamanTotems.TotemType totem_type = 1; +} +message APLValueCatExcessEnergy { } \ No newline at end of file diff --git a/sim/druid/feral/apl_values.go b/sim/druid/feral/apl_values.go new file mode 100644 index 0000000000..c8c76d8a94 --- /dev/null +++ b/sim/druid/feral/apl_values.go @@ -0,0 +1,69 @@ +package feral + +import ( + "time" + + "github.com/wowsims/wotlk/sim/core" + "github.com/wowsims/wotlk/sim/core/proto" +) + +func (cat *FeralDruid) NewAPLValue(rot *core.APLRotation, config *proto.APLValue) core.APLValue { + switch config.Value.(type) { + case *proto.APLValue_CatExcessEnergy: + return cat.newValueCatExcessEnergy(rot, config.GetCatExcessEnergy()) + default: + return nil + } +} + +type APLValueCatExcessEnergy struct { + core.DefaultAPLValueImpl + cat *FeralDruid +} + +func (cat *FeralDruid) newValueCatExcessEnergy(rot *core.APLRotation, config *proto.APLValueCatExcessEnergy) core.APLValue { + return &APLValueCatExcessEnergy{ + cat: cat, + } +} +func (value *APLValueCatExcessEnergy) Type() proto.APLValueType { + return proto.APLValueType_ValueTypeFloat +} +func (value *APLValueCatExcessEnergy) GetFloat(sim *core.Simulation) float64 { + cat := value.cat + pendingPool := PoolingActions{} + pendingPool.create(4) + + curCp := cat.ComboPoints() + simTimeRemain := sim.GetRemainingDuration() + rakeDot := cat.Rake.CurDot() + ripDot := cat.Rip.CurDot() + mangleRefreshPending := cat.bleedAura.IsActive() && cat.bleedAura.RemainingDuration(sim) < (simTimeRemain-time.Second) + endThresh := time.Second * 10 + + if ripDot.IsActive() && (ripDot.RemainingDuration(sim) < simTimeRemain-endThresh) && curCp == 5 { + ripCost := core.Ternary(cat.berserkExpectedAt(sim, ripDot.ExpiresAt()), cat.Rip.DefaultCast.Cost*0.5, cat.Rip.DefaultCast.Cost) + pendingPool.addAction(ripDot.ExpiresAt(), ripCost) + cat.ripRefreshPending = true + } + if rakeDot.IsActive() && (rakeDot.RemainingDuration(sim) < simTimeRemain-rakeDot.Duration) { + rakeCost := core.Ternary(cat.berserkExpectedAt(sim, rakeDot.ExpiresAt()), cat.Rake.DefaultCast.Cost*0.5, cat.Rake.DefaultCast.Cost) + pendingPool.addAction(rakeDot.ExpiresAt(), rakeCost) + } + if mangleRefreshPending { + mangleCost := core.Ternary(cat.berserkExpectedAt(sim, cat.bleedAura.ExpiresAt()), cat.MangleCat.DefaultCast.Cost*0.5, cat.MangleCat.DefaultCast.Cost) + pendingPool.addAction(cat.bleedAura.ExpiresAt(), mangleCost) + } + if cat.SavageRoarAura.IsActive() { + roarCost := core.Ternary(cat.berserkExpectedAt(sim, cat.SavageRoarAura.ExpiresAt()), cat.SavageRoar.DefaultCast.Cost*0.5, cat.SavageRoar.DefaultCast.Cost) + pendingPool.addAction(cat.SavageRoarAura.ExpiresAt(), roarCost) + } + + pendingPool.sort() + + floatingEnergy := pendingPool.calcFloatingEnergy(cat, sim) + return cat.CurrentEnergy() - floatingEnergy +} +func (value *APLValueCatExcessEnergy) String() string { + return "Cat Excess Energy()" +} diff --git a/sim/druid/feral/pooling_actions.go b/sim/druid/feral/pooling_actions.go index eae84c2a74..64ebd6c4c7 100644 --- a/sim/druid/feral/pooling_actions.go +++ b/sim/druid/feral/pooling_actions.go @@ -30,15 +30,15 @@ func (pa *PoolingActions) sort() { }) } -func (pa *PoolingActions) calcFloatingEnergy(currentTime time.Duration, tfExpectedBefore func(refreshTime time.Duration) bool) float64 { +func (pa *PoolingActions) calcFloatingEnergy(cat *FeralDruid, sim *core.Simulation) float64 { floatingEnergy := 0.0 - previousTime := currentTime + previousTime := sim.CurrentTime tfPending := false for _, s := range pa.actions { delta_t := float64((s.refreshTime - previousTime) / core.EnergyTickDuration) if !tfPending { - tfPending = tfExpectedBefore(s.refreshTime) + tfPending = cat.tfExpectedBefore(sim, s.refreshTime) if tfPending { s.cost -= 60 } diff --git a/sim/druid/feral/rotation.go b/sim/druid/feral/rotation.go index 180010a1d2..ec8302fbfd 100644 --- a/sim/druid/feral/rotation.go +++ b/sim/druid/feral/rotation.go @@ -457,11 +457,8 @@ func (cat *FeralDruid) doRotation(sim *core.Simulation) (bool, time.Duration) { pendingPool.addAction(ripDot.ExpiresAt(), ripCost) cat.ripRefreshPending = true } - if rakeDot.IsActive() && (rakeDot.RemainingDuration(sim) < simTimeRemain-rakeDot.Duration) { + if poolForRake && rakeDot.IsActive() && (rakeDot.RemainingDuration(sim) < simTimeRemain-rakeDot.Duration) { rakeCost := core.Ternary(cat.berserkExpectedAt(sim, rakeDot.ExpiresAt()), cat.Rake.DefaultCast.Cost*0.5, cat.Rake.DefaultCast.Cost) - if !poolForRake { - rakeCost = 0 - } pendingPool.addAction(rakeDot.ExpiresAt(), rakeCost) } if mangleRefreshPending { @@ -550,9 +547,7 @@ func (cat *FeralDruid) doRotation(sim *core.Simulation) (bool, time.Duration) { flowershiftNow = flowerEnd+time.Duration(math.Floor(energyToDump/42)*float64(time.Second)) < sim.CurrentTime+simTimeRemain } - floatingEnergy := pendingPool.calcFloatingEnergy(sim.CurrentTime, func(refreshTime time.Duration) bool { - return cat.tfExpectedBefore(sim, refreshTime) - }) + floatingEnergy := pendingPool.calcFloatingEnergy(cat, sim) excessE := curEnergy - floatingEnergy timeToNextAction := time.Duration(0) diff --git a/sim/druid/feral/rotation_aoe.go b/sim/druid/feral/rotation_aoe.go index e48ab49544..e516829122 100644 --- a/sim/druid/feral/rotation_aoe.go +++ b/sim/druid/feral/rotation_aoe.go @@ -82,9 +82,7 @@ func (cat *FeralDruid) doAoeRotation(sim *core.Simulation) (bool, time.Duration) pendingPool.sort() - floatingEnergy := pendingPool.calcFloatingEnergy(sim.CurrentTime, func(refreshTime time.Duration) bool { - return cat.tfExpectedBefore(sim, refreshTime) - }) + floatingEnergy := pendingPool.calcFloatingEnergy(cat, sim) excessE := curEnergy - floatingEnergy timeToNextAction := time.Duration(0) diff --git a/ui/core/components/individual_sim_ui/apl_values.ts b/ui/core/components/individual_sim_ui/apl_values.ts index 708208195c..9cd539c237 100644 --- a/ui/core/components/individual_sim_ui/apl_values.ts +++ b/ui/core/components/individual_sim_ui/apl_values.ts @@ -1,5 +1,6 @@ import { Class, + Spec, } from '../../proto/common.js'; import { @@ -62,6 +63,7 @@ import { APLValueNextRuneCooldown, APLValueNumberTargets, APLValueTotemRemainingTime, + APLValueCatExcessEnergy, } from '../../proto/apl.js'; import { EventID } from '../../typed_event.js'; @@ -894,4 +896,13 @@ const valueKindFactories: {[f in NonNullable]: ValueKindConfig, isPrepull: boolean) => player.spec == Spec.SpecFeralDruid, + fields: [ + ], + }), };