What Does the Myfirstaddon Do Again?
This guide describes how to brand a simple HelloWorld addon, use slash commands and store user settings.
Contents
- i Getting started
- 2 Hello World
- 2.1 Running scripts
- 2.ii Creating an AddOn
- 3 Evolution tips
- 4 Responding to events
- 4.ane Handling multiple events
- 5 Slash commands
- six SavedVariables
- 7 Options Panel
- 8 AddOn namespace
- 9 Publishing
Getting started
- You need a basic agreement of Lua, otherwise run across Introduction to Lua or other tutorials.
- A simple text editor like VS Code or Notepad++.
Hello World
Running scripts
You lot tin execute Lua scripts from the chat window or in a macro with the /run or /script command. In that location is no deviation between them.
/run print("How-do-you-do World!") To quickly turn scripts like this into an addon, just remove the "/run" part and paste it into https://addon.bool.no/
Creating an AddOn
An addon consists of Lua/XML files and a TOC file. We won't be using XML since most things that are possible in XML can also be done in Lua.
Go to your AddOns folder and create a new binder with the following files:
World of Warcraft\_retail_\Interface\AddOns\HelloWorld
HelloWorld.lua
HelloWorld.toc
## Interface: 90200 ## Version: 1.0.0 ## Title: Hello Globe ## Notes: My first addon ## Writer: YourName HelloWorld.lua
- The name of the TOC file must match the folder name or the addon won't be detected by the game.
- The TOC Interface metadata
90200every bit returned by GetBuildInfo() tells which version of the game the addon was made for. If they don't match then the addon will be marked out-of-appointment in the addon list.
Load upward Earth of Warcraft, the addon should show upwardly in the addon listing and greet you upon login.
-
VS Code
-
AddOn List
-
In-game
Evolution tips
See also: Lua Coding Tips
- When updating addon lawmaking employ /reload to test the new changes, you lot may want to put it on a macro hotkey; every bit well as temporarily disabling any unnecessary addons that would increase loading time.
- Get an mistake reporting addon similar BugSack or plow on
/console scriptErrors 1 - There is the /dump slash command for general debugging, /etrace for showing events and /fstack for debugging visible UI elements.
- Consign, clone, download or bookmark Blizzard'southward user interface lawmaking a.grand.a. the FrameXML. If you don't know what a specific API does it'south best to just reference it in FrameXML. Not everything is documented so we by and large look through the code from Blizzard or other addons.
- For VS Code the Lua extension by Sumneko adds IntelliSense features like code completion.
Responding to events
- Main commodity: Handling events
Nigh every action in the game is an Issue which tells the UI that something happened. For example CHAT_MSG_CHANNEL fires when someone sends a bulletin in a chat channel similar Full general and Trade.
To respond to events you create a frame with CreateFrame() and register the events to it.
Event payload in the chat window and /etrace
local function OnEvent ( cocky , result , ...) impress ( result , ...) stop local f = CreateFrame ( "Frame" ) f : RegisterEvent ( "CHAT_MSG_CHANNEL" ) f : SetScript ( "OnEvent" , OnEvent ) Another instance, to play a sound on levelup with PlaySoundFile() or PlayMusic() y'all register for the PLAYER_LEVEL_UP event.
local f = CreateFrame ( "Frame" ) f : RegisterEvent ( "PLAYER_LEVEL_UP" ) f : SetScript ( "OnEvent" , function () PlayMusic ( 642322 ) -- sound/music/pandaria/mus_50_toast_b_hero_01.mp3 end ) Handling multiple events
When registering multiple events it can be messy if there are a lot of them.
local function OnEvent ( self , event , ...) if issue == "ADDON_LOADED" then local addOnName = ... print ( result , addOnName ) elseif event == "PLAYER_ENTERING_WORLD" then local isLogin , isReload = ... print ( event , isLogin , isReload ) elseif event == "CHAT_MSG_CHANNEL" then local text , playerName , _ , channelName = ... print ( event , text , playerName , channelName ) end end local f = CreateFrame ( "Frame" ) f : RegisterEvent ( "ADDON_LOADED" ) f : RegisterEvent ( "PLAYER_ENTERING_WORLD" ) f : RegisterEvent ( "CHAT_MSG_CHANNEL" ) f : SetScript ( "OnEvent" , OnEvent ) Which can be refactored to this:
local f = CreateFrame ( "Frame" ) function f : OnEvent ( effect , ...) cocky [ outcome ]( self , issue , ...) end role f : ADDON_LOADED ( result , addOnName ) print ( event , addOnName ) terminate function f : PLAYER_ENTERING_WORLD ( outcome , isLogin , isReload ) print ( event , isLogin , isReload ) finish part f : CHAT_MSG_CHANNEL ( event , text , playerName , _ , channelName ) impress ( outcome , text , playerName , channelName ) end f : RegisterEvent ( "ADDON_LOADED" ) f : RegisterEvent ( "PLAYER_ENTERING_WORLD" ) f : RegisterEvent ( "CHAT_MSG_CHANNEL" ) f : SetScript ( "OnEvent" , f . OnEvent ) Slash commands
- Principal article: Creating a slash command
- FrameXML: RegisterNewSlashCommand()
Slash commands are an like shooting fish in a barrel mode to let users interact with your addon. Whatsoever SLASH_* globals will automatically exist registered as a slash command.
Using a slash control
-- increment the index for each slash command SLASH_HELLOW1 = "/helloworld" SLASH_HELLOW2 = "/hw" -- ascertain the corresponding slash command handler SlashCmdList.HELLOW = part(msg, editBox) local name1, name2 = strsplit(" ", msg) if #name1 > 0 and then -- check for empty string print(format("hello %s and also %s", name1, name2 or "Carol")) else print("Please give at to the lowest degree one name") stop stop
Nosotros tin can also add a shorter /reload command.
SLASH_NEWRELOAD1 = "/rl" SlashCmdList . NEWRELOAD = ReloadUI SavedVariables
- Main article: Saving variables between game sessions
To store data or save user settings, set the SavedVariables in the TOC which will persist betwixt sessions. You tin /reload instead of restarting the game customer when updating the TOC file.
## Interface: 90200 ## Version: 1.0.0 ## Title: Hi World ## Notes: My first addon ## Author: YourName ## SavedVariables: HelloWorldDB HelloWorld.lua SavedVariables are only attainable once the corresponding ADDON_LOADED event fires. This example prints how many times you logged in (or reloaded) with the addon enabled.
local function OnEvent ( cocky , event , addOnName ) if addOnName == "HelloWorld" so -- name as used in the folder name and TOC file name HelloWorldDB = HelloWorldDB or {} -- initialize it to a table if this is the commencement time HelloWorldDB . sessions = ( HelloWorldDB . sessions or 0 ) + 1 impress ( "You lot loaded this addon " .. HelloWorldDB . sessions .. " times" ) end end local f = CreateFrame ( "Frame" ) f : RegisterEvent ( "ADDON_LOADED" ) f : SetScript ( "OnEvent" , OnEvent ) This case initializes the SavedVariables with default values. It also updates the DB when new keys are added to the defaults tabular array.
The CopyTable() function is divers in FrameXML. GetBuildInfo() is an API function. Depending on the addon consider using AceDB for managing SavedVariables.
local defaults = { sessions = 0 , someOption = true , --someNewOption = "banana", } local part OnEvent ( self , event , addOnName ) if addOnName == "HelloWorld" then HelloWorldDB = HelloWorldDB or {} self . db = HelloWorldDB -- makes it more readable and generally a good practice for k , five in pairs ( defaults ) practice -- re-create the defaults table and mayhap any new options if self . db [ thousand ] == nothing then -- avoids resetting whatever false values self . db [ chiliad ] = v end end self . db . sessions = self . db . sessions + 1 print ( "Yous loaded this addon " .. self . db . sessions .. " times" ) print ( "someOption is" , self . db . someOption ) local version , build , _ , tocversion = GetBuildInfo () print ( format ( "The current WoW build is %s (%d) and TOC is %d" , version , build , tocversion )) end end local f = CreateFrame ( "Frame" ) f : RegisterEvent ( "ADDON_LOADED" ) f : SetScript ( "OnEvent" , OnEvent ) SLASH_HELLOW1 = "/hw" SLASH_HELLOW2 = "/helloworld" SlashCmdList . HELLOW = function ( msg , editBox ) if msg == "reset" then HelloWorldDB = CopyTable ( defaults ) -- reset to defaults f . db = HelloWorldDB print ( "DB has been reset to default" ) elseif msg == "toggle" then f . db . someOption = non f . db . someOption impress ( "Toggled someOption to" , f . db . someOption ) end end Tips for troubleshooting tables:
-
/dump HelloWorldDBor/tinspect HelloWorldDBor/run for grand, v in pairs(HelloWorldDB) do print(1000, v) stopshows the contents of a (global) tabular array. -
/run wipe(HelloWorldDB)or/run for k in pairs(HelloWorldDB) do HelloWorldDB[k] = nil terminateempties the table. -
/run HelloWorldDB = nothing; ReloadUI()removes the tabular array reference and reloads the UI.
Options Panel
- Main article: Using the Interface Options Addons panel
It would exist more user-friendly to provide a graphical user interface. This example prints a message when you lot jump, if the choice is enabled. Information technology likewise opens the options panel with the slash command.
FrameXML:
- InterfaceOptions_AddCategory()
- InterfaceOptionsCheckButtonTemplate
- UIPanelButtonTemplate
Options Console 1
Options Panel 2
local f = CreateFrame ( "Frame" ) f . defaults = { someOption = true , } function f : OnEvent ( event , addOnName ) if addOnName == "HelloWorld" and so HelloWorldDB = HelloWorldDB or CopyTable ( self . defaults ) self . db = HelloWorldDB cocky : SetupOptions () hooksecurefunc ( "JumpOrAscendStart" , function () if cocky . db . someOption then print ( "Your character jumped." ) end cease ) end finish f : RegisterEvent ( "ADDON_LOADED" ) f : SetScript ( "OnEvent" , f . OnEvent ) function f : SetupOptions () self . panel = CreateFrame ( "Frame" ) self . panel . proper noun = "HelloWorld" local cb = CreateFrame ( "CheckButton" , zip , cocky . panel , "InterfaceOptionsCheckButtonTemplate" ) cb : SetPoint ( "TOPLEFT" , 20 , - twenty ) cb . Text : SetText ( "Print when you jump" ) cb . SetValue = role ( _ , value ) self . db . someOption = ( value == "1" ) -- value can be either "0" or "i" end cb : SetChecked ( self . db . someOption ) -- set the initial checked state local btn = CreateFrame ( "Push" , nada , self . console , "UIPanelButtonTemplate" ) btn : SetPoint ( "TOPLEFT" , cb , 0 , - 40 ) btn : SetText ( "Click me" ) btn : SetWidth ( 100 ) btn : SetScript ( "OnClick" , function () impress ( "You lot clicked me!" ) finish ) InterfaceOptions_AddCategory ( self . console ) end SLASH_HELLOW1 = "/hw" SLASH_HELLOW2 = "/helloworld" SlashCmdList . HELLOW = part ( msg , editBox ) -- https://github.com/Stanzilla/WoWUIBugs/issues/89 InterfaceOptionsFrame_OpenToCategory ( f . panel ) InterfaceOptionsFrame_OpenToCategory ( f . panel ) terminate This reference instance has a couple of checkboxes (with related functionality) and a reset button.
| Options Panel example 2 |
|---|
| GitHub source HelloWorld.toc ## Interface: 90200 ## Version: i.0.0 ## Title: Hi Earth ## Notes: My first addon ## Author: YourName ## SavedVariables: HelloWorldDB Core.lua Options.lua Core.lua HelloWorld = CreateFrame ( "Frame" ) office HelloWorld : OnEvent ( event , ...) self [ event ]( self , consequence , ...) end HelloWorld : SetScript ( "OnEvent" , HelloWorld . OnEvent ) HelloWorld : RegisterEvent ( "ADDON_LOADED" ) function HelloWorld : ADDON_LOADED ( event , addOnName ) if addOnName == "HelloWorld" and then HelloWorldDB = HelloWorldDB or {} self . db = HelloWorldDB for m , five in pairs ( self . defaults ) exercise if self . db [ one thousand ] == cipher then self . db [ g ] = v end finish self . db . sessions = cocky . db . sessions + one print ( "You loaded this addon " .. self . db . sessions .. " times" ) local version , build , _ , tocversion = GetBuildInfo () print ( format ( "The electric current WoW build is %due south (%d) and TOC is %d" , version , build , tocversion )) self : RegisterEvent ( "PLAYER_ENTERING_WORLD" ) hooksecurefunc ( "JumpOrAscendStart" , self . JumpOrAscendStart ) self : SetupOptions () self : UnregisterEvent ( issue ) end end function HelloWorld : PLAYER_ENTERING_WORLD ( event , isLogin , isReload ) if isLogin and self . db . hullo then DoEmote ( "Hello" ) end end -- annotation we don't pass `cocky` here considering of hooksecurefunc, hence the dot instead of colon role HelloWorld . JumpOrAscendStart () if HelloWorld . db . leap then print ( "Your grapheme jumped." ) finish end function HelloWorld : COMBAT_LOG_EVENT_UNFILTERED ( event ) -- it'southward more convenient to work with the CLEU params as a vararg cocky : CLEU ( CombatLogGetCurrentEventInfo ()) end local playerGUID = UnitGUID ( "player" ) local MSG_DAMAGE = "Your %due south hitting %due south for %d damage." function HelloWorld : CLEU (...) local timestamp , subevent , _ , sourceGUID , sourceName , sourceFlags , sourceRaidFlags , destGUID , destName , destFlags , destRaidFlags = ... local spellId , spellName , spellSchool local amount , overkill , school , resisted , blocked , captivated , disquisitional , glancing , crushing , isOffHand local isDamageEvent if subevent == "SWING_DAMAGE" then amount , overkill , school , resisted , blocked , absorbed , critical , glancing , burdensome , isOffHand = select ( 12 , ...) isDamageEvent = truthful elseif subevent == "SPELL_DAMAGE" and then spellId , spellName , spellSchool , amount , overkill , school , resisted , blocked , absorbed , critical , glancing , crushing , isOffHand = select ( 12 , ...) isDamageEvent = truthful end if isDamageEvent and sourceGUID == playerGUID so -- get the link of the spell or the MELEE globalstring local activity = spellId and GetSpellLink ( spellId ) or MELEE print ( MSG_DAMAGE : format ( action , destName , amount )) stop end SLASH_HELLOW1 = "/hw" SLASH_HELLOW2 = "/helloworld" SlashCmdList . HELLOW = office ( msg , editBox ) -- https://github.com/Stanzilla/WoWUIBugs/issues/89 InterfaceOptionsFrame_OpenToCategory ( HelloWorld . panel_main ) InterfaceOptionsFrame_OpenToCategory ( HelloWorld . panel_main ) end Options.lua HelloWorld . defaults = { sessions = 0 , hello = fake , mushroom = faux , leap = true , combat = true , --someNewOption = "banana", } local callbacks = {} local role CreateIcon ( icon , width , height , parent ) local f = CreateFrame ( "Frame" , nil , parent ) f : SetSize ( width , height ) f . tex = f : CreateTexture () f . tex : SetAllPoints ( f ) f . tex : SetTexture ( icon ) return f end -- if `update` is passed, telephone call information technology when the selection is initialized and when clicked function HelloWorld : CreateCheckbox ( savedvar , proper name , parent , update ) local cb = CreateFrame ( "CheckButton" , goose egg , parent , "InterfaceOptionsCheckButtonTemplate" ) cb . Text : SetText ( proper noun ) cb . SetValue = function ( _ , value ) if blazon ( value ) == "string" then value = ( value == "i" ) terminate self . db [ savedvar ] = value cb : SetChecked ( self . db [ savedvar ]) if update so update ( value ) end end cb : SetValue ( cocky . db [ savedvar ]) -- init self : RegisterCallback ( "OnReset" , function () cb : SetValue ( self . defaults [ savedvar ]) end ) return cb end role HelloWorld : SetupOptions () -- principal console self . panel_main = CreateFrame ( "Frame" ) cocky . panel_main . name = "HelloWorld" local cb_hello = self : CreateCheckbox ( "hello" , "Exercise the |cFFFFFF00/how-do-you-do|r emote when y'all login" , cocky . panel_main ) cb_hello : SetPoint ( "TOPLEFT" , xx , - xx ) local cb_mushroom = self : CreateCheckbox ( "mushroom" , "Show a mushroom on your screen" , cocky . panel_main , self . UpdateIcon ) cb_mushroom : SetPoint ( "TOPLEFT" , cb_hello , 0 , - 30 ) local cb_jump = cocky : CreateCheckbox ( "jump" , "Print when y'all jump" , self . panel_main ) cb_jump : SetPoint ( "TOPLEFT" , cb_mushroom , 0 , - xxx ) local cb_combat = self : CreateCheckbox ( "combat" , "Print when you harm a unit" , self . panel_main , function ( value ) cocky : UpdateEvent ( value , "COMBAT_LOG_EVENT_UNFILTERED" ) end ) cb_combat : SetPoint ( "TOPLEFT" , cb_jump , 0 , - 30 ) local btn_reset = CreateFrame ( "Button" , nada , cocky . panel_main , "UIPanelButtonTemplate" ) btn_reset : SetPoint ( "TOPLEFT" , cb_combat , 0 , - 40 ) btn_reset : SetText ( RESET ) btn_reset : SetWidth ( 100 ) btn_reset : SetScript ( "OnClick" , office () HelloWorldDB = CopyTable ( HelloWorld . defaults ) self . db = HelloWorldDB self : FireCallbacks ( "OnReset" ) end ) InterfaceOptions_AddCategory ( HelloWorld . panel_main ) -- sub panel local panel_shroom = CreateFrame ( "Frame" ) panel_shroom . name = "Shrooms" panel_shroom . parent = self . panel_main . proper name for i = 1 , 10 do local icon = CreateIcon ( "interface/icons/inv_mushroom_11" , 32 , 32 , panel_shroom ) icon : SetPoint ( "TOPLEFT" , 20 , - 32 * i ) end InterfaceOptions_AddCategory ( panel_shroom ) end -- crappy callback handler office HelloWorld : RegisterCallback ( name , func ) callbacks [ name ] = callbacks [ name ] or {} callbacks [ name ][ func ] = truthful end office HelloWorld : FireCallbacks ( name ) for func in pairs ( callbacks [ name ]) do func () cease end office HelloWorld . UpdateIcon ( value ) if not HelloWorld . mushroom then HelloWorld . mushroom = CreateIcon ( "interface/icons/inv_mushroom_11" , 64 , 64 , UIParent ) HelloWorld . mushroom : SetPoint ( "CENTER" ) end if value then HelloWorld . mushroom : Show () else HelloWorld . mushroom : Hide () end finish -- a bit more efficient to register/unregister the event when it fires a lot function HelloWorld : UpdateEvent ( value , issue ) if value then self : RegisterEvent ( event ) else cocky : UnregisterEvent ( consequence ) end stop |
AddOn namespace
- Master commodity: Using the AddOn namespace
The addon namespace is a individual table shared between Lua files in the same addon. This way y'all can avoid leaking variables to the global environment.
HelloWorld.toc
## Interface: 90200 ## Version: one.0.0 ## Title: Hello Globe FileA.lua FileB.lua
FileA.lua
local _ , ns = ... ns . foo = "Banana" FileB.lua
local addonName , ns = ... print ( addonName , ns . foo ) -- prints "HelloWorld" and "Assistant"
Or y'all tin use a unique global variable.
FileA.lua
MyAddon = {} MyAddon . value = 0 function MyAddon : DoSomething ( increment ) cocky . value = cocky . value + increase end MyAddon : DoSomething ( two ) FileB.lua
MyAddon : DoSomething ( 3 ) print ( MyAddon . value ) -- 5 Publishing
Addons can be published on CurseForge (guide) and/or WoWInterface (guide).
Follow-up: Ace3 for Dummies
Other guides
- MMO-Champion: Creating Your Own WoW Addon
Source: https://wowpedia.fandom.com/wiki/Create_a_WoW_AddOn_in_15_Minutes
0 Response to "What Does the Myfirstaddon Do Again?"
Post a Comment