Plantillas de código de addons de WoW Forever para pegar

Publicado Actualizado Informes de la beta de la compilación 1.60.1.69913

Compartir

Guía de addons página 5 de 7

Copia estas plantillas para la interfaz 16001: hello world, SavedVariables, una barra segura, el compartimento de addons y el bloqueo de combate. Siguen la documentación de Mainline de la Warcraft Wiki, así que verifica cada una en tu compilación de la beta.

Pregunta a nuestra IA sudorosa y degenerada sobre plantillas de código de addons

Hello World, SavedVariables, barras seguras para secretos, compartimento de addon y más

Cualquier cosa que cubran nuestras guías. Las respuestas salen de esta página y del resto de nuestras guías, con los valores reales en gráficos y las secciones de la guía enlazadas. Es IA, así que comprueba la fuente antes de citarla.

Cada bloque es un patrón completo para la interfaz 16001. El nombre de la carpeta y el del archivo TOC deben coincidir.

Interfaz TOC
16001
SavedVariables
Error reportado como corregido
Addon compartment
Sin probar aquí
Bloqueo de combate
En cola hasta la regeneración

Esta biblioteca forma parte de la guía para desarrollar addons de WoW Forever. Para el diseño del cliente que hay detrás de estos fragmentos, consulta los patrones de addons de WoW Forever. Para comprobar firmas concretas, usa la hoja de referencia de la API de Forever. Para un addon terminado y descargable que une notas, una sonda, el compartimento y una barra segura, revisa el addon de referencia Forever Beacon.

Este sitio no ha ejecutado ninguna de estas plantillas en un cliente de Forever. Siguen la documentación Mainline de la Warcraft Wiki, los informes de la beta citados y el código fuente conservado del cliente de Blizzard de la compilación 70338. La sonda independiente de ID de proyecto sí se ejecutó en el juego; eso no significa que estos ejemplos se hayan probado en vivo.

Las incorporaciones del 13 de octubre citan el extracto conservado del cliente 70338. Los espejos de las fuentes enlazadas pueden mostrar una compilación posterior.

Funciones con más de un comando

Una función de Lua puede contener varias instrucciones. Pon cada operación en su propia línea entre function y end; se ejecutan en orden cuando llamas a la función. Este ejemplo imprime un mensaje local y reproduce un sonido de interfaz:

Ready.lua
local function AnnounceReady()
    print("[Forever Notes] Ready.")
    PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON)
end

AnnounceReady()

No necesitas varias definiciones de función ni puntos y coma. Un return anticipado termina la función, y un error sin capturar detiene las instrucciones restantes.Fuente: Lua 5,1 Reference Manual, function definitions and blocks y error handling, consultado el 13 oct 2026. Las instrucciones de Lua son distintas de los comandos de barra de las macros, como /cast. Estos ejemplos imprimen texto y reproducen un sonido; no implementan acciones de combate protegidas.

En el chat, el diagnóstico equivalente es /run print("Ready."); PlaySound(SOUNDKIT.IG_MAINMENU_OPTION_CHECKBOX_ON). Mantén el código reutilizable en un archivo Lua incluido en tu TOC. La hoja de referencia de ID de sonidos enumera las constantes usadas aquí.

Comando de mensaje y alias de comandos de barra

Para un mensaje que solo tú puedas ver, usa print("Hello.") o DEFAULT_CHAT_FRAME:AddMessage("Hello.", 1, 0.82, 0). En el chat, escribe /run print("Hello."). Imprimen el mensaje localmente; no envían un mensaje al grupo ni un susurro.

Para definir una función con más de un comando de barra, asigna alias numerados a la misma clave SlashCmdList. Este ejemplo completo de dos archivos hace que tanto /wfgmessage Hello como /wfgmsg Hello impriman localmente el texto proporcionado:

WFGMessageExample.toc
## Interface: 16001
## Title: WFG Message Example
## Notes: Two slash commands for one local message handler.
## Version: 0.1.0

WFGMessageExample.lua
WFGMessageExample.lua
local function ShowLocalMessage(text)
    if not text:find("%S") then
        print("Usage: /wfgmessage Hello")
        return
    end
    DEFAULT_CHAT_FRAME:AddMessage("[WFG Message] " .. text, 1, 0.82, 0)
end

