Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rogue stealth #3460

Merged
merged 11 commits into from
Aug 15, 2023
626 changes: 313 additions & 313 deletions sim/rogue/TestAssassination.results

Large diffs are not rendered by default.

480 changes: 240 additions & 240 deletions sim/rogue/TestSubtlety.results

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion sim/rogue/ambush.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (rogue *Rogue) registerAmbushSpell() {
IgnoreHaste: true,
},
ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool {
return !rogue.PseudoStats.InFrontOfTarget && rogue.GetMHWeapon().WeaponType == proto.WeaponType_WeaponTypeDagger
return !rogue.PseudoStats.InFrontOfTarget && rogue.GetMHWeapon().WeaponType == proto.WeaponType_WeaponTypeDagger && rogue.IsStealthed()
},

BonusCritRating: []float64{0, 2, 4, 6}[rogue.Talents.TurnTheTables]*core.CritRatingPerCritChance +
Expand All @@ -38,6 +38,7 @@ func (rogue *Rogue) registerAmbushSpell() {
ThreatMultiplier: 1,

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
rogue.BreakStealth(sim)
baseDamage := 330 +
spell.Unit.MHNormalizedWeaponDamage(sim, spell.MeleeAttackPower()) +
spell.BonusWeaponDamage()
Expand Down
1 change: 1 addition & 0 deletions sim/rogue/backstab.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (rogue *Rogue) registerBackstabSpell() {
ThreatMultiplier: 1,

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
rogue.BreakStealth(sim)
baseDamage := 310 +
spell.Unit.MHNormalizedWeaponDamage(sim, spell.MeleeAttackPower()) +
spell.BonusWeaponDamage()
Expand Down
1 change: 1 addition & 0 deletions sim/rogue/envenom.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func (rogue *Rogue) registerEnvenom() {
ThreatMultiplier: 1,

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
rogue.BreakStealth(sim)
comboPoints := rogue.ComboPoints()
// - the aura is active even if the attack fails to land
// - the aura is applied before the hit effect
Expand Down
1 change: 1 addition & 0 deletions sim/rogue/eviscerate.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func (rogue *Rogue) registerEviscerate() {
ThreatMultiplier: 1,

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
rogue.BreakStealth(sim)
comboPoints := rogue.ComboPoints()
flatBaseDamage := 127 + 370*float64(comboPoints)
// tooltip implies 3..7% AP scaling, but testing shows it's fixed at 7% (3.4.0.46158)
Expand Down
1 change: 1 addition & 0 deletions sim/rogue/expose_armor.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func (rogue *Rogue) registerExposeArmorSpell() {
ThreatMultiplier: 1,

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
rogue.BreakStealth(sim)
result := spell.CalcOutcome(sim, target, spell.OutcomeMeleeSpecialHit)
if result.Landed() {
debuffAura := rogue.ExposeArmorAuras.Get(target)
Expand Down
1 change: 1 addition & 0 deletions sim/rogue/fan_of_knives.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func (rogue *Rogue) registerFanOfKnives() {
},

ApplyEffects: func(sim *core.Simulation, unit *core.Unit, spell *core.Spell) {
rogue.BreakStealth(sim)
// Calc and apply all OH hits first, because MH hits can benefit from an OH felstriker proc.
for i, aoeTarget := range sim.Encounter.TargetUnits {
baseDamage := ohSpell.Unit.OHWeaponDamage(sim, ohSpell.MeleeAttackPower())
Expand Down
1 change: 1 addition & 0 deletions sim/rogue/feint.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func (rogue *Rogue) registerFeintSpell() {
ThreatMultiplier: 1,

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
rogue.BreakStealth(sim)
spell.CalcAndDealOutcome(sim, target, spell.OutcomeMeleeSpecialHit)
},
})
Expand Down
3 changes: 2 additions & 1 deletion sim/rogue/garrote.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (rogue *Rogue) registerGarrote() {
IgnoreHaste: true,
},
ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool {
return !rogue.PseudoStats.InFrontOfTarget
return !rogue.PseudoStats.InFrontOfTarget && rogue.IsStealthed()
},

DamageMultiplier: 1 +
Expand Down Expand Up @@ -60,6 +60,7 @@ func (rogue *Rogue) registerGarrote() {
},

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
rogue.BreakStealth(sim)
result := spell.CalcOutcome(sim, target, spell.OutcomeMeleeSpecialNoBlockDodgeParryNoCrit)
if result.Landed() {
rogue.AddComboPoints(sim, 1, spell.ComboPointMetrics())
Expand Down
1 change: 1 addition & 0 deletions sim/rogue/ghostly_strike.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func (rogue *Rogue) registerGhostlyStrikeSpell() {
ThreatMultiplier: 1,

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
rogue.BreakStealth(sim)
baseDamage := spell.Unit.MHNormalizedWeaponDamage(sim, spell.MeleeAttackPower())

result := spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeWeaponSpecialHitAndCrit)
Expand Down
1 change: 1 addition & 0 deletions sim/rogue/hemorrhage.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func (rogue *Rogue) registerHemorrhageSpell() {
ThreatMultiplier: 1,

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
rogue.BreakStealth(sim)
baseDamage := 0 +
spell.Unit.MHNormalizedWeaponDamage(sim, spell.MeleeAttackPower()) +
spell.BonusWeaponDamage()
Expand Down
1 change: 1 addition & 0 deletions sim/rogue/killing_spree.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func (rogue *Rogue) registerKillingSpreeSpell() {
},

ApplyEffects: func(sim *core.Simulation, u *core.Unit, s2 *core.Spell) {
rogue.BreakStealth(sim)
rogue.KillingSpreeAura.Activate(sim)
},
})
Expand Down
97 changes: 6 additions & 91 deletions sim/rogue/master_of_subtlety.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,109 +12,24 @@ func getMasterOfSubtletySpellID(talentPoints int32) int32 {
}

func (rogue *Rogue) registerMasterOfSubtletyCD() {
if rogue.Talents.MasterOfSubtlety == 0 {
return
}

var MasterOfSubtletyID = core.ActionID{SpellID: getMasterOfSubtletySpellID(rogue.Talents.MasterOfSubtlety)}

percent := []float64{1, 1.04, 1.07, 1.1}[rogue.Talents.MasterOfSubtlety]

effectDuration := time.Second * 6
if rogue.StealthAura.IsActive() {
effectDuration = core.NeverExpires
}

rogue.MasterOfSubtletyAura = rogue.RegisterAura(core.Aura{
Label: "Master of Subtlety",
ActionID: MasterOfSubtletyID,
Duration: time.Second * 6,
Duration: effectDuration,
OnGain: func(aura *core.Aura, sim *core.Simulation) {
rogue.PseudoStats.DamageDealtMultiplier *= percent
},
OnExpire: func(aura *core.Aura, sim *core.Simulation) {
rogue.PseudoStats.DamageDealtMultiplier *= 1 / percent
},
})

const garroteMinDuration = time.Second * 9 // heuristically, 3 Garrote ticks are better DPE than regular builders

garrote := func(sim *core.Simulation, rogue *Rogue) (bool, int32) {
if !rogue.Rotation.OpenWithGarrote || rogue.PseudoStats.InFrontOfTarget {
return false, 0
}

if !rogue.GCD.IsReady(sim) || rogue.CurrentEnergy() < rogue.Garrote.DefaultCast.Cost {
return true, 0
}

if rogue.Garrote.CurDot().IsActive() || sim.GetRemainingDuration() <= garroteMinDuration {
return true, 0
}

if rogue.Talents.Initiative == 0 {
return true, 1
}
return true, 2
}

premed := func(sim *core.Simulation, rogue *Rogue) (bool, int32) {
if rogue.Premeditation == nil {
return false, 0
}

if !rogue.Premeditation.IsReady(sim) {
return true, 0
}
return true, 2
}

rogue.MasterOfSubtlety = rogue.RegisterSpell(core.SpellConfig{
ActionID: MasterOfSubtletyID,
Cast: core.CastConfig{
DefaultCast: core.Cast{
GCD: 0,
},
IgnoreHaste: true,
CD: core.Cooldown{
Timer: rogue.NewTimer(),
Duration: time.Second * time.Duration(180-30*rogue.Talents.Elusiveness),
},
},
ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
rogue.MasterOfSubtletyAura.Activate(sim)

_, premedCPs := premed(sim, rogue)
_, garroteCPs := garrote(sim, rogue)

if premedCPs > 0 && rogue.ComboPoints()+premedCPs+garroteCPs <= 5 {
rogue.Premeditation.Cast(sim, target)
}

if garroteCPs > 0 {
rogue.Garrote.Cast(sim, target)
}
},
})

rogue.AddMajorCooldown(core.MajorCooldown{
Spell: rogue.MasterOfSubtlety,
Type: core.CooldownTypeDPS,
ShouldActivate: func(sim *core.Simulation, character *core.Character) bool {
if rogue.MasterOfSubtletyAura.IsActive() {
return false // possible after preparation
}

if sim.GetRemainingDuration() < garroteMinDuration {
return true // getting the buff up under non-ideal circumstances is fine at end of combat
}

wantPremed, premedCPs := premed(sim, rogue)
if wantPremed && premedCPs == 0 {
return false // essentially sync with premed if possible
}

wantGarrote, garroteCPs := garrote(sim, rogue)
if wantGarrote && garroteCPs == 0 {
return false
}

return rogue.ComboPoints()+garroteCPs+premedCPs <= 5+1 // heuristically, "<= 5" is too strict (since omitting premed is fine)
},
})
}
1 change: 1 addition & 0 deletions sim/rogue/mutilate.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func (rogue *Rogue) registerMutilateSpell() {
ThreatMultiplier: 1,

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
rogue.BreakStealth(sim)
result := spell.CalcOutcome(sim, target, spell.OutcomeMeleeSpecialHit) // Miss/Dodge/Parry/Hit
if result.Landed() {
rogue.AddComboPoints(sim, 2, spell.ComboPointMetrics())
Expand Down
32 changes: 8 additions & 24 deletions sim/rogue/overkill.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,25 @@ import (

var OverkillActionID = core.ActionID{SpellID: 58426}

func (rogue *Rogue) registerOverkillCD() {
func (rogue *Rogue) registerOverkill() {
if !rogue.Talents.Overkill {
return
}

effectDuration := time.Second * 20
if rogue.StealthAura.IsActive() {
effectDuration = core.NeverExpires
}

rogue.OverkillAura = rogue.RegisterAura(core.Aura{
Label: "Overkill",
ActionID: OverkillActionID,
Duration: time.Second * 20,
Duration: effectDuration,
OnGain: func(aura *core.Aura, sim *core.Simulation) {
rogue.ApplyEnergyTickMultiplier(0.3)
},
OnExpire: func(aura *core.Aura, sim *core.Simulation) {
rogue.ApplyEnergyTickMultiplier(-0.3)
},
})
rogue.Overkill = rogue.RegisterSpell(core.SpellConfig{
ActionID: OverkillActionID,
Cast: core.CastConfig{
CD: core.Cooldown{
Timer: rogue.NewTimer(),
Duration: time.Second * time.Duration(180-30*rogue.Talents.Elusiveness),
},
},
ApplyEffects: func(sim *core.Simulation, _ *core.Unit, spell *core.Spell) {
rogue.OverkillAura.Activate(sim)
},
})

rogue.AddMajorCooldown(core.MajorCooldown{
Spell: rogue.Overkill,
Type: core.CooldownTypeDPS,

ShouldActivate: func(sim *core.Simulation, c *core.Character) bool {
return !rogue.OverkillAura.IsActive() && rogue.CurrentEnergy() < 50
},
})

}
3 changes: 3 additions & 0 deletions sim/rogue/premeditation.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ func (rogue *Rogue) registerPremeditation() {
Duration: time.Second * 20,
},
},
ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool {
return rogue.IsStealthed()
},

ApplyEffects: func(sim *core.Simulation, _ *core.Unit, spell *core.Spell) {
rogue.AddComboPoints(sim, 2, comboMetrics)
Expand Down
4 changes: 2 additions & 2 deletions sim/rogue/preparation.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func (rogue *Rogue) registerPreparationCD() {

// Spells affected by Preparation are: Cold Blood, Shadowstep, Vanish (Overkill/Master of Subtlety), Evasion, Sprint
// If Glyph of Preparation is applied, Blade Flurry, Dismantle, and Kick are also affected
var affectedSpells = []*core.Spell{rogue.ColdBlood, rogue.Shadowstep, rogue.MasterOfSubtlety, rogue.Overkill}
var affectedSpells = []*core.Spell{rogue.ColdBlood, rogue.Shadowstep, rogue.Vanish}
if rogue.GetCharacter().HasGlyph(int32(proto.RogueMajorGlyph_GlyphOfPreparation)) {
affectedSpells = append(affectedSpells, rogue.BladeFlurry)
}
Expand Down Expand Up @@ -43,7 +43,7 @@ func (rogue *Rogue) registerPreparationCD() {
Type: core.CooldownTypeDPS,
Priority: core.CooldownPriorityDefault,
ShouldActivate: func(sim *core.Simulation, character *core.Character) bool {
return rogue.MasterOfSubtlety != nil && !rogue.MasterOfSubtlety.CD.IsReady(sim)
return !rogue.Vanish.CD.IsReady(sim)
},
})
}
Loading
Loading