From ff47acd69fad12e5710b4e1923fa5fa5b56a616b Mon Sep 17 00:00:00 2001 From: NerdEgghead Date: Mon, 25 Dec 2023 19:46:03 -0800 Subject: [PATCH] Implemented APL wrapper for Feral DPS legacy rotation. Changes to be committed: modified: proto/apl.proto modified: sim/druid/feral/apl_values.go modified: sim/druid/feral/feral.go modified: sim/druid/feral/rotation.go modified: ui/core/components/individual_sim_ui/apl_actions.ts --- proto/apl.proto | 10 +++- sim/druid/feral/apl_values.go | 60 +++++++++++++++++++ sim/druid/feral/feral.go | 2 + sim/druid/feral/rotation.go | 4 +- .../individual_sim_ui/apl_actions.ts | 20 ++++++- 5 files changed, 91 insertions(+), 5 deletions(-) diff --git a/proto/apl.proto b/proto/apl.proto index 43ac3b3455..34217456a9 100644 --- a/proto/apl.proto +++ b/proto/apl.proto @@ -46,7 +46,7 @@ message APLListItem { APLAction action = 3; // The action to be performed. } -// NextIndex: 18 +// NextIndex: 19 message APLAction { APLValue condition = 1; // If set, action will only execute if value is true or != 0. @@ -74,6 +74,9 @@ message APLAction { APLActionCancelAura cancel_aura = 10; APLActionTriggerICD trigger_icd = 11; APLActionItemSwap item_swap = 17; + + // Class or Spec-specific actions + APLActionCatOptimalRotationAction cat_optimal_rotation_action = 18; } } @@ -261,6 +264,9 @@ message APLActionItemSwap { SwapSet swap_set = 1; } +message APLActionCatOptimalRotationAction { +} + /////////////////////////////////////////////////////////////////////////// // VALUES /////////////////////////////////////////////////////////////////////////// @@ -510,4 +516,4 @@ message APLValueWarlockShouldRecastDrainSoul { } message APLValueWarlockShouldRefreshCorruption { UnitReference target_unit = 1; -} \ No newline at end of file +} diff --git a/sim/druid/feral/apl_values.go b/sim/druid/feral/apl_values.go index c23c9dcc60..528b135e7a 100644 --- a/sim/druid/feral/apl_values.go +++ b/sim/druid/feral/apl_values.go @@ -84,3 +84,63 @@ func (value *APLValueCatNewSavageRoarDuration) GetDuration(_ *core.Simulation) t func (value *APLValueCatNewSavageRoarDuration) String() string { return "New Savage Roar Duration()" } + +func (cat *FeralDruid) NewAPLAction(rot *core.APLRotation, config *proto.APLAction) core.APLActionImpl { + switch config.Action.(type) { + case *proto.APLAction_CatOptimalRotationAction: + return cat.newActionCatOptimalRotationAction(rot, config.GetCatOptimalRotationAction()) + default: + return nil + } +} + +type APLActionCatOptimalRotationAction struct { + cat *FeralDruid + lastAction time.Duration +} + +func (impl *APLActionCatOptimalRotationAction) GetInnerActions() []*core.APLAction { return nil } +func (impl *APLActionCatOptimalRotationAction) GetAPLValues() []core.APLValue { return nil } +func (impl *APLActionCatOptimalRotationAction) Finalize(*core.APLRotation) {} +func (impl *APLActionCatOptimalRotationAction) GetNextAction(*core.Simulation) *core.APLAction { + return nil +} + +func (cat *FeralDruid) newActionCatOptimalRotationAction(_ *core.APLRotation, _ *proto.APLActionCatOptimalRotationAction) core.APLActionImpl { + return &APLActionCatOptimalRotationAction{ + cat: cat, + } +} + +func (action *APLActionCatOptimalRotationAction) IsReady(sim *core.Simulation) bool { + return sim.CurrentTime > action.lastAction +} + +func (action *APLActionCatOptimalRotationAction) Execute(sim *core.Simulation) { + cat := action.cat + + // If a melee swing resulted in an Omen proc, then schedule the + // next player decision based on latency. + if cat.Talents.OmenOfClarity && cat.ClearcastingAura.RemainingDuration(sim) == cat.ClearcastingAura.Duration { + // Kick gcd loop, also need to account for any gcd 'left' + // otherwise it breaks gcd logic + kickTime := max(cat.NextGCDAt(), sim.CurrentTime+cat.latency) + cat.NextRotationAction(sim, kickTime) + } + + if cat.GCD.IsReady(sim) && (cat.rotationAction == nil || sim.CurrentTime >= cat.rotationAction.NextActionAt) { + cat.OnGCDReady(sim) + } + + cat.OnEnergyGain(sim) + action.lastAction = sim.CurrentTime +} + +func (action *APLActionCatOptimalRotationAction) Reset(*core.Simulation) { + action.cat.usingHardcodedAPL = true + action.lastAction = core.DurationFromSeconds(-100) +} + +func (action *APLActionCatOptimalRotationAction) String() string { + return "Execute Optimal Cat Action()" +} diff --git a/sim/druid/feral/feral.go b/sim/druid/feral/feral.go index 91459020aa..13e085a5e8 100644 --- a/sim/druid/feral/feral.go +++ b/sim/druid/feral/feral.go @@ -77,6 +77,7 @@ type FeralDruid struct { bleedAura *core.Aura lastShift time.Duration ripRefreshPending bool + usingHardcodedAPL bool rotationAction *core.PendingAction } @@ -120,4 +121,5 @@ func (cat *FeralDruid) Reset(sim *core.Simulation) { cat.readyToShift = false cat.waitingForTick = false cat.berserkUsed = false + cat.rotationAction = nil } diff --git a/sim/druid/feral/rotation.go b/sim/druid/feral/rotation.go index 897d6b7133..815ad6ccf3 100644 --- a/sim/druid/feral/rotation.go +++ b/sim/druid/feral/rotation.go @@ -10,7 +10,7 @@ import ( ) func (cat *FeralDruid) OnEnergyGain(sim *core.Simulation) { - if cat.IsUsingAPL { + if cat.IsUsingAPL && !cat.usingHardcodedAPL { return } @@ -25,7 +25,7 @@ func (cat *FeralDruid) OnEnergyGain(sim *core.Simulation) { } func (cat *FeralDruid) OnGCDReady(sim *core.Simulation) { - if cat.IsUsingAPL { + if cat.IsUsingAPL && !cat.usingHardcodedAPL { return } diff --git a/ui/core/components/individual_sim_ui/apl_actions.ts b/ui/core/components/individual_sim_ui/apl_actions.ts index 3157bf0893..cb93eda523 100644 --- a/ui/core/components/individual_sim_ui/apl_actions.ts +++ b/ui/core/components/individual_sim_ui/apl_actions.ts @@ -1,3 +1,8 @@ +import { + Class, + Spec, +} from '../../proto/common.js'; + import { APLAction, @@ -22,6 +27,8 @@ import { APLActionItemSwap, APLActionItemSwap_SwapSet as ItemSwapSet, + APLActionCatOptimalRotationAction, + APLValue, } from '../../proto/apl.js'; @@ -561,4 +568,15 @@ const actionKindFactories: {[f in NonNullable]: ActionKindConfig< itemSwapSetFieldConfig('swapSet'), ], }), -}; \ No newline at end of file + + // Class/spec specific actions + ['catOptimalRotationAction']: inputBuilder({ + label: 'Optimal Rotation Action', + submenu: ['Feral Druid'], + shortDescription: 'Executes optimized Feral DPS rotation using hardcoded legacy algorithm.', + includeIf: (player: Player, isPrepull: boolean) => player.spec == Spec.SpecFeralDruid, + newValue: () => APLActionCatOptimalRotationAction.create(), + fields: [ + ], + }), +};