Portrait Michael Malura

WoW Addon Development - Complete Reference

Link to the project: GitHub - WoW Addons

Comprehensive documentation for World of Warcraft addon development with a focus on TBC/Classic.


Table of Contents

  1. Getting Started
  2. Addon Structure & TOC File
  3. Lua Basics
  4. Event System
  5. Widget API - UI Elements
  6. Common API Functions
  7. Slash Commands
  8. SavedVariables - Persistent Data
  9. XML UI Layout
  10. Secure Templates & Combat Lockdown
  11. TBC Classic Specifics
  12. Resources

Getting Started

What you need

  • Text editor: VS Code, Notepad++, Sublime Text
  • WoW client: Classic/TBC installed
  • Basic knowledge: No programming experience required

Addon location

World of Warcraft\_classic_\Interface\AddOns\DeinAddonName\

Your first addon

1. Create a folder: Interface\AddOns\HalloWelt\

2. Create the TOC file (HalloWelt.toc):

## Interface: 11508
## Title: Hallo Welt
## Notes: Mein erstes Addon!
## Author: DeinName
## Version: 1.0.0

HalloWelt.lua

3. Create the Lua file (HalloWelt.lua):

local frame = CreateFrame("Frame")
frame:RegisterEvent("PLAYER_LOGIN")
frame:SetScript("OnEvent", function(self, event)
    print("Hallo, Welt!")
end)

4. Load the addon: type /reload in the game

Development tools

Command Description
/reload Reload the UI
/dump ausdruck Evaluate a Lua expression
/etrace Event trace window
/fstack Frame stack inspector

Addon Structure & TOC File

Typical folder structure

MeinAddon/
├── MeinAddon.toc        # Pflicht: Addon-Manifest
├── MeinAddon.lua        # Haupt-Code-Datei
├── Config.lua           # Einstellungen
├── embeds.xml           # Bibliotheks-Includes
├── libs/                # Externe Bibliotheken
└── Locales/             # Lokalisierung

TOC file format

## Interface: 11508
## Title: Mein Addon
## Notes: Beschreibung des Addons
## Author: Dein Name
## Version: 1.0.0
## SavedVariables: MeinAddonDB
## SavedVariablesPerCharacter: MeinAddonCharDB
## OptionalDeps: Ace3

embeds.xml
Core.lua
Config.lua

Important TOC directives

Directive Description
Interface Client version
Title Display name in the addon list
Notes Description shown as a tooltip
SavedVariables Account-wide saved data
SavedVariablesPerCharacter Per-character data
Dependencies Required addons
OptionalDeps Optional dependencies
LoadOnDemand Load on demand (1 = yes)

Interface versions

Client Version
Classic Era/Anniversary 11508
TBC Classic 20504
Wrath Classic 30403
Cataclysm Classic 40400

Check the current version: /dump select(4, GetBuildInfo())


Lua Basics

Variables

-- Lokale Variable (bevorzugt)
local meinWert = "Hallo"

-- Globale Variable (vermeiden wenn möglich)
MeineGlobaleVar = "Welt"

-- Datentypen
local text = "String"           -- Text
local zahl = 100                -- Zahl
local dezimal = 0.75            -- Dezimalzahl
local aktiv = true              -- Boolean
local nichts = nil              -- Nil (kein Wert)
local tabelle = {1, 2, 3}       -- Array
local dict = {name = "Held"}    -- Dictionary

Operators

-- Arithmetik
local a = 10 + 5    -- 15
local b = 10 - 5    -- 5
local c = 10 * 5    -- 50
local d = 10 / 5    -- 2
local e = 10 % 3    -- 1 (Modulo)

-- Vergleich
a == b              -- gleich
a ~= b              -- ungleich (NICHT !=)
a < b               -- kleiner
a > b               -- größer

-- Logisch
true and false      -- false
true or false       -- true
not true            -- false

-- String-Verkettung
"Hallo" .. " " .. "Welt"  -- "Hallo Welt"

Control structures

-- If/Else
local leben = UnitHealth("player")

if leben > 50 then
    print("Alles gut")
elseif leben > 25 then
    print("Vorsicht!")
else
    print("Kritisch!")
end

-- Ternärer Stil (Lua-Idiom)
local status = (leben > 50) and "Gut" or "Schlecht"

Loops

-- For-Schleife (numerisch)
for i = 1, 10 do
    print(i)
end

-- For-Schleife (Array)
local items = {"Schwert", "Schild", "Trank"}
for index, item in ipairs(items) do
    print(index, item)
end

-- For-Schleife (Dictionary)
local stats = {staerke = 100, beweglichkeit = 50}
for key, value in pairs(stats) do
    print(key, value)
end

-- While-Schleife
local count = 0
while count < 5 do
    print(count)
    count = count + 1
end

Functions

-- Einfache Funktion
local function gruessen(name)
    print("Hallo, " .. name)
end

gruessen("Spieler")

-- Funktion mit Rückgabe
local function addieren(a, b)
    return a + b
end

