Link to the project: GitHub - WoW Addons
Comprehensive documentation for World of Warcraft addon development with a focus on TBC/Classic.
Table of Contents
- Getting Started
- Addon Structure & TOC File
- Lua Basics
- Event System
- Widget API - UI Elements
- Common API Functions
- Slash Commands
- SavedVariables - Persistent Data
- XML UI Layout
- Secure Templates & Combat Lockdown
- TBC Classic Specifics
- 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