From 372432440d311ba3dd06a1872a98cea2588d2758 Mon Sep 17 00:00:00 2001 From: sanguinerarogue Date: Sun, 8 Dec 2024 12:21:58 -0700 Subject: [PATCH] First pass at warrior --- sim/warlock/dps/TestWarlockDSRuin.results | 8 +- sim/warlock/dps/TestWarlockSMRuin.results | 8 +- sim/warrior/deep_wounds.go | 9 +- .../dps_warrior/TestP1DPSWarrior.results | 64 +-- sim/warrior/execute.go | 27 +- sim/warrior/hamstring.go | 23 +- sim/warrior/heroic_strike_cleave.go | 46 +- sim/warrior/mortal_strike.go | 12 +- sim/warrior/overpower.go | 20 +- sim/warrior/quick_strike.go | 49 -- sim/warrior/raging_blow.go | 66 --- sim/warrior/rampage.go | 56 -- sim/warrior/rend.go | 16 +- sim/warrior/revenge.go | 24 +- sim/warrior/runes.go | 517 ------------------ sim/warrior/shield_slam.go | 34 +- sim/warrior/shockwave.go | 50 -- sim/warrior/slam.go | 100 +--- sim/warrior/stances.go | 106 +--- sim/warrior/sunder_armor.go | 58 +- sim/warrior/sweeping_strikes.go | 2 +- .../tank_warrior/TestP1TankWarrior.results | 60 +- sim/warrior/thunder_clap.go | 37 +- sim/warrior/warrior.go | 47 +- sim/warrior/whirlwind.go | 54 +- 25 files changed, 144 insertions(+), 1349 deletions(-) delete mode 100644 sim/warrior/quick_strike.go delete mode 100644 sim/warrior/raging_blow.go delete mode 100644 sim/warrior/rampage.go delete mode 100644 sim/warrior/runes.go delete mode 100644 sim/warrior/shockwave.go diff --git a/sim/warlock/dps/TestWarlockDSRuin.results b/sim/warlock/dps/TestWarlockDSRuin.results index 695113d49..ea3a204c1 100644 --- a/sim/warlock/dps/TestWarlockDSRuin.results +++ b/sim/warlock/dps/TestWarlockDSRuin.results @@ -106,8 +106,8 @@ dps_results: { dps_results: { key: "TestWarlockDSRuin-Phase1-AllItems-DeathmistRaiment" value: { - dps: 861.87319 - tps: 922.49357 + dps: 793.71062 + tps: 846.83968 } } dps_results: { @@ -155,8 +155,8 @@ dps_results: { dps_results: { key: "TestWarlockDSRuin-Phase1-AllItems-PlagueheartRaiment" value: { - dps: 1007.47674 - tps: 1069.18079 + dps: 941.36791 + tps: 996.27398 } } dps_results: { diff --git a/sim/warlock/dps/TestWarlockSMRuin.results b/sim/warlock/dps/TestWarlockSMRuin.results index 0fe707b71..bd91e54a1 100644 --- a/sim/warlock/dps/TestWarlockSMRuin.results +++ b/sim/warlock/dps/TestWarlockSMRuin.results @@ -106,8 +106,8 @@ dps_results: { dps_results: { key: "TestWarlockSMRuin-Phase1-AllItems-DeathmistRaiment" value: { - dps: 1080.97179 - tps: 903.66134 + dps: 993.7709 + tps: 821.18879 } } dps_results: { @@ -155,8 +155,8 @@ dps_results: { dps_results: { key: "TestWarlockSMRuin-Phase1-AllItems-PlagueheartRaiment" value: { - dps: 1235.01715 - tps: 1049.06851 + dps: 1138.48078 + tps: 955.41236 } } dps_results: { diff --git a/sim/warrior/deep_wounds.go b/sim/warrior/deep_wounds.go index b0a9b4742..eb291e9a8 100644 --- a/sim/warrior/deep_wounds.go +++ b/sim/warrior/deep_wounds.go @@ -42,7 +42,7 @@ func (warrior *Warrior) applyDeepWounds() { }, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - spell.Dot(target).ApplyOrRefresh(sim) + spell.Dot(target).Apply(sim) spell.CalcAndDealOutcome(sim, target, spell.OutcomeAlwaysHitNoHitCounter) }, }) @@ -66,16 +66,15 @@ func (warrior *Warrior) applyDeepWounds() { })) } +// Needs confirmation on classic functionality. It is regular dot instead of a stacking one and if so does that mean it is only overwritten by a "higher" crit? func (warrior *Warrior) procDeepWounds(sim *core.Simulation, target *core.Unit, isOh bool) { dot := warrior.DeepWounds.Dot(target) - outstandingDamage := core.TernaryFloat64(dot.IsActive(), dot.SnapshotBaseDamage*float64(dot.NumberOfTicks-dot.TickCount), 0) - var awd float64 if isOh { attackTableOh := warrior.AttackTables[target.UnitIndex][proto.CastType_CastTypeOffHand] adm := warrior.AutoAttacks.OHAuto().AttackerDamageMultiplier(attackTableOh) - awd = warrior.AutoAttacks.OH().CalculateAverageWeaponDamage(dot.Spell.MeleeAttackPower()) * 0.5 * adm + awd = warrior.AutoAttacks.OH().CalculateAverageWeaponDamage(dot.Spell.MeleeAttackPower()) * 0.5 * adm // Does this not include the DWS talent? } else { // MH attackTableMh := warrior.AttackTables[target.UnitIndex][proto.CastType_CastTypeMainHand] adm := warrior.AutoAttacks.MHAuto().AttackerDamageMultiplier(attackTableMh) @@ -84,7 +83,7 @@ func (warrior *Warrior) procDeepWounds(sim *core.Simulation, target *core.Unit, newDamage := awd * 0.2 * float64(warrior.Talents.DeepWounds) - dot.SnapshotBaseDamage = (outstandingDamage + newDamage) / float64(dot.NumberOfTicks) + dot.SnapshotBaseDamage = newDamage // / float64(dot.NumberOfTicks) dot.SnapshotAttackerMultiplier = 1 warrior.DeepWounds.Cast(sim, target) diff --git a/sim/warrior/dps_warrior/TestP1DPSWarrior.results b/sim/warrior/dps_warrior/TestP1DPSWarrior.results index 69b097bf9..5c259c84d 100644 --- a/sim/warrior/dps_warrior/TestP1DPSWarrior.results +++ b/sim/warrior/dps_warrior/TestP1DPSWarrior.results @@ -50,8 +50,8 @@ character_stats_results: { stat_weights_results: { key: "TestP1DPSWarrior-Phase1-StatWeights-Default" value: { - weights: 0.44532 - weights: 0.13025 + weights: 0.39666 + weights: 0.07969 weights: 0 weights: 0 weights: 0 @@ -67,9 +67,9 @@ stat_weights_results: { weights: 0 weights: 0 weights: 0 - weights: 0.39162 + weights: 0.26669 weights: 0 - weights: 2.74696 + weights: 1.70977 weights: 0 weights: 0 weights: 0 @@ -99,98 +99,98 @@ stat_weights_results: { dps_results: { key: "TestP1DPSWarrior-Phase1-Average-Default" value: { - dps: 251.86628 - tps: 220.05239 + dps: 235.27671 + tps: 228.53922 } } dps_results: { key: "TestP1DPSWarrior-Phase1-Settings-Human-p0.bis-DPS-p1-FullBuffs-P1-Consumes-LongMultiTarget" value: { - dps: 35.95075 - tps: 121.17562 + dps: 37.15578 + tps: 126.45111 } } dps_results: { key: "TestP1DPSWarrior-Phase1-Settings-Human-p0.bis-DPS-p1-FullBuffs-P1-Consumes-LongSingleTarget" value: { - dps: 32.91426 - tps: 33.524 + dps: 34.11929 + tps: 38.79949 } } dps_results: { key: "TestP1DPSWarrior-Phase1-Settings-Human-p0.bis-DPS-p1-FullBuffs-P1-Consumes-ShortSingleTarget" value: { - dps: 88.77662 - tps: 81.49139 + dps: 84.5334 + tps: 85.77948 } } dps_results: { key: "TestP1DPSWarrior-Phase1-Settings-Human-p0.bis-DPS-p1-NoBuffs-P1-Consumes-LongMultiTarget" value: { - dps: 17.18813 - tps: 106.16552 + dps: 17.3583 + tps: 110.61313 } } dps_results: { key: "TestP1DPSWarrior-Phase1-Settings-Human-p0.bis-DPS-p1-NoBuffs-P1-Consumes-LongSingleTarget" value: { - dps: 14.89313 - tps: 19.1071 + dps: 15.0633 + tps: 23.5547 } } dps_results: { key: "TestP1DPSWarrior-Phase1-Settings-Human-p0.bis-DPS-p1-NoBuffs-P1-Consumes-ShortSingleTarget" value: { - dps: 42.96501 - tps: 44.8421 + dps: 41.23016 + tps: 51.13688 } } dps_results: { key: "TestP1DPSWarrior-Phase1-Settings-Orc-p0.bis-DPS-p1-FullBuffs-P1-Consumes-LongMultiTarget" value: { - dps: 37.75094 - tps: 122.61577 + dps: 38.91529 + tps: 127.85871 } } dps_results: { key: "TestP1DPSWarrior-Phase1-Settings-Orc-p0.bis-DPS-p1-FullBuffs-P1-Consumes-LongSingleTarget" value: { - dps: 34.71445 - tps: 34.96415 + dps: 35.87879 + tps: 40.20709 } } dps_results: { key: "TestP1DPSWarrior-Phase1-Settings-Orc-p0.bis-DPS-p1-FullBuffs-P1-Consumes-ShortSingleTarget" value: { - dps: 91.92593 - tps: 84.01084 + dps: 86.69236 + tps: 87.50664 } } dps_results: { key: "TestP1DPSWarrior-Phase1-Settings-Orc-p0.bis-DPS-p1-NoBuffs-P1-Consumes-LongMultiTarget" value: { - dps: 18.37064 - tps: 107.11153 + dps: 18.46606 + tps: 111.49933 } } dps_results: { key: "TestP1DPSWarrior-Phase1-Settings-Orc-p0.bis-DPS-p1-NoBuffs-P1-Consumes-LongSingleTarget" value: { - dps: 16.07564 - tps: 20.0531 + dps: 16.17106 + tps: 24.4409 } } dps_results: { key: "TestP1DPSWarrior-Phase1-Settings-Orc-p0.bis-DPS-p1-NoBuffs-P1-Consumes-ShortSingleTarget" value: { - dps: 46.51015 - tps: 47.67821 + dps: 44.00168 + tps: 53.3541 } } dps_results: { key: "TestP1DPSWarrior-Phase1-SwitchInFrontOfTarget-Default" value: { - dps: 198.04954 - tps: 174.84326 + dps: 192.89083 + tps: 189.33816 } } diff --git a/sim/warrior/execute.go b/sim/warrior/execute.go index 9fc9045c9..7d5a5177a 100644 --- a/sim/warrior/execute.go +++ b/sim/warrior/execute.go @@ -2,32 +2,13 @@ package warrior import ( "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" ) func (warrior *Warrior) registerExecuteSpell() { - hasSuddenDeathRune := warrior.HasRune(proto.WarriorRune_RuneSuddenDeath) - flatDamage := map[int32]float64{ - 25: 125, - 40: 325, - 50: 450, - 60: 600, - }[warrior.Level] - - convertedRageDamage := map[int32]float64{ - 25: 3, - 40: 9, - 50: 12, - 60: 15, - }[warrior.Level] - - spellID := map[int32]int32{ - 25: 5308, - 40: 20660, - 50: 20661, - 60: 20662, - }[warrior.Level] + flatDamage := 600.0 + convertedRageDamage := 15.0 + spellID := int32(20662) var rageMetrics *core.ResourceMetrics warrior.Execute = warrior.RegisterSpell(BattleStance|BerserkerStance, core.SpellConfig{ @@ -48,7 +29,7 @@ func (warrior *Warrior) registerExecuteSpell() { }, }, ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool { - return sim.IsExecutePhase20() || (hasSuddenDeathRune && warrior.SuddenDeathAura.IsActive()) + return sim.IsExecutePhase20() }, CritDamageBonus: warrior.impale(), diff --git a/sim/warrior/hamstring.go b/sim/warrior/hamstring.go index fdd8e2898..81408ad25 100644 --- a/sim/warrior/hamstring.go +++ b/sim/warrior/hamstring.go @@ -5,26 +5,9 @@ import ( ) func (warrior *Warrior) registerHamstringSpell() { - damage := map[int32]float64{ - 25: 5, - 40: 18, - 50: 18, - 60: 45, - }[warrior.Level] - - spellID := map[int32]int32{ - 25: 1715, - 40: 7372, - 50: 7372, - 60: 27584, - }[warrior.Level] - - spell_level := map[int32]int32{ - 25: 8, - 40: 32, - 50: 32, - 60: 54, - }[warrior.Level] + damage := 45.0 + spellID := int32(27584) + spell_level := 54.0 warrior.Hamstring = warrior.RegisterSpell(BattleStance|BerserkerStance, core.SpellConfig{ ActionID: core.ActionID{SpellID: spellID}, diff --git a/sim/warrior/heroic_strike_cleave.go b/sim/warrior/heroic_strike_cleave.go index 1d23cb9b4..dfae1f524 100644 --- a/sim/warrior/heroic_strike_cleave.go +++ b/sim/warrior/heroic_strike_cleave.go @@ -5,27 +5,10 @@ import ( ) func (warrior *Warrior) registerHeroicStrikeSpell(realismICD *core.Cooldown) { - flatDamageBonus := map[int32]float64{ - 25: 44, - 40: 80, - 50: 111, - 60: core.TernaryFloat64(core.IncludeAQ, 157, 138), - }[warrior.Level] - - spellID := map[int32]int32{ - 25: 1608, - 40: 11565, - 50: 11566, - 60: core.TernaryInt32(core.IncludeAQ, 25286, 11567), - }[warrior.Level] - + flatDamageBonus := core.TernaryFloat64(core.IncludeAQ, 157, 138) + spellID := core.TernaryInt32(core.IncludeAQ, 25286, 11567) // No known equation - threat := map[int32]float64{ - 25: 68, //guess - 40: 103, //guess - 50: 120, - 60: core.TernaryFloat64(core.IncludeAQ, 173, 145), - }[warrior.Level] + threat := core.TernaryFloat64(core.IncludeAQ, 173, 145) warrior.HeroicStrike = warrior.RegisterSpell(AnyStance, core.SpellConfig{ ActionID: core.ActionID{SpellID: spellID}, @@ -64,26 +47,9 @@ func (warrior *Warrior) registerHeroicStrikeSpell(realismICD *core.Cooldown) { } func (warrior *Warrior) registerCleaveSpell(realismICD *core.Cooldown) { - flatDamageBonus := map[int32]float64{ - 25: 5, - 40: 18, - 50: 32, - 60: 50, - }[warrior.Level] - - spellID := map[int32]int32{ - 25: 845, - 40: 11608, - 50: 11609, - 60: 20569, - }[warrior.Level] - - threat := map[int32]float64{ - 25: 20, //guess - 40: 60, //guess - 50: 80, - 60: 100, - }[warrior.Level] + flatDamageBonus := 50.0 + spellID := int32(20569) + threat := 100.0 flatDamageBonus *= []float64{1, 1.4, 1.8, 2.2}[warrior.Talents.ImprovedCleave] diff --git a/sim/warrior/mortal_strike.go b/sim/warrior/mortal_strike.go index ff28faa1b..0bd19a28f 100644 --- a/sim/warrior/mortal_strike.go +++ b/sim/warrior/mortal_strike.go @@ -11,17 +11,9 @@ func (warrior *Warrior) registerMortalStrikeSpell(cdTimer *core.Timer) { return } - bonusDamage := map[int32]float64{ - 40: 85, - 50: 110, - 60: 160, - }[warrior.Level] + bonusDamage := 160.0 - spellID := map[int32]int32{ - 40: 12294, - 50: 21551, - 60: 21553, - }[warrior.Level] + spellID := int32(21553) warrior.MortalStrike = warrior.RegisterSpell(AnyStance, core.SpellConfig{ SpellCode: SpellCode_WarriorMortalStrike, diff --git a/sim/warrior/overpower.go b/sim/warrior/overpower.go index 024003188..6d6849a8f 100644 --- a/sim/warrior/overpower.go +++ b/sim/warrior/overpower.go @@ -4,25 +4,11 @@ import ( "time" "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" ) func (warrior *Warrior) registerOverpowerSpell(cdTimer *core.Timer) { - hasTasteForBloodRune := warrior.HasRune(proto.WarriorRune_RuneTasteForBlood) - - bonusDamage := map[int32]float64{ - 25: 5, - 40: 15, - 50: 25, - 60: 35, - }[warrior.Level] - - spellID := map[int32]int32{ - 25: 7384, - 40: 7887, - 50: 11584, - 60: 11585, - }[warrior.Level] + bonusDamage := 35.0 + spellID := int32(11585) warrior.RegisterAura(core.Aura{ Label: "Overpower Trigger", @@ -66,7 +52,7 @@ func (warrior *Warrior) registerOverpowerSpell(cdTimer *core.Timer) { }, }, ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool { - return warrior.OverpowerAura.IsActive() || (hasTasteForBloodRune && warrior.TasteForBloodAura.IsActive()) + return warrior.OverpowerAura.IsActive() }, BonusCritRating: 25 * core.CritRatingPerCritChance * float64(warrior.Talents.ImprovedOverpower), diff --git a/sim/warrior/quick_strike.go b/sim/warrior/quick_strike.go deleted file mode 100644 index 2b38b44fb..000000000 --- a/sim/warrior/quick_strike.go +++ /dev/null @@ -1,49 +0,0 @@ -package warrior - -import ( - "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" -) - -func (warrior *Warrior) registerQuickStrike() { - if !warrior.HasRune(proto.WarriorRune_RuneQuickStrike) { - return - } - - warrior.QuickStrike = warrior.RegisterSpell(AnyStance, core.SpellConfig{ - ActionID: core.ActionID{SpellID: 429765}, - SpellSchool: core.SpellSchoolPhysical, - DefenseType: core.DefenseTypeMelee, - ProcMask: core.ProcMaskMeleeMHSpecial, - Flags: core.SpellFlagMeleeMetrics | core.SpellFlagAPL | SpellFlagOffensive, - - RageCost: core.RageCostOptions{ - Cost: 20 - float64(warrior.Talents.ImprovedHeroicStrike), - Refund: 0.8, - }, - Cast: core.CastConfig{ - DefaultCast: core.Cast{ - GCD: core.GCDDefault, - }, - }, - - ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool { - return warrior.MainHand().HandType == proto.HandType_HandTypeTwoHand - }, - - CritDamageBonus: warrior.impale(), - - DamageMultiplier: 1, - ThreatMultiplier: 1, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - baseDamage := sim.Roll(0.25, 0.35) * spell.MeleeAttackPower() - - result := spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeWeaponSpecialHitAndCrit) - - if !result.Landed() { - spell.IssueRefund(sim) - } - }, - }) -} diff --git a/sim/warrior/raging_blow.go b/sim/warrior/raging_blow.go deleted file mode 100644 index c6a121942..000000000 --- a/sim/warrior/raging_blow.go +++ /dev/null @@ -1,66 +0,0 @@ -package warrior - -import ( - "time" - - "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" -) - -// A ferocious strike that deals 100% weapon damage, but can only be used while Enrage, Berserker Rage, or Bloodrage is active. -// Raging blow cooldown is reduced by 1 second when you use another melee ability while enraged. -func (warrior *Warrior) registerRagingBlow() { - if !warrior.HasRune(proto.WarriorRune_RuneRagingBlow) { - return - } - - warrior.RegisterAura(core.Aura{ - Label: "Raging Blow CDR", - Duration: core.NeverExpires, - OnReset: func(aura *core.Aura, sim *core.Simulation) { - aura.Activate(sim) - }, - OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) { - if spell.ProcMask.Matches(core.ProcMaskMeleeSpecial) && warrior.IsEnraged() && !warrior.RagingBlow.CD.IsReady(sim) && spell.SpellCode != SpellCode_WarriorRagingBlow { - warrior.RagingBlow.CD.Timer.Set(time.Duration(*warrior.RagingBlow.CD.Timer) - time.Second*1) - } - }, - }) - - warrior.RagingBlow = warrior.RegisterSpell(AnyStance, core.SpellConfig{ - SpellCode: SpellCode_WarriorRagingBlow, - ActionID: core.ActionID{SpellID: 402911}, - SpellSchool: core.SpellSchoolPhysical, - DefenseType: core.DefenseTypeMelee, - ProcMask: core.ProcMaskMeleeMHSpecial, - Flags: core.SpellFlagMeleeMetrics | core.SpellFlagAPL | SpellFlagOffensive, - - RageCost: core.RageCostOptions{ - Cost: 0, - }, - Cast: core.CastConfig{ - DefaultCast: core.Cast{ - GCD: core.GCDDefault, - }, - IgnoreHaste: true, - CD: core.Cooldown{ - Timer: warrior.NewTimer(), - Duration: time.Second * 8, - }, - }, - ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool { - return warrior.IsEnraged() - }, - - CritDamageBonus: warrior.impale(), - - DamageMultiplier: 1, - ThreatMultiplier: 1, - BonusCoefficient: 1, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - baseDamage := spell.Unit.MHNormalizedWeaponDamage(sim, spell.MeleeAttackPower()) - spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeWeaponSpecialHitAndCrit) - }, - }) -} diff --git a/sim/warrior/rampage.go b/sim/warrior/rampage.go deleted file mode 100644 index c3f9d4f29..000000000 --- a/sim/warrior/rampage.go +++ /dev/null @@ -1,56 +0,0 @@ -package warrior - -import ( - "time" - - "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" - "github.com/wowsims/classic/sim/core/stats" -) - -// Go on a rampage, increasing your attack power by 10% for 30 sec. This ability can only be used while Enraged. -func (warrior *Warrior) registerRampage() { - if !warrior.HasRune(proto.WarriorRune_RuneRampage) { - return - } - - actionID := core.ActionID{SpellID: int32(proto.WarriorRune_RuneRampage)} - statDep := warrior.NewDynamicMultiplyStat(stats.AttackPower, 1.10) - - warrior.RampageAura = warrior.RegisterAura(core.Aura{ - Label: "Rampage", - ActionID: actionID, - Duration: time.Second * 30, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - warrior.EnableDynamicStatDep(sim, statDep) - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - warrior.DisableDynamicStatDep(sim, statDep) - }, - }) - - warrior.Rampage = warrior.RegisterSpell(AnyStance, core.SpellConfig{ - ActionID: actionID, - Flags: core.SpellFlagAPL | core.SpellFlagCastTimeNoGCD, - - Cast: core.CastConfig{ - CD: core.Cooldown{ - Timer: warrior.NewTimer(), - Duration: time.Minute * 2, - }, - }, - - ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool { - return warrior.IsEnraged() - }, - - ApplyEffects: func(sim *core.Simulation, _ *core.Unit, _ *core.Spell) { - warrior.RampageAura.Activate(sim) - }, - }) - - warrior.AddMajorCooldown(core.MajorCooldown{ - Type: core.CooldownTypeDPS, - Spell: warrior.Rampage.Spell, - }) -} diff --git a/sim/warrior/rend.go b/sim/warrior/rend.go index e11636971..b39ddce7e 100644 --- a/sim/warrior/rend.go +++ b/sim/warrior/rend.go @@ -4,15 +4,9 @@ import ( "time" "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" ) -// Blood Frenzy -// Rend can now be used in Berserker stance, Rend's damage is increased by 100%, -// and Rend deals additional damage equal to 3% of your Attack Power each time it deals damage. - func (warrior *Warrior) registerRendSpell() { - hasBloodFrenzyRune := warrior.HasRune(proto.WarriorRune_RuneBloodFrenzy) rend := map[int32]struct { ticks int32 @@ -26,9 +20,6 @@ func (warrior *Warrior) registerRendSpell() { }[warrior.Level] baseDamage := rend.damage - if hasBloodFrenzyRune { - baseDamage *= 2 - } damageMultiplier := []float64{1, 1.15, 1.25, 1.35}[warrior.Talents.ImprovedRend] @@ -60,12 +51,7 @@ func (warrior *Warrior) registerRendSpell() { NumberOfTicks: rend.ticks, TickLength: time.Second * 3, OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) { - damage := baseDamage - if hasBloodFrenzyRune { - damage += .03 * dot.Spell.MeleeAttackPower() - } - - dot.Snapshot(target, damage, isRollover) + dot.Snapshot(target, baseDamage, isRollover) }, OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) { dot.CalcAndDealPeriodicSnapshotDamage(sim, target, dot.OutcomeTick) diff --git a/sim/warrior/revenge.go b/sim/warrior/revenge.go index 1209a83ca..51b080ec7 100644 --- a/sim/warrior/revenge.go +++ b/sim/warrior/revenge.go @@ -13,18 +13,10 @@ var RevengeBaseDamage = [RevengeRanks + 1][]float64{{0, 0}, {12, 14}, {18, 22}, var RevengeLevel = [RevengeRanks + 1]int{0, 14, 24, 34, 44, 54, 60} func (warrior *Warrior) registerRevengeSpell(cdTimer *core.Timer) { - rank := []int{ - 25: 2, - 40: 3, - 50: 4, - 60: core.TernaryInt(core.IncludeAQ, 6, 5), - }[warrior.Level] - actionID := core.ActionID{SpellID: RevengeSpellId[rank]} - basedamageLow := RevengeBaseDamage[rank][0] - basedamageHigh := RevengeBaseDamage[rank][1] - // Added in SoD phase 5 - apCoeff := 0.15 - cooldown := time.Second * 5 + actionID := core.ActionID{SpellID: core.TernaryInt32(core.IncludeAQ, 25288, 11601)} + basedamageLow := core.TernaryFloat64(core.IncludeAQ, 81, 64) + basedamageHigh := core.TernaryFloat64(core.IncludeAQ, 99, 78) + revengeLevel := core.TernaryFloat64(core.IncludeAQ, 60.0, 54.0) warrior.revengeProcAura = warrior.RegisterAura(core.Aura{ Label: "Revenge", @@ -62,10 +54,6 @@ func (warrior *Warrior) registerRevengeSpell(cdTimer *core.Timer) { GCD: core.GCDDefault, }, IgnoreHaste: true, - CD: core.Cooldown{ - Timer: cdTimer, - Duration: cooldown, - }, }, ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool { return warrior.revengeProcAura.IsActive() @@ -75,11 +63,11 @@ func (warrior *Warrior) registerRevengeSpell(cdTimer *core.Timer) { DamageMultiplier: 1, ThreatMultiplier: 2.25, - FlatThreatBonus: 2.25 * 2 * float64(RevengeLevel[rank]), + FlatThreatBonus: 2.25 * 2 * revengeLevel, BonusCoefficient: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - baseDamage := sim.Roll(basedamageLow, basedamageHigh) + apCoeff*spell.MeleeAttackPower() + baseDamage := sim.Roll(basedamageLow, basedamageHigh) result := spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeSpecialHitAndCrit) if !result.Landed() { diff --git a/sim/warrior/runes.go b/sim/warrior/runes.go deleted file mode 100644 index 33d8b65f4..000000000 --- a/sim/warrior/runes.go +++ /dev/null @@ -1,517 +0,0 @@ -package warrior - -import ( - "slices" - "time" - - "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" - "github.com/wowsims/classic/sim/core/stats" -) - -func (warrior *Warrior) ApplyRunes() { - // Head - warrior.applyVigilance() - warrior.applyEndlessRage() - warrior.applyShieldMastery() - warrior.applyTasteForBlood() - - // Cloak - warrior.applySuddenDeath() - warrior.applyFreshMeat() - warrior.registerShockwaveSpell() - - // Chest - warrior.applyFlagellation() - warrior.registerRagingBlow() - warrior.applyBloodFrenzy() - - // Bracers - warrior.registerRampage() - warrior.applySwordAndBoard() - warrior.applyWreckingCrew() - - // Gloves - warrior.applySingleMindedFury() - warrior.registerQuickStrike() - - // Waist - warrior.applyFocusedRage() - warrior.applyBloodSurge() - - // Pants - warrior.applyFrenziedAssault() - warrior.applyConsumedByRage() - // Furious Thunder implemented in thunder_clap.go - - // Boots - // Gladiator implemented on stances.go -} - -func (warrior *Warrior) applyVigilance() { - if !warrior.HasRune(proto.WarriorRune_RuneVigilance) { - return - } - - warrior.PseudoStats.ThreatMultiplier *= 1.1 -} - -func (warrior *Warrior) applyEndlessRage() { - if !warrior.HasRune(proto.WarriorRune_RuneEndlessRage) { - return - } - - warrior.AddDamageDealtRageMultiplier(1.25) -} - -func (warrior *Warrior) applyShieldMastery() { - if !warrior.HasRune(proto.WarriorRune_RuneShieldMastery) { - return - } - - buffAura := warrior.RegisterAura(core.Aura{ - Label: "Shield Mastery Buff", - Duration: core.NeverExpires, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - warrior.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexPhysical] *= 1.1 - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - warrior.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexPhysical] /= 1.1 - }, - }) - - // Use a hidden aura to periodically verify that the player is still using a 2H weapon mid-sim, for example if Item Swapping - warrior.RegisterAura(core.Aura{ - Label: "Shield Mastery Dummy", - Duration: core.NeverExpires, - OnReset: func(aura *core.Aura, sim *core.Simulation) { - core.StartPeriodicAction(sim, core.PeriodicActionOptions{ - Period: time.Second * 2, - TickImmediately: true, - OnAction: func(sim *core.Simulation) { - if !warrior.PseudoStats.CanBlock { - buffAura.Deactivate(sim) - return - } - - buffAura.Activate(sim) - }, - }) - }, - }) -} - -// You gain Rage from Physical damage taken as if you were wearing no armor. -func (warrior *Warrior) applyFlagellation() { - if !warrior.HasRune(proto.WarriorRune_RuneFlagellation) { - return - } - - // TODO: Rage gain from hits -} - -func (warrior *Warrior) applyBloodFrenzy() { - if !warrior.HasRune(proto.WarriorRune_RuneBloodFrenzy) { - return - } - - core.MakePermanent(warrior.RegisterAura(core.Aura{ - Label: "Blood Frenzy Dummy", - OnInit: func(aura *core.Aura, sim *core.Simulation) { - warrior.Rend.StanceMask |= BerserkerStance - }, - })) -} - -func (warrior *Warrior) applyFrenziedAssault() { - if !warrior.HasRune(proto.WarriorRune_RuneFrenziedAssault) { - return - } - - actionID := core.ActionID{SpellID: 431046} - rageMetrics := warrior.NewRageMetrics(actionID) - - buffAura := warrior.RegisterAura(core.Aura{ - ActionID: actionID, - Label: "Frenzied Assault", - Duration: core.NeverExpires, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - warrior.MultiplyMeleeSpeed(sim, 1.3) - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - warrior.MultiplyMeleeSpeed(sim, 1/1.3) - }, - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if spell.ProcMask.Matches(core.ProcMaskWhiteHit) && result.Landed() { - warrior.AddRage(sim, core.Ternary(result.DidCrit(), 4.0, 2.0), rageMetrics) - } - }, - }) - - // Use a dummy aura to periodically verify that the player is still using a 2H weapon mid-sim, for example if Item Swapping - warrior.RegisterAura(core.Aura{ - Label: "Frenzied Assault Dummy", - Duration: core.NeverExpires, - OnReset: func(aura *core.Aura, sim *core.Simulation) { - core.StartPeriodicAction(sim, core.PeriodicActionOptions{ - Period: time.Second * 2, - TickImmediately: true, - OnAction: func(sim *core.Simulation) { - if warrior.MainHand().HandType != proto.HandType_HandTypeTwoHand { - buffAura.Deactivate(sim) - return - } - - if !buffAura.IsActive() { - buffAura.Activate(sim) - } - }, - }) - }, - }) -} - -// Enrages you (activating abilities which require being Enraged) for 12 sec after you exceed 60 Rage. -// In addition, Whirlwind also strikes with off-hand melee weapons while you are Enraged -func (warrior *Warrior) applyConsumedByRage() { - if !warrior.HasRune(proto.WarriorRune_RuneConsumedByRage) { - return - } - - warrior.ConsumedByRageAura = warrior.GetOrRegisterAura(core.Aura{ - Label: "Enrage (Consumed by Rage)", - ActionID: core.ActionID{SpellID: 425415}, - Duration: time.Second * 12, - }) - - warrior.ConsumedByRageAura.NewExclusiveEffect("Enrage", true, core.ExclusiveEffect{Priority: 0}) - - warrior.RegisterAura(core.Aura{ - Label: "Consumed By Rage Trigger", - Duration: core.NeverExpires, - OnReset: func(aura *core.Aura, sim *core.Simulation) { - aura.Activate(sim) - }, - OnRageChange: func(aura *core.Aura, sim *core.Simulation, metrics *core.ResourceMetrics) { - // Refunding rage should not enable CBR - if warrior.CurrentRage() < 60 || metrics.ActionID.OtherID == proto.OtherAction_OtherActionRefund { - return - } - - warrior.ConsumedByRageAura.Activate(sim) - }, - }) -} - -func (warrior *Warrior) applyFocusedRage() { - if !warrior.HasRune(proto.WarriorRune_RuneFocusedRage) { - return - } - - warrior.OnSpellRegistered(func(spell *core.Spell) { - if spell.Flags.Matches(SpellFlagOffensive) && spell.Cost != nil { - spell.Cost.FlatModifier -= 3 - } - }) -} - -func (warrior *Warrior) applyBloodSurge() { - if !warrior.HasRune(proto.WarriorRune_RuneBloodSurge) { - return - } - - warrior.BloodSurgeAura = warrior.RegisterAura(core.Aura{ - Label: "Blood Surge Proc", - ActionID: core.ActionID{SpellID: 413399}, - Duration: time.Second * 15, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - warrior.Slam.CastTimeMultiplier -= 1 - warrior.Slam.Cost.Multiplier -= 100 - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - warrior.Slam.CastTimeMultiplier += 1 - warrior.Slam.Cost.Multiplier += 100 - }, - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - // removed even if slam doesn't land - if spell.SpellCode == SpellCode_WarriorSlamMH { - aura.Deactivate(sim) - } - }, - }) - - affectedSpells := make(map[*core.Spell]bool) - - core.MakePermanent(warrior.RegisterAura(core.Aura{ - Label: "Blood Surge Trigger", - OnInit: func(aura *core.Aura, sim *core.Simulation) { - if warrior.Slam == nil { - aura.Deactivate(sim) - return - } - - affectedSpells[warrior.HeroicStrike.Spell] = true - affectedSpells[warrior.Whirlwind.Spell] = true - - if warrior.Bloodthirst != nil { - affectedSpells[warrior.Bloodthirst.Spell] = true - } - - if warrior.HasRune(proto.WarriorRune_RuneQuickStrike) { - affectedSpells[warrior.QuickStrike.Spell] = true - } - }, - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if result.Landed() && affectedSpells[spell] && sim.Proc(0.3, "Blood Surge") { - warrior.BloodSurgeAura.Activate(sim) - } - }, - })) -} - -func (warrior *Warrior) applyTasteForBlood() { - if !warrior.HasRune(proto.WarriorRune_RuneTasteForBlood) { - return - } - - warrior.TasteForBloodAura = warrior.RegisterAura(core.Aura{ - ActionID: core.ActionID{SpellID: 426969}, - Label: "Taste for Blood", - Duration: time.Second * 9, - OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) { - if spell.SpellCode == SpellCode_WarriorOverpower { - aura.Deactivate(sim) - } - }, - }) - - icd := core.Cooldown{ - Timer: warrior.NewTimer(), - Duration: time.Millisecond * 5800, - } - - core.MakePermanent(warrior.RegisterAura(core.Aura{ - Label: "Taste for Blood Trigger", - OnPeriodicDamageDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if spell.SpellCode == SpellCode_WarriorRend && icd.IsReady(sim) { - icd.Use(sim) - warrior.TasteForBloodAura.Activate(sim) - } - }, - })) -} - -// Your melee hits have a 10% chance to grant Sudden Death. Sudden Death allows one use of Execute regardless of the target's health state. -// When Execute is enabled by Sudden Death, you will retain 10 rage after using Execute. -func (warrior *Warrior) applySuddenDeath() { - if !warrior.HasRune(proto.WarriorRune_RuneSuddenDeath) { - return - } - - rageMetrics := warrior.NewRageMetrics(core.ActionID{SpellID: int32(proto.WarriorRune_RuneSuddenDeath)}) - - minRageKept := 10.0 - procChance := 0.10 - - warrior.SuddenDeathAura = warrior.RegisterAura(core.Aura{ - Label: "Sudden Death", - ActionID: core.ActionID{SpellID: 440114}, - Duration: time.Second * 10, - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if result.Landed() && spell.SpellCode == SpellCode_WarriorExecute { // removed only when landed - warrior.AddRage(sim, minRageKept, rageMetrics) - aura.Deactivate(sim) - } - }, - }) - - warrior.RegisterAura(core.Aura{ - Label: "Sudden Death Trigger", - Duration: core.NeverExpires, - OnReset: func(aura *core.Aura, sim *core.Simulation) { - aura.Activate(sim) - }, - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !result.Landed() || !spell.ProcMask.Matches(core.ProcMaskMelee) { - return - } - - if sim.Proc(procChance, "Sudden Death") { - warrior.SuddenDeathAura.Activate(sim) - } - }, - }) -} - -// Damaging a target with Bloodthirst has a 100% chance the first time and a 10% chance each subsequent time to -// Enrage you (activating abilities which requiring being Enraged), and cause you to deal 10% increased Physical damage for 12 sec. -func (warrior *Warrior) applyFreshMeat() { - if !warrior.HasRune(proto.WarriorRune_RuneFreshMeat) { - return - } - - warrior.FreshMeatEnrageAura = warrior.RegisterAura(core.Aura{ - ActionID: core.ActionID{SpellID: 14201}, - Label: "Enrage (Fresh Meat)", - Duration: time.Second * 12, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - warrior.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexPhysical] *= 1.1 - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - warrior.PseudoStats.SchoolDamageDealtMultiplier[stats.SchoolIndexPhysical] /= 1.1 - }, - }) - - var damagedUnits map[int32]bool - affectedSpellCodes := []int32{SpellCode_WarriorBloodthirst, SpellCode_WarriorMortalStrike, SpellCode_WarriorShieldSlam} - - warrior.RegisterAura(core.Aura{ - Label: "Fresh Meat Trigger", - Duration: core.NeverExpires, - OnReset: func(aura *core.Aura, sim *core.Simulation) { - damagedUnits = make(map[int32]bool) - aura.Activate(sim) - }, - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !slices.Contains(affectedSpellCodes, spell.SpellCode) { - return - } - - procChance := 0.10 - if !damagedUnits[result.Target.UnitIndex] { - procChance = 1.00 - damagedUnits[result.Target.UnitIndex] = true - } - - if sim.Proc(procChance, "Fresh Meat") { - warrior.FreshMeatEnrageAura.Activate(sim) - } - }, - }) -} - -// Your melee critical hits Enrage you (activating abilities which require being Enraged), and increase Mortal Strike, Bloodthirst, and Shield Slam damage by 10% for 12 sec. -func (warrior *Warrior) applyWreckingCrew() { - if !warrior.HasRune(proto.WarriorRune_RuneWreckingCrew) { - return - } - - var affectedSpells []*WarriorSpell - warrior.WreckingCrewEnrageAura = warrior.RegisterAura(core.Aura{ - Label: "Enrage (Wrecking Crew)", - ActionID: core.ActionID{SpellID: 427066}, - Duration: time.Second * 6, - OnInit: func(aura *core.Aura, sim *core.Simulation) { - affectedSpells = core.FilterSlice( - []*WarriorSpell{warrior.MortalStrike, warrior.Bloodthirst, warrior.ShieldSlam}, - func(spell *WarriorSpell) bool { return spell != nil }, - ) - }, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - for _, spell := range affectedSpells { - spell.DamageMultiplier *= 1.1 - } - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - for _, spell := range affectedSpells { - spell.DamageMultiplier /= 1.1 - } - }, - }) - - warrior.RegisterAura(core.Aura{ - Label: "Wrecking Crew Trigger", - Duration: core.NeverExpires, - OnReset: func(aura *core.Aura, sim *core.Simulation) { - aura.Activate(sim) - }, - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if spell.ProcMask.Matches(core.ProcMaskMelee) && result.Outcome.Matches(core.OutcomeCrit) { - warrior.WreckingCrewEnrageAura.Activate(sim) - } - }, - }) -} - -func (warrior *Warrior) applySwordAndBoard() { - if !warrior.HasRune(proto.WarriorRune_RuneSwordAndBoard) || !warrior.Talents.ShieldSlam { - return - } - - sabAura := warrior.GetOrRegisterAura(core.Aura{ - Label: "Sword And Board", - ActionID: core.ActionID{SpellID: int32(proto.WarriorRune_RuneSwordAndBoard)}, - Duration: 5 * time.Second, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - warrior.ShieldSlam.Cost.Multiplier -= 100 - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - warrior.ShieldSlam.Cost.Multiplier += 100 - }, - OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) { - if spell.SpellCode == SpellCode_WarriorShieldSlam { - aura.Deactivate(sim) - } - }, - }) - - core.MakePermanent(warrior.GetOrRegisterAura(core.Aura{ - Label: "Sword And Board Trigger", - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !result.Landed() { - return - } - - if spell.SpellCode != SpellCode_WarriorRevenge && spell.SpellCode != SpellCode_WarriorDevastate { - return - } - - if sim.Proc(0.3, "Sword And Board") { - sabAura.Activate(sim) - warrior.ShieldSlam.CD.Reset() - } - }, - })) -} - -// While dual-wielding, your movement speed is increased by 10% and you gain 2% attack speed each time your melee auto-attack strikes the same target as your previous auto-attack, stacking up to 5 times. -// Lasts 10 sec or until your auto-attack strikes a different target. -func (warrior *Warrior) applySingleMindedFury() { - if !warrior.HasRune(proto.WarriorRune_RuneSingleMindedFury) { - return - } - - buffAura := warrior.RegisterAura(core.Aura{ - ActionID: core.ActionID{SpellID: 461470}, - Label: "Single-Minded Fury Attack Speed", - Duration: time.Second * 10, - MaxStacks: 5, - OnStacksChange: func(aura *core.Aura, sim *core.Simulation, oldStacks int32, newStacks int32) { - warrior.MultiplyAttackSpeed(sim, 1/(1+.02*float64(oldStacks))) - warrior.MultiplyAttackSpeed(sim, 1+.02*float64(newStacks)) - }, - }) - - warrior.RegisterAura(core.Aura{ - Label: "Single-Minded Fury Trigger", - Duration: core.NeverExpires, - OnReset: func(aura *core.Aura, sim *core.Simulation) { - warrior.lastMeleeAutoTarget = nil - aura.Activate(sim) - }, - OnSpellHitDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) { - if !warrior.AutoAttacks.IsDualWielding || !result.Landed() || !spell.ProcMask.Matches(core.ProcMaskMeleeWhiteHit) { - return - } - - if warrior.lastMeleeAutoTarget == nil || warrior.lastMeleeAutoTarget != result.Target { - warrior.lastMeleeAutoTarget = result.Target - buffAura.Deactivate(sim) - return - } - - buffAura.Activate(sim) - buffAura.AddStack(sim) - }, - }) -} diff --git a/sim/warrior/shield_slam.go b/sim/warrior/shield_slam.go index fe82ea003..7d7ac0571 100644 --- a/sim/warrior/shield_slam.go +++ b/sim/warrior/shield_slam.go @@ -4,7 +4,6 @@ import ( "time" "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/stats" ) func (warrior *Warrior) registerShieldSlamSpell() { @@ -12,24 +11,16 @@ func (warrior *Warrior) registerShieldSlamSpell() { return } - rank := map[int32]struct { - spellID int32 - damageLow float64 - damageHigh float64 - threat float64 - }{ - 40: {spellID: 23922, damageLow: 225, damageHigh: 235, threat: 178}, - 50: {spellID: 23923, damageLow: 264, damageHigh: 276, threat: 203}, - 60: {spellID: 23925, damageLow: 342, damageHigh: 358, threat: 254}, - }[warrior.Level] + spellID := int32(23925) + damageLow := 342.0 + damageHigh := 358.0 + threat := 254.0 apCoef := 0.15 - defendersResolveAura := core.DefendersResolveAttackPower(warrior.GetCharacter()) - warrior.ShieldSlam = warrior.RegisterSpell(AnyStance, core.SpellConfig{ SpellCode: SpellCode_WarriorShieldSlam, - ActionID: core.ActionID{SpellID: rank.spellID}, + ActionID: core.ActionID{SpellID: spellID}, SpellSchool: core.SpellSchoolPhysical, DefenseType: core.DefenseTypeMelee, ProcMask: core.ProcMaskMeleeMHSpecial, // TODO really? @@ -56,22 +47,15 @@ func (warrior *Warrior) registerShieldSlamSpell() { CritDamageBonus: warrior.impale(), DamageMultiplier: 1, - ThreatMultiplier: 2, - FlatThreatBonus: rank.threat * 2, + ThreatMultiplier: 1, + FlatThreatBonus: threat * 2, BonusCoefficient: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - damage := sim.Roll(rank.damageLow, rank.damageHigh) + warrior.BlockValue()*2 + apCoef*spell.MeleeAttackPower() + damage := sim.Roll(damageLow, damageHigh) + warrior.BlockValue()*2 + apCoef*spell.MeleeAttackPower() result := spell.CalcAndDealDamage(sim, target, damage, spell.OutcomeMeleeSpecialHitAndCrit) - if result.Landed() { - if stacks := int32(warrior.GetStat(stats.Defense)); stacks > 0 { - defendersResolveAura.Activate(sim) - if defendersResolveAura.GetStacks() != stacks { - defendersResolveAura.SetStacks(sim, stacks) - } - } - } else { + if !result.Landed() { spell.IssueRefund(sim) } }, diff --git a/sim/warrior/shockwave.go b/sim/warrior/shockwave.go deleted file mode 100644 index 60af30940..000000000 --- a/sim/warrior/shockwave.go +++ /dev/null @@ -1,50 +0,0 @@ -package warrior - -import ( - "time" - - "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" -) - -func (warrior *Warrior) registerShockwaveSpell() { - if !warrior.HasRune(proto.WarriorRune_RuneShockwave) { - return - } - - apCoef := 0.50 - - warrior.Shockwave = warrior.RegisterSpell(DefensiveStance, core.SpellConfig{ - ActionID: core.ActionID{SpellID: int32(proto.WarriorRune_RuneShockwave)}, - SpellSchool: core.SpellSchoolPhysical, - DefenseType: core.DefenseTypeRanged, - ProcMask: core.ProcMaskRangedSpecial, - Flags: core.SpellFlagMeleeMetrics | core.SpellFlagAPL | SpellFlagOffensive, - - RageCost: core.RageCostOptions{ - Cost: 15, - }, - Cast: core.CastConfig{ - DefaultCast: core.Cast{ - GCD: core.GCDDefault, - }, - CD: core.Cooldown{ - Timer: warrior.NewTimer(), - Duration: 20 * time.Second, - }, - }, - - CritDamageBonus: warrior.impale(), - - DamageMultiplier: 1, - ThreatMultiplier: 1, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - baseDamage := apCoef * spell.MeleeAttackPower() - for _, aoeTarget := range sim.Encounter.TargetUnits { - // Shockwave can miss and be blocked, but it can't be dodged or parried - spell.CalcAndDealDamage(sim, aoeTarget, baseDamage, spell.OutcomeMeleeSpecialNoDodgeParry) - } - }, - }) -} diff --git a/sim/warrior/slam.go b/sim/warrior/slam.go index 5ec5c623e..5809437de 100644 --- a/sim/warrior/slam.go +++ b/sim/warrior/slam.go @@ -4,45 +4,12 @@ import ( "time" "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" ) func (warrior *Warrior) registerSlamSpell() { - if warrior.Level < 30 { - return - } - - hasBloodSurgeRune := warrior.HasRune(proto.WarriorRune_RuneBloodSurge) - - var castTime time.Duration - var cooldown core.Cooldown - if warrior.HasRune(proto.WarriorRune_RunePreciseTiming) { - castTime = 0 - cooldown = core.Cooldown{ - Timer: warrior.NewTimer(), - Duration: 6 * time.Second, - } - } else { - castTime = time.Millisecond*1500 - time.Millisecond*100*time.Duration(warrior.Talents.ImprovedSlam) - } - - requiredLevel := map[int32]int{ - 40: 38, - 50: 46, - 60: 54, - }[warrior.Level] - - spellID := map[int32]int32{ - 40: 8820, - 50: 11604, - 60: 11605, - }[warrior.Level] - - warrior.SlamMH = warrior.newSlamHitSpell(true) - canHitOffhand := hasBloodSurgeRune && warrior.AutoAttacks.IsDualWielding - if canHitOffhand { - warrior.SlamOH = warrior.newSlamHitSpell(false) - } + requiredLevel := 54 + spellID := int32(11605) + flatDamageBonus := 87.0 warrior.Slam = warrior.RegisterSpell(AnyStance, core.SpellConfig{ SpellCode: SpellCode_WarriorSlam, @@ -61,9 +28,8 @@ func (warrior *Warrior) registerSlamSpell() { Cast: core.CastConfig{ DefaultCast: core.Cast{ GCD: core.GCDDefault, - CastTime: castTime, + CastTime: time.Millisecond*1500 - time.Millisecond*100*time.Duration(warrior.Talents.ImprovedSlam), }, - CD: cooldown, ModifyCast: func(sim *core.Simulation, spell *core.Spell, cast *core.Cast) { if spell.CastTime() > 0 { warrior.AutoAttacks.StopMeleeUntil(sim, sim.CurrentTime+cast.CastTime, true) @@ -71,66 +37,20 @@ func (warrior *Warrior) registerSlamSpell() { }, }, - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - doOffhandHit := canHitOffhand && warrior.BloodSurgeAura.IsActive() - warrior.SlamMH.Cast(sim, target) - if doOffhandHit { - warrior.SlamOH.Cast(sim, target) - } - }, - }) -} - -func (warrior *Warrior) newSlamHitSpell(isMH bool) *WarriorSpell { - spellID := map[int32]int32{ - 40: 8820, - 50: 11604, - 60: 11605, - }[warrior.Level] - - requiredLevel := map[int32]float64{ - 40: 38, - 50: 46, - 60: 54, - }[warrior.Level] - - flatDamageBonus := map[int32]float64{ - 40: 43, - 50: 68, - 60: 87, - }[warrior.Level] - - procMask := core.ProcMaskMeleeMHSpecial - flags := core.SpellFlagMeleeMetrics | core.SpellFlagNoOnCastComplete - damageFunc := warrior.MHWeaponDamage - if !isMH { - flatDamageBonus /= 2 - procMask = core.ProcMaskMeleeOHSpecial - flags |= core.SpellFlagPassiveSpell - damageFunc = warrior.OHWeaponDamage - } - - return warrior.RegisterSpell(AnyStance, core.SpellConfig{ - SpellCode: core.Ternary(isMH, SpellCode_WarriorSlamMH, SpellCode_WarriorSlamOH), - ActionID: core.ActionID{SpellID: spellID}.WithTag(int32(core.Ternary(isMH, 1, 2))), - SpellSchool: core.SpellSchoolPhysical, - DefenseType: core.DefenseTypeMelee, - ProcMask: procMask, - Flags: flags, - CritDamageBonus: warrior.impale(), - FlatThreatBonus: 1 * requiredLevel, DamageMultiplier: 1, ThreatMultiplier: 1, + FlatThreatBonus: 140, // Should this be 54 or the old 140 value from before SoD? BonusCoefficient: 1, + ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - baseDamage := flatDamageBonus + damageFunc(sim, spell.MeleeAttackPower()) - result := spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeWeaponSpecialHitAndCrit) + baseDamage := flatDamageBonus + spell.Unit.MHWeaponDamage(sim, spell.MeleeAttackPower()) - if isMH && !result.Landed() { - warrior.Slam.IssueRefund(sim) + result := spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeWeaponSpecialHitAndCrit) + if !result.Landed() { + spell.IssueRefund(sim) } }, }) diff --git a/sim/warrior/stances.go b/sim/warrior/stances.go index 6da7e21fd..9837f11a5 100644 --- a/sim/warrior/stances.go +++ b/sim/warrior/stances.go @@ -4,7 +4,6 @@ import ( "time" "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" "github.com/wowsims/classic/sim/core/stats" ) @@ -14,16 +13,15 @@ const ( BattleStance Stance = 1 << iota DefensiveStance BerserkerStance - GladiatorStance - AnyStance = BattleStance | DefensiveStance | BerserkerStance | GladiatorStance + AnyStance = BattleStance | DefensiveStance | BerserkerStance ) func (stance Stance) Matches(other Stance) bool { return (stance & other) != 0 } -var StanceCodes = []int32{SpellCode_WarriorStanceBattle, SpellCode_WarriorStanceDefensive, SpellCode_WarriorStanceBerserker, SpellCode_WarriorStanceGladiator} +var StanceCodes = []int32{SpellCode_WarriorStanceBattle, SpellCode_WarriorStanceDefensive, SpellCode_WarriorStanceBerserker} const stanceEffectCategory = "Stance" @@ -36,7 +34,6 @@ func (warrior *Warrior) makeStanceSpell(stance Stance, aura *core.Aura, stanceCD BattleStance: SpellCode_WarriorStanceBattle, DefensiveStance: SpellCode_WarriorStanceDefensive, BerserkerStance: SpellCode_WarriorStanceBerserker, - GladiatorStance: SpellCode_WarriorStanceGladiator, }[stance] actionID := aura.ActionID maxRetainedRage := 5 * float64(warrior.Talents.TacticalMastery) @@ -141,112 +138,13 @@ func (warrior *Warrior) registerBerserkerStanceAura() { }) } -// An aggressive stance that increases damage while you are wearing a shield by 10% and increases block chance by 10%, but reduces armor by 30% and threat generated by 30%. -// In addition, you gain 50% increased Rage when your auto-attack damages an enemy not targeting you. -// While wearing a shield in Gladiator Stance, you may use all abilities that are restricted to other stances. -func (warrior *Warrior) registerGladiatorStanceAura() { - if !warrior.HasRune(proto.WarriorRune_RuneGladiatorStance) { - return - } - - warrior.gladiatorStanceDamageMultiplier = 1.1 - isTanking := warrior.IsTanking() - - gladStanceDamageAura := warrior.RegisterAura(core.Aura{ - Label: "Gladiator Stance Damage Bonus", - Duration: core.NeverExpires, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - aura.Unit.PseudoStats.DamageDealtMultiplier *= warrior.gladiatorStanceDamageMultiplier - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - aura.Unit.PseudoStats.DamageDealtMultiplier /= warrior.gladiatorStanceDamageMultiplier - }, - }) - - // Use a periodic action aura to verify that the player is still using a shield for the damage bonus and stance override. - // This is needed if Item Swapping. - var gladStanceValidationPA *core.PendingAction - var gladStanceStanceOverrideEE *core.ExclusiveEffect - gladStanceValidationAura := warrior.RegisterAura(core.Aura{ - Label: "Gladiator Stance Shield Validation", - Duration: core.NeverExpires, - OnReset: func(aura *core.Aura, sim *core.Simulation) { - gladStanceValidationPA = nil - }, - OnGain: func(aura *core.Aura, sim *core.Simulation) { - gladStanceValidationPA = core.StartPeriodicAction(sim, core.PeriodicActionOptions{ - Period: time.Second * 2, - TickImmediately: true, - OnAction: func(sim *core.Simulation) { - if warrior.GladiatorStanceAura.IsActive() && warrior.PseudoStats.CanBlock { - if !gladStanceDamageAura.IsActive() { - gladStanceDamageAura.Activate(sim) - } - if !gladStanceStanceOverrideEE.IsActive() { - gladStanceStanceOverrideEE.Activate(sim) - } - } else { - gladStanceDamageAura.Deactivate(sim) - gladStanceStanceOverrideEE.Deactivate(sim) - return - } - }, - }) - }, - OnExpire: func(aura *core.Aura, sim *core.Simulation) { - if gladStanceValidationPA != nil { - gladStanceValidationPA.Cancel(sim) - } - if gladStanceDamageAura.IsActive() { - gladStanceDamageAura.Deactivate(sim) - } - if !gladStanceStanceOverrideEE.IsActive() { - gladStanceStanceOverrideEE.Activate(sim) - } - }, - }) - - warrior.GladiatorStanceAura = warrior.RegisterAura(core.Aura{ - Label: "Gladiator Stance", - ActionID: core.ActionID{SpellID: int32(proto.WarriorRune_RuneGladiatorStance)}, - Duration: core.NeverExpires, - }) - warrior.GladiatorStanceAura.NewExclusiveEffect(stanceEffectCategory, true, core.ExclusiveEffect{ - OnGain: func(ee *core.ExclusiveEffect, sim *core.Simulation) { - ee.Aura.Unit.AddStatDynamic(sim, stats.Block, 10*core.BlockRatingPerBlockChance) - ee.Aura.Unit.PseudoStats.ArmorMultiplier *= 0.7 - ee.Aura.Unit.PseudoStats.ThreatMultiplier *= 0.7 - if !isTanking { - warrior.AddDamageDealtRageMultiplier(1.5) - } - - gladStanceValidationAura.Activate(sim) - }, - OnExpire: func(ee *core.ExclusiveEffect, sim *core.Simulation) { - ee.Aura.Unit.AddStatDynamic(sim, stats.Block, -10*core.BlockRatingPerBlockChance) - ee.Aura.Unit.PseudoStats.ArmorMultiplier /= 0.7 - ee.Aura.Unit.PseudoStats.ThreatMultiplier /= 0.7 - if !isTanking { - warrior.AddDamageDealtRageMultiplier(1 / 1.5) - } - - gladStanceValidationAura.Deactivate(sim) - }, - }) - gladStanceStanceOverrideEE = warrior.newStanceOverrideExclusiveEffect(AnyStance, warrior.GladiatorStanceAura) -} - func (warrior *Warrior) registerStances() { warrior.Stances = make([]*WarriorSpell, 0) stanceCD := warrior.NewTimer() warrior.registerBattleStanceAura() warrior.registerDefensiveStanceAura() warrior.registerBerserkerStanceAura() - warrior.registerGladiatorStanceAura() warrior.BattleStance = warrior.makeStanceSpell(BattleStance, warrior.BattleStanceAura, stanceCD) warrior.DefensiveStance = warrior.makeStanceSpell(DefensiveStance, warrior.DefensiveStanceAura, stanceCD) warrior.BerserkerStance = warrior.makeStanceSpell(BerserkerStance, warrior.BerserkerStanceAura, stanceCD) - if warrior.HasRune(proto.WarriorRune_RuneGladiatorStance) { - warrior.GladiatorStance = warrior.makeStanceSpell(GladiatorStance, warrior.GladiatorStanceAura, stanceCD) - } } diff --git a/sim/warrior/sunder_armor.go b/sim/warrior/sunder_armor.go index 14d2a2ab5..1f2e39805 100644 --- a/sim/warrior/sunder_armor.go +++ b/sim/warrior/sunder_armor.go @@ -2,62 +2,17 @@ package warrior import ( "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" ) func (warrior *Warrior) registerSunderArmorSpell() *WarriorSpell { warrior.SunderArmorAuras = warrior.NewEnemyAuraArray(core.SunderArmorAura) - spellID := map[int32]int32{ - 25: 7405, - 40: 8380, - 50: 11596, - 60: 11597, - }[warrior.Level] + spellID := int32(11597) - spell_level := map[int32]int32{ - 25: 22, - 40: 34, - 50: 46, - 60: 58, - }[warrior.Level] + spell_level := 58 - var effectiveStacks int32 var canApplySunder bool - if warrior.HasRune(proto.WarriorRune_RuneDevastate) { - warrior.Devastate = warrior.RegisterSpell(DefensiveStance, core.SpellConfig{ - SpellCode: SpellCode_WarriorDevastate, - ActionID: core.ActionID{SpellID: int32(proto.WarriorRune_RuneDevastate)}, - SpellSchool: core.SpellSchoolPhysical, - DefenseType: core.DefenseTypeMelee, - ProcMask: core.ProcMaskMeleeMHSpecial, // TODO check whether this can actually proc stuff or not - Flags: core.SpellFlagMeleeMetrics | SpellFlagOffensive, - - CritDamageBonus: warrior.impale(), - DamageMultiplier: 1.5, - ThreatMultiplier: 1, - BonusCoefficient: 1, - - ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool { - return warrior.PseudoStats.CanBlock - }, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - threatMultiplier := 1.0 - if warrior.Stance == DefensiveStance { - threatMultiplier = 1.50 - } - spell.ThreatMultiplier *= threatMultiplier - - weapon := warrior.AutoAttacks.MH() - baseDamage := weapon.CalculateAverageWeaponDamage(spell.MeleeAttackPower()) / weapon.SwingSpeed - multiplier := 1 + 0.1*float64(effectiveStacks) - spell.CalcAndDealDamage(sim, target, baseDamage*multiplier, spell.OutcomeMeleeSpecialCritOnly) - spell.ThreatMultiplier /= threatMultiplier - }, - }) - } return warrior.RegisterSpell(AnyStance, core.SpellConfig{ ActionID: core.ActionID{SpellID: spellID}, @@ -78,16 +33,13 @@ func (warrior *Warrior) registerSunderArmorSpell() *WarriorSpell { ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool { sa := warrior.SunderArmorAuras.Get(target) if sa.IsActive() { - effectiveStacks = sa.GetStacks() canApplySunder = true } else if sa.ExclusiveEffects[0].Category.AnyActive() { - effectiveStacks = sa.MaxStacks canApplySunder = false } else { - effectiveStacks = 0 canApplySunder = true } - return canApplySunder || warrior.Devastate != nil + return canApplySunder }, ThreatMultiplier: 1, @@ -103,10 +55,6 @@ func (warrior *Warrior) registerSunderArmorSpell() *WarriorSpell { return } - if warrior.Devastate != nil { - warrior.Devastate.Cast(sim, target) - } - if canApplySunder { sa := warrior.SunderArmorAuras.Get(target) sa.Activate(sim) diff --git a/sim/warrior/sweeping_strikes.go b/sim/warrior/sweeping_strikes.go index f1ecdd391..f3a68fd33 100644 --- a/sim/warrior/sweeping_strikes.go +++ b/sim/warrior/sweeping_strikes.go @@ -62,7 +62,7 @@ func (warrior *Warrior) registerSweepingStrikesCD() { var spellToUse *WarriorSpell - if spell.SpellCode == SpellCode_WarriorWhirlwindMH || (spell.SpellCode == SpellCode_WarriorExecute && !sim.IsExecutePhase20()) { + if spell.SpellCode == SpellCode_WarriorWhirlwind || (spell.SpellCode == SpellCode_WarriorExecute && !sim.IsExecutePhase20()) { spellToUse = hitSpecialNormalized } else { curDmg = result.Damage diff --git a/sim/warrior/tank_warrior/TestP1TankWarrior.results b/sim/warrior/tank_warrior/TestP1TankWarrior.results index 14fe50081..c12d27da6 100644 --- a/sim/warrior/tank_warrior/TestP1TankWarrior.results +++ b/sim/warrior/tank_warrior/TestP1TankWarrior.results @@ -50,7 +50,7 @@ character_stats_results: { stat_weights_results: { key: "TestP1TankWarrior-Phase1-StatWeights-Default" value: { - weights: 0.23674 + weights: 0.17708 weights: 0 weights: 0 weights: 0 @@ -67,7 +67,7 @@ stat_weights_results: { weights: 0 weights: 0 weights: 0 - weights: 0.14335 + weights: 0.09215 weights: 0 weights: 0 weights: 0 @@ -99,98 +99,98 @@ stat_weights_results: { dps_results: { key: "TestP1TankWarrior-Phase1-Average-Default" value: { - dps: 194.79525 - tps: 318.60109 + dps: 192.40783 + tps: 350.5516 } } dps_results: { key: "TestP1TankWarrior-Phase1-Settings-Human-p0.bis-Protection-p1-FullBuffs-P1-Consumes-LongMultiTarget" value: { - dps: 26.09262 - tps: 151.45021 + dps: 29.00503 + tps: 164.07561 } } dps_results: { key: "TestP1TankWarrior-Phase1-Settings-Human-p0.bis-Protection-p1-FullBuffs-P1-Consumes-LongSingleTarget" value: { - dps: 22.95352 - tps: 44.87155 + dps: 25.86593 + tps: 57.49694 } } dps_results: { key: "TestP1TankWarrior-Phase1-Settings-Human-p0.bis-Protection-p1-FullBuffs-P1-Consumes-ShortSingleTarget" value: { - dps: 33.04408 - tps: 64.7106 + dps: 37.0221 + tps: 81.69333 } } dps_results: { key: "TestP1TankWarrior-Phase1-Settings-Human-p0.bis-Protection-p1-NoBuffs-P1-Consumes-LongMultiTarget" value: { - dps: 11.63948 - tps: 129.84277 + dps: 12.23574 + tps: 139.00552 } } dps_results: { key: "TestP1TankWarrior-Phase1-Settings-Human-p0.bis-Protection-p1-NoBuffs-P1-Consumes-LongSingleTarget" value: { - dps: 9.27833 - tps: 24.42714 + dps: 9.87459 + tps: 33.58989 } } dps_results: { key: "TestP1TankWarrior-Phase1-Settings-Human-p0.bis-Protection-p1-NoBuffs-P1-Consumes-ShortSingleTarget" value: { - dps: 11.78536 - tps: 32.92882 + dps: 12.17571 + tps: 44.54797 } } dps_results: { key: "TestP1TankWarrior-Phase1-Settings-Orc-p0.bis-Protection-p1-FullBuffs-P1-Consumes-LongMultiTarget" value: { - dps: 27.57978 - tps: 148.67352 + dps: 30.64155 + tps: 161.52221 } } dps_results: { key: "TestP1TankWarrior-Phase1-Settings-Orc-p0.bis-Protection-p1-FullBuffs-P1-Consumes-LongSingleTarget" value: { - dps: 24.44069 - tps: 46.84486 + dps: 27.50246 + tps: 59.69355 } } dps_results: { key: "TestP1TankWarrior-Phase1-Settings-Orc-p0.bis-Protection-p1-FullBuffs-P1-Consumes-ShortSingleTarget" value: { - dps: 35.40788 - tps: 66.99448 + dps: 39.52057 + tps: 84.17854 } } dps_results: { key: "TestP1TankWarrior-Phase1-Settings-Orc-p0.bis-Protection-p1-NoBuffs-P1-Consumes-LongMultiTarget" value: { - dps: 12.34613 - tps: 125.89922 + dps: 13.04393 + tps: 135.21376 } } dps_results: { key: "TestP1TankWarrior-Phase1-Settings-Orc-p0.bis-Protection-p1-NoBuffs-P1-Consumes-LongSingleTarget" value: { - dps: 9.98498 - tps: 25.23359 + dps: 10.68278 + tps: 34.54812 } } dps_results: { key: "TestP1TankWarrior-Phase1-Settings-Orc-p0.bis-Protection-p1-NoBuffs-P1-Consumes-ShortSingleTarget" value: { - dps: 12.82153 - tps: 33.22788 + dps: 13.29143 + tps: 44.96597 } } dps_results: { key: "TestP1TankWarrior-Phase1-SwitchInFrontOfTarget-Default" value: { - dps: 164.70533 - tps: 271.42344 + dps: 165.42962 + tps: 304.62732 } } diff --git a/sim/warrior/thunder_clap.go b/sim/warrior/thunder_clap.go index 93ec384b0..95a9c8857 100644 --- a/sim/warrior/thunder_clap.go +++ b/sim/warrior/thunder_clap.go @@ -4,45 +4,24 @@ import ( "time" "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" ) -// Thunder Clap now increases the time between attacks by an additional 6%, can be used in any stance, deals 100% increased damage, and deals 50% increased threat. func (warrior *Warrior) registerThunderClapSpell() { - hasFuriousThunder := warrior.HasRune(proto.WarriorRune_RuneFuriousThunder) + spellID := int32(11581) + baseDamage := 103.0 + duration := time.Second * 30 - info := map[int32]struct { - spellID int32 - baseDamage float64 - duration time.Duration - }{ - 25: {spellID: 8198, baseDamage: 23, duration: time.Second * 14}, - 40: {spellID: 8205, baseDamage: 55, duration: time.Second * 22}, - 50: {spellID: 11580, baseDamage: 82, duration: time.Second * 26}, - 60: {spellID: 11581, baseDamage: 103, duration: time.Second * 30}, - }[warrior.Level] - - damageMultiplier := 1.0 - threatMultiplier := 2.5 - apCoef := 0.05 attackSpeedReduction := int32(10) stanceMask := BattleStance - if hasFuriousThunder { - damageMultiplier *= 2 - threatMultiplier *= 1.5 - attackSpeedReduction += 6 - stanceMask = AnyStance - } - warrior.ThunderClapAuras = warrior.NewEnemyAuraArray(func(target *core.Unit) *core.Aura { - return core.ThunderClapAura(target, info.spellID, info.duration, attackSpeedReduction) + return core.ThunderClapAura(target, spellID, duration, attackSpeedReduction) }) results := make([]*core.SpellResult, min(4, warrior.Env.GetNumTargets())) warrior.ThunderClap = warrior.RegisterSpell(stanceMask, core.SpellConfig{ - ActionID: core.ActionID{SpellID: info.spellID}, + ActionID: core.ActionID{SpellID: spellID}, SpellSchool: core.SpellSchoolPhysical, DefenseType: core.DefenseTypeMagic, ProcMask: core.ProcMaskSpellDamage, @@ -64,12 +43,12 @@ func (warrior *Warrior) registerThunderClapSpell() { CritDamageBonus: warrior.impale(), - DamageMultiplier: damageMultiplier, - ThreatMultiplier: threatMultiplier, + DamageMultiplier: 1, + ThreatMultiplier: 2.5, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { for idx := range results { - results[idx] = spell.CalcDamage(sim, target, info.baseDamage+apCoef*spell.MeleeAttackPower(), spell.OutcomeMagicHitAndCrit) + results[idx] = spell.CalcDamage(sim, target, baseDamage, spell.OutcomeMagicHitAndCrit) target = sim.Environment.NextTargetUnit(target) } diff --git a/sim/warrior/warrior.go b/sim/warrior/warrior.go index 4c6fc62be..f6d2f43c0 100644 --- a/sim/warrior/warrior.go +++ b/sim/warrior/warrior.go @@ -18,24 +18,17 @@ const ( SpellCode_WarriorBloodthirst SpellCode_WarriorDeepWounds - SpellCode_WarriorDevastate SpellCode_WarriorExecute SpellCode_WarriorMortalStrike SpellCode_WarriorOverpower - SpellCode_WarriorRagingBlow SpellCode_WarriorRend SpellCode_WarriorRevenge SpellCode_WarriorShieldSlam SpellCode_WarriorSlam - SpellCode_WarriorSlamMH - SpellCode_WarriorSlamOH SpellCode_WarriorStanceBattle SpellCode_WarriorStanceBerserker - SpellCode_WarriorStanceGladiator SpellCode_WarriorStanceDefensive SpellCode_WarriorWhirlwind - SpellCode_WarriorWhirlwindMH - SpellCode_WarriorWhirlwindOH ) var TalentTreeSizes = [3]int{18, 17, 17} @@ -64,19 +57,12 @@ type Warrior struct { revengeProcAura *core.Aura OverpowerAura *core.Aura - BloodSurgeAura *core.Aura - RampageAura *core.Aura - SuddenDeathAura *core.Aura - TasteForBloodAura *core.Aura lastMeleeAutoTarget *core.Unit // Enrage Auras BerserkerRageAura *core.Aura BloodrageAura *core.Aura - ConsumedByRageAura *core.Aura EnrageAura *core.Aura - FreshMeatEnrageAura *core.Aura - WreckingCrewEnrageAura *core.Aura // Reaction time values reactionTime time.Duration @@ -92,7 +78,6 @@ type Warrior struct { BattleStance *WarriorSpell DefensiveStance *WarriorSpell BerserkerStance *WarriorSpell - GladiatorStance *WarriorSpell Bloodrage *WarriorSpell BerserkerRage *WarriorSpell @@ -107,24 +92,16 @@ type Warrior struct { ShieldBlock *WarriorSpell ShieldSlam *WarriorSpell Slam *WarriorSpell - SlamMH *WarriorSpell - SlamOH *WarriorSpell SunderArmor *WarriorSpell Devastate *WarriorSpell ThunderClap *WarriorSpell Whirlwind *WarriorSpell - WhirlwindMH *WarriorSpell - WhirlwindOH *WarriorSpell DeepWounds *WarriorSpell ConcussionBlow *WarriorSpell - RagingBlow *WarriorSpell Hamstring *WarriorSpell - Rampage *WarriorSpell - Shockwave *WarriorSpell HeroicStrike *WarriorSpell HeroicStrikeQueue *WarriorSpell - QuickStrike *WarriorSpell Cleave *WarriorSpell CleaveQueue *WarriorSpell curQueueAura *core.Aura @@ -133,10 +110,8 @@ type Warrior struct { BattleStanceAura *core.Aura DefensiveStanceAura *core.Aura BerserkerStanceAura *core.Aura - GladiatorStanceAura *core.Aura defensiveStanceThreatMultiplier float64 - gladiatorStanceDamageMultiplier float64 ShieldBlockAura *core.Aura @@ -279,15 +254,8 @@ func (warrior *Warrior) Reset(sim *core.Simulation) { case proto.WarriorStance_WarriorStanceBerserker: warrior.Stance = BerserkerStance warrior.BerserkerStanceAura.Activate(sim) - case proto.WarriorStance_WarriorStanceGladiator: - warrior.Stance = GladiatorStance - warrior.GladiatorStanceAura.Activate(sim) default: - // Fallback to checking for Glad Stance rune or checking talent tree - if warrior.GladiatorStanceAura != nil { - warrior.Stance = GladiatorStance - warrior.GladiatorStanceAura.Activate(sim) - } else if warrior.PrimaryTalentTree == ArmsTree { + if warrior.PrimaryTalentTree == ArmsTree { warrior.Stance = BattleStance warrior.BattleStanceAura.Activate(sim) } else if warrior.PrimaryTalentTree == FuryTree { @@ -326,19 +294,6 @@ type WarriorAgent interface { GetWarrior() *Warrior } -func (warrior *Warrior) HasRune(rune proto.WarriorRune) bool { - return false // warrior.HasRuneById(int32(rune)) -} - -func (warrior *Warrior) IsEnraged() bool { - return warrior.BloodrageAura.IsActive() || - warrior.BerserkerRageAura.IsActive() || - (warrior.EnrageAura != nil && warrior.EnrageAura.IsActive()) || - (warrior.ConsumedByRageAura != nil && warrior.ConsumedByRageAura.IsActive()) || - (warrior.FreshMeatEnrageAura != nil && warrior.FreshMeatEnrageAura.IsActive()) || - (warrior.WreckingCrewEnrageAura != nil && warrior.WreckingCrewEnrageAura.IsActive()) -} - type WarriorSpell struct { *core.Spell StanceMask Stance diff --git a/sim/warrior/whirlwind.go b/sim/warrior/whirlwind.go index 6ca42c183..965fc0c86 100644 --- a/sim/warrior/whirlwind.go +++ b/sim/warrior/whirlwind.go @@ -4,21 +4,10 @@ import ( "time" "github.com/wowsims/classic/sim/core" - "github.com/wowsims/classic/sim/core/proto" ) func (warrior *Warrior) registerWhirlwindSpell() { - if warrior.Level < 36 { - return - } - - hasConsumedByRageRune := warrior.HasRune(proto.WarriorRune_RuneConsumedByRage) - - warrior.WhirlwindMH = warrior.newWhirlwindHitSpell(true) - canHitOffhand := hasConsumedByRageRune && warrior.AutoAttacks.IsDualWielding - if canHitOffhand { - warrior.WhirlwindOH = warrior.newWhirlwindHitSpell(false) - } + results := make([]*core.SpellResult, min(4, warrior.Env.GetNumTargets())) warrior.Whirlwind = warrior.RegisterSpell(BerserkerStance, core.SpellConfig{ SpellCode: SpellCode_WarriorWhirlwind, @@ -41,34 +30,6 @@ func (warrior *Warrior) registerWhirlwindSpell() { Duration: time.Second * 10, }, }, - - ApplyEffects: func(sim *core.Simulation, target *core.Unit, _ *core.Spell) { - for _, aoeTarget := range sim.Encounter.TargetUnits { - warrior.WhirlwindMH.Cast(sim, aoeTarget) - if canHitOffhand && warrior.IsEnraged() { - warrior.WhirlwindOH.Cast(sim, aoeTarget) - } - } - }, - }) -} - -func (warrior *Warrior) newWhirlwindHitSpell(isMH bool) *WarriorSpell { - procMask := core.ProcMaskMeleeSpecial - damageFunc := warrior.MHNormalizedWeaponDamage - if !isMH { - procMask = core.ProcMaskMeleeOHSpecial - damageFunc = warrior.OHNormalizedWeaponDamage - } - - return warrior.RegisterSpell(AnyStance, core.SpellConfig{ - SpellCode: core.Ternary(isMH, SpellCode_WarriorWhirlwindMH, SpellCode_WarriorWhirlwindOH), - ActionID: core.ActionID{SpellID: 1680}.WithTag(int32(core.Ternary(isMH, 1, 2))), - SpellSchool: core.SpellSchoolPhysical, - DefenseType: core.DefenseTypeMelee, - ProcMask: procMask, - Flags: core.SpellFlagMeleeMetrics | core.SpellFlagNoOnCastComplete | core.SpellFlagPassiveSpell, - CritDamageBonus: warrior.impale(), DamageMultiplier: 1, @@ -76,8 +37,15 @@ func (warrior *Warrior) newWhirlwindHitSpell(isMH bool) *WarriorSpell { BonusCoefficient: 1, ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) { - baseDamage := damageFunc(sim, spell.MeleeAttackPower()) - spell.CalcAndDealDamage(sim, target, baseDamage, spell.OutcomeMeleeWeaponSpecialHitAndCrit) + for idx := range results { + baseDamage := spell.Unit.MHNormalizedWeaponDamage(sim, spell.MeleeAttackPower()) + results[idx] = spell.CalcDamage(sim, target, baseDamage, spell.OutcomeMeleeWeaponSpecialHitAndCrit) + target = sim.Environment.NextTargetUnit(target) + } + + for _, result := range results { + spell.DealDamage(sim, result) + } }, }) -} +} \ No newline at end of file