Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: wowsims/cata
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.0.186
Choose a base ref
...
head repository: wowsims/cata
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Jan 12, 2025

  1. Add Warlock P4 WIP set

    1337LutZ committed Jan 12, 2025
    Copy the full SHA
    f08bc63 View commit details

Commits on Jan 27, 2025

  1. [Rogue] Improve default Subtlety APL and add Mastery Pre-Pull APL; Re…

    …factor Subtlety Mastery into a SpellMod
    TheBackstabi committed Jan 27, 2025
    Copy the full SHA
    7315fb9 View commit details
  2. Copy the full SHA
    297128e View commit details
  3. Remove rotational blocker on Berserk when a Stampede proc is active.

    This sims as a wash for pre-P4 setups but has a substantial impact on
    4pT13 setups:
    
    Pre-Raid, Mono-Cat: +1.3 DPS
    P3, Mono-Cat: -0.1 DPS
    P4, Mono-Cat: +582 DPS
    P4, Hybrid: +471 DPS
    P3, Hybrid: -1.6 DPS
    Pre-Raid, Hybrid: +0.5 DPS
    
     On branch feral
     Changes to be committed:
    	modified:   sim/druid/feral/TestFeral.results
    	modified:   sim/druid/feral/rotation.go
    NerdEgghead committed Jan 27, 2025
    Copy the full SHA
    da9ca82 View commit details

Commits on Jan 28, 2025

  1. Delay TF usage in 4pT13 setups if a Stampede proc is already active.

    Minor impact but included for thoroughness:
    
    P4, Mono-Cat: +2.6 DPS
    P4, Hybrid: +2.3 DPS
    
     On branch feral
     Changes to be committed:
    	modified:   sim/druid/feral/TestFeral.results
    	modified:   sim/druid/feral/rotation.go
    NerdEgghead committed Jan 28, 2025
    Copy the full SHA
    2438856 View commit details
  2. Merge pull request #1325 from wowsims/feral

    Feral 4pT13 Rotation Adjustments
    NerdEgghead authored Jan 28, 2025
    Copy the full SHA
    bb461e5 View commit details
  3. Merge updated test

    TheBackstabi committed Jan 28, 2025
    Copy the full SHA
    41020a1 View commit details
  4. Copy the full SHA
    aaa68d0 View commit details
  5. Copy the full SHA
    bf29bce View commit details
  6. [Unholy] Update APL

    hillerstorm committed Jan 28, 2025
    Copy the full SHA
    9df398d View commit details
  7. Merge pull request #1327 from hillerstorm/uh_apl_update

    [Unholy] Update APL
    hillerstorm authored Jan 28, 2025
    Copy the full SHA
    fc70a5e View commit details

Commits on Jan 29, 2025

  1. Copy the full SHA
    d222161 View commit details
  2. Merge pull request #1326 from TheBackstabi/master

    [Rogue] T13 Updates for EPs and Vial of Shadows
    TheBackstabi authored Jan 29, 2025
    Copy the full SHA
    bf50fb4 View commit details
  3. Implement findings

    1337LutZ committed Jan 29, 2025
    Copy the full SHA
    41b2235 View commit details
  4. Merge pull request #1329 from wowsims/fix/dtr-aoe-proc-per-cast

    Implement findings
    1337LutZ authored Jan 29, 2025
    Copy the full SHA
    72afc4a View commit details
  5. Refactored equip scaling code for higher performance and item swap

    compatibility.
    
     On branch feral
     Changes to be committed:
    	modified:   sim/core/character.go
    	modified:   sim/core/item_swaps.go
    	modified:   sim/core/stats/stats.go
    	modified:   sim/druid/forms.go
    	modified:   sim/druid/talents.go
    NerdEgghead committed Jan 29, 2025
    Copy the full SHA
    e7f9ae8 View commit details
  6. Copy the full SHA
    3e21d6e View commit details
  7. Merge pull request #1330 from wowsims/feral

    Refactor equip scaling code for higher performance and item swap compatibility
    NerdEgghead authored Jan 29, 2025
    Copy the full SHA
    306b6b4 View commit details