local summe = addieren(5, 3)  -- 8

-- Mehrere Rückgabewerte
local function getSpielerInfo()
    return "Spielername", 60, "Krieger"
end

local name, level, klasse = getSpielerInfo()

-- Anonyme Funktion
frame:SetScript("OnClick", function(self, button)
    print("Geklickt mit:", button)
end)

Tables

-- Array (1-indiziert!)
local items = {"Schwert", "Schild"}
print(items[1])      -- "Schwert"
print(#items)        -- 2 (Länge)

table.insert(items, "Trank")
table.remove(items, 1)

-- Dictionary
local spieler = {
    name = "Held",
    level = 60,
    klasse = "Krieger"
}

print(spieler.name)       -- "Held"
print(spieler["level"])   -- 60

-- Tabelle als Objekt
local MeinAddon = {}

function MeinAddon:Initialisieren()
    self.aktiv = true
end

MeinAddon:Initialisieren()

Event System

Basic event handling

-- 1. Frame erstellen
local frame = CreateFrame("Frame")

-- 2. Events registrieren
frame:RegisterEvent("PLAYER_LOGIN")
frame:RegisterEvent("PLAYER_LOGOUT")

-- 3. Event-Handler setzen
frame:SetScript("OnEvent", function(self, event, ...)
    if event == "PLAYER_LOGIN" then
        print("Spieler eingeloggt!")
    elseif event == "PLAYER_LOGOUT" then
        print("Spieler loggt aus!")
    end
end)

Event handler parameters

Parameter Description
self The receiving frame
event Event name (string)
... Event-specific arguments

Event registration

frame:RegisterEvent("UNIT_HEALTH")
frame:UnregisterEvent("UNIT_HEALTH")
frame:UnregisterAllEvents()

-- Unit-spezifisches Event
frame:RegisterUnitEvent("UNIT_HEALTH", "player", "target")

Important events

Login/Logout:

Event Description
PLAYER_LOGIN Player fully logged in
PLAYER_LOGOUT Player logging out
PLAYER_ENTERING_WORLD Loading screen finished
ADDON_LOADED Addon loaded (for SavedVariables)

Combat:

Event Description
PLAYER_REGEN_DISABLED Combat starts
PLAYER_REGEN_ENABLED Combat ends
COMBAT_LOG_EVENT_UNFILTERED Combat log event
PLAYER_DEAD Player died

Units:

Event Arguments Description
UNIT_HEALTH unitId Health changed
UNIT_POWER_UPDATE unitId, powerType Power changed
UNIT_AURA unitId Buffs/debuffs changed
UNIT_TARGET unitId Target changed

Group:

Event Description
GROUP_ROSTER_UPDATE Group composition changed
PARTY_MEMBER_ENABLE Group member online

Dispatch table pattern

local frame = CreateFrame("Frame")
local events = {}

function events:PLAYER_LOGIN()
    print("Eingeloggt!")
end

function events:UNIT_HEALTH(unit)
    if unit == "player" then
        print("Leben:", UnitHealth("player"))
    end
end

frame:SetScript("OnEvent", function(self, event, ...)
    if events[event] then
        events[event](self, ...)
    end
end)

for event in pairs(events) do
    frame:RegisterEvent(event)
end

Widget API - UI Elements

Creating a frame

-- CreateFrame(frameType, name, parent, template)
local frame = CreateFrame("Frame", "MeinFrame", UIParent)
frame:SetSize(200, 100)
frame:SetPoint("CENTER")

Common frame types

Type Description
Frame Base container
Button Clickable button
CheckButton Checkbox
EditBox Text input field
ScrollFrame Scrollable area
Slider Slider control
StatusBar Progress bar

Size and position

frame:SetSize(200, 100)
frame:SetWidth(200)
frame:SetHeight(100)

-- Ankerpunkte: TOPLEFT, TOP, TOPRIGHT, LEFT, CENTER, RIGHT, BOTTOMLEFT, BOTTOM, BOTTOMRIGHT
frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
frame:SetPoint("TOPLEFT", parentFrame, "BOTTOMLEFT", 10, -5)
frame:ClearAllPoints()

Visibility

frame:Show()
frame:Hide()
frame:SetShown(true)
local isShown = frame:IsShown()
local isVisible = frame:IsVisible()

Creating textures

-- Einfarbige Textur
local texture = frame:CreateTexture(nil, "BACKGROUND")
texture:SetAllPoints(frame)
texture:SetColorTexture(0, 0, 0, 0.8)  -- RGBA

-- Bild-Textur
local icon = frame:CreateTexture(nil, "ARTWORK")
icon:SetSize(32, 32)
icon:SetPoint("LEFT", 5, 0)
icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")

FontStrings (text)

local text = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
text:SetPoint("CENTER")
text:SetText("Hallo Welt")
text:SetTextColor(1, 1, 0)  -- Gelb

Buttons

local button = CreateFrame("Button", "MeinButton", UIParent, "UIPanelButtonTemplate")
button:SetSize(100, 25)
button:SetPoint("CENTER")
button:SetText("Klick mich")

button:SetScript("OnClick", function(self, mouseButton, down)
    print("Button geklickt mit:", mouseButton)
end)

StatusBar (health bar)

local bar = CreateFrame("StatusBar", nil, UIParent)
bar:SetSize(200, 20)
bar:SetPoint("CENTER")
bar:SetMinMaxValues(0, 100)
bar:SetValue(75)
bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
bar:SetStatusBarColor(0, 1, 0)  -- Grün

Script handlers

frame:SetScript("OnLoad", function(self) end)
frame:SetScript("OnShow", function(self) end)
frame:SetScript("OnHide", function(self) end)
frame:SetScript("OnUpdate", function(self, elapsed) end)
frame:SetScript("OnEvent", function(self, event, ...) end)
frame:SetScript("OnEnter", function(self) end)
frame:SetScript("OnLeave", function(self) end)
frame:SetScript("OnMouseDown", function(self, button) end)
frame:SetScript("OnMouseUp", function(self, button) end)

Common API Functions

Unit IDs

UnitId Description
player The player
target Current target
focus Focus target
pet Pet
party1-party4 Group members
raid1-raid40 Raid members
mouseover Unit under the mouse cursor
targettarget Target of the target

Unit functions

-- Grundinfos
local name = UnitName("target")
local level = UnitLevel("target")
local class, classFile = UnitClass("target")  -- "Krieger", "WARRIOR"
local race = UnitRace("target")
local guid = UnitGUID("target")

-- Klassifikation
local classification = UnitClassification("target")
-- "normal", "elite", "rareelite", "rare", "worldboss"

-- Fraktion
local faction = UnitFactionGroup("target")  -- "Horde", "Alliance"
local isEnemy = UnitIsEnemy("player", "target")
local isFriend = UnitIsFriend("player", "target")

-- Existenz prüfen
local exists = UnitExists("target")
local isPlayer = UnitIsPlayer("target")
local isSameUnit = UnitIsUnit("player", "target")

Health and power

-- Leben
local health = UnitHealth("player")
local maxHealth = UnitHealthMax("player")
local healthPercent = (health / maxHealth) * 100

-- Kraft (Mana, Wut, Energie)
local power = UnitPower("player")
local maxPower = UnitPowerMax("player")
local powerType = UnitPowerType("player")
-- 0=Mana, 1=Wut, 2=Fokus, 3=Energie

-- Tot?
local isDead = UnitIsDead("target")
local isGhost = UnitIsGhost("player")

Buff/debuff functions

-- Buff nach Name
local name, icon, count, debuffType, duration, expirationTime =
    UnitAura("player", "Segen der Macht", "HELPFUL")

-- Alle Buffs durchlaufen
for i = 1, 40 do
    local name, _, _, _, _, _, source, _, _, spellId =
        UnitAura("player", i, "HELPFUL")
    if not name then break end
    print(i, name, spellId)
end

-- Alle Debuffs durchlaufen
for i = 1, 40 do
    local name = UnitAura("player", i, "HARMFUL")
    if not name then break end
    print(i, name)
end

Spell functions

local name, rank, icon, castTime, minRange, maxRange = GetSpellInfo(spellId)

local start, duration, enabled = GetSpellCooldown(spellId)
local remaining = (start + duration) - GetTime()

local isKnown = IsSpellKnown(spellId)

Item functions

local name, link, quality, ilvl, reqLevel, class, subclass,
      maxStack, equipSlot, texture, vendorPrice = GetItemInfo(itemId)

local count = GetItemCount(itemId)
local countMitBank = GetItemCount(itemId, true)

Chat functions

SendChatMessage("Hallo!", "SAY")
SendChatMessage("Gruppennachricht", "PARTY")
SendChatMessage("Schlachtzugnachricht", "RAID")
SendChatMessage("Flüstern", "WHISPER", nil, "Spielername")
SendChatMessage("Gildennachricht", "GUILD")

print("Nachricht")
DEFAULT_CHAT_FRAME:AddMessage("Rote Nachricht", 1, 0, 0)

Time functions

local time = GetTime()  -- Sekunden, hohe Präzision
local hours, minutes = GetGameTime()  -- Serverzeit
local formatted = date("%Y-%m-%d %H:%M:%S")

Group functions

local numMembers = GetNumGroupMembers()
local inRaid = IsInRaid()
local inGroup = IsInGroup()

local name, rank, subgroup, level, class, fileName, zone,
      online, isDead, role, isML = GetRaidRosterInfo(i)

Slash Commands

Simple slash command

SLASH_MEINADDON1 = "/meinaddon"
SLASH_MEINADDON2 = "/ma"

SlashCmdList["MEINADDON"] = function(msg, editBox)
    print("Befehl empfangen:", msg)
end

Parsing arguments

SLASH_MEINADDON1 = "/meinaddon"

SlashCmdList["MEINADDON"] = func
Share X Reddit Hacker News LinkedIn E-Mail