Skip to main content

State Registrar

The State Registrar provides a robust system for tracking and managing state in your Aurora modules. It's particularly useful for tracking time-windowed events and maintaining state access.

Core Implementation

The state registrar consists of two main components:

  • StateExpression: The core class for managing individual states
  • StateManager: A global manager for all state expressions

Creating a State Expression

local myState = Aurora.StateExpression:New("my_state", 0)

The New method accepts:

  • name: Unique identifier for the state
  • default: Initial value (defaults to 0)

State Expression Properties

Each state expression contains:

{
name = "string", -- Unique identifier
value = any, -- Current state value
lastUpdate = number, -- Last update timestamp
window = number -- Time window in seconds (default: 5)
}

Core Methods

-- Register an event handler
myState:RegisterHandler("EVENT_NAME", function(self, now, ...)
-- Handle the event
end)

-- Update state based on an event
myState:Update("EVENT_NAME", ...)

-- Set the time window
myState:SetWindow(10) -- 10 seconds

Accessing State Values

-- Using __call metamethod
local value = myState()

-- Direct access
local directValue = myState.value

Advanced Example: Damage Tracker

Here's a comprehensive example that demonstrates the power of the state registrar by implementing a damage tracking system:

local StateExpression = Aurora.StateExpression

-- Create damage tracking state
local damageTaken = StateExpression:New("damage_taken")
damageTaken.hits = {} -- Store individual hits

-- Cleanup function for managing the hit window
local function cleanupOldHits(self, now)
local windowStart = now - self.window
local removed = 0

-- Remove old hits
while self.hits[1] and self.hits[1].time < windowStart do
table.remove(self.hits, 1)
removed = removed + 1
end

-- Recalculate total
local total = 0
for _, hit in ipairs(self.hits) do
total = total + (tonumber(hit.amount) or 0)
end

-- Update only if changed
if total ~= self.value then
self.value = total
end

return removed
end

-- Setup event handling
local f = CreateFrame("Frame")
f:RegisterEvent("UNIT_COMBAT")

-- Periodic cleanup
local timerFrame = CreateFrame("Frame")
timerFrame:SetScript("OnUpdate", function(self, elapsed)
if #damageTaken.hits > 0 then
cleanupOldHits(damageTaken, GetTime())
end
end)

-- Event forwarding
f:SetScript("OnEvent", function(self, event, ...)
damageTaken:Update(event, ...)
end)

-- Register damage handler
damageTaken:RegisterHandler("UNIT_COMBAT", function(self, now, unitTarget, event, flagText, amount, schoolMask)
if unitTarget ~= "player" then return end

-- Process damage amount and type
local damageAmount = tonumber(amount) or 0
local damageType = "Unknown"

-- Map damage school types
local schoolTypes = {
[1] = "Physical",
[2] = "Holy",
[4] = "Fire",
[8] = "Nature",
[16] = "Frost",
[32] = "Shadow",
[64] = "Arcane"
}
damageType = schoolTypes[schoolMask] or "Unknown"

-- Record hit details
table.insert(self.hits, {
time = now,
amount = damageAmount,
type = damageType,
isCrit = flagText == "CRITICAL",
isCrushing = flagText == "CRUSHING",
isGlancing = flagText == "GLANCING"
})

cleanupOldHits(self, now)
end)

-- Register in global state
Aurora.state.damage_taken = damageTaken

Using the Damage Tracker

-- Get total damage in current window
local totalDamage = damageTaken()

-- Analyze recent hits
for _, hit in ipairs(damageTaken.hits) do
if hit.isCrit then
print(string.format("%s critical hit for %d!",
hit.type, hit.amount))
end
end

-- Get damage by type
local function getDamageByType(type)
local total = 0
for _, hit in ipairs(damageTaken.hits) do
if hit.type == type then
total = total + hit.amount
end
end
return total
end

local physicalDamage = getDamageByType("Physical")