Commits on Jan 30, 2025

  1. Remove cancelled PendingActions from the queue so that they can be

    safely re-used without re-allocating a new one.
    
     On branch rotation_timing
     Changes to be committed:
    	modified:   sim/core/gcd.go
    	modified:   sim/core/pending_action.go
    NerdEgghead committed Jan 30, 2025
    Copy the full SHA
    e4d4e39 View commit details
  2. Attempted Killing Spree adjustment for compatibility with rotation timer

    re-use.
    
     On branch rotation_timing
     Changes to be committed:
    	modified:   sim/rogue/combat/killing_spree.go
    NerdEgghead committed Jan 30, 2025
    Copy the full SHA
    6c901ad View commit details
  3. Update Combat tests after GCD timer change

     On branch rotation_timing
     Changes to be committed:
    	modified:   sim/rogue/combat/TestCombat.results
    NerdEgghead committed Jan 30, 2025
    Copy the full SHA
    db38291 View commit details
  4. Add devserver input for memory profiling to supplement the existing CPU

    profiling option.
    
     On branch rotation_timing
     Changes to be committed:
    	modified:   sim/web/main.go
    NerdEgghead committed Jan 30, 2025
    Copy the full SHA
    e6c537c View commit details
  5. Pre-allocate Feral PoolingActions structs for performance

     On branch rotation_timing
     Changes to be committed:
    	modified:   sim/druid/feral/apl_values.go
    	modified:   sim/druid/feral/feral.go
    	modified:   sim/druid/feral/pooling_actions.go
    	modified:   sim/druid/feral/rotation.go
    NerdEgghead committed Jan 30, 2025
    Copy the full SHA
    eeeb689 View commit details
  6. Merge pull request #1331 from wowsims/rotation_timing

    Re-use rotation action instead of re-allocating
    NerdEgghead authored Jan 30, 2025
    Copy the full SHA
    df7d538 View commit details
  7. Copy the full SHA
    d12b875 View commit details
  8. Add spell masks

    1337LutZ committed Jan 30, 2025
    Copy the full SHA
    0149db5 View commit details
  9. Update tests

    1337LutZ committed Jan 30, 2025
    Copy the full SHA
    0e7b516 View commit details

Commits on Jan 31, 2025

  1. Exposed APL list representation of default rotation to the UI

     On branch feral
     Changes to be committed:
    	modified:   ui/druid/feral/presets.ts
    	modified:   ui/druid/feral/sim.ts
    NerdEgghead committed Jan 31, 2025
    Copy the full SHA
    42cc1bd View commit details
  2. Set default Savage Roar aura duration on initilization to the maximum

    5-CP value to enable pre-pull activations in custom APLs.
    
     On branch feral
     Changes to be committed:
    	modified:   sim/druid/savage_roar.go
    NerdEgghead committed Jan 31, 2025
    Copy the full SHA
    779c8e6 View commit details
  3. Added preset build example for simulating single target burst profiles

    such as Spine of Deathwing tendons.
    
     On branch feral
     Changes to be committed:
    	new file:   ui/druid/feral/apls/tendon.apl.json
    	modified:   ui/druid/feral/presets.ts
    	modified:   ui/druid/feral/sim.ts
    NerdEgghead committed Jan 31, 2025
    Copy the full SHA
    9135b4b View commit details
  4. Merge pull request #1333 from wowsims/feral

    [Feral] Preset build for single target burst profile
    NerdEgghead authored Jan 31, 2025
    Copy the full SHA
    133f86b View commit details
  5. Copy the full SHA
    f4e8c2a View commit details
  6. Add back updateCastSpeed

    1337LutZ committed Jan 31, 2025
    Copy the full SHA
    fb62d27 View commit details
  7. Copy the full SHA
    70f94a7 View commit details
  8. Update tests

    1337LutZ committed Jan 31, 2025
    Copy the full SHA
    b8e2663 View commit details
  9. Copy the full SHA
    53d86cf View commit details
  10. Update Warlock P4 WIP sets

    1337LutZ committed Jan 31, 2025
    Copy the full SHA
    724b8dd View commit details
  11. Copy the full SHA
    4d7f2c8 View commit details
  12. Merge pull request #1334 from wowsims/feature/warlock-p4

    [WARLOCK] P4 WIP
    1337LutZ authored Jan 31, 2025
    Copy the full SHA
    194d040 View commit details