SLASH_WFGMESSAGEEXAMPLE1 = "/wfgmessage"
SLASH_WFGMESSAGEEXAMPLE2 = "/wfgmsg"
SlashCmdList["WFGMESSAGEEXAMPLE"] = ShowLocalMessage

Usa el nombre de carpeta WFGMessageExample y después reinicia el cliente al añadirla. La clave de registro en mayúsculas debe coincidir con ambos globales SLASH_. Registra los alias al cargar el archivo, no cada vez que se ejecuta el manejador. El registro de comandos de barra de Blizzard usa este mismo patrón de globales numerados.Fuente: registro de comandos de barra de Blizzard, compilación conservada del cliente 70338, inspeccionada el 13 oct 2026.

Si "message command" («comando de mensaje») significa enviar chat a otras personas, es una operación aparte: C_ChatInfo.SendChatMessage("Hello.", "PARTY") envía al chat de grupo cuando el cliente lo permite. Llámalo solo por una acción deliberada del jugador y comprueba las restricciones actuales del chat. La documentación generada marca esta API como HasRestrictions; esta página no afirma que se permita el envío automático arbitrario.Fuente: documentación de ChatInfo de Blizzard, SendChatMessage, compilación conservada del cliente 70338, inspeccionada el 13 oct 2026.

Mostrar un mensaje emergente

Registra una entrada específica del addon en StaticPopupDialogs y después llama a StaticPopup_Show con esa clave. Este ejemplo muestra un mensaje informativo fijo con un botón de aceptar y cierre mediante Escape. Ponlo en un archivo Lua cargado por tu TOC:

PopupMessage.lua
StaticPopupDialogs["WFG_EXAMPLE_READY_MESSAGE"] = {
    text = "Forever Notes is ready.",
    button1 = OKAY,
    timeout = 0,
    whileDead = true,
    hideOnEscape = true,
}

SLASH_WFGPOPUPEXAMPLE1 = "/wfgpopup"
SlashCmdList["WFGPOPUPEXAMPLE"] = function()
    StaticPopup_Show("WFG_EXAMPLE_READY_MESSAGE")
end

Después de cargar el addon, escribe /wfgpopup. El mensaje permanece hasta que lo cierres porque timeout = 0. Usa tu propia clave, deja intactas las definiciones de diálogos de Blizzard y solicita el mensaje deliberadamente en lugar de repetirlo desde un bucle de actualización. Este ejemplo no se ha ejecutado en el cliente de Forever.Fuente: implementación de StaticPopup de Blizzard, StaticPopup_Show, timeout, whileDead y hideOnEscape, compilación conservada del cliente 70338, inspeccionada el 13 oct 2026.

Hello

El addon más pequeño: imprime un mensaje una vez al iniciar sesión y nunca toca un nombre que pueda ser secreto.

HelloForever.toc
## Interface: 16001
## Title: Hello Forever
## Notes: Prints once on login, without touching a secret name.
## Author: YourName
## Version: 0.1.0
## Category: Miscellaneous

HelloForever.lua
HelloForever.lua
local addonName = ...
local frame = CreateFrame("Frame")
frame:SetScript("OnEvent", function()
    print("|cffd4a85a[" .. addonName .. "]|r Forever client accepted this addon.")
end)
frame:RegisterEvent("PLAYER_LOGIN")

SavedVariables

Declara la tabla en el TOC: ## SavedVariables: BeaconNotesDB. El nombre es global porque el cliente la rellena después de ejecutar tus archivos. Tócala solo en ADDON_LOADED y combina las claves por defecto para no sobrescribir nunca los ajustes de un jugador.

SavedVariables.lua
local addonName, ns = ...
local defaults = { scale = 1, locked = false }

local events = {}
function events:ADDON_LOADED(loaded)
    if loaded ~= addonName then return end
    BeaconNotesDB = BeaconNotesDB or {}
    local db = BeaconNotesDB
    for key, value in pairs(defaults) do
        if db[key] == nil then db[key] = value end
    end
    ns.db = db
end

local frame = CreateFrame("Frame")
frame:SetScript("OnEvent", function(self, event, ...)
    if events[event] then events[event](self, ...) end
end)
frame:RegisterEvent("ADDON_LOADED")

No asignes BeaconNotesDB = BeaconNotesDB or {} en el ámbito del archivo. Es la trampa 08 de los errores habituales de la IA con addons.

Barra segura

En la compilación 69913, UnitHealth("player") devolvió un número secreto. Las reglas de Midnight de la Warcraft Wiki dicen que UnitHealthMax también puede ser secreto. No puedes calcular un porcentaje con ellos en Lua, pero un StatusBar los acepta directamente.

HealthBar.lua
local bar = CreateFrame("StatusBar", nil, UIParent)
bar:SetSize(200, 14)
bar:SetPoint("CENTER", 0, -40)
bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
bar:SetStatusBarColor(0.55, 0.12, 0.12)

local function Paint()
    -- Both values may be secret. The bar may have them. Lua may not do math on them.
    bar:SetMinMaxValues(0, UnitHealthMax("player"))
    bar:SetValue(UnitHealth("player"))
end

local frame = CreateFrame("Frame")
frame:SetScript("OnEvent", Paint)
frame:RegisterUnitEvent("UNIT_HEALTH", "player")
frame:RegisterUnitEvent("UNIT_MAXHEALTH", "player")
frame:RegisterEvent("PLAYER_ENTERING_WORLD")

Compartimento de addons

El compartimento de addons del minimapa no necesita LibDataBroker: el TOC nombra funciones globales y el cliente las llama. La Warcraft Wiki documenta estos campos para Mainline. Este sitio no los ha probado en un cliente de Forever, así que comprueba que la entrada aparece.

CompartmentStub.toc
## Interface: 16001
## Title: Compartment Stub
## Notes: Minimap compartment without LibDataBroker.
## IconTexture: Interface\Icons\INV_Misc_Note_01
## AddonCompartmentFunc: CompartmentStub_OnClick
## AddonCompartmentFuncOnEnter: CompartmentStub_OnEnter
## AddonCompartmentFuncOnLeave: CompartmentStub_OnLeave

CompartmentStub.lua
CompartmentStub.lua
function CompartmentStub_OnClick()
    print("|cffd4a85a[Stub]|r compartment click")
end

function CompartmentStub_OnEnter(_, owner)
    GameTooltip:SetOwner(owner, "ANCHOR_LEFT")
    GameTooltip:SetText("Compartment Stub", 1, 0.82, 0.45)
    GameTooltip:Show()
end

function CompartmentStub_OnLeave()
    GameTooltip:Hide()
end

Cola de bloqueo de combate

En combate, mientras InCombatLockdown() sea verdadero, los addons no pueden mostrar, ocultar, mover ni cambiar los atributos de marcos protegidos. Pon esos cambios en cola y ejecútalos cuando se dispare PLAYER_REGEN_ENABLED.

Queue.lua
local pending = {}

local function Queue(fn)
    if InCombatLockdown() then
        pending[#pending + 1] = fn
    else
        fn()
    end
end

local frame = CreateFrame("Frame")
frame:RegisterEvent("PLAYER_REGEN_ENABLED")
frame:SetScript("OnEvent", function()
    for i = 1, #pending do pending[i]() end
    wipe(pending)
end)

-- Use Queue only for protected frames.
-- Your own unprotected frames may Show() during combat.

Para prompts que generan código con esta forma, consulta los prompts de IA listos para usar.

Programa para Forever; compara la constante con nombre del ID de proyecto

WOW_PROJECT_IDera 18 en la sonda en vivo conservada de la compilación 1.60.1.70338 de la beta de Forever, del 12 de octubre de 2026. El código fuente de Blizzard llama a ese valor WOW_PROJECT_CAMELOT. WOW_PROJECT_MAINLINE era 1 y WOW_PROJECT_CLASSIC era 2, así que la rama habitual if WOW_PROJECT_ID == WOW_PROJECT_MAINLINE era falsa en esa compilación. El informe anterior de la comunidad sobre 1 corresponde a la compilación 69913; conserva su número de compilación en lugar de tratarlo como la constante actual.Fuente: constantes de proyecto Camelot de Blizzard, archivos conservados del cliente 70338, inspeccionados el 13 oct 2026. Constantes medidas en el cliente de la beta 1.60.1.70338 el 12 oct 2026.

Un addon que solo se ejecuta en Forever no tiene nada que ramificar. Escribe la versión para Forever y dilo en un comentario, para que el siguiente lector no busque la rama.

Detect.lua
-- Build 70338 recorded WOW_PROJECT_ID 18, named WOW_PROJECT_CAMELOT.
-- WOW_PROJECT_MAINLINE was 1 and WOW_PROJECT_CLASSIC was 2, so an
-- "is this Retail" or "is this Classic" branch was false; retest.
-- A Forever-only add-on writes for Forever directly.
local FOREVER_PROJECT_ID = WOW_PROJECT_CAMELOT

El ejemplo anterior de la wiki etiqueta una rama como "camelot/forever" porque Forever es un tipo de juego dentro de la familia Mainline. Esa arquitectura compartida no determina su ID de proyecto. La declaración de Blizzard en el Discord de WoW UI, citada por Wowhead, llama a Forever y Midnight "two game types within the Mainline code family." La misma declaración llama al tipo de Forever "Camelot (to be renamed before launch)" y dice que Forever comparte "the vast majority of APIs available in 12.1.5," lo que explica por qué las páginas Mainline de la wiki de abajo son aplicables.Fuente: WoW: Forever Will Have Addon Changes from Midnight, consultada el 28 sept 2026.

Para addons compartidos dirigidos a varios clientes, compara WOW_PROJECT_ID con WOW_PROJECT_CAMELOT cuando esa constante exista y después comprueba la API de la función. Un addon solo para Forever puede dirigirse a Forever directamente. También puedes dividir los archivos mediante el TOC. Para las reglas actuales, el indicador de Hardcore o la preferencia de experiencia Classic/Modern, usa la referencia de GameRules; un ID de cliente no responde a esas preguntas.

<details><summary>Notas técnicas: constantes de expansión</summary>

El ejemplo conservado de la wiki anida LE_EXPANSION_LEVEL_CURRENT y LE_EXPANSION_CLASSIC dentro de su rama de ID de proyecto Mainline. Esa rama es falsa en el cliente de Forever 70338 capturado. El cliente de la era Midnight es otro tipo de juego de la familia Mainline; no deduzcas su ID de proyecto a partir de esa relación.

</details>

Divide archivos con variables del TOC

Las variables del TOC [Family] y [Game] se expanden al cargar, de modo que un mismo AddonName.toc puede apuntar a archivos distintos para cada cliente sin ninguna bifurcación en Lua.

Split.toc
## Interface: 16001
## Title: Split Example

[Family]\File.lua
[Game]\File.lua

[Family]se convierte en Mainline o Classic. [Game] se convierte en Camelot en un cliente de Forever. Los otros tipos de juego listados incluyen Standard, Vanilla y TBC.Fuente: formato TOC, consultada el 28 sept 2026.

La misma página lista un sufijo camelot específico del cliente, de modo que AddonName_Camelot.toc se carga en lugar del archivo por defecto. Su tabla de interfaz pone 16001 en la fila llamada "Forever Beta" y deja en blanco "Forever Test" y "Forever", así que no des por hecho que ese número seguirá vigente después de la beta.

Consulta un objeto sin pelearte con la caché

C_Item.GetItemInfoDevuelve 18 valores separados, no una tabla: nombre, enlace, calidad, nivel y así hasta itemDescription. Responde nil hasta que el cliente haya guardado ese objeto en caché, exista o no el objeto.Fuente: C_Item.GetItemInfo, consultada el 28 sept 2026.

Solicita los datos y luego lee el evento

ItemLookup.lua
local function OnItemData(_, event, itemID, success)
    if not success then return end
    local name = C_Item.GetItemInfo(itemID)
    if name then print(name, "is cached now") end
end

local frame = CreateFrame("Frame")
frame:SetScript("OnEvent", OnItemData)
frame:RegisterEvent("ITEM_DATA_LOAD_RESULT")

C_Item.RequestLoadItemDataByID(4306)

Desde el parche 10.0.5, el servidor puede responder success = false para un ID de objeto concreto, así que comprueba ese indicador antes de volver a llamar a GetItemInfo con el mismo ID en la misma sesión.Fuente: ITEM_DATA_LOAD_RESULT, consultada el 28 sept 2026.

RequestLoadItemDataByID también acepta un enlace de objeto completo, así que puedes pasarle lo que te dé un gancho de descripción emergente o un enlace del chat, sin extraer antes el ID numérico.

Consulta un hechizo de la misma forma

C_Spell.GetSpellInfodevuelve una sola tabla, a diferencia de la llamada de objetos: spellInfo.name, spellInfo.iconID, spellInfo.castTime y el resto. Sustituyó a las antiguas GetSpellInfo y C_SpellBook.GetSpellInfo, que eran globales.Fuente: C_Spell.GetSpellInfo, consultada el 28 sept 2026.

SpellLookup.lua
local function OnSpellData(_, event, spellID, success)
    local info = C_Spell.GetSpellInfo(spellID)
    if not info then return end
    -- success == false can still mean a partial record, such as no description.
    print(info.name, info.castTime, success and "full" or "partial")
end

local frame = CreateFrame("Frame")
frame:SetScript("OnEvent", OnSpellData)
frame:RegisterEvent("SPELL_DATA_LOAD_RESULT")

C_Spell.RequestLoadSpellData(2061)

La wiki dice que success "Might return false when only partial spell data is available" (puede devolver false cuando solo hay datos parciales del hechizo), como todo salvo la descripción.Fuente: SPELL_DATA_LOAD_RESULT, consultada el 28 sept 2026. Por eso la plantilla lee la tabla incluso con un resultado false en lugar de abandonar. La lista de errores de la IA en addons recoge otros casos en los que una respuesta generada se salta una comprobación como esta.

Ambas llamadas aceptan un ID de hechizo o un nombre de hechizo. Si pasas un nombre, C_Spell.GetSpellInfo busca primero la sustitución actual de ese hechizo en el jugador, así que una habilidad transformada puede devolver el icono del hechizo que la sustituye.

Retrasa trabajo sin un script OnUpdate

C_Timer.Afterprograma una devolución de llamada única. Un mensaje de Blizzard citado en la wiki dice que el sistema de temporizadores solo trabaja cuando se crea o se destruye un temporizador, mientras que un manejador OnUpdate cuesta una llamada de Lua en cada fotograma renderizado.Fuente: C_Timer.After, consultada el 28 sept 2026.

Debounce.lua
local pendingSave = false

local function ScheduleSave()
    if pendingSave then return end
    pendingSave = true
    C_Timer.After(2, function()
        pendingSave = false
        -- update an in-memory table or refresh a preview here
    end)
end

Use this to debounce follow-up work after a setting changes, such as updating an in-memory table or refreshing a preview. The timer does not write SavedVariables to disk; the client persists declared variables on logout or UI reload.

Lee los metadatos de tu propio TOC

C_AddOns.GetAddOnMetadata(name, variable)lee Title, Notes, Author, Version o cualquier campo X- personalizado directamente de tu propio archivo TOC, sin distinguir mayúsculas de minúsculas.Fuente: C_AddOns.GetAddOnMetadata, consultada el 28 sept 2026.

Version.lua
local addonName = ...
local version = C_AddOns.GetAddOnMetadata(addonName, "Version") or "dev"
print(("%s v%s loaded"):format(addonName, version))

A diferencia de la antigua global GetAddOnMetadata, esta llamada lanza un error si el nombre no coincide con un addon instalado, así que envuelve en pcall cualquier consulta de la que no estés seguro. Muestra la versión en una descripción emergente del compartimento para que un jugador que informe de un error pueda dártela sin abrir el archivo TOC.

Añade un panel de opciones con la API Settings

La wiki dice que la API Settings se rehízo en el parche 10.0.0, con más cambios incompatibles en 11.0.2. El código de opciones copiado de un tutorial anterior a 10,0 no coincidirá con las llamadas de abajo, ni tampoco una respuesta de IA entrenada con esos tutoriales.Fuente: Settings API, consultada el 28 sept 2026.

Options.lua
local addonName, ns = ...

function ns.BuildOptions(db)
    local category = Settings.RegisterVerticalLayoutCategory("Beacon Notes")
    local setting = Settings.RegisterAddOnSetting(
        category, "BeaconNotes_Locked", "locked", db,
        "boolean", "Lock the notes frame", false
    )
    Settings.CreateCheckbox(category, setting, "Prevents dragging the frame.")
    Settings.RegisterAddOnCategory(category)
end

-- Call ns.BuildOptions(ns.db) at the end of your ADDON_LOADED handler.

Crea el panel dentro de ADDON_LOADED, después de que el patrón de SavedVariables anterior haya rellenado la tabla. En el ámbito del archivo la tabla guardada aún no existe, así que el ajuste se enlazaría a nil.

Fija un punto de ruta sin una llamada protegida

Un reporte de la comunidad en la beta de Warcraft Forever dice que C_SuperTrack.SetSuperTrackedUserWaypoint está protegida en este cliente. La llamada falla sin avisar y solo suma uno a un contador de errores de interfaz del panel AddOns, que "only resets on a full client restart, not on /reload."Fuente: Addons, consultada el 28 sept 2026. (solo se restablece al reiniciar el cliente por completo, no con /reload). El mismo reporte señala C_Map.SetUserWaypoint como la forma segura de colocar una marca. Considera ambas cosas sin confirmar hasta que las pruebes.

Los puntos de ruta son el terreno de los addons de guías. Zygor Guides anuncia una "3D Waypoint Arrow" y RestedXP usa "Follow the arrow" como eslogan, así que comprueba cómo se comporta un addon de rutas en la beta antes de construir tu propio código de marcas sobre él.

Waypoint.lua
local mapID = C_Map.GetBestMapForUnit("player")

if mapID and C_Map.CanSetUserWaypointOnMap(mapID) then
    local pos = C_Map.GetPlayerMapPosition(mapID, "player")
    if pos then
        C_Map.SetUserWaypoint(UiMapPoint.CreateFromVector2D(mapID, pos))
    end
end

El mismo reporte indica que ChatFrame_OpenChat y escribir atributos seguros en los marcos de unidad de Blizzard también están protegidos, con HandleModifiedItemClick como vía segura para los enlaces con Mayús+clic. Ninguna de las tres lanza un error capturable, así que un envoltorio pcall no te dirá que la llamada falló. Contrasta una plantilla nueva con la guía para desarrolladores de addons y la descarga de Forever Beacon antes de publicarla, porque nada de esta página se ha ejecutado aún en un cliente de Forever.

Datos clave

  • Interfaz TOC Cambiada16001 en la fila Forever Beta de la tabla TOC de Warcraft Wiki; confírmalo con /dump select(4, GetBuildInfo())Classic: Classic Era indica 11509 en la misma tabla.
  • SavedVariablesReportados como perdidos entre sesiones a mediados de septiembre; una respuesta del 25 de septiembre dice que la compilación 70009 vuelve a cargarlos
  • Addon compartment Novedad en ForeverLos campos del TOC están documentados para Mainline; aún no se han probado aquí en un cliente de Forever
  • Bloqueo de combate Igual que en ClassicLos marcos protegidos no se pueden mostrar, ocultar ni mover en combate; pon la acción en cola hasta PLAYER_REGEN_ENABLED

Fuentes

  1. TOC formatWarcraft Wiki
  2. Saving variables between game sessionsWarcraft Wiki
  3. WOWF Beta: Addon SavedVariables appear to write correctly to disk but are not restored at startupWorld of Warcraft Forums (player bug report)
  4. Addon compartmentWarcraft Wiki
  5. InCombatLockdownWarcraft Wiki
  6. Secret ValuesWarcraft Wiki
  7. AddonsWarcraft Forever
  8. WOW_PROJECT_IDWarcraft Wiki
  9. WoW: Forever Will Have Addon Changes from MidnightWowhead
  10. C_Item.GetItemInfoWarcraft Wiki
  11. ITEM_DATA_LOAD_RESULTWarcraft Wiki
  12. C_Spell.GetSpellInfoWarcraft Wiki
  13. C_Timer.AfterWarcraft Wiki
  14. C_AddOns.GetAddOnMetadataWarcraft Wiki
  15. Settings APIWarcraft Wiki
  16. What the WoW Forever beta breaks for addons: secret health values, dead secure snippets, new APIsWoW Forever Builds
  17. SPELL_DATA_LOAD_RESULTWarcraft Wiki
  18. RestedXP home pageRestedXP
  19. Zygor Guides home pageZygor Guides
  20. Blizzard client source: ProjectConstants.lua (retained build 70338; mirror may change)Blizzard Entertainment (source mirrored by Gethe)
  21. Blizzard client source: SlashCommandsRegistry.lua (retained build 70338; mirror may change)Blizzard Entertainment (source mirrored by Gethe)
  22. Blizzard client source: StaticPopup.lua (retained build 70338; mirror may change)Blizzard Entertainment (source mirrored by Gethe)
  23. Lua 5.1 Reference ManualLua.org
  24. Blizzard client source: ChatInfoDocumentation.lua (retained build 70338; mirror may change)Blizzard Entertainment (source mirrored by Gethe)