Skip to main content

Overview

Custom code in WeakAuras allows you to write Lua scripts for triggers, actions, animations, and display logic. This provides unlimited flexibility for creating sophisticated, custom behavior.
Custom code runs in the WoW Lua environment. Bugs can cause errors or performance issues. Test thoroughly!

Code Execution Contexts

WeakAuras provides several places for custom code:

Custom Triggers

Detect when to show/hide displays

Custom Actions

Execute code on trigger/untrigger

Custom Functions

Generate dynamic values (icon, name, etc.)

Custom Conditions

Advanced condition checking

Aura Environment

Custom code runs in an isolated environment:
// aura_env - Persistent storage for your aura
aura_env.myVariable = "persistent value"
aura_env.counter = (aura_env.counter or 0) + 1

// Access trigger state
aura_env.state = {
  show = true,
  value = 100
}

// Access region
aura_env.region = region  -- Display region
aura_env.id = "MyAura"    -- Aura ID
aura_env.cloneId = ""     -- Clone identifier
aura_env persists for the lifetime of the aura. Use it to store state between function calls.

Custom Trigger Functions

Basic Trigger

function()
  local health = UnitHealth("player")
  local maxHealth = UnitHealthMax("player")
  return health < maxHealth * 0.3
end

With Event

function(event, unit)
  if unit ~= "player" then 
    return false 
  end
  
  return UnitAffectingCombat("player")
end

With State Management

function(allStates, event, ...)
  allStates[""] = allStates[""] or {}
  local state = allStates[""]
  
  state.show = true
  state.changed = true
  state.value = UnitHealth("player")
  state.total = UnitHealthMax("player")
  
  return true
end

Custom Actions

On Show

// Executes when display appears
function()
  print("Display shown!")
  PlaySoundFile("Sound\\Interface\\RaidWarning.wav")
end

On Hide

// Executes when display hides
function()
  aura_env.wasActive = false
  // Reset state, timers, etc.
end

Every Frame

function()
  // Runs every frame while shown
  local elapsed = GetTime() - (aura_env.lastUpdate or 0)
  if elapsed > 1 then
    aura_env.lastUpdate = GetTime()
    // Do periodic work
  end
end
Throttle every frame code to avoid performance impact.

Custom Functions

Custom Name

function()
  local target = UnitName("target")
  if target then
    return "Targeting: " .. target
  end
  return "No Target"
end

Custom Icon

function()
  local _, class = UnitClass("player")
  if class == "PRIEST" then
    return "Interface\\Icons\\Spell_Holy_PowerWordShield"
  elseif class == "WARRIOR" then
    return "Interface\\Icons\\Ability_Warrior_ShieldMastery"
  end
  return "Interface\\Icons\\INV_Misc_QuestionMark"
end

Custom Duration

function()
  local start, duration = GetSpellCooldown(12345)
  if duration > 0 then
    return duration, start + duration
  end
  return 0, 0
end

Custom Stacks

function()
  local comboPoints = GetComboPoints("player", "target")
  local soulShards = UnitPower("player", SPELL_POWER_SOUL_SHARDS)
  return comboPoints + soulShards
end

Custom Text

Create complex formatted text:
function()
  local health = UnitHealth("player")
  local maxHealth = UnitHealthMax("player")
  local percent = floor((health / maxHealth) * 100)
  
  // Format with separators
  local formatted = string.format("%d", health)
  formatted = formatted:reverse():gsub("(%d%d%d)", "%1,")
  formatted = formatted:reverse():gsub("^,", "")
  
  return formatted .. " (" .. percent .. "%)"
end

WeakAuras API

Core Functions

// Region access
WeakAuras.GetRegion(id)
WeakAuras.GetRegion(id, cloneId)

// Display data
WeakAuras.GetData(id)

// State access
WeakAuras.GetTriggerStateForTrigger(id, triggernum)
WeakAuras.GetActiveStates(id)

// Event handling
WeakAuras.ScanEvents(event, ...)

Region Methods

region:Expand()
region:Collapse()
region:SetDurationInfo(duration, expirationTime)
region:SetIcon(texture)
region:SetRegionWidth(width)
region:SetRegionHeight(height)

Utility Functions

// Time formatting
WeakAuras.TimeToString(seconds)  -- "1m 30s"

// Text replacement
WeakAuras.ReplaceRaidMarkerSymbols(text)

// Model loading
WeakAuras.SetModel(model, path, isUnit, displayInfo)

Common Patterns

Tracking Multiple Values

aura_env.values = aura_env.values or {}

function(allStates, event, ...)
  // Track up to 5 combo points
  for i = 1, 5 do
    local cloneId = "cp" .. i
    allStates[cloneId] = allStates[cloneId] or {}
    
    local active = i <= GetComboPoints("player", "target")
    allStates[cloneId].show = active
    allStates[cloneId].changed = true
  end
  
  return true
end

Cooldown Grouping

aura_env.spells = {12345, 23456, 34567}

function(allStates, event, ...)
  for _, spellId in ipairs(aura_env.spells) do
    local start, duration = GetSpellCooldown(spellId)
    local cloneId = "spell_" .. spellId
    
    allStates[cloneId] = allStates[cloneId] or {}
    
    if duration > 1.5 then  // On cooldown
      allStates[cloneId].show = true
      allStates[cloneId].duration = duration
      allStates[cloneId].expirationTime = start + duration
      allStates[cloneId].progressType = "timed"
    else
      allStates[cloneId].show = false
    end
    
    allStates[cloneId].changed = true
  end
  
  return true
end

Damage Meter

aura_env.damage = aura_env.damage or {}