Commits on Feb 1, 2025

  1. Merge pull request #1332 from wowsims/fix/optimise-combust-calc

    [MAGE] Optimize combustion APL performance
    1337LutZ authored Feb 1, 2025
    Copy the full SHA
    ee92f54 View commit details
  2. Copy the full SHA
    e92734c View commit details
  3. Merge pull request #1336 from wowsims/fix/rogue-fixes

    Fix mastery logic and slayers gear
    1337LutZ authored Feb 1, 2025
    Copy the full SHA
    204c0c4 View commit details

Commits on Feb 2, 2025

  1. Fix item notices

    1337LutZ committed Feb 2, 2025
    Copy the full SHA
    a5f6b61 View commit details
  2. WIP - Add P4

    1337LutZ committed Feb 2, 2025
    Copy the full SHA
    8598ec9 View commit details
  3. Copy the full SHA
    0f9b5a8 View commit details
  4. Merge pull request #1338 from wowsims/feature/mage-p4

    [MAGE] P4 - WIP sets
    1337LutZ authored Feb 2, 2025
    Copy the full SHA
    05d30ff View commit details
  5. Apply correct proc masks

    1337LutZ committed Feb 2, 2025
    Copy the full SHA
    03ef813 View commit details
  6. Merge pull request #1339 from wowsims/fix/trinket-proc-mask

    [TRINKETS] Apply correct proc masks
    1337LutZ authored Feb 2, 2025
    Copy the full SHA
    7545404 View commit details
