Skip to content

Commit

Permalink
Merge pull request #4042 from wowsims/apl_boss_values
Browse files Browse the repository at this point in the history
Boss spell related apl values for tank sims
  • Loading branch information
rosenrusinov authored Nov 13, 2023
2 parents 2c86c99 + 2042b62 commit 5416524
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 8 deletions.
1 change: 1 addition & 0 deletions proto/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ message SpellStats {
bool has_shield = 6; // Whether this spell applies a shield effect.
bool prepull_only = 5; // Whether this spell may only be cast during prepull.
bool encounter_only = 8; // Whether this spell may only be cast during the encounter (not prepull).
bool has_cast_time = 9; // Whether this spell has a cast time or not.
}
message APLActionStats {
repeated string warnings = 1;
Expand Down
16 changes: 15 additions & 1 deletion proto/apl.proto
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ message APLAction {
}
}

// NextIndex: 64
// NextIndex: 66
message APLValue {
oneof value {
// Operators
Expand All @@ -97,6 +97,10 @@ message APLValue {
APLValueIsExecutePhase is_execute_phase = 41;
APLValueNumberTargets number_targets = 28;

// Boss values
APLValueBossSpellTimeToReady boss_spell_time_to_ready = 64;
APLValueBossSpellIsCasting boss_spell_is_casting = 65;

// Resource values
APLValueCurrentHealth current_health = 26;
APLValueCurrentHealthPercent current_health_percent = 27;
Expand Down Expand Up @@ -321,6 +325,16 @@ message APLValueIsExecutePhase {
ExecutePhaseThreshold threshold = 1;
}

message APLValueBossSpellTimeToReady {
UnitReference target_unit = 1;
ActionID spell_id = 2;
}

message APLValueBossSpellIsCasting {
UnitReference target_unit = 1;
ActionID spell_id = 2;
}

message APLValueCurrentHealth {
UnitReference source_unit = 1;
}
Expand Down
11 changes: 11 additions & 0 deletions sim/core/apl_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,17 @@ func (rot *APLRotation) GetAPLSpell(spellId *proto.ActionID) *Spell {
return spell
}

func (rot *APLRotation) GetTargetAPLSpell(spellId *proto.ActionID, targetUnit UnitReference) *Spell {
actionID := ProtoToActionID(spellId)
target := targetUnit.Get()
spell := target.GetSpell(actionID)

if spell == nil {
rot.ValidationWarning("%s does not know spell %s", target.Label, actionID)
}
return spell
}

func (rot *APLRotation) GetAPLDot(targetUnit UnitReference, spellId *proto.ActionID) *Dot {
spell := rot.GetAPLSpell(spellId)

Expand Down
6 changes: 6 additions & 0 deletions sim/core/apl_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ func (rot *APLRotation) newAPLValue(config *proto.APLValue) APLValue {
case *proto.APLValue_NumberTargets:
return rot.newValueNumberTargets(config.GetNumberTargets())

// Boss
case *proto.APLValue_BossSpellIsCasting:
return rot.newValueBossSpellIsCasting(config.GetBossSpellIsCasting())
case *proto.APLValue_BossSpellTimeToReady:
return rot.newValueBossSpellTimeToReady(config.GetBossSpellTimeToReady())

// Resources
case *proto.APLValue_CurrentHealth:
return rot.newValueCurrentHealth(config.GetCurrentHealth())
Expand Down
56 changes: 56 additions & 0 deletions sim/core/apl_values_boss.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package core

import (
"fmt"
"time"

"github.com/wowsims/wotlk/sim/core/proto"
)

type APLValueBossSpellIsCasting struct {
DefaultAPLValueImpl
spell *Spell
}

func (rot *APLRotation) newValueBossSpellIsCasting(config *proto.APLValueBossSpellIsCasting) APLValue {
spell := rot.GetTargetAPLSpell(config.SpellId, rot.GetTargetUnit(config.TargetUnit))
if spell == nil {
return nil
}
return &APLValueBossSpellIsCasting{
spell: spell,
}
}
func (value *APLValueBossSpellIsCasting) Type() proto.APLValueType {
return proto.APLValueType_ValueTypeBool
}
func (value *APLValueBossSpellIsCasting) GetBool(sim *Simulation) bool {
return value.spell.Unit.Hardcast.ActionID == value.spell.ActionID && value.spell.Unit.Hardcast.Expires > sim.CurrentTime
}
func (value *APLValueBossSpellIsCasting) String() string {
return fmt.Sprintf("Boss is Casting(%s)", value.spell.ActionID)
}

type APLValueBossSpellTimeToReady struct {
DefaultAPLValueImpl
spell *Spell
}

func (rot *APLRotation) newValueBossSpellTimeToReady(config *proto.APLValueBossSpellTimeToReady) APLValue {
spell := rot.GetTargetAPLSpell(config.SpellId, rot.GetTargetUnit(config.TargetUnit))
if spell == nil {
return nil
}
return &APLValueBossSpellTimeToReady{
spell: spell,
}
}
func (value *APLValueBossSpellTimeToReady) Type() proto.APLValueType {
return proto.APLValueType_ValueTypeDuration
}
func (value *APLValueBossSpellTimeToReady) GetDuration(sim *Simulation) time.Duration {
return value.spell.TimeToReady(sim)
}
func (value *APLValueBossSpellTimeToReady) String() string {
return fmt.Sprintf("Boss Spell Time to Ready(%s)", value.spell.ActionID)
}
1 change: 1 addition & 0 deletions sim/core/unit.go
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ func (unit *Unit) GetMetadata() *proto.UnitMetadata {
HasShield: spell.shields != nil || spell.selfShield != nil,
PrepullOnly: spell.Flags.Matches(SpellFlagPrepullOnly),
EncounterOnly: spell.Flags.Matches(SpellFlagEncounterOnly),
HasCastTime: spell.DefaultCast.CastTime > 0,
}
})

Expand Down
5 changes: 3 additions & 2 deletions sim/encounters/icc/lichking25h_ai.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func (ai *LichKing25HAI) Initialize(target *core.Target, _ *proto.Target) {
}

func (ai *LichKing25HAI) Reset(*core.Simulation) {
ai.SoulReaper.CD.Set(ai.SoulReaper.CD.Duration)
}

func (ai *LichKing25HAI) registerSoulReaperSpell(target *core.Target) {
Expand All @@ -80,7 +81,7 @@ func (ai *LichKing25HAI) registerSoulReaperSpell(target *core.Target) {
ActionID: core.ActionID{SpellID: 69409},
SpellSchool: core.SpellSchoolShadow,
ProcMask: core.ProcMaskMeleeMHSpecial,
Flags: core.SpellFlagNone,
Flags: core.SpellFlagAPL,

Cast: core.CastConfig{
CD: core.Cooldown{
Expand Down Expand Up @@ -135,7 +136,7 @@ func (ai *LichKing25HAI) registerSoulReaperSpell(target *core.Target) {
func (ai *LichKing25HAI) DoAction(sim *core.Simulation) {
if ai.Target.GCD.IsReady(sim) {
if ai.Target.CurrentTarget != nil {
if ai.SoulReaper.IsReady(sim) && sim.CurrentTime >= ai.SoulReaper.CD.Duration {
if ai.SoulReaper.IsReady(sim) {
// Based on log analysis, Soul Reaper appears to have a ~75% chance to "proc" on every 1.62 second server tick once it is off cooldown.
// Note that analysis based only on the cast intervals supported a ~40% proc chance fit. However, many of the apparent delays in Soul Reaper casts are
// due to Defile and Infest casts that take priority when the cooldowns overlap. Once these CD conflicts are corrected for, the variance in Soul Reaper
Expand Down
9 changes: 5 additions & 4 deletions sim/encounters/icc/sindragosa25h_ai.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ type Sindragosa25HAI struct {
FrostAura *core.Spell
FrostBreath *core.Spell
FrostBreathDebuff *core.Aura
FirstBreath time.Duration

IncludeMysticBuffet bool
MysticBuffetAuras []*core.Aura
Expand Down Expand Up @@ -88,7 +87,9 @@ func (ai *Sindragosa25HAI) Reset(sim *core.Simulation) {
breathPeriod := time.Millisecond * 22680
maxBreathsPossible := (sim.Duration - time.Millisecond*1500) / breathPeriod
latestAllowedBreath := sim.Duration - time.Millisecond*1500 - breathPeriod*maxBreathsPossible - time.Millisecond*1620
ai.FirstBreath = core.DurationFromSeconds(sim.RandomFloat("Frost Breath Timing") * latestAllowedBreath.Seconds())
firstBreath := core.DurationFromSeconds(sim.RandomFloat("Frost Breath Timing") * latestAllowedBreath.Seconds())

ai.FrostBreath.CD.Set(firstBreath)
}

func (ai *Sindragosa25HAI) registerPermeatingChillAura(target *core.Target) {
Expand Down Expand Up @@ -290,7 +291,7 @@ func (ai *Sindragosa25HAI) registerFrostBreathSpell(target *core.Target) {
ActionID: actionID,
SpellSchool: core.SpellSchoolFrost,
ProcMask: core.ProcMaskSpellDamage,
Flags: core.SpellFlagNone,
Flags: core.SpellFlagAPL,

Cast: core.CastConfig{
CD: core.Cooldown{
Expand Down Expand Up @@ -324,7 +325,7 @@ func (ai *Sindragosa25HAI) DoAction(sim *core.Simulation) {
}

if ai.Target.CurrentTarget != nil {
if ai.FrostBreath.IsReady(sim) && sim.CurrentTime >= ai.FirstBreath {
if ai.FrostBreath.IsReady(sim) {
ai.Target.Unit.AutoAttacks.StopMeleeUntil(sim, sim.CurrentTime+time.Millisecond*1500, false)
ai.FrostBreath.Cast(sim, ai.Target.CurrentTarget)
return
Expand Down
29 changes: 28 additions & 1 deletion ui/core/components/individual_sim_ui/apl_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ActionID } from '../../proto/common.js';
import { BooleanPicker } from '../boolean_picker.js';
import { APLValueRuneSlot, APLValueRuneType } from '../../proto/apl.js';

export type ACTION_ID_SET = 'auras' | 'stackable_auras' | 'icd_auras' | 'exclusive_effect_auras' | 'castable_spells' | 'channel_spells' | 'dot_spells' | 'shield_spells';
export type ACTION_ID_SET = 'auras' | 'stackable_auras' | 'icd_auras' | 'exclusive_effect_auras' | 'spells' | 'castable_spells' | 'channel_spells' | 'dot_spells' | 'shield_spells' | 'non_instant_spells';

const actionIdSets: Record<ACTION_ID_SET, {
defaultLabel: string,
Expand Down Expand Up @@ -58,6 +58,17 @@ const actionIdSets: Record<ACTION_ID_SET, {
});
},
},
// Used for non categorized lists
'spells': {
defaultLabel: 'Spell',
getActionIDs: async (metadata) => {
return metadata.getSpells().filter(spell => spell.data.isCastable).map(actionId => {
return {
value: actionId.id,
};
});
},
},
'castable_spells': {
defaultLabel: 'Spell',
getActionIDs: async (metadata) => {
Expand All @@ -74,10 +85,12 @@ const actionIdSets: Record<ACTION_ID_SET, {
[{
value: ActionId.fromEmpty(),
headerText: 'Spells',
submenu: ['Spells'],
}],
(spells || []).map(actionId => {
return {
value: actionId.id,
submenu: ['Spells'],
extraCssClasses: (actionId.data.prepullOnly
? ['apl-prepull-actions-only']
: (actionId.data.encounterOnly
Expand All @@ -88,10 +101,12 @@ const actionIdSets: Record<ACTION_ID_SET, {
[{
value: ActionId.fromEmpty(),
headerText: 'Cooldowns',
submenu: ['Cooldowns'],
}],
(cooldowns || []).map(actionId => {
return {
value: actionId.id,
submenu: ['Cooldowns'],
extraCssClasses: (actionId.data.prepullOnly
? ['apl-prepull-actions-only']
: (actionId.data.encounterOnly
Expand All @@ -102,16 +117,28 @@ const actionIdSets: Record<ACTION_ID_SET, {
[{
value: ActionId.fromEmpty(),
headerText: 'Placeholders',
submenu: ['Placeholders'],
}],
placeholders.map(actionId => {
return {
value: actionId,
submenu: ['Placeholders'],
tooltip: 'The Prepull Potion if CurrentTime < 0, or the Combat Potion if combat has started.',
};
}),
].flat();
},
},
'non_instant_spells': {
defaultLabel: 'Non-instant Spell',
getActionIDs: async (metadata) => {
return metadata.getSpells().filter(spell => spell.data.isCastable && spell.data.hasCastTime).map(actionId => {
return {
value: actionId.id,
};
});
},
},
'channel_spells': {
defaultLabel: 'Channeled Spell',
getActionIDs: async (metadata) => {
Expand Down
24 changes: 24 additions & 0 deletions ui/core/components/individual_sim_ui/apl_values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ import {
APLValueWarlockShouldRecastDrainSoul,
APLValueWarlockShouldRefreshCorruption,
APLValueCatNewSavageRoarDuration,
APLValueBossSpellTimeToReady,
APLValueBossSpellIsCasting,
} from '../../proto/apl.js';

import { EventID } from '../../typed_event.js';
Expand Down Expand Up @@ -554,6 +556,28 @@ const valueKindFactories: {[f in NonNullable<APLValueKind>]: ValueKindConfig<APL
fields: [],
}),

// Boss
'bossSpellIsCasting': inputBuilder({
label: 'Spell is Casting',
submenu: ['Boss'],
shortDescription: '',
newValue: APLValueBossSpellIsCasting.create,
fields: [
AplHelpers.unitFieldConfig('targetUnit', 'targets'),
AplHelpers.actionIdFieldConfig('spellId', 'non_instant_spells', 'targetUnit', 'currentTarget'),
]
}),
'bossSpellTimeToReady': inputBuilder({
label: 'Spell Time to Ready',
submenu: ['Boss'],
shortDescription: '',
newValue: APLValueBossSpellTimeToReady.create,
fields: [
AplHelpers.unitFieldConfig('targetUnit', 'targets'),
AplHelpers.actionIdFieldConfig('spellId', 'spells', 'targetUnit', 'currentTarget'),
]
}),

// Resources
'currentHealth': inputBuilder({
label: 'Health',
Expand Down

0 comments on commit 5416524

Please sign in to comment.