From accf3998c53bfc9cfe0be7767575db379f964e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Hillerstr=C3=B6m?= Date: Mon, 16 Dec 2024 10:56:23 +0100 Subject: [PATCH] Replace Time to Max with Time to Target for more flexibility --- proto/apl.proto | 12 ++-- sim/core/apl_value.go | 8 +-- sim/core/apl_values_resources.go | 56 ++++++++++++------- sim/core/energy.go | 6 +- sim/core/focus.go | 6 +- .../individual_sim_ui/apl_values.ts | 24 ++++---- 6 files changed, 66 insertions(+), 46 deletions(-) diff --git a/proto/apl.proto b/proto/apl.proto index 64afde9fa1..10fb6e4f21 100644 --- a/proto/apl.proto +++ b/proto/apl.proto @@ -127,8 +127,8 @@ message APLValue { APLValueMaxRunicPower max_runic_power = 86; APLValueEnergyRegenPerSecond energy_regen_per_second = 90; APLValueFocusRegenPerSecond focus_regen_per_second = 91; - APLValueEnergyTimeToMax energy_time_to_max = 92; - APLValueFocusTimeToMax focus_time_to_max = 93; + APLValueEnergyTimeToTarget energy_time_to_target = 92; + APLValueFocusTimeToTarget focus_time_to_target = 93; // Unit values APLValueUnitIsMoving unit_is_moving = 72; @@ -450,8 +450,12 @@ message APLValueMaxFocus {} message APLValueMaxRunicPower {} message APLValueEnergyRegenPerSecond {} message APLValueFocusRegenPerSecond {} -message APLValueEnergyTimeToMax {} -message APLValueFocusTimeToMax {} +message APLValueEnergyTimeToTarget { + APLValue target_energy = 1; +} +message APLValueFocusTimeToTarget { + APLValue target_focus = 1; +} enum APLValueRuneType { RuneUnknown = 0; diff --git a/sim/core/apl_value.go b/sim/core/apl_value.go index 6fc9abe0fa..f1f5e25cc8 100644 --- a/sim/core/apl_value.go +++ b/sim/core/apl_value.go @@ -132,10 +132,10 @@ func (rot *APLRotation) newAPLValue(config *proto.APLValue) APLValue { value = rot.newValueEnergyRegenPerSecond(config.GetEnergyRegenPerSecond(), config.Uuid) case *proto.APLValue_FocusRegenPerSecond: value = rot.newValueFocusRegenPerSecond(config.GetFocusRegenPerSecond(), config.Uuid) - case *proto.APLValue_EnergyTimeToMax: - value = rot.newValueEnergyTimeToMax(config.GetEnergyTimeToMax(), config.Uuid) - case *proto.APLValue_FocusTimeToMax: - value = rot.newValueFocusTimeToMax(config.GetFocusTimeToMax(), config.Uuid) + case *proto.APLValue_EnergyTimeToTarget: + value = rot.newValueEnergyTimeToTarget(config.GetEnergyTimeToTarget(), config.Uuid) + case *proto.APLValue_FocusTimeToTarget: + value = rot.newValueFocusTimeToTarget(config.GetFocusTimeToTarget(), config.Uuid) // Resources Runes case *proto.APLValue_CurrentRuneCount: diff --git a/sim/core/apl_values_resources.go b/sim/core/apl_values_resources.go index e274134394..7333f54998 100644 --- a/sim/core/apl_values_resources.go +++ b/sim/core/apl_values_resources.go @@ -222,29 +222,37 @@ func (value *APLValueFocusRegenPerSecond) String() string { return "Focus Regen Per Second" } -type APLValueFocusTimeToMax struct { +type APLValueFocusTimeToTarget struct { DefaultAPLValueImpl - unit *Unit + unit *Unit + targetFocus APLValue } -func (rot *APLRotation) newValueFocusTimeToMax(_ *proto.APLValueFocusTimeToMax, uuid *proto.UUID) APLValue { +func (rot *APLRotation) newValueFocusTimeToTarget(config *proto.APLValueFocusTimeToTarget, uuid *proto.UUID) APLValue { unit := rot.unit if !unit.HasFocusBar() { rot.ValidationMessageByUUID(uuid, proto.LogLevel_Warning, "%s does not use Focus", unit.Label) return nil } - return &APLValueFocusTimeToMax{ - unit: unit, + + targetFocus := rot.coerceTo(rot.newAPLValue(config.TargetFocus), proto.APLValueType_ValueTypeFloat) + if targetFocus == nil { + return nil + } + + return &APLValueFocusTimeToTarget{ + unit: unit, + targetFocus: targetFocus, } } -func (value *APLValueFocusTimeToMax) Type() proto.APLValueType { +func (value *APLValueFocusTimeToTarget) Type() proto.APLValueType { return proto.APLValueType_ValueTypeDuration } -func (value *APLValueFocusTimeToMax) GetDuration(sim *Simulation) time.Duration { - return value.unit.TimeToMaxFocus() +func (value *APLValueFocusTimeToTarget) GetDuration(sim *Simulation) time.Duration { + return value.unit.TimeToTargetFocus(value.targetFocus.GetFloat(sim)) } -func (value *APLValueFocusTimeToMax) String() string { - return "Estimated Time To Max Focus" +func (value *APLValueFocusTimeToTarget) String() string { + return "Estimated Time To Target Focus" } type APLValueCurrentEnergy struct { @@ -322,29 +330,37 @@ func (value *APLValueEnergyRegenPerSecond) String() string { return "Energy Regen Per Second" } -type APLValueEnergyTimeToMax struct { +type APLValueEnergyTimeToTarget struct { DefaultAPLValueImpl - unit *Unit + unit *Unit + targetEnergy APLValue } -func (rot *APLRotation) newValueEnergyTimeToMax(_ *proto.APLValueEnergyTimeToMax, uuid *proto.UUID) APLValue { +func (rot *APLRotation) newValueEnergyTimeToTarget(config *proto.APLValueEnergyTimeToTarget, uuid *proto.UUID) APLValue { unit := rot.unit if !unit.HasEnergyBar() { rot.ValidationMessageByUUID(uuid, proto.LogLevel_Warning, "%s does not use Energy", unit.Label) return nil } - return &APLValueEnergyTimeToMax{ - unit: unit, + + targetEnergy := rot.coerceTo(rot.newAPLValue(config.TargetEnergy), proto.APLValueType_ValueTypeFloat) + if targetEnergy == nil { + return nil + } + + return &APLValueEnergyTimeToTarget{ + unit: unit, + targetEnergy: targetEnergy, } } -func (value *APLValueEnergyTimeToMax) Type() proto.APLValueType { +func (value *APLValueEnergyTimeToTarget) Type() proto.APLValueType { return proto.APLValueType_ValueTypeDuration } -func (value *APLValueEnergyTimeToMax) GetDuration(sim *Simulation) time.Duration { - return value.unit.TimeToMaxEnergy() +func (value *APLValueEnergyTimeToTarget) GetDuration(sim *Simulation) time.Duration { + return value.unit.TimeToTargetEnergy(value.targetEnergy.GetFloat(sim)) } -func (value *APLValueEnergyTimeToMax) String() string { - return "Estimated Time To Max Energy" +func (value *APLValueEnergyTimeToTarget) String() string { + return "Estimated Time To Target Energy" } type APLValueCurrentComboPoints struct { diff --git a/sim/core/energy.go b/sim/core/energy.go index 27c827cfd3..bd2d12da1b 100644 --- a/sim/core/energy.go +++ b/sim/core/energy.go @@ -68,12 +68,12 @@ func (eb *energyBar) EnergyRegenPerSecond() float64 { return 10.0 * eb.hasteRatingMultiplier * eb.energyRegenMultiplier } -func (eb *energyBar) TimeToMaxEnergy() time.Duration { - if eb.currentEnergy == eb.maxEnergy { +func (eb *energyBar) TimeToTargetEnergy(targetEnergy float64) time.Duration { + if eb.currentEnergy >= targetEnergy { return time.Duration(0) } - return DurationFromSeconds((eb.maxEnergy - eb.currentEnergy) / eb.EnergyRegenPerSecond()) + return DurationFromSeconds((targetEnergy - eb.currentEnergy) / eb.EnergyRegenPerSecond()) } func (eb *energyBar) AddEnergy(sim *Simulation, amount float64, metrics *ResourceMetrics) { diff --git a/sim/core/focus.go b/sim/core/focus.go index e73a62dfa1..370aa37f5e 100644 --- a/sim/core/focus.go +++ b/sim/core/focus.go @@ -81,12 +81,12 @@ func (fb *focusBar) FocusRegenPerSecond() float64 { } } -func (fb *focusBar) TimeToMaxFocus() time.Duration { - if fb.currentFocus == fb.maxFocus { +func (fb *focusBar) TimeToTargetFocus(targetFocus float64) time.Duration { + if fb.currentFocus >= targetFocus { return time.Duration(0) } - return DurationFromSeconds((fb.maxFocus - fb.currentFocus) / fb.FocusRegenPerSecond()) + return DurationFromSeconds((targetFocus - fb.currentFocus) / fb.FocusRegenPerSecond()) } func (fb *focusBar) getTotalRegenMultiplier() float64 { diff --git a/ui/core/components/individual_sim_ui/apl_values.ts b/ui/core/components/individual_sim_ui/apl_values.ts index 39781031dc..8db5d2fe59 100644 --- a/ui/core/components/individual_sim_ui/apl_values.ts +++ b/ui/core/components/individual_sim_ui/apl_values.ts @@ -47,9 +47,9 @@ import { APLValueDotRemainingTime, APLValueDotTickFrequency, APLValueEnergyRegenPerSecond, - APLValueEnergyTimeToMax, + APLValueEnergyTimeToTarget, APLValueFocusRegenPerSecond, - APLValueFocusTimeToMax, + APLValueFocusTimeToTarget, APLValueFrontOfTarget, APLValueGCDIsReady, APLValueGCDTimeToReady, @@ -725,13 +725,13 @@ const valueKindFactories: { [f in NonNullable]: ValueKindConfig, _isPrepull: boolean) => player.getClass() == Class.ClassHunter, fields: [], }), - focusTimeToMax: inputBuilder({ - label: 'Estimated Time To Max Focus', + focusTimeToTarget: inputBuilder({ + label: 'Estimated Time To Target Focus', submenu: ['Resources', 'Focus'], - shortDescription: 'Estimated time until max Focus is reached.', - newValue: APLValueFocusTimeToMax.create, + shortDescription: 'Estimated time until target Focus is reached, will return 0 if at or above target.', + newValue: APLValueFocusTimeToTarget.create, includeIf: (player: Player, _isPrepull: boolean) => player.getClass() == Class.ClassHunter, - fields: [], + fields: [valueFieldConfig('targetFocus')], }), currentEnergy: inputBuilder({ label: 'Current Energy', @@ -769,17 +769,17 @@ const valueKindFactories: { [f in NonNullable]: ValueKindConfig, _isPrepull: boolean) { const clss = player.getClass(); const spec = player.getSpec(); return spec === Spec.SpecFeralDruid || spec === Spec.SpecGuardianDruid || clss === Class.ClassRogue; }, - fields: [], + fields: [valueFieldConfig('targetEnergy')], }), currentComboPoints: inputBuilder({ label: 'Combo Points',