function(event, timestamp, subevent, ...)
  if subevent ~= "SPELL_DAMAGE" then return end
  
  local sourceGUID = select(1, ...)
  local amount = select(12, ...)
  
  if sourceGUID == UnitGUID("player") then
    aura_env.damage.total = (aura_env.damage.total or 0) + amount
    aura_env.damage.lastAmount = amount
    aura_env.damage.lastTime = GetTime()
  end
end

Buff Tracking with Priorities

aura_env.priority = {
  ["Bloodlust"] = 100,
  ["Power Infusion"] = 90,
  ["Heroism"] = 80
}

function(allStates, event, ...)
  local bestBuff = nil
  local bestPriority = 0
  
  for buffName, priority in pairs(aura_env.priority) do
    local name, _, _, count, _, duration, expires = 
      UnitBuff("player", buffName)
    
    if name and priority > bestPriority then
      bestBuff = {
        name = name,
        stacks = count,
        duration = duration,
        expirationTime = expires
      }
      bestPriority = priority
    end
  end
  
  if bestBuff then
    allStates[""] = {
      show = true,
      changed = true,
      name = bestBuff.name,
      stacks = bestBuff.stacks,
      duration = bestBuff.duration,
      expirationTime = bestBuff.expirationTime,
      progressType = "timed"
    }
    return true
  end
  
  return false
end

Advanced Techniques

Creating Timers

aura_env.timers = aura_env.timers or {}

function createTimer(name, duration, callback)
  local timerId = C_Timer.NewTimer(duration, function()
    callback()
    aura_env.timers[name] = nil
  end)
  
  aura_env.timers[name] = timerId
end

function cancelTimer(name)
  if aura_env.timers[name] then
    aura_env.timers[name]:Cancel()
    aura_env.timers[name] = nil
  end
end

Addon Communication

// Init code
local addonPrefix = "MyWA"
C_ChatInfo.RegisterAddonMessagePrefix(addonPrefix)

// Send message
function sendMessage(message)
  C_ChatInfo.SendAddonMessage(addonPrefix, message, "RAID")
end

// Receive message (in event trigger)
function(event, prefix, message, channel, sender)
  if prefix == "MyWA" then
    // Process message
    aura_env.lastMessage = message
    return true
  end
end

Frame Creation

// Init code
aura_env.frame = aura_env.frame or CreateFrame("Frame", nil, UIParent)
local f = aura_env.frame

f:SetSize(200, 100)
f:SetPoint("CENTER")
f:SetBackdrop({
  bgFile = "Interface\\Buttons\\WHITE8X8"
})
f:SetBackdropColor(0, 0, 0, 0.8)
f:Show()

SavedVariables Integration

// Access saved variables
local myAddonData = MyAddonDB or {}

// Use in trigger
function()
  if myAddonData.someValue > 100 then
    return true
  end
  return false
end

Debugging

function(...)
  print("WeakAuras Debug:", ...)
  
  local args = {...}
  for i, v in ipairs(args) do
    print(i, v, type(v))
  end
end

Error Handling

function()
  local success, result = pcall(function()
    // Your code here
    return UnitHealth("player") < 1000
  end)
  
  if not success then
    print("WeakAuras Error:", result)
    return false
  end
  
  return result
end

State Inspection

// Custom action
function()
  print("Current State:")
  for k, v in pairs(aura_env.state or {}) do
    print("  ", k, "=", v)
  end
end

Profiling

aura_env.profile = aura_env.profile or {}

function profileStart(name)
  aura_env.profile[name] = debugprofilestop()
end

function profileEnd(name)
  local elapsed = debugprofilestop() - (aura_env.profile[name] or 0)
  print(name, "took", elapsed, "ms")
end

// Usage
profileStart("myFunction")
// ... code ...
profileEnd("myFunction")

WeakAuras Debug Tools

// Enable profiling
/wa pstart

// Stop profiling
/wa pstop

// View debug log
/wa debuglog

// Reload all auras
/wa reload

// Test aura (options open)
// Triggers OPTIONS event

Security and Limitations

Some WoW API functions are protected and cannot be called from addons:
  • CastSpellByID/CastSpellByName (in combat)
  • Target functions (in combat)
  • Some UI functions
Code that modifies Blizzard UI can cause taint, breaking default UI functionality. Be careful with frame hooks.
Custom code runs frequently. Keep it optimized:
  • Cache API results
  • Avoid expensive operations
  • Throttle updates
  • Profile with /wa pstart

Best Practices

Code Templates

Throttled Frame Update

aura_env.throttle = 0.5  // seconds
aura_env.lastUpdate = 0

function()
  local now = GetTime()
  if now - aura_env.lastUpdate >= aura_env.throttle then
    aura_env.lastUpdate = now
    // Do work
    return true
  end
  return false
end

Addon Detector

function()
  local loaded, finished = IsAddOnLoaded("MyAddon")
  return loaded and finished
end

Multi-Criteria Trigger

function()
  local criteria = {
    UnitHealth("player") < 10000,
    UnitPower("player", 0) > 5000,
    UnitAffectingCombat("player"),
    GetComboPoints("player", "target") >= 5
  }
  
  local count = 0
  for _, met in ipairs(criteria) do
    if met then count = count + 1 end
  end
  
  return count >= 3  // At least 3 criteria met
end

Troubleshooting

  • Check for Lua errors in chat
  • Verify function syntax
  • Ensure return value is correct type
  • Check /wa debuglog for errors
  • Profile with /wa pstart and /wa pstop
  • Check for expensive API calls
  • Reduce update frequency
  • Cache repeated calculations
  • Ensure changed = true is set
  • Verify trigger is returning correct value
  • Check if state properties are valid
  • Inspect state with print debugging

Custom Triggers

Full custom trigger guide

Conditions

Advanced condition logic

Animations

Custom animation functions

Build docs developers (and LLMs) love