Skip to content

Commit

Permalink
Merge pull request #7 from wowsims/hunter
Browse files Browse the repository at this point in the history
Hunter: Carve and Explosive Shot runes
  • Loading branch information
rosenrusinov authored Feb 1, 2024
2 parents 5d8f7b5 + 611a5a3 commit 5e49e19
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 119 deletions.
86 changes: 0 additions & 86 deletions sim/_hunter/explosive_shot.go

This file was deleted.

9 changes: 9 additions & 0 deletions sim/core/spell_outcome.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ func (dot *Dot) OutcomeTickPhysicalCrit(sim *Simulation, result *SpellResult, at
}
}

func (dot *Dot) OutcomeTickSnapshotCrit(sim *Simulation, result *SpellResult, attackTable *AttackTable) {
if sim.RandomFloat("Snapshot Crit Roll") < dot.SnapshotCritChance {
result.Outcome = OutcomeCrit
result.Damage *= dot.Spell.CritMultiplier
} else {
result.Outcome = OutcomeHit
}
}

func (dot *Dot) OutcomeSnapshotCrit(sim *Simulation, result *SpellResult, _ *AttackTable) {
if dot.Spell.CritMultiplier == 0 {
panic("Spell " + dot.Spell.ActionID.String() + " missing CritMultiplier")
Expand Down
96 changes: 96 additions & 0 deletions sim/hunter/carve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package hunter

import (
"time"

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

func (hunter *Hunter) registerCarveSpell() {
if !hunter.HasRune(proto.HunterRune_RuneHandsCarve) {
return
}

actionID := core.ActionID{SpellID: 425711}
numHits := hunter.Env.GetNumTargets()
results := make([]*core.SpellResult, numHits)

if hunter.AutoAttacks.IsDualWielding {
hunter.CarveOh = hunter.RegisterSpell(core.SpellConfig{
ActionID: actionID.WithTag(1),
SpellSchool: core.SpellSchoolPhysical,
ProcMask: core.ProcMaskMeleeOHSpecial,
Flags: core.SpellFlagMeleeMetrics | core.SpellFlagIncludeTargetBonusDamage | core.SpellFlagNoOnCastComplete,

DamageMultiplier: 1,
CritMultiplier: hunter.critMultiplier(true, hunter.CurrentTarget),
})
}

hunter.CarveMh = hunter.RegisterSpell(core.SpellConfig{
ActionID: core.ActionID{SpellID: 425711},
SpellSchool: core.SpellSchoolPhysical,
ProcMask: core.ProcMaskMeleeMHSpecial,
Flags: core.SpellFlagMeleeMetrics | core.SpellFlagAPL | core.SpellFlagIncludeTargetBonusDamage,

ManaCost: core.ManaCostOptions{
BaseCost: 0.04,
},
Cast: core.CastConfig{
DefaultCast: core.Cast{
GCD: core.GCDDefault,
},
IgnoreHaste: true, // Hunter GCD is locked at 1.5s
CD: core.Cooldown{
Timer: hunter.NewTimer(),
Duration: time.Second * 6,
},
},
ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool {
return hunter.DistanceFromTarget <= 5
},

DamageMultiplier: 1,
CritMultiplier: hunter.critMultiplier(true, hunter.CurrentTarget),
ThreatMultiplier: 1,

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
curTarget := target
for hitIndex := int32(0); hitIndex < numHits; hitIndex++ {
baseDamage := 0.5*spell.Unit.MHNormalizedWeaponDamage(sim, spell.MeleeAttackPower()) +
spell.BonusWeaponDamage()

results[hitIndex] = spell.CalcDamage(sim, curTarget, baseDamage, spell.OutcomeMeleeWeaponSpecialHitAndCrit)

curTarget = sim.Environment.NextTargetUnit(curTarget)
}

curTarget = target
for hitIndex := int32(0); hitIndex < numHits; hitIndex++ {
spell.DealDamage(sim, results[hitIndex])
curTarget = sim.Environment.NextTargetUnit(curTarget)
}

if hunter.CarveOh != nil {
hunter.CarveOh.Cast(sim, target)

curTarget = target
for hitIndex := int32(0); hitIndex < numHits; hitIndex++ {
baseDamage := 0.5*spell.Unit.OHNormalizedWeaponDamage(sim, spell.MeleeAttackPower()) +
spell.BonusWeaponDamage()

results[hitIndex] = hunter.CarveOh.CalcDamage(sim, curTarget, baseDamage, hunter.CarveOh.OutcomeMeleeWeaponSpecialHitAndCrit)

curTarget = sim.Environment.NextTargetUnit(curTarget)
}

curTarget = target
for hitIndex := int32(0); hitIndex < numHits; hitIndex++ {
hunter.CarveOh.DealDamage(sim, results[hitIndex])
curTarget = sim.Environment.NextTargetUnit(curTarget)
}
}
},
})
}
100 changes: 100 additions & 0 deletions sim/hunter/explosive_shot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package hunter

import (
"fmt"
"time"

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

func (hunter *Hunter) registerExplosiveShotSpell(timer *core.Timer) {
if !hunter.HasRune(proto.HunterRune_RuneHandsExplosiveShot) {
return
}

actionID := core.ActionID{SpellID: 409552}
numHits := hunter.Env.GetNumTargets()

level := float64(hunter.GetCharacter().Level)
baseCalc := (2.976264 + 0.641066*level + 0.022519*level*level)
baseLowDamage := baseCalc * 0.36
baseHighDamage := baseCalc * 0.54

manaCostMultiplier := 1 - 0.02*float64(hunter.Talents.Efficiency)
if hunter.HasRune(proto.HunterRune_RuneChestMasterMarksman) {
manaCostMultiplier -= 0.25
}
hunter.ExplosiveShot = hunter.RegisterSpell(core.SpellConfig{
ActionID: actionID,
SpellSchool: core.SpellSchoolFire,
ProcMask: core.ProcMaskRangedSpecial,
Flags: core.SpellFlagMeleeMetrics | core.SpellFlagAPL | core.SpellFlagIgnoreResists | core.SpellFlagBinary,
MissileSpeed: 24,

ManaCost: core.ManaCostOptions{
BaseCost: 0.035,
Multiplier: manaCostMultiplier,
},
Cast: core.CastConfig{
DefaultCast: core.Cast{
GCD: core.GCDDefault,
},
IgnoreHaste: true,
CD: core.Cooldown{
Timer: timer,
Duration: time.Second * 6,
},
},
ExtraCastCondition: func(sim *core.Simulation, target *core.Unit) bool {
return hunter.DistanceFromTarget >= 8
},

BonusCritRating: 0,
DamageMultiplierAdditive: 1,
DamageMultiplier: 1,
CritMultiplier: hunter.critMultiplier(true, hunter.CurrentTarget),
ThreatMultiplier: 1,

Dot: core.DotConfig{
Aura: core.Aura{
Label: fmt.Sprintf("ExplosiveShot-%d", actionID.SpellID),
},
NumberOfTicks: 2,
TickLength: time.Second * 1,
OnSnapshot: func(sim *core.Simulation, target *core.Unit, dot *core.Dot, isRollover bool) {
dot.SnapshotBaseDamage = sim.Roll(baseLowDamage, baseHighDamage) + 0.039*dot.Spell.RangedAttackPower(target)
attackTable := dot.Spell.Unit.AttackTables[target.UnitIndex]
dot.SnapshotCritChance = dot.Spell.PhysicalCritChance(attackTable)
dot.SnapshotAttackerMultiplier = dot.Spell.AttackerDamageMultiplier(attackTable)
},
OnTick: func(sim *core.Simulation, target *core.Unit, dot *core.Dot) {
dot.CalcAndDealPeriodicSnapshotDamage(sim, target, dot.OutcomeTickSnapshotCrit)
},
},

ApplyEffects: func(sim *core.Simulation, target *core.Unit, spell *core.Spell) {
baseDamage := sim.Roll(baseLowDamage, baseHighDamage) + 0.039*spell.RangedAttackPower(target)
result := spell.CalcDamage(sim, target, baseDamage, spell.OutcomeRangedHitAndCrit)

spell.WaitTravelTime(sim, func(s *core.Simulation) {
spell.DealDamage(sim, result)

if result.Landed() {
curTarget := target
for hitIndex := int32(0); hitIndex < numHits; hitIndex++ {
if curTarget != target {
baseDamage = sim.Roll(baseLowDamage, baseHighDamage) + 0.039*spell.RangedAttackPower(curTarget)
spell.CalcAndDealDamage(sim, curTarget, baseDamage, spell.OutcomeRangedCritOnly)
}

dot := spell.Dot(curTarget)
dot.Apply(sim)

curTarget = sim.Environment.NextTargetUnit(curTarget)
}
}
})
},
})
}
49 changes: 23 additions & 26 deletions sim/hunter/hunter.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,23 @@ type Hunter struct {
AspectOfTheHawk *core.Spell
AspectOfTheViper *core.Spell

AimedShot *core.Spell
ArcaneShot *core.Spell
ChimeraShot *core.Spell
ExplosiveShotR4 *core.Spell
ExplosiveShotR3 *core.Spell
ExplosiveTrap *core.Spell
KillCommand *core.Spell
KillShot *core.Spell
MultiShot *core.Spell
RapidFire *core.Spell
RaptorStrike *core.Spell
FlankingStrike *core.Spell
ScorpidSting *core.Spell
SerpentSting *core.Spell
SilencingShot *core.Spell
Volley *core.Spell
AimedShot *core.Spell
ArcaneShot *core.Spell
ChimeraShot *core.Spell
ExplosiveShot *core.Spell
ExplosiveTrap *core.Spell
KillCommand *core.Spell
KillShot *core.Spell
MultiShot *core.Spell
RapidFire *core.Spell
RaptorStrike *core.Spell
FlankingStrike *core.Spell
ScorpidSting *core.Spell
SerpentSting *core.Spell
SilencingShot *core.Spell
Volley *core.Spell
CarveMh *core.Spell
CarveOh *core.Spell

SerpentStingChimeraShot *core.Spell

Expand Down Expand Up @@ -110,23 +111,19 @@ func (hunter *Hunter) Initialize() {

multiShotTimer := hunter.NewTimer()
arcaneShotTimer := hunter.NewTimer()
//fireTrapTimer := hunter.NewTimer()

hunter.registerSerpentStingSpell()

hunter.registerArcaneShotSpell(arcaneShotTimer)
hunter.registerExplosiveShotSpell(arcaneShotTimer)

hunter.registerMultiShotSpell(multiShotTimer)
// hunter.registerAimedShotSpell(arcaneShotTimer)

hunter.registerChimeraShotSpell()
// hunter.registerBlackArrowSpell(fireTrapTimer)
// hunter.registerExplosiveShotSpell(arcaneShotTimer)
// hunter.registerExplosiveTrapSpell(fireTrapTimer)
// hunter.registerKillShotSpell()

hunter.registerRaptorStrikeSpell()
hunter.registerFlankingStrikeSpell()
// hunter.registerScorpidStingSpell()
// hunter.registerSilencingShotSpell()
// hunter.registerSteadyShotSpell()
// hunter.registerVolleySpell()
hunter.registerCarveSpell()

hunter.registerKillCommand()
//hunter.registerRapidFireCD()
Expand Down
Loading

0 comments on commit 5e49e19

Please sign in to comment.