Showing with 3,874 additions and 5,080 deletions.
  1. +41 −22 sim/common/cata/dragonwrath.go
  2. +2 −0 sim/common/cata/other_effects.go
  3. +8 −8 sim/common/cata/stat_bonus_procs.go
  4. +48 −40 sim/core/character.go
  5. +4 −10 sim/core/gcd.go
  6. +1 −1 sim/core/item_swaps.go
  7. +5 −0 sim/core/pending_action.go
  8. +3 −3 sim/core/spell_mod.go
  9. +8 −0 sim/core/stats/stats.go
  10. +9 −9 sim/death_knight/blood/TestBlood.results
  11. +9 −9 sim/death_knight/frost/TestFrost.results
  12. +857 −857 sim/death_knight/unholy/TestUnholy.results
  13. +40 −40 sim/druid/balance/TestBalance.results
  14. +1 −1 sim/druid/balance/dragonwrath.go
  15. +166 −166 sim/druid/feral/TestFeral.results
  16. +7 −1 sim/druid/feral/apl_values.go
  17. +2 −0 sim/druid/feral/feral.go
  18. +4 −0 sim/druid/feral/pooling_actions.go
  19. +19 −22 sim/druid/feral/rotation.go
  20. +3 −3 sim/druid/forms.go
  21. +7 −7 sim/druid/guardian/TestGuardian.results
  22. +1 −1 sim/druid/items.go
  23. +1 −1 sim/druid/savage_roar.go
  24. +7 −7 sim/druid/talents.go
  25. +6 −6 sim/hunter/beast_mastery/TestBM.results
  26. +18 −18 sim/hunter/marksmanship/TestMM.results
  27. +6 −6 sim/hunter/survival/TestSV.results
  28. +8 −17 sim/mage/apl_values.go
  29. +6 −6 sim/mage/arcane/TestArcane.results
  30. +35 −0 sim/mage/combustion.go
  31. +30 −30 sim/mage/fire/TestFire.results
  32. +1 −1 sim/mage/mage.go
  33. +8 −3 sim/mage/talents_fire.go
  34. +18 −18 sim/paladin/protection/TestProtection.results
  35. +18 −18 sim/paladin/retribution/TestRetribution.results
  36. +2 −1 sim/paladin/talents_holy.go
  37. +0 −1 sim/paladin/talents_retribution.go
  38. +16 −16 sim/priest/shadow/TestShadow.results
  39. +104 −104 sim/rogue/assassination/TestAssassination.results
  40. +7 −32 sim/rogue/assassination/assassination.go
  41. +9 −5 sim/rogue/assassination/venomous_wounds.go
  42. +896 −896 sim/rogue/combat/TestCombat.results
  43. +1 −2 sim/rogue/combat/killing_spree.go
  44. +1 −1 sim/rogue/items.go
  45. +879 −879 sim/rogue/subtlety/TestSubtlety.results
  46. +11 −15 sim/rogue/subtlety/shadowstep.go
  47. +19 −12 sim/rogue/subtlety/subtlety.go
  48. +32 −32 sim/shaman/elemental/TestElemental.results
  49. +3 −2 sim/shaman/elemental/dragonwrath.go
  50. +6 −6 sim/shaman/enhancement/TestEnhancement.results
  51. +16 −16 sim/warlock/affliction/TestAffliction.results
  52. +24 −24 sim/warlock/demonology/TestDemonology.results
  53. +16 −16 sim/warlock/destruction/TestDestruction.results
  54. +6 −6 sim/warrior/arms/TestArms.results
  55. +6 −6 sim/warrior/protection/TestProtectionWarrior.results
  56. +16 −1 sim/web/main.go
  57. +5 −6 ui/core/constants/item_notices.tsx
  58. +47 −1,607 ui/death_knight/unholy/apls/default.apl.json
  59. +19 −0 ui/druid/feral/apls/tendon.apl.json
  60. +19 −1 ui/druid/feral/presets.ts
  61. +2 −1 ui/druid/feral/sim.ts
  62. +21 −0 ui/mage/fire/gear_sets/p4_fire.gear.json
  63. +31 −0 ui/mage/fire/presets.ts
  64. +4 −2 ui/mage/fire/sim.tsx
  65. +2 −2 ui/rogue/assassination/gear_sets/p4_assassination.gear.json
  66. +22 −0 ui/rogue/assassination/presets.ts
  67. +13 −2 ui/rogue/assassination/sim.ts
  68. +1 −1 ui/rogue/combat/gear_sets/p4_combat.gear.json
  69. +6 −6 ui/rogue/combat/presets.ts
  70. +23 −12 ui/rogue/combat/sim.ts
  71. +5 −4 ui/rogue/subtlety/apls/subtlety.apl.json
  72. +36 −0 ui/rogue/subtlety/apls/subtlety_mastery_prepull.apl.json
  73. +2 −2 ui/rogue/subtlety/gear_sets/p4_subtlety.gear.json
  74. +2 −0 ui/rogue/subtlety/presets.ts
  75. +1 −1 ui/rogue/subtlety/sim.ts
  76. +19 −15 ui/warlock/demonology/apls/incinerate.apl.json
  77. +15 −11 ui/warlock/demonology/apls/shadow-bolt.apl.json
  78. +21 −0 ui/warlock/demonology/gear_sets/p4.gear.json
  79. +74 −0 ui/warlock/demonology/gear_sets/p4_item_swap.gear.json
  80. +5 −1 ui/warlock/demonology/presets.ts
  81. +2 −2 ui/warlock/demonology/sim.ts
63 changes: 41 additions & 22 deletions sim/common/cata/dragonwrath.go
Original file line number Diff line number Diff line change
@@ -197,9 +197,10 @@ func init() {
character := a.GetCharacter()
unit := &character.Unit
registerSpells(unit)
var config *DragonwrathClassConfig

unit.OnSpellRegistered(func(spell *core.Spell) {
if val, ok := classConfig[a.GetCharacter().Spec]; ok {
if val, ok := classConfig[character.Spec]; ok {
for id, c := range val.spellConfig {
if c.spellCopyHandler != nil && id == spell.SpellID && spell.ActionID.Tag < 71086 {
c.spellCopyHandler(unit, spell)
@@ -214,6 +215,17 @@ func init() {
aura := core.MakePermanent(unit.RegisterAura(core.Aura{
ActionID: core.ActionID{ItemID: 71086},
Label: "Dragonwrath, Tarecgosa's Rest - Handler",
OnInit: func(aura *core.Aura, sim *core.Simulation) {
config = classConfig[character.Spec]
if config == nil {

// Create an empty config for this spell
config = CreateDTRClassConfig(character.Spec, 0.0)
classConfig[character.Spec] = config

log.Printf("Using DTR for spec %s which is not implemented. Using default config", character.Spec)
}
},
OnReset: func(aura *core.Aura, sim *core.Simulation) {
lastTimestamp = time.Duration(0)
spellList = map[*core.Spell]bool{}
@@ -240,23 +252,19 @@ func init() {
return
}

config := classConfig[a.GetCharacter().Spec]
if config == nil {

// Create an empty config for this spell
config = CreateDTRClassConfig(a.GetCharacter().Spec, 0.0)
classConfig[a.GetCharacter().Spec] = config

log.Printf("Using DTR for spec %s which is not implemented. Using default config", a.GetCharacter().Spec)
}

// for now make it generic, might change this later
// this rule should disable impact replication of related dot spells
if spell.ActionID.Tag > 0 && spell.CurDot() != nil {
return
}

if lastTimestamp != sim.CurrentTime {
lastTimestamp = sim.CurrentTime
spellList = map[*core.Spell]bool{}
}

procChance := config.procChance
isAoESpell := false
if val, ok := config.spellConfig[spell.SpellID]; ok {
if val.supress&supressImpact > 0 {
return
@@ -267,6 +275,10 @@ func init() {
return
}

if _, ok := spellList[spell]; ok {
return
}

// make sure the same spell impact can only trigger once per timestamp (AoE Impact spells like Arcane Explosion or Mind Sear)
if val.procPerCast {
if lastTimestamp != sim.CurrentTime {
@@ -284,6 +296,7 @@ func init() {

// reduce proc chance for AoE Spells
if val.isAoESpell {
isAoESpell = true
procChance *= 2.0 / 9.0
}
}
@@ -292,24 +305,30 @@ func init() {
return
}

// AoE spells can only proc once per round
if isAoESpell {
// spell has not been checked yet, add it
spellList[spell] = true
}

config.getImpactHandler(spell.SpellID)(sim, spell, result)
},

OnCastComplete: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell) {
if val, ok := config.spellConfig[spell.SpellID]; ok {
if val.isAoESpell {
spellList = map[*core.Spell]bool{}
return
}
}
if _, ok := spellList[spell]; ok {
spellList[spell] = false
}
},
OnPeriodicDamageDealt: func(aura *core.Aura, sim *core.Simulation, spell *core.Spell, result *core.SpellResult) {
if !result.Landed() {
return
}

config := classConfig[a.GetCharacter().Spec]
if config == nil {

// Create an empty config for this spell
config = CreateDTRClassConfig(a.GetCharacter().Spec, 0.0)
classConfig[a.GetCharacter().Spec] = config

log.Printf("Using DTR for spec %s which is not implemented. Using default config", a.GetCharacter().Spec)
}

if val, ok := config.spellConfig[spell.SpellID]; ok {
if val.supress&supressDoT > 0 {
return
2 changes: 2 additions & 0 deletions sim/common/cata/other_effects.go
Original file line number Diff line number Diff line change
@@ -1415,6 +1415,7 @@ type IgniteDamageCalculator func(result *core.SpellResult) float64

type IgniteConfig struct {
ActionID core.ActionID
ClassSpellMask int64
DisableCastMetrics bool
DotAuraLabel string
DotAuraTag string
@@ -1435,6 +1436,7 @@ func RegisterIgniteEffect(unit *core.Unit, config IgniteConfig) *core.Spell {
ActionID: config.ActionID,
SpellSchool: core.SpellSchoolFire,
ProcMask: core.ProcMaskSpellProc,
ClassSpellMask: config.ClassSpellMask,
Flags: spellFlags,
DamageMultiplier: 1,
ThreatMultiplier: 1,
16 changes: 8 additions & 8 deletions sim/common/cata/stat_bonus_procs.go
Original file line number Diff line number Diff line change
@@ -492,7 +492,7 @@ func init() {
Bonus: stats.Stats{stats.SpellPower: 912},
Duration: time.Second * 20,
Callback: core.CallbackOnSpellHitDealt | core.CallbackOnPeriodicDamageDealt | core.CallbackOnHealDealt | core.CallbackOnPeriodicHealDealt,
ProcMask: core.ProcMaskSpellHealing | core.ProcMaskSpellDamage,
ProcMask: core.ProcMaskDirect | core.ProcMaskSpellHealing,
Outcome: core.OutcomeHit,
ProcChance: 0.25,
ICD: time.Second * 55,
@@ -849,7 +849,7 @@ func init() {
AuraID: 85027,
Bonus: stats.Stats{stats.SpellPower: 963},
Duration: time.Second * 20,
Callback: core.CallbackOnSpellHitDealt | core.CallbackOnHealDealt,
Callback: core.CallbackOnSpellHitDealt | core.CallbackOnPeriodicDamageDealt | core.CallbackOnHealDealt | core.CallbackOnPeriodicHealDealt,
ProcMask: core.ProcMaskDirect | core.ProcMaskSpellHealing | core.ProcMaskProc,
Outcome: core.OutcomeLanded,
ProcChance: 0.25,
@@ -862,7 +862,7 @@ func init() {
AuraID: 99719,
Bonus: stats.Stats{stats.SpellPower: 1077},
Duration: time.Second * 20,
Callback: core.CallbackOnSpellHitDealt | core.CallbackOnHealDealt,
Callback: core.CallbackOnSpellHitDealt | core.CallbackOnPeriodicDamageDealt | core.CallbackOnHealDealt | core.CallbackOnPeriodicHealDealt,
ProcMask: core.ProcMaskDirect | core.ProcMaskSpellHealing | core.ProcMaskProc,
Outcome: core.OutcomeLanded,
ProcChance: 0.25,
@@ -875,7 +875,7 @@ func init() {
AuraID: 99742,
Bonus: stats.Stats{stats.SpellPower: 1218},
Duration: time.Second * 20,
Callback: core.CallbackOnSpellHitDealt | core.CallbackOnHealDealt,
Callback: core.CallbackOnSpellHitDealt | core.CallbackOnPeriodicDamageDealt | core.CallbackOnHealDealt | core.CallbackOnPeriodicHealDealt,
ProcMask: core.ProcMaskDirect | core.ProcMaskSpellHealing | core.ProcMaskProc,
Outcome: core.OutcomeLanded,
ProcChance: 0.25,
@@ -888,7 +888,7 @@ func init() {
AuraID: 102435,
Bonus: stats.Stats{stats.SpellPower: 1287},
Duration: time.Second * 20,
Callback: core.CallbackOnSpellHitDealt | core.CallbackOnHealDealt,
Callback: core.CallbackOnSpellHitDealt | core.CallbackOnPeriodicDamageDealt | core.CallbackOnHealDealt | core.CallbackOnPeriodicHealDealt,
ProcMask: core.ProcMaskDirect | core.ProcMaskSpellHealing | core.ProcMaskProc,
Outcome: core.OutcomeLanded,
ProcChance: 0.25,
@@ -901,7 +901,7 @@ func init() {
AuraID: 105137,
Bonus: stats.Stats{stats.SpellPower: 1452},
Duration: time.Second * 20,
Callback: core.CallbackOnSpellHitDealt | core.CallbackOnHealDealt,
Callback: core.CallbackOnSpellHitDealt | core.CallbackOnPeriodicDamageDealt | core.CallbackOnHealDealt | core.CallbackOnPeriodicHealDealt,
ProcMask: core.ProcMaskDirect | core.ProcMaskSpellHealing | core.ProcMaskProc,
Outcome: core.OutcomeLanded,
ProcChance: 0.25,
@@ -1138,8 +1138,8 @@ func init() {
AuraID: []int32{109787, 107982, 109789}[version],
Bonus: stats.Stats{stats.HasteRating: []float64{2573, 2904, 3278}[version]},
Duration: time.Second * 20,
Callback: core.CallbackOnSpellHitDealt,
ProcMask: core.ProcMaskDirect | core.ProcMaskMeleeProc,
Callback: core.CallbackOnSpellHitDealt | core.CallbackOnPeriodicDamageDealt,
ProcMask: core.ProcMaskDirect | core.ProcMaskSpellProc,
Outcome: core.OutcomeLanded,
ProcChance: 0.15,
ICD: time.Second * 115,
88 changes: 48 additions & 40 deletions sim/core/character.go
Original file line number Diff line number Diff line change
@@ -56,9 +56,6 @@ type Character struct {
// Base stats for this Character.
baseStats stats.Stats

// Handles scaling that only affects stats from items
itemStatMultipliers stats.Stats

// Bonus stats for this Character, specified in the UI and/or EP
// calculator
bonusStats stats.Stats
@@ -73,9 +70,8 @@ type Character struct {
glyphs [9]int32
PrimaryTalentTree uint8

// Used to track if we need to separately apply multipliers, because
// equipment was already applied
equipStatsApplied bool
// Used for effects like "Increased Armor Value from Items"
*EquipScalingManager

// Provides major cooldown management behavior.
majorCooldownManager
@@ -163,9 +159,6 @@ func NewCharacter(party *Party, partyIndex int, player *proto.Player) Character

character.AddStats(character.baseStats)
character.addUniversalStatDependencies()
for i := range character.itemStatMultipliers {
character.itemStatMultipliers[i] = 1
}

if player.BonusStats != nil {
if player.BonusStats.Stats != nil {
@@ -193,66 +186,81 @@ func NewCharacter(party *Party, partyIndex int, player *proto.Player) Character
character.enableItemSwap(player.ItemSwap, character.DefaultMeleeCritMultiplier(), character.DefaultMeleeCritMultiplier(), 0)
}

character.EquipScalingManager = character.NewEquipScalingManager()

return character
}

func (character *Character) applyEquipScaling(stat stats.Stat, multiplier float64) float64 {
var oldValue = character.EquipStats()[stat]
character.itemStatMultipliers[stat] *= multiplier
var newValue = character.EquipStats()[stat]
return newValue - oldValue
type EquipScalingManager struct {
itemStatMultipliers map[stats.Stat]float64
cachedEquipStats stats.Stats
equipStatsApplied bool
equipCacheValid bool
}

func (character *Character) NewEquipScalingManager() *EquipScalingManager {
return &EquipScalingManager{
itemStatMultipliers: make(map[stats.Stat]float64),
cachedEquipStats: character.Equipment.Stats().Add(character.bonusStats),
equipCacheValid: true,
}
}

func (character *Character) AddDynamicEquipStats(sim *Simulation, equipStats stats.Stats) {
character.AddStatsDynamic(sim, equipStats.ApplyMultipliers(character.itemStatMultipliers))
character.equipCacheValid = false
}

func (character *Character) applyEquipScalingInternal(stat stats.Stat, multiplier float64) float64 {
character.updateCachedEquipStats()
oldMultiplier, exists := character.itemStatMultipliers[stat]

if !exists {
oldMultiplier = 1.0
}

newMultiplier := oldMultiplier * multiplier
character.itemStatMultipliers[stat] = newMultiplier

return character.cachedEquipStats[stat] * (newMultiplier - oldMultiplier)
}

func (character *Character) ApplyEquipScaling(stat stats.Stat, multiplier float64) {
var statDiff stats.Stats
statDiff[stat] = character.applyEquipScaling(stat, multiplier)
statDiff := character.applyEquipScalingInternal(stat, multiplier)
// Equipment stats already applied, so need to manually at the bonus to
// the character now to ensure correct values
if character.equipStatsApplied {
character.AddStats(statDiff)
character.AddStat(stat, statDiff)
}
}

func (character *Character) ApplyDynamicEquipScaling(sim *Simulation, stat stats.Stat, multiplier float64) {
statDiff := character.applyEquipScaling(stat, multiplier)
character.AddStatDynamic(sim, stat, statDiff)
}

func (character *Character) ApplyBuildPhaseEquipScaling(sim *Simulation, stat stats.Stat, multiplier float64) {
if character.Env.MeasuringStats && (character.Env.State != Finalized) {
character.ApplyEquipScaling(stat, multiplier)
} else {
character.ApplyDynamicEquipScaling(sim, stat, multiplier)
statDiff := character.applyEquipScalingInternal(stat, multiplier)
character.AddStatDynamic(sim, stat, statDiff)
}
}

func (character *Character) RemoveEquipScaling(stat stats.Stat, multiplier float64) {
var statDiff stats.Stats
statDiff[stat] = character.applyEquipScaling(stat, 1/multiplier)
// Equipment stats already applied, so need to manually at the bonus to
// the character now to ensure correct values
if character.equipStatsApplied {
character.AddStats(statDiff)
}
character.ApplyEquipScaling(stat, 1/multiplier)
}

func (character *Character) RemoveDynamicEquipScaling(sim *Simulation, stat stats.Stat, multiplier float64) {
statDiff := character.applyEquipScaling(stat, 1/multiplier)
character.AddStatDynamic(sim, stat, statDiff)
character.ApplyDynamicEquipScaling(sim, stat, 1/multiplier)
}

func (character *Character) RemoveBuildPhaseEquipScaling(sim *Simulation, stat stats.Stat, multiplier float64) {
if character.Env.MeasuringStats && (character.Env.State != Finalized) {
character.RemoveEquipScaling(stat, multiplier)
} else {
character.RemoveDynamicEquipScaling(sim, stat, multiplier)
func (character *Character) updateCachedEquipStats() {
if !character.equipCacheValid {
character.cachedEquipStats = character.Equipment.Stats().Add(character.bonusStats)
character.equipCacheValid = true
}
}

func (character *Character) EquipStats() stats.Stats {
var baseEquipStats = character.Equipment.Stats()
var bonusEquipStats = baseEquipStats.Add(character.bonusStats)
return bonusEquipStats.DotProduct(character.itemStatMultipliers)
character.updateCachedEquipStats()
return character.cachedEquipStats.ApplyMultipliers(character.itemStatMultipliers)
}

func (character *Character) applyEquipment() {
14 changes: 4 additions & 10 deletions sim/core/gcd.go
Original file line number Diff line number Diff line change
@@ -57,18 +57,12 @@ func (unit *Unit) SetRotationTimer(sim *Simulation, rotationReadyAt time.Duratio

unit.RotationTimer.Set(rotationReadyAt)

if unit.rotationAction.consumed {
unit.rotationAction.cancelled = false
unit.rotationAction.NextActionAt = rotationReadyAt
} else {
if !unit.rotationAction.consumed {
unit.rotationAction.Cancel(sim)
oldAction := unit.rotationAction.OnAction
unit.rotationAction = &PendingAction{
NextActionAt: rotationReadyAt,
Priority: ActionPriorityGCD,
OnAction: oldAction,
}
}

unit.rotationAction.cancelled = false
unit.rotationAction.NextActionAt = rotationReadyAt
sim.AddPendingAction(unit.rotationAction)
}

2 changes: 1 addition & 1 deletion sim/core/item_swaps.go
Original file line number Diff line number Diff line change
@@ -340,7 +340,7 @@ func (swap *ItemSwap) SwapItems(sim *Simulation, swapSet proto.APLActionItemSwap
if sim.Log != nil {
sim.Log("Item Swap - Stats Change: %v", statsToSwap.FlatString())
}
character.AddStatsDynamic(sim, statsToSwap)
character.AddDynamicEquipStats(sim, statsToSwap)

if !isPrepull && !isReset && weaponSlotSwapped {
character.AutoAttacks.StopMeleeUntil(sim, sim.CurrentTime, false)
5 changes: 5 additions & 0 deletions sim/core/pending_action.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package core

import (
"slices"
"time"
)

@@ -48,4 +49,8 @@ func (pa *PendingAction) Cancel(sim *Simulation) {
}

pa.cancelled = true

if i := slices.Index(sim.pendingActions, pa); i != -1 {
sim.pendingActions = append(sim.pendingActions[:i], sim.pendingActions[i+1:]...)
}
}
Loading