This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
AzerothAdmin is a World of Warcraft 3.3.5 (Wrath of the Lich King) addon for AzerothCore server administrators and Game Masters. It provides an in-game GUI for executing GM commands. Written entirely in Lua using the Ace3 addon framework.
There is no traditional build system. This is a pure Lua addon loaded directly by the WoW client.
- Release packaging: Automated via
.github/workflows/release.yml, triggered by pushing version tags (v*) - Release output: ZIP containing two addons:
AzerothAdmin/(main) andAzerothAdmin_Models/(on-demand model data from_build/AzerothAdmin_Models/) - Installation: Extract ZIP to
World of Warcraft/Interface/AddOns/ - No tests: Testing is done manually in-game on a 3.3.5 client
- Libraries — Ace3 framework + FrameLib, Graph
- Locales — 12 languages (enUS is the default/fallback)
- Commands — Server command functions (Main, Tele, Char, Ticket, Misc, Server, NPC, GO)
- Data — DBC constants, teleport locations
- Core — Init.lua (button registration), AzerothAdmin.lua (main addon class), DropDownStyle
- Modules — Linkifier (chat link parsing)
- Frames — UI layout definitions, loaded last since they reference everything above
Core/AzerothAdmin.lua— Main addon class (Ace3 addon), database defaults, event handlers, core logic (~3300 lines)Core/Init.lua— Registers all button scripts and control handlersFrames/Frames.lua— Orchestrates UI creation, calls allFrames_Section*.luabuildersCommands/*.lua— Each file corresponds to a tab and contains functions that send server commands via chatData/DBC.lua— Game database constants (items, spells, creatures)Data/TeleportTable.lua— Predefined teleport location database
- Ace3 addon: Created via
LibStub("AceAddon-3.0"):NewAddon("AzerothAdmin", "AceConsole-3.0", "AceEvent-3.0", "AceHook-3.0") - Two locale namespaces:
AzerothAdmin(UI labels/tooltips) andAzerothAdmin-Strings(command strings/messages) - FrameLib: Custom library wrapping WoW's
CreateFrameAPI for cleaner frame definitions - Database: AceDB-3.0 with
char(per-character) andprofile(account-wide) scopes. Saved asAzerothAdminDb/AzerothAdminDbPerChar - Models addon:
Data/Models.luawas moved to a separate LoadOnDemand addon (_build/AzerothAdmin_Models/) to save ~5.6 MB of memory
The addon has 8 tabs, each with a matching Frames/Frames_Section*.lua (layout) and Commands/*.lua (logic): Main, Tele, Char, Ticket, Misc, Server, NPC, GO. There is also a Log tab (Frames only, no Commands file).
Buttons in Frames/Frames_Section*.lua are positioned using offX (column) and offY (row) offsets from TOPLEFT of ma_midframe. The Char tab uses this grid:
| Column | offX |
|---|---|
| ColOne | 140 |
| ColTwo | 222 |
| ColThree | 303 |
| ColFour | 385 |
| ColFive | 467 |
| ColSix | 548 |
| ColSeven | 630 |
| Row | offY |
|---|---|
| RowOne | -30 |
| RowTwo | -52 |
| RowThree | -74 |
| RowFour | -95 |
| RowFive | -117 |
| RowSix | -139 |
| RowSeven | -160 |
| RowEight | -182 |
| RowNine | -204 |
Standard button size is 80x20. Columns 1-3 overlap with the player model area (bottom-left). The parameter box and clear button sit at offY = -231.
Current button positions (Label = locale display text):
| Position | Col1 (140) | Col2 (222) | Col3 (303) | Col4 (385) | Col5 (467) | Col6 (548) | Col7 (630) |
|---|---|---|---|---|---|---|---|
| Row1 (-30) | Kill | Revive | GPS | Jail (A) | Jail (H) | UnJail | Ban |
| Row2 (-52) | Show GUID | PLAYER Info | Distance | Appear | G Create | BanInfo | |
| Row3 (-74) | MaxSkill | Repair | CombatStop | Recall | G Invite | BanList | |
| Row4 (-95) | Kick | BindSight | UnBindSight | Summon | G Rank | UnBan | |
| Row5 (-117) | Cooldown | Morph | Demorph | GroupSumn | G UnInv | ||
| Row6 (-139) | Rename | Aura | UnAura | Mute | Bank | G Delete | |
| Row7 (-160) | Chng Faction | Freeze | UnFreeze | UnMute | Dismount | ||
| Row8 (-182) | Chng Race | Possess | UnPossess | Damage | |||
| Row9 (-204) | Customize | ShowArea | HideArea | Save |
Top bar (offY -1 to -4): LearnLang dropdown + Learn button, Reset dropdown + Reset button, Modify dropdown + EditBox + Modify button, Ban type dropdown. Bottom bar (offY -231): Parameter label, EditBox (240x20), Clear button.
Most tabs use free-form positioning rather than a strict grid. Key patterns:
- Main Tab: Left sidebar (offX 10-174) for GM toggles and display controls (On buttons 120px at offX 10, Off buttons 40px at offX 134). Sidebar runs from offY -10 to -230. Right columns (offX 333-660) for command buttons. Speed/Scale sliders at offX 205. Parameter area at offY -231 (label offX 180, editbox offX 264, Clear Params offX 508). No strict grid.
- Tele Tab: Three-column scroll list layout (offX 5/220/420) with FauxScrollFrames. Action buttons at offX 660. Vertical chaining with offY -2 gaps.
- Ticket Tab: Horizontal bottom button bar at offY -226 (offX 5-505 in 85px steps). Top area has scroll lists and detail panels.
- Misc Tab: Left-aligned settings column (offX 6-160) with checkboxes, sliders, color swatches. Dropdowns at TOPRIGHT/BOTTOMRIGHT.
- Server Tab: Left area with network graphs (offX 10, 170). Right sidebar buttons at TOPRIGHT. Bottom bar for announce/settings.
- NPC Tab: Uses same grid system as Char tab (same column/row offsets). Has model viewer at BOTTOMLEFT, emote dropdowns at top. ColFive is 498, ColSix is 585, ColSeven is TOPRIGHT-5 (different from Char). See NPC Tab Button Map below.
- GO Tab: Three zones — left controls (offX 5-105), center model (offX 182-262), right movement panel (offX 584-706). Free-form positioning throughout.
Col1=140, Col2=222, Col3=303, Col4=385, Col5=498, Col6=585, Col7=TOPRIGHT-5. Same row offsets as Char tab.
| Position | Col1 (140) | Col2 (222) | Col3 (303) | Col4 (385) | Col5 (498) | Col6 (585) | Col7 (TR-5) |
|---|---|---|---|---|---|---|---|
| Row1 (-30) | Kill | Respawn | NPC Info | Way Show | |||
| Row2 (-52) | Freeze | Way All Del | NPC Add | NPC Unfreeze Way | |||
| Row3 (-74) | Way Add | Way Show On | NPC Add ID | Move Stack | |||
| Row4 (-95) | Way End Add | Way Show Off | NPC Del | Come To Me | |||
| Row5 (-117) | Way M Add | Way M Del | NPC Move | Unfreeze Rnd | NPC Distance | ||
| Row6 (-139) | Possess | UnPossess | NPC Go | [dist box] | NPC Clear | ||
| Row7 (-160) | Morph | Demorph | NPC Say | Get GUID | [GUID box] | Show GUID | |
| Row8 (-182) | Bindsight | UnBindsight | NPC Yell | ID Down | [NPC ID box] | ID Up | |
| Row9 (-204) | Aura | UnAura | Display Down | [Display box] | Display Up |
Top bar (offY -1 to -4): Emote dropdown 1 (offX 5) + Play button (offX 124), Emote dropdown 2 (offX 210) + Play button (offX 299). Bottom bar (offY -231/-232): Parameter label (offX 142), EditBox 240x20 (offX 226), Clear Params (offX 470).
Button frame names (e.g., ma_namegobutton) often do not match their visible UI labels (e.g., "Summon"). The source of truth for button labels is Locales/enUS/enUS.lua, where locale keys map to display text. When looking for a button by its visible label, search the locale file first, then trace the locale key back to the frame definition in Frames_Section*.lua.
Common non-obvious mappings:
ma_namegobutton= "Summon" (.summoncommand)ma_gonamebutton= "Appear" (.appearcommand)ma_groupgobutton= "GroupSumn" (.groupsummoncommand)ma_charchnagefaction= "Chng Faction" (note: typo in key name is intentional/legacy)
Some buttons (Kill, Kick, Revive, Save) are dynamically enabled/disabled in Core/AzerothAdmin.lua via the PLAYER_TARGET_CHANGED event based on target state (alive/dead, player/NPC, self/other).
Four steps:
- Add localized tooltip and button text in
Locales/<locale>/<locale>.lua - Write the command function in
Commands/<section>.lua - Register the button, function, text, and tooltip in
Core/Init.lua - Draw the button in
Frames/Frames_Section<section>.lua
- Locale strings use two files per language:
<locale>.lua(UI text) andstrings_<locale>.lua(command strings). enUS is always the default fallback. - Backslash paths in the
.tocfile — WoW requires backslash separators in.tocload lists. - Interface version is
30300(WoW 3.3.5). Do not change this. - Commands are sent to the server by writing to the chat frame (e.g.,
SendChatMessage(".gm on")).
All EditBoxes should be created as bare CreateFrame("EditBox", ...) frames (no InputBoxTemplate) and styled with the following pattern:
do
local eb = CreateFrame("EditBox", "my_editbox", parent)
FrameLib:AddGroupFrame("groupname", eb)
eb:SetSize(width, height)
eb:SetPoint("ANCHOR", parent, "ANCHOR", offX, offY)
eb:SetAutoFocus(false)
eb:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
eb:SetFontObject("ChatFontNormal")
eb:SetTextInsets(4, 4, 2, 2)
eb:SetBackdrop({
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\Buttons\\WHITE8X8",
edgeSize = 2,
insets = { left = 2, right = 2, top = 2, bottom = 2 }
})
eb:SetBackdropColor(0, 0, 0, 0.6)
eb:SetBackdropBorderColor(0.35, 0.35, 0.35, 1)
end- Add
eb:SetMaxLetters(n)only when a character limit is required. - Do not use
inherits = "InputBoxTemplate"— it adds internal textures that conflict withSetBackdropand cannot be cleanly overridden. OnEscapePressedmust always be set manually since bare EditBoxes do not inherit it.
All CheckBoxes using OptionsCheckButtonTemplate must have their built-in textures hidden and a custom background applied immediately after the FrameLib:BuildButton call:
FrameLib:BuildButton({
type = "CheckButton",
name = "my_checkbox",
group = "mygroup",
parent = ma_midframe,
setpoint = { ... },
text = Locale["my_label"],
inherits = "OptionsCheckButtonTemplate"
})
do
local cb = _G["my_checkbox"]
cb:GetNormalTexture():SetAlpha(0)
cb:GetHighlightTexture():SetAlpha(0)
cb:GetPushedTexture():SetAlpha(0)
local bg = cb:CreateTexture(nil, "BACKGROUND")
bg:SetSize(18, 18)
bg:SetPoint("CENTER", cb, "CENTER", 0, 0)
bg:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
bg:SetVertexColor(0.2, 0.2, 0.2, 0.8)
endGetNormalTexture()— hides the box border at restGetHighlightTexture()— hides the hover glowGetPushedTexture()— hides the border shown on click- The 18x18 background gives a dark gray backdrop contrasting against the addon's dark frame
- Do not use
_G["buttonNameHighlight"]—OptionsCheckButtonTemplatedoes not create a named highlight child; useGetHighlightTexture()instead
All Sliders using OptionsSliderTemplate must have their backdrop removed and replaced with a custom 4px track line immediately after the FrameLib:BuildFrame call:
FrameLib:BuildFrame({
type = "Slider",
name = "my_slider",
group = "mygroup",
parent = ma_midframe,
size = { width = 80 },
setpoint = { ... },
inherits = "OptionsSliderTemplate"
})
do
local s = _G["my_slider"]
s:SetBackdrop(nil)
local line = s:CreateTexture(nil, "BACKGROUND")
line:SetHeight(4)
line:SetPoint("LEFT", s, "LEFT", 10, 0)
line:SetPoint("RIGHT", s, "RIGHT", -10, 0)
line:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
line:SetVertexColor(0.35, 0.35, 0.35, 1)
endSetBackdrop(nil)removes the default raised border/background — there is no named child texture for it- The 10px insets keep the line from extending past the thumb endpoints
- The
$parentThumbtexture (the drag handle) is the only named child ofOptionsSliderTemplateand is left untouched
Always update CHANGELOG.md before committing. Follow the existing format:
- Use category prefixes: ADD, REMOVE, FIX, IMPROVE, REPLACE, REORGANIZE, CHANGE, MIGRATE, OPTIMIZE
- Keep entries concise and user-facing — omit internal details like frame names, offX values, or file lists
- Group related changes into a single entry with sub-bullets only when needed for clarity
- Minor changes (typo fixes, internal cleanup) get a single line with no sub-bullets
A PreToolUse hook will block commits that don't include CHANGELOG.md changes.
- GM Commands: https://www.azerothcore.org/wiki/gm-commands
- Wiki Home: https://www.azerothcore.org/wiki/home