Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions sim/core/aura.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type OnReset func(aura *Aura, sim *Simulation)
type OnDoneIteration func(aura *Aura, sim *Simulation)
type OnGain func(aura *Aura, sim *Simulation)
type OnExpire func(aura *Aura, sim *Simulation)
type OnRestore func(aura *Aura, sim *Simulation, state AuraState)
type OnStacksChange func(aura *Aura, sim *Simulation, oldStacks int32, newStacks int32)
type OnEncounterStart func(aura *Aura, sim *Simulation)

Expand Down Expand Up @@ -87,6 +88,7 @@ type Aura struct {
OnDoneIteration OnDoneIteration
OnGain OnGain
OnExpire OnExpire
OnRestore OnRestore
OnStacksChange OnStacksChange // Invoked when the number of stacks of this aura changes.

OnApplyEffects OnApplyEffects // Invoked when a spell cast is completing, before apply effects are called
Expand Down Expand Up @@ -647,6 +649,10 @@ restart:
// Adds a new aura to the simulation. If an aura with the same ID already
// exists it will be replaced with the new one.
func (aura *Aura) Activate(sim *Simulation) {
aura.activate(sim, true)
}

func (aura *Aura) activate(sim *Simulation, triggerOnGain bool) {
if aura == nil {
return
}
Expand Down Expand Up @@ -748,7 +754,7 @@ func (aura *Aura) Activate(sim *Simulation) {
}

// don't invoke possible callbacks until the internal state is consistent
if aura.OnGain != nil {
if triggerOnGain && aura.OnGain != nil {
aura.OnGain(aura, sim)
}
}
Expand Down Expand Up @@ -1148,8 +1154,21 @@ func (aura *Aura) SaveState(sim *Simulation) AuraState {
}

func (aura *Aura) RestoreState(state AuraState, sim *Simulation) {
if !aura.active {
aura.Activate(sim)
// If the aura has an OnRestore callback, we need special handling to properly
// restart any periodic actions without causing issues like double-stacking.
if aura.OnRestore != nil {
// Deactivate first to cancel any existing periodic actions
if aura.active {
aura.Deactivate(sim)
}
// Activate without triggering OnGain's immediate effects
aura.activate(sim, false)
// Then call the restore callback to restart periodic actions properly
aura.OnRestore(aura, sim, state)
} else {
if !aura.active {
aura.Activate(sim)
}
}

aura.UpdateExpires(state.RemainingDuration + sim.CurrentTime)
Expand Down
52 changes: 37 additions & 15 deletions sim/core/aura_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,25 @@ func (character *Character) NewTemporaryStatBuffWithStacks(config TemporaryStatB
})

if config.TimePerStack > 0 {
var pa *PendingAction

startStackingAction := func(sim *Simulation, tickImmediately bool, numTicks int) {
pa = StartPeriodicAction(sim, PeriodicActionOptions{
Period: config.TimePerStack,
NumTicks: numTicks,
TickImmediately: tickImmediately,
OnAction: func(sim *Simulation) {
if stackingAura.IsActive() {
if config.DecrementStacks {
stackingAura.RemoveStack(sim)
} else {
stackingAura.AddStack(sim)
}
}
},
})
}

aura := character.RegisterAura(Aura{
Label: config.AuraLabel,
ActionID: config.ActionID,
Expand All @@ -352,21 +371,24 @@ func (character *Character) NewTemporaryStatBuffWithStacks(config TemporaryStatB
stackingAura.SetStacks(sim, config.MaxStacks)
}

StartPeriodicAction(sim, PeriodicActionOptions{
Period: config.TimePerStack,
NumTicks: int(config.MaxStacks),
TickImmediately: config.TickImmediately,
OnAction: func(sim *Simulation) {
// Aura might not be active because of stuff like mage alter time being cast right before this aura being activated
if stackingAura.IsActive() {
if config.DecrementStacks {
stackingAura.RemoveStack(sim)
} else {
stackingAura.AddStack(sim)
}
}
},
})
startStackingAction(sim, config.TickImmediately, int(config.MaxStacks))
},
OnExpire: func(aura *Aura, sim *Simulation) {
if pa != nil {
pa.Cancel(sim)
pa = nil
}
},
OnRestore: func(aura *Aura, sim *Simulation, state AuraState) {
// When restoring (e.g., via Alter Time), we need to restart the periodic action
// but without TickImmediately to avoid adding an extra stack.
// Note: We don't activate the stacking aura here because it will be restored
// separately by Alter Time's restoration loop with the correct duration.

remainingTicks := int(config.MaxStacks - state.Stacks)
if remainingTicks > 0 {
startStackingAction(sim, false, remainingTicks)
}
},
})
return stackingAura, aura
Expand Down
Loading