Wowpedia

We have moved to Warcraft Wiki. Click here for information and the new URL.

READ MORE

Wowpedia
 
im>Trela
No edit summary
 
Line 1: Line 1:
  +
[[Category:Tutorials| WelcomeHome - Your first Ace3 Addon]]
{{zonebox
 
  +
== Introduction ==
|faction=Alliance
 
|name=Lordaeron
 
|ss= Lordaeron's coat of arms.jpg
 
|capital=[[Capital City|Lordaeron]]<ref>"...Lordaeron's capital city (also called Lordaeron — silly humans).", [[Lands of Conflict]], pg. 108.</ref>
 
|races={{RaceIcon|Human|Male|Small}}{{RaceIcon|Human|Female|Small}} [[Human]]<br/> {{RaceIcon|Undead|Male|Small}}{{RaceIcon|Undead|Female|Small}} [[Forsaken]] <br/> {{RaceIcon|Undead|Male|Small}}{{RaceIcon|Undead|Female|Small}} [[Scourge]]
 
|government= Hereditary monarchy (Formerly){{WC3-inline}}
 
|rulers= [[Sylvanas Windrunner]], King [[Terenas Menethil II]] (former), King [[Arthas Menethil]] (former), [[Balnazzar]] (former),
 
|affiliation= Contested {{wow-inline}}, Alliance {{WC3-inline}}
 
}}
 
   
'''Lordaeron''' is a continent and a former [[human]] [[kingdom]] (aka '''Lordaeron Empire'''<ref>{{ref game|title=Warcraft III|expansion=[[The Frozen Throne]]|campaign=[[Legacy of the Damned]]|mission=[[King Arthas]]}}</ref>) of the [[Eastern Kingdoms]]. Its name comes from the three main [[Alliance]] race languages: "lorn" (in [[Dwarven|Dwarven]]) that means "land", "daer" (in [[Common]]) that means "people" and "ronae" (in [[Thalassian]]) that means peaceful.{{Cite|LoC|19}} Lordaeron's [[Capital City]] is also called Lordaeron<ref>"...Lordaeron's capital city (also called Lordaeron — silly humans).", Lands of Conflict, 108</ref>. Originally one of the [[Seven Kingdoms]] to emerge during the collapse of [[Arathor]], Lordaeron was ruled by the [[House of Menethil]] and provided refuge for the people of [[Stormwind]] following that kingdom's destruction at the end of the [[First War]]. During the [[Second War]], Lordaeron spearheaded the [[Alliance of Lordaeron|first Alliance's]] campaign against the [[Orcish Horde]], but was almost entirely consumed by the [[Scourge]] during the [[Third War]]. This former [[nation]]'s{{cite|WRPG|50}} {{cite|LoC|20, 31}} {{cite|LoM|170}} territory is now contested between the Scourge, the [[Forsaken]], and fragmented pockets of human resistance, most notably the [[Scarlet Crusade]] and the [[Argent Dawn]].
 
   
  +
== Getting Started ==
==City-state of Arathor==
 
[[Image:Lorderan Tabard.jpg|thumb|left|A Lordaeron tapestry.]]
 
Lordaeron was settled by the disenfranchised lords of [[Strom]], capital of the nation of [[Arathor]], who were part of a group that left Strom because they were desirous of the verdant lands to the north. The capital [[City of Lordaeron|city]] served as a spiritual destination for the citizens of the seven human kingdoms.
 
   
  +
<p>Since this is a tutorial about using the Ace3 libraries, I will start by showing you what we need to do to get a barebones addon loaded into the game.
==Lordaeron in Warcraft II==
 
  +
</p><p>Every addon is stored in a folder underneath the WoW main folder called &lt;WoW&gt;\Interface\Addons. Each addon goes in its own folder named after the addon. So to get started we will create a new folder called “WelcomeHome”.
{{WC2-section}}
 
  +
</p><p>When WoW finds a folder in the Addons directory, it looks inside that folder for a table of contents (TOC) file that has the same name as the folder. This TOC file contains a manifest of all the other files that make up the addon and is used by WoW to load your addon.
[[Image:Lorderan-flag.jpg|thumb|The flag of Lordaeron.]]
 
  +
</p><p>Let’s go ahead and create a barebones TOC file called WelcomeHome.toc:
*'''Leader:''' ''King Terenas II''
 
  +
</p>
*'''Nation Color:''' ''White''
 
  +
<pre>## Interface: 30000
*'''Background:''' ''Ruled by the benevolent King Terenas II, the nation of Lordaeron stands as the last bastion of hope for humanity. The armies of Lordaeron were the first to heed the call to arms issued by Sir Lothar and the people of Azeroth. As patron of the Alliance, King Terenas II has assumed the heavy mantle of leadership to protect all who abide in his domain. The armies of Lordaeron are deeply religious and are driven by the belief that humanity must stand steadfast against the blasphemous onslaught of the Horde.{{Cite|W2Man|42}}''
 
  +
## Title: Welcome Home
  +
## Notes: Displays a welcome message when you get to your home zone.
  +
## Author: Your Name Here
  +
## Version: 0.1
  +
## X-Category: Interface Enhancements
   
  +
Core.lua
==The Alliance of Lordaeron==
 
  +
</pre>
Following the opening of the [[Dark Portal]], the nations of [[Azeroth]] and [[Khaz Modan]] were conquered by the [[Horde (pre-Thrall)|Horde]]. The refugees from Azeroth, led by Lord [[Anduin Lothar]], fled across the sea to Lordaeron. There, Lothar convinced the leaders of the human nations, as well as the [[dwarf|dwarves]] of [[Ironforge]], [[gnome]]s of [[Gnomeregan]], and [[high elf|high elves]] of [[Quel'Thalas]], to join forces in the [[Alliance of Lordaeron]].
 
  +
<p>These are the basic metadata associated with your addon. For the X-Category option, it is recommended that you pick one of the [[Ace2 Categories|Ace2 Categories]] to be consistent with the rest of Ace. There are many other attributes you can include in your TOC file, for a more comprehensive list see [http://www.wowwiki.com/The_TOC_Format The TOC Format] on wowwiki.com.
  +
</p><p>Then create an empty text file called Core.lua in the same directory and start up WoW. After logging in, you should be able to click the “Addons” button in the lower left and see that your addon is being recognized by the game. Go ahead and make sure that it is enabled before exiting out of that screen and then out of the game.
  +
</p><p>Step 1 complete! We are in the game. But we aren’t doing anything yet. That is next.
  +
</p>
   
  +
== Bringing Ace3 to the Party ==
Under the leadership of King [[Terenas Menethil II]] and Lord Lothar, the Alliance was victorious, pushing the Horde back to the Dark Portal, and destroying the gateway to the orcs' homeworld. Lothar fell in the assault on [[Blackrock Spire]], and with the loss of his political skill, rifts developed between the Alliance nations. The main issue of dispute was a tax levied by King Terenas to finance the internment of the orcs. Though Lordaeron attempted to retain its central role, several nations pulled their support from the Alliance.
 
   
  +
Since this tutorial is about learning to create addons using Ace3, it is time for us to go ahead and bring those libraries into our addon. Ace3 uses a concept called “Embedded Libraries” that allows mod developers to have the libraries in their codebase without actually duplicating the code when it is loaded along side other mods that use the same libraries.
==The Scourge of Lordaeron==
 
Following years of debate over the internment of the orcs, a [[plague]] appeared in the north of Lordaeron. As it spread throughout the towns and cities of Lordaeron, one after another fell to the [[Scourge]], most notably [[Andorhal]] and [[Stratholme]]. Finally, with the corruption of the heir to the throne of Lordaeron, Prince [[Arthas Menethil]], the capital city itself succumbed. Arthas' captains [[Falric]] and [[Marwyn]] alone were responsible for the slaughter of the aristocracy. Thus, in an ironic twist of fate, it was Lordaeron that lay in ruins, with refugees streaming to [[Khaz Modan]] and [[Azeroth]].
 
   
  +
This means you need to get local copies of the libraries you intend to use. At this point the easiest way to do this is to download them from the Ace SVN files mirror and put them on your system.
Arthas briefly assumed the title of King, and ruled as an agent of the [[Scourge]], before the eruption of a civil war between his forces, the remaining [[dreadlord]]s, and the free undead led by [[Sylvanas Windrunner]]. Windrunner's forces, the [[Forsaken]], achieved a victory over the dreadlords and Scourge, claiming the ruined former capital of Lordaeron (the "[[Ruins of Lordaeron]]"), under which they constructed the [[Undercity]].
 
   
  +
Open a browser and go to [http://files.wowace.com/ http://files.wowace.com/]. Download Ace3. This is the most recent build of the Ace libraries. Unzip it into a folder called Libs in your Addon's directory. Then go ahead and delete all of the new folders and files <b>except</b> these:
==Modern Lordaeron==
 
The remaining indigenous humans of Lordaeron may be broken down into three major groups, the Loyalists of Stormwind, the [[Scarlet Crusade]] and the [[Argent Dawn]]. The first are the folk of southern Lordaeron which have remained loyal to the Alliance, and have given their loyalty to the monarchy at Stormwind. These humans control three towns, scattered evenly across the southern regions of the nation. Southshore is the most important of these townships, and the only port north of the [[Thandol Span]] under Alliance control. To the north, nestled in the rolling foothills of Alterac lies the town of Hillsbrad, surrounded by the rich [[Hillsbrad Fields]]. The town controls an important mine on the coast, [[Azurelode Mine]]. Far to the west of these towns lies the town of [[Pyrewood Village]], which receives it aid not from Stormwind, but from [[Dalaran]]. The town is friendly to the Alliance, but the dire curse laid upon it by the mage [[Arugal]] has prevented it from further integration. To the south of the town lies a refugee camp outside of the [[Greymane Wall]], and its folk also support the Alliance.
 
   
  +
*[[AceAddon-3.0]]
The [[Scarlet Crusade]] hold lands scattered throughout northern Lordaeron, and are by far the most numerically well endowed of the human factions of the former kingdom. Nominally a part of the Alliance, they are so fearful of the undead that they will attack any individual on sight. They hold several key positions in the [[Tirisfal Glades]], including farmlands in the west, and their great stronghold in the region, the [[Scarlet Monastery]] above the north coast. In the [[Western Plaguelands]] they control the entire northern part of the zone from [[Hearthglen]]. They also maintain a presence in [[Andorhal]]. In the Eastern Plaguelands they control [[Tyr's Hand]], and still nominally control the [[Scarlet Enclave]] beyond, although it has become depopulated due to invasion by the Scourge, and migration to Northrend. Their capital lies at Stratholme, where they hold a portion of the city.
 
  +
*[[AceConfig-3.0]]
  +
*[[AceConsole-3.0]]
  +
*[[AceDB-3.0]]
  +
*[[AceEvent-3.0]]
  +
*[[AceGUI-3.0]]
  +
*[[CallbackHandler-1.0]]
  +
*[[LibStub]]
   
  +
<i>There are lots of Ace3 libraries for you to use, but for this tutorial we only need some of them. For more information on these libraries, please visit the [[Ace3]] page.</i>
The Argent Dawn has bases throughout the Plaguelands, and is made up of those humans who have taken a more moderate approach to the war against the dead, accepting Horde and even Forsaken into their ranks.
 
   
  +
You should now have a folder structure something like this:
The Alliance has further strength in the region as well. A small [[high elf|high elven]] outpost can also be found near the bounds of the [[Ghostlands]] in the [[Eastern Plaguelands]], [[Quel'Lithien Lodge]]. These elves have stayed committed to the Alliance, and are hostile to the Horde. Dalaranian troops also have a presence in southern [[Silverpine Forest]].
 
   
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> WelcomeHome
The Forsaken managed to take control of the heart of the old kingdom, claiming the [[Ruins of Lordaeron]] (and the [[Undercity]] beneath) as their base of operations. They use it to keep hold over much of the [[Tirisfal Glades]], and control the towns [[Brill]] and [[Deathknell]] along the main east-west road of the region. They also control [[The Sepulcher]] in the center of [[Silverpine Forest]], and the town of [[Tarren Mill]] in the [[Hillsbrad Foothills]].
 
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> Libs
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceAddon-3.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceAddon-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceAddon-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfig-3.0
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigCmd-3.0
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigDialog-3.0
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigDropdown-3.0
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigRegistry-3.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConfig-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConfig-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConsole-3.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConsole-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConsole-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceDB-3.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceDB-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceDB-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceEvent-3.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceEvent-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceEvent-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceGUI-3.0
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> widgets
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceGUI-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceGUI-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> CallbackHandler-1.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> CallbackHandler-1.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> CallbackHandler-1.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> LibStub
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> LibStub.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> Core.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> WelcomeHome.toc
   
  +
You now have the necessary Ace3 libraries in your addon's Libs folder, but if you were to launch WoW right now, they wouldn't be loaded. We need to tell WoW to load the files when it loads your addon and for that we're going to use a file called embeds.xml. So create a new file with the text below and save it as <tt>"embeds.xml"</tt> in the same folder as your .TOC file.
The Syndicate, in an attempt to punish Lordaeron for its role in the fall of their kingdom of [[Alterac]] have taken the fortress of [[Durnholde Keep]], and the surrounding countryside, although the [[Ravenholdt]] (a league of assassins) have attempted to curb their strength from their base at [[Ravenholdt Manor]] (Which also lies in former Lordaeron).
 
   
  +
<pre><Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
Finally, the Undead [[Scourge]] holds [[Stratholme]] and [[Andorhal]], as well as [[Scholomance]] in the southern [[Western Plaguelands]]. The Scourge are overwhelmingly the dominate force in much of Lordaeron, with a presence in all its zones except the [[Hillsbrad Foothills]].
 
  +
xsi:schemaLocation="http://www.blizzard.com/wow/ui/
  +
..\FrameXML\UI.xsd">
  +
<Script file="Libs\LibStub\LibStub.lua"/>
  +
<Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
  +
<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
  +
<Include file="Libs\AceGUI-3.0\AceGUI-3.0.xml"/>
  +
<Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/>
  +
<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/>
  +
<Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/>
  +
<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
  +
</Ui>
  +
</pre>
   
  +
The first three lines are just some xml-voodoo, just copy and paste. Then it's a line each for all the libraries we want to load. It is vital that we load LibStub first, since all the other libs depend on that. Then CallbackHandler that is used by several of our libraries and AceGUI needs to be loaded before AceConfig. Otherwise the order isn't important. Notice that it's &lt;Script&nbsp;/&gt; to load lua files and &lt;Include&nbsp;/&gt; to load xml files.
On a side note, the mage [[Arugal]] controls large tracts of land in Silverpine Forest from [[Shadowfang Keep]].
 
   
  +
Now let's update the .TOC file to include the embeds.xml. We include it above Core.lua to ensure that the libraries are loaded and available before the code in Core.lua is executed.
[[Image:LordaeronLoC.JPG|thumb|Lordaeron in the RPG.]]
 
[[Image:LordaeronWC3.JPG|thumb|Lordaeron in Warcraft III.]]
 
   
  +
<pre>## Interface: 20400
==Areas==
 
  +
## Title: Welcome Home
The [[Capital City of Lordaeron]] once controlled the [[Eastern Plaguelands]], the [[Western Plaguelands]], the [[Tirisfal Glades]], the [[Hillsbrad Foothills]], most of [[Silverpine Forest]], and part of the [[Alterac Mountains]] ([[Chillwind Point]], and the town of [[Strahnbrad]]).
 
  +
## Notes: Displays a welcome message when you get to your home zone.
Also possibly [[North Lordaeron]], which is currently a [[Closed Zone]].
 
  +
## Author: Your Name Here
  +
## Version: 0.1
  +
## SavedVariables: WelcomeHomeDB
  +
## OptionalDeps: Ace3
  +
## X-Embeds: Ace3
  +
## X-Category: Interface Enhancements
   
  +
embeds.xml
===Zones in World of Warcraft===
 
*[[Alterac Mountains]]
 
*[[Arathi Highlands]]
 
*[[Eastern Plaguelands]]
 
*[[Eversong Woods]]
 
*[[Ghostlands]]
 
*[[Hillsbrad Foothills]]
 
*[[Hinterlands]]
 
*[[Isle of Quel'Danas]]
 
*[[Silverpine Forest]]
 
*[[Tirisfal Glades]]
 
*[[Western Plaguelands]]
 
*[[Quel'thalas]] (comprised of [[Eversong Woods]] and [[Ghostlands]])
 
   
  +
Core.lua
===Other regions===
 
  +
</pre>
*[[Crestfall]]
 
*[[Dalaran]]
 
*[[Gilneas]]
 
*[[Kul Tiras]]
 
*[[Tol Barad]]
 
*[[Zul'Dare]]
 
*[[Channel Islands]]
 
*[[Northeron]]
 
*[[Undercity]] - Though Undercity is located in Tirisfal Glades, the city is also an underground region.{{cite|LoC|106}}
 
   
  +
I've included a few other things in the TOC that aren't actually required yet (e.g. SavedVariables), but we will need all of that eventually. The OptionalDeps part is there in case a user of your addon comes along later and chooses to use a standalone Ace3 instead of using the embedded libraries in your Libs folder and the X-Embeds is there to tell updaters what libs it needs to download separately if the user don't want to use the embedded libs.
==Notable Leaders==
 
* {{RaceIcon|Human|Male|Small}} [[Terenas Menethil II]]
 
* {{RaceIconExt|Arthas|Small}} [[Arthas Menethil]]
 
* {{RaceIconExt|Dreadlord|Small}} [[Balnazzar]]
 
* {{RaceIconExt|Sylvanas|Small}} [[Sylvanas Windrunner]]
 
   
  +
== Saying Hello ==
This is a chart following the events of [[Warcraft III]].
 
{| class="darktable" width="100%" style="text-align:center" cellspacing="0"
 
|-
 
!History:
 
|style="background:#555555;"|Monarchy
 
|style="background:#444444;"|Anarchy
 
|style="background:#555555;"|Burning Legion invasion
 
|style="background:#444444;"|Dreadlords Revolt to survive
 
|style="background:#555555;"|Forsaken revolt
 
|-
 
!Ruler:
 
|style="background:#008800;"|Terenas
 
|colspan="2" style="background:#000088;"|Arthas
 
|style="background:#880088;"|Balnazzar
 
|style="background:#666688;"|Sylvanas
 
|}
 
   
  +
This section is going to move a little faster than we have been moving. Start by opening Core.lua in a text editor and typing in the following code:
==See also==
 
*[[The Seven Kingdoms]]
 
*[[The Alliance of Lordaeron]]
 
*[[Ruins of Lordaeron]]
 
   
  +
<pre>WelcomeHome = LibStub("AceAddon-3.0"):NewAddon("WelcomeHome", "AceConsole-3.0")
==References==
 
<references/>
 
   
  +
function WelcomeHome:OnInitialize()
  +
-- Called when the addon is loaded
  +
end
   
  +
function WelcomeHome:OnEnable()
{{Lordaeron}}
 
  +
-- Called when the addon is enabled
{{Human nations}}
 
  +
end
{{Azeroth}}
 
  +
[[Category:Eastern Kingdoms]]
 
  +
function WelcomeHome:OnDisable()
[[Category:Human territories]]
 
  +
-- Called when the addon is disabled
[[Category:Lore]]
 
  +
end
[[Category:Human nations]]
 
  +
</pre>
  +
  +
This is the basic starting structure of an Ace3 addon. The first line creates an instance of the AceAddon class using the NewAddon() operator. Since we will also be printing to the chat window and accepting slash commands, I've gone ahead and included the mixin for [[AceConsole-3.0]]. Among other things, the AceConsole-3.0 mixin will get us access to the [http://wiki.wowace.com/index.php/AceConsole-3.0_API_Documentation#:Print.28....29 Print] method.
  +
  +
<i>Mixins are a concept common in many object oriented languages that allow you to bring additional functionality into your class. (For more on mixins, see the [http://en.wikipedia.org/wiki/Mixin mixins page on Wikipedia].)</i>
  +
  +
Following that are three method overrides. The first is executed only once when the UI loads and the next two are executed when the addon is enabled and disabled. (You can enable and disable Ace addons using /ace commands.)
  +
  +
It is important to note that OnEnable is called at the beginning if the addon is enabled when the UI loads. You will often have to choose which of these two method overrides to use. Generally, you should use the OnInitialize for those things that you don't need to undo and redo if the addon is disabled and enabled. Another important part of this decision has to do with what other things you will need and when you can be sure they are ready. More on this later...
  +
  +
Just to demonstrate that the addon is loading, we will have the addon print "Hello World!" to the chat window by adding one line of code to the OnEnable method.
  +
  +
<pre>function WelcomeHome:OnEnable()
  +
self:Print("Hello World!")
  +
end
  +
</pre>
  +
  +
Go ahead and restart WoW and enter in to the game. Once you are in, you should be able to scroll up in the chat window and find your message. You are now the proud author of the WoW version of Hello World!.
  +
  +
Before we go any further, go back over to your code and remove the print message. It is generally considered bad Ace form for your addon to toss a bunch of text into the chat window just because it has loaded. Too many addons do this already and there are other ways ("/ace list") to find out what Ace addons have loaded. You can also remove the OnDisable method since we won't be using it.
  +
  +
== Responding to Events ==
  +
  +
So, as I said, this addon will give a welcome message to the player when they arrive in their home zone. How will we know they are in their home zone? Simple, we will respond to one of the [http://www.wowwiki.com/Events/Z ZONE_CHANGED events] which the game fires when the player enters a new zone.
  +
  +
I’m sure you are asking yourself “Events? What the heck are those?” It turns out that like many other programming environments, WoW is event driven. Events are raised when things happen in the game and you can fire events when things happen in your code. In fact, without events, your addon would never even know what to do and when to start doing things. Eventually, everything your addon does is in response to an event.
  +
  +
Looking at the [http://www.wowwiki.com/Events/Z events list], it appears that ZONE_CHANGED will be the one we want, so let's hook that one by making a few changes to our Core.lua file.
  +
  +
Before we can subscribe to events, we need to get event support into our addon by including another mixin into our addon. We need to change the line where we create our addon to include the [[AceEvent-3.0]] mixin. Change your first line as shown here:
  +
  +
<pre>WelcomeHome = LibStub("AceAddon-3.0"):NewAddon("WelcomeHome", "AceConsole-3.0", "AceEvent-3.0")
  +
</pre>
  +
  +
Once we've done that, we will use the RegisterEvent method that is now available to us to subscribe to the ZONE_CHANGED event. We will replace the OnEnable override with the following code:
  +
  +
<pre>function WelcomeHome:OnEnable()
  +
-- Called when the addon is enabled
  +
self:RegisterEvent("ZONE_CHANGED")
  +
end
  +
</pre>
  +
  +
We also need to add in the event handler for that event by adding the following code to our Core.lua file:
  +
  +
<pre>function WelcomeHome:ZONE_CHANGED()
  +
self:Print("You have changed zones!")
  +
end
  +
</pre>
  +
  +
You might be wondering if we should call [[AceEvent-3.0#:UnregisterEvent.28.29|UnregisterEvent]] from our OnDisable override, but we don't have to do that because AceEvent-3.0 does it for us.
  +
  +
Now head back into the game and have your character leave the area he/she is presently in. You should see your message go by down in the chat area when you change zone.
  +
  +
== Reloading Your Addon Without Restarting WoW ==
  +
  +
Before we go any further, I want to show you a trick that will come in handy for you as you work on your addons.
  +
  +
Leave the game up and running, but switch over to your text editor. Change the event handler as follows...
  +
  +
<pre>function WelcomeHome:ZONE_CHANGED()
  +
self:Print("This is a different message!")
  +
end
  +
</pre>
  +
  +
Make sure you save your changes and then go back over to the game and switch zones. Which message did you see? The old one.
  +
  +
Why? Because WoW doesn't automatically reload your addon just because you made a change. One way to get your addon reloaded is to restart the game. Ugh. much better way to get it reloaded is by using the ReloadUI() command from the command line using the /script chat command:
  +
  +
<pre>/script ReloadUI()
  +
</pre>
  +
  +
Your screen will freeze for a minute while the UI reloads itself and all your other addons reload, but when it comes back up you should now be able to switch zones and see the new message.
  +
  +
From now on you can use this technique to reload your mod when you have made changes.
  +
  +
<i>Note: if you have [[AceConsole-2.0]] loaded with any of your addons or if you have the standalone version of [[Ace3]], you can use the following command to reload your UI:</i>
  +
  +
<pre>/rl
  +
</pre>
  +
  +
== Working with the WoW API ==
  +
  +
We now have a method that we know will get called when the player changes zones. But what zone is he in? Where does he keep his hearthstone set?
  +
  +
If we head back over to the [http://www.wowwiki.com/World_of_Warcraft_API World of Warcraft API] page and look in the [http://www.wowwiki.com/World_of_Warcraft_API#Character_Functions Character Functions] section, we will see the answer to the second question is a function called [http://www.wowwiki.com/API_GetBindLocation GetBindLocation]. This function returns the subzone name (e.g. "Tarren Mill") that contains the Inn where your hearthstone is set.
  +
  +
Next we need to figure out how to find what subzone we are in. If we look in the [http://www.wowwiki.com/World_of_Warcraft_API#Location_Functions Location Functions] section of the API docs, we will find a function called [http://www.wowwiki.com/API_GetSubZoneText GetSubZoneText] that looks about right. It returns either an empty string (if you aren't in a subzone) or the name of the subzone you are in.
  +
  +
We should be able to simply compare these two values in our event handler to decide whether we are home or not:
  +
  +
<pre>function WelcomeHome:ZONE_CHANGED()
  +
if GetBindLocation() == GetSubZoneText() then
  +
self:Print("Welcome Home!")
  +
end
  +
end
  +
</pre>
  +
  +
That's it! We should now have a functional addon doing what we want. Reload your UI and test it out.
  +
  +
== Chat Commands and Configuration ==
  +
But there are still lots of interesting things we can do. Let's start by adding support for the out-of-the-box slash commands by creating an options table at the top of the file and registering it with [[AceConfig-3.0]]:
  +
  +
<pre>WelcomeHome = LibStub("AceAddon-3.0"):NewAddon("WelcomeHome", "AceConsole-3.0", "AceEvent-3.0")
  +
  +
local options = {
  +
name = "WelcomeHome",
  +
handler = WelcomeHome,
  +
type = 'group',
  +
args = {
  +
},
  +
}
  +
  +
function WelcomeHome:OnInitialize()
  +
-- Called when the addon is loaded
  +
LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options, {"welcomehome", "wh"})
  +
end
  +
</pre>
  +
  +
At this point the options table is empty and we haven't provided any commands. For a complete rundown of the structure of the options table, visit the [[AceConfig3|AceConfig]] docs.
  +
  +
If you reload the UI and type '''/welcomehome''' or '''/wh''', you should now see a help message in your chat box that prints the Addon name, description and available commands.
  +
  +
Now lets add a command that lets the user change the text that is displayed by updating the options table:
  +
  +
<pre>local options = {
  +
name = "WelcomeHome",
  +
handler = WelcomeHome,
  +
type = "group",
  +
args = {
  +
msg = {
  +
type = "input",
  +
name = "Message",
  +
desc = "The message to be displayed when you get home.",
  +
usage = "&lt;Your message&gt;",
  +
get = "GetMessage",
  +
set = "SetMessage",
  +
},
  +
},
  +
}
  +
</pre>
  +
  +
This will define a new slash command called '''msg''' that takes a text argument and uses the functions named to get and set the underlying variables. Let's quickly write those methods:
  +
  +
<pre>function WelcomeHome:GetMessage(info)
  +
return self.message
  +
end
  +
  +
function WelcomeHome:SetMessage(info, newValue)
  +
self.message = newValue
  +
end
  +
</pre>
  +
  +
''Note'': You can find more information to the ''info'' argument [[AceConfig3#Callback_handling|here]]. Using the info argument is an advanced topic, for beginners it's enough to know that there is such an argument, so just ignore it until you get more familiar with AceConfig-3.0.
  +
  +
Then reload your UI and type '''/wh''' to see the command. Notice that at this point there is no value for the message string. That is because we didn't provide a default.
  +
We can fix that by adding the following line right after the call to RegisterOptionsTable:
  +
  +
WelcomeHome.message = "Welcome Home!"
  +
  +
The final step is to change the print line in our event handler to use the new message:
  +
  +
<pre>function WelcomeHome:ZONE_CHANGED()
  +
if GetBindLocation() == GetSubZoneText() then
  +
self:Print(self.message)
  +
end
  +
end
  +
</pre>
  +
  +
Play around with it for a little while and see how it works.
  +
  +
== GUI and Blizzard Interface Options ==
  +
  +
But why stop at chat commands? All cool addons have a '''G'''raphical '''U'''ser '''I'''nterface these days so WelcomeHome shouldn't be any different! For this we need to handle our chat commands slightly different.
  +
  +
<pre>function WelcomeHome:OnInitialize()
  +
-- Called when the addon is loaded
  +
LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options)
  +
self:RegisterChatCommand("welcomehome", "ChatCommand")
  +
self:RegisterChatCommand("wh", "ChatCommand")
  +
WelcomeHome.message = "Welcome Home!"
  +
end
  +
</pre>
  +
  +
Here we use the [[AceConsole-3.0|AceConsole]] method RegisterChatCommand to make '''/welcomehome''' and '''/wh''' call our ChatCommand method instead. In this function we can do whatever we want. And we want AceConfigDialog to open a GUI for our options if there's no other input, otherwise we'll let AceConfigCmd handle the input like it would before. This way we have both a GUI and chat commands (using the same options table) so our users can use whatever they please.
  +
  +
<pre>function WelcomeHome:ChatCommand(input)
  +
if not input or input:trim() == "" then
  +
LibStub("AceConfigDialog-3.0"):Open("WelcomeHome")
  +
else
  +
LibStub("AceConfigCmd-3.0").HandleCommand(WelcomeHome, "wh", "WelcomeHome", input)
  +
end
  +
end
  +
</pre>
  +
  +
=== Blizzard Interface Options ===
  +
  +
When Blizzard remade their Interface Options with patch 2.4 they introduced the ability for addons to add their options to the "Addons" tab. Doing this with Ace3 is really easy. All we need to do is call the AceConfigDialog-3.0 method AddToBlizOptions. This method returns a frame we need to specify later to open the Blizzard Interface Options at the page for our options, so we save it in ''self.optionsFrame''.
  +
  +
<pre>function WelcomeHome:OnInitialize()
  +
-- Called when the addon is loaded
  +
LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options)
  +
self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("WelcomeHome", "WelcomeHome")
  +
self:RegisterChatCommand("wh", "ChatCommand")
  +
self:RegisterChatCommand("welcomehome", "ChatCommand")
  +
WelcomeHome.message = "Welcome Home!"
  +
end
  +
</pre>
  +
  +
Now we can replace
  +
  +
LibStub("AceConfigDialog-3.0"):Open("WelcomeHome")
  +
  +
in our ChatCommand method with:
  +
  +
InterfaceOptionsFrame_OpenToFrame(self.optionsFrame)
  +
  +
to make our chat commands open the Interface Options instead.
  +
  +
== Making the Message More Prominent ==
  +
  +
Now that we have the addon working, let's tweak it a bit. Let's add a way to display the message somewhere that is a bit more prominent. To do this we will show the message in a frame called UIErrorsFrame and add some new options to let the user decide what they want.
  +
  +
<i>Note: I'm going to assume that you are starting to figure this out and can figure out where this code goes.</i>
  +
  +
First the new options table:
  +
  +
<pre>local options = {
  +
name = "WelcomeHome",
  +
handler = WelcomeHome,
  +
type = "group",
  +
args = {
  +
msg = {
  +
type = "input",
  +
name = "Message",
  +
desc = "The message text to be displayed",
  +
usage = "<Your message here>",
  +
get = "GetMessage",
  +
set = "SetMessage",
  +
},
  +
showInChat = {
  +
type = "toggle",
  +
name = "Show in Chat",
  +
desc = "Toggles the display of the message in the chat window.",
  +
get = "IsShowInChat",
  +
set = "ToggleShowInChat",
  +
},
  +
showOnScreen = {
  +
type = "toggle",
  +
name = "Show on Screen",
  +
desc = "Toggles the display of the message on the screen.",
  +
get = "IsShowOnScreen",
  +
set = "ToggleShowOnScreen"
  +
},
  +
},
  +
}
  +
</pre>
  +
  +
Then the new default values:
  +
  +
WelcomeHome.showInChat = false
  +
WelcomeHome.showOnScreen = true
  +
  +
Implementations of the new command's get/set methods:
  +
  +
<pre>function WelcomeHome:IsShowInChat(info)
  +
return self.showInChat
  +
end
  +
  +
function WelcomeHome:ToggleShowInChat(info, value)
  +
self.showInChat = value
  +
end
  +
  +
function WelcomeHome:IsShowOnScreen(info)
  +
return self.showOnScreen
  +
end
  +
  +
function WelcomeHome:ToggleShowOnScreen(info, value)
  +
self.showOnScreen = value
  +
end
  +
</pre>
  +
  +
And finally the new event handler method:
  +
  +
<pre>function WelcomeHome:ZONE_CHANGED()
  +
if GetBindLocation() == GetSubZoneText() then
  +
if self.showInChat then
  +
self:Print(self.message)
  +
end
  +
  +
if self.showOnScreen then
  +
UIErrorsFrame:AddMessage(self.message, 1.0, 1.0, 1.0, 5.0)
  +
end
  +
end
  +
end
  +
</pre>
  +
  +
<i>Note: You can learn more about [http://www.wowwiki.com/API_ScrollingMessageFrame_AddMessage ScrollingMessageFrame:AddMessage] on wowwiki.com.</i>
  +
  +
== Saving Configuration Between Sessions ==
  +
One thing you may have noticed is that your settings about where to show the message aren't persisted between sessions. When you logout and back in, you will have the default settings again. This isn't ideal. We should be saving these settings somehow.
  +
  +
WoW provides a way for you to do this called [http://www.wowwiki.com/HOWTO:_Save_Variables_Between_Game_Sessions Saved Variables], but there is an Ace way to do it called [[AceDB-3.0]]. We already have the library listed in our TOC file, and now it's time to use it.
  +
  +
<pre>
  +
local defaults = {
  +
profile = {
  +
message = "Welcome Home!",
  +
showInChat = false,
  +
showOnScreen = true,
  +
},
  +
}
  +
  +
function WelcomeHome:OnInitialize()
  +
-- Called when the addon is loaded
  +
self.db = LibStub("AceDB-3.0"):New("WelcomeHomeDB", defaults, "Default")
  +
  +
LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options)
  +
self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("WelcomeHome", "WelcomeHome")
  +
self:RegisterChatCommand("wh", "ChatCommand")
  +
self:RegisterChatCommand("welcomehome", "ChatCommand")
  +
end
  +
</pre>
  +
  +
(In this example we associate these variables with the Ace3 <i>profile</i>, but you also can associate them with the char, class, realm or account. See [[AceDB-3.0 API Documentation]] for more information.)
  +
  +
The string "WelcomeHomeDB" refer to the SavedVariables definition in the TOC file. If you don't specify anything for the third argument all characters using the addon will get their own profile. Here we specify "Default". This means that unless you change profile manually all character will share the same "Default" profile.
  +
  +
After that we need to replace all of the references to the old variables (e.g. self.message or WelcomeHome.message) with <b>self.db.profile.&lt;variablename&gt;</b> (e.g. self.db.profile.message). This will require us to change the get/set methods referenced by our options table and our event handler.
  +
  +
Here are the new command get/set methods:
  +
  +
<pre>function WelcomeHome:GetMessage(info)
  +
return self.db.profile.message
  +
end
  +
  +
function WelcomeHome:SetMessage(info, newValue)
  +
self.db.profile.message = newValue
  +
end
  +
  +
function WelcomeHome:IsShowInChat(info)
  +
return self.db.profile.showInChat
  +
end
  +
  +
function WelcomeHome:ToggleShowInChat(info, value)
  +
self.db.profile.showInChat = value
  +
end
  +
  +
function WelcomeHome:IsShowOnScreen(info)
  +
return self.db.profile.showOnScreen
  +
end
  +
  +
function WelcomeHome:ToggleShowOnScreen(info, value)
  +
self.db.profile.showOnScreen = value
  +
end
  +
</pre>
  +
  +
And here is the new event handler:
  +
  +
<pre>function WelcomeHome:ZONE_CHANGED()
  +
if GetBindLocation() == GetSubZoneText() then
  +
if self.db.profile.showInChat then
  +
self:Print(self.db.profile.message);
  +
end
  +
  +
if self.db.profile.showOnScreen then
  +
UIErrorsFrame:AddMessage(self.db.profile.message, 1.0, 1.0, 1.0, 5.0)
  +
end
  +
end
  +
end
  +
</pre>
  +
  +
As you can see, it was just a simple substitution of the old variable with the new AceDB variable. Reload your UI and nothing should change. Except now if you change any of the settings, they will be persisted across restarts.
  +
  +
This whole exercise was a bit silly, of course, because in a real world addon you would start out using AceDB for your configuration data, but now you know what it is for and why you should be using it.
  +
  +
== Localizing Your Strings with AceLocale ==
  +
  +
One thing a lot of new addon developers (especially those in the U.S.) forget about is how many people will be wanting to use their addon in a non-english version of the game client. This means that there should be a nice easy way for them to localize the strings in your addon into their language. (Or even better, submit the localized strings to you so you can include them in your next release.)
  +
  +
With Ace3, we do this using the [[AceLocale-3.0]] library. This library is not a mixin like the other libraries we've used so far, so the way we use it is a bit different.
  +
  +
=== Adding AceLocale-3.0 to the Project ===
  +
  +
Let's start by grabbing the AceLocale-3.0 folder from the package that we downloaded earlier and putting it into our Libs folder. We also should create our first localization database file. Create an empty file called Locale-enUS.lua and save it in the same folder as your TOC file.
  +
  +
At this point our folder structure should look like this:
  +
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> WelcomeHome
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> Libs
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceAddon-3.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceAddon-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceAddon-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfig-3.0
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigCmd-3.0
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigDialog-3.0
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigDropdown-3.0
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigRegistry-3.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConfig-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConfig-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConsole-3.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConsole-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConsole-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceDB-3.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceDB-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceDB-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceEvent-3.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceEvent-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceEvent-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceGUI-3.0
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> widgets
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceGUI-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceGUI-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceLocale-3.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceLocale-3.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceLocale-3.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> CallbackHandler-1.0
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> CallbackHandler-1.0.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> CallbackHandler-1.0.xml
  +
<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> LibStub
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> LibStub.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> Core.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> embeds.xml
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> Locale-enUS.lua
  +
<html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> WelcomeHome.toc
  +
  +
Then, we need to update our embeds.xml file to reference these new files as shown here:
  +
  +
<pre><Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  +
xsi:schemaLocation="http://www.blizzard.com/wow/ui/
  +
..\FrameXML\UI.xsd">
  +
<Script file="Libs\LibStub\LibStub.lua"/>
  +
<Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
  +
<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
  +
<Include file="libs\AceGUI-3.0\AceGUI-3.0.xml"/>
  +
<Include file="libs\AceConfig-3.0\AceConfig-3.0.xml"/>
  +
<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/>
  +
<Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/>
  +
<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
  +
<Include file="Libs\AceLocale-3.0\AceLocale-3.0.xml"/>
  +
</Ui>
  +
</pre>
  +
  +
And our .TOC file:
  +
  +
<pre>## Interface: 20400
  +
## Title: Welcome Home
  +
## Notes: Displays a welcome message when you get to your home zone.
  +
## Author: Your Name Here
  +
## Version: 0.1
  +
## SavedVariables: WelcomeHomeDB
  +
## OptionalDeps: Ace3
  +
## X-Embeds: Ace3
  +
## X-Category: Interface Enhancements
  +
  +
embeds.xml
  +
  +
Locale-enUS.lua
  +
Core.lua
  +
</pre>
  +
  +
Notice that the localization file comes before the Core.lua file. This is important or your localization database will not be initialized before it is used by the code in your addon.
  +
  +
Since we have updated our TOC file, you will need to restart WoW to get these changes loaded into the game. The ReloadUI trick won't work this time. Once you've done that, go ahead and login and make sure everything is still working.
  +
  +
=== Building the Localization Database ===
  +
Now that we have a place to put the localized strings for the US English (enUS) version of our Addon, we need to pass through the source code looking for strings that should be localizable. (In your future addon work, I would recommend going ahead and doing this step at the beginning of your development instead of waiting until the end as we did here.)
  +
  +
After taking a pass through Core.lua, I added the following lines to Locale-enUS.lua:
  +
  +
<pre>local L = LibStub("AceLocale-3.0"):NewLocale("WelcomeHome", "enUS", true)
  +
  +
-- Chat commands
  +
L["welcomehome"] = true
  +
L["wh"] = true
  +
  +
L["Welcome Home!"] = true -- default message
  +
  +
L["Message"] = true
  +
L["The message to be displayed when you get home."] = true
  +
L["&lt;Your message&gt;"] = true -- usage
  +
  +
L["Show in Chat"] = true
  +
L["Toggles the display of the message in the chat window."] = true
  +
  +
L["Show on Screen"] = true
  +
L["Toggles the display of the message on the screen."] = true
  +
</pre>
  +
  +
This file has two parts. The first part creates a new AceLocale instance with the name "WelcomeHome". "enUS" is the language code for US English and <tt>true</tt> as the third argument tells AceLocale that this is the default locale. The second part is the actual translations. Having the values be <tt>true</tt> makes them the same as their keys. This means that in our code, when we ask for the localized version of "Welcome Home!", we will get "Welcome Home!", or another string depending on the language.
  +
  +
=== Using the Localized Strings in the Addon ===
  +
  +
To use AceLocale in our addon now we ask AceLocale for an instance with translations and it will give us a table for the current locale or the default if the it don't have a translation for the language the user is playing in.
  +
  +
Now, add the following line to the top of Core.lua (be sure you add it above the options table definition).
  +
  +
local L = LibStub("AceLocale-3.0"):GetLocale("WelcomeHome")
  +
  +
Now we can use this anywhere we need a translated string by putting <tt>L[&nbsp;]</tt> around it. For example the first part of our options table now becomes this:
  +
  +
<pre>local options = {
  +
name = "WelcomeHome",
  +
handler = WelcomeHome,
  +
type = "group",
  +
args = {
  +
msg = {
  +
type = "input",
  +
name = L["Message"],
  +
desc = L["The message to be displayed when you get home."],
  +
usage = L["&lt;Your message&gt;"],
  +
get = "GetMessage",
  +
set = "SetMessage",
  +
},
  +
  +
...etc...
  +
</pre>
  +
  +
This process is pretty straightforward, so I won't bother showing all of it here. You can see the final source files at the end of the article to see if you got them all in your addon.
  +
  +
Using AceLocale is a simple way to get good localization support in your addon.
  +
  +
  +
=== Mixed-in variables ===
  +
  +
If you want to use text elements mixed with variables for different output you can also use functions in your locale table.
  +
So the word or text element order does not matter in your script and translations will sound more natural.
  +
  +
<pre>
  +
-- enUS/enGB:
  +
L['We heartly welcome player X.'] = function(X)
  +
return 'We heartly welcome player ' .. X;
  +
end
  +
</pre>
  +
<pre>
  +
-- deDE:
  +
L['We heartly welcome player X.'] = function(X)
  +
return 'Wir hei\195\159en Spieler ' .. X .. ' herzlich Willkommen';
  +
end
  +
</pre>
  +
<pre>
  +
-- script.lua:
  +
self:Print(L['We heartly welcome player X.'](playername));
  +
</pre>
  +
  +
== Conclusion ==
  +
  +
<p>You have started from nothing and created a localizable addon that uses chat commands, responds to events and provides feedback to the user in a couple of different ways. There are a number of interesting things you can do to this addon if you want to keep going. Some examples:
  +
</p>
  +
<ul><li> Integrate it with FuBar and provide a configuration menu in addition to slash commands
  +
</li><li> Provide different messages when you zone into other areas (e.g. Welcome to The Badlands, Home of Uldaman!")
  +
</li></ul>
  +
<p>Hopefully that helps you get started with Ace3 development and leads you down the road to successful addon authoring. Best of luck!
  +
</p>
  +
  +
== Join the Ace Community ==
  +
  +
<p>As you get more interested in Ace3 development, you should consider getting involved in the Ace community at [http://www.wowace.com wowace.com]. They have a [[What is the SVN?|Subversion server]] for version control, [http://www.wowace.com/forums a web-based forum], an IRC channel and a [[Main Page|great wiki]] full of documentation and help.
  +
</p><p>You also should consider publishing your Ace3 addon at [http://www.wowinterface.com/ wowinterface.com] in their [http://www.wowinterface.com/downloads/cat47.html Ace section].
  +
</p>
  +
  +
== Final Source Files ==
  +
  +
Here is the final contents of WelcomeHome.toc, embeds.xml, Core.lua and Locale-enUS.lua in case you want to cheat and go right to the end.
  +
  +
=== WelcomeHome.toc ===
  +
  +
<pre>## Interface: 20400
  +
## Title: Welcome Home
  +
## Notes: Displays a welcome message when you get to your home zone.
  +
## Author: Your Name Here
  +
## Version: 0.1
  +
## SavedVariables: WelcomeHomeDB
  +
## OptionalDeps: Ace3
  +
## X-Embeds: Ace3
  +
## X-Category: Interface Enhancements
  +
  +
embeds.xml
  +
  +
Locale-enUS.lua
  +
Core.lua
  +
</pre>
  +
  +
=== embeds.xml ===
  +
  +
<pre><Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  +
xsi:schemaLocation="http://www.blizzard.com/wow/ui/
  +
..\FrameXML\UI.xsd">
  +
<Script file="Libs\LibStub\LibStub.lua"/>
  +
<Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
  +
<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
  +
<Include file="Libs\AceGUI-3.0\AceGUI-3.0.xml"/>
  +
<Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/>
  +
<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/>
  +
<Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/>
  +
<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
  +
<Include file="Libs\AceLocale-3.0\AceLocale-3.0.xml"/>
  +
</Ui>
  +
</pre>
  +
  +
=== Core.lua ===
  +
  +
<pre>WelcomeHome = LibStub("AceAddon-3.0"):NewAddon("WelcomeHome", "AceConsole-3.0", "AceEvent-3.0")
  +
local L = LibStub("AceLocale-3.0"):GetLocale("WelcomeHome")
  +
  +
  +
local options = {
  +
name = "WelcomeHome",
  +
handler = WelcomeHome,
  +
type = "group",
  +
args = {
  +
msg = {
  +
type = "input",
  +
name = L["Message"],
  +
desc = L["The message to be displayed when you get home."],
  +
usage = L["<Your message>"],
  +
get = "GetMessage",
  +
set = "SetMessage",
  +
},
  +
showInChat = {
  +
type = "toggle",
  +
name = L["Show in Chat"],
  +
desc = L["Toggles the display of the message in the chat window."],
  +
get = "IsShowInChat",
  +
set = "ToggleShowInChat",
  +
},
  +
showOnScreen = {
  +
type = "toggle",
  +
name = L["Show on Screen"],
  +
desc = L["Toggles the display of the message on the screen."],
  +
get = "IsShowOnScreen",
  +
set = "ToggleShowOnScreen",
  +
},
  +
},
  +
}
  +
  +
function WelcomeHome:OnInitialize()
  +
-- Called when the addon is loaded
  +
self.db = LibStub("AceDB-3.0"):New("WelcomeHomeDB", defaults, "Default")
  +
  +
LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options)
  +
self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("WelcomeHome", "WelcomeHome")
  +
self:RegisterChatCommand("wh", "ChatCommand")
  +
self:RegisterChatCommand("welcomehome", "ChatCommand")
  +
end
  +
  +
function WelcomeHome:OnEnable()
  +
-- Called when the addon is enabled
  +
self:RegisterEvent("ZONE_CHANGED")
  +
end
  +
  +
function WelcomeHome:ZONE_CHANGED()
  +
if GetBindLocation() == GetSubZoneText() then
  +
if self.db.profile.showInChat then
  +
self:Print(self.db.profile.message)
  +
end
  +
  +
if self.db.profile.showOnScreen then
  +
UIErrorsFrame:AddMessage(self.db.profile.message, 1.0, 1.0, 1.0, 5.0)
  +
end
  +
end
  +
end
  +
  +
function WelcomeHome:ChatCommand(input)
  +
if not input or input:trim() == "" then
  +
InterfaceOptionsFrame_OpenToFrame(self.optionsFrame)
  +
else
  +
LibStub("AceConfigCmd-3.0").HandleCommand(WelcomeHome, "wh", "WelcomeHome", input)
  +
end
  +
end
  +
  +
function WelcomeHome:GetMessage(info)
  +
return self.db.profile.message
  +
end
  +
  +
function WelcomeHome:SetMessage(info, newValue)
  +
self.db.profile.message = newValue
  +
end
  +
  +
function WelcomeHome:IsShowInChat(info)
  +
return self.db.profile.showInChat
  +
end
  +
  +
function WelcomeHome:ToggleShowInChat(info, value)
  +
self.db.profile.showInChat = value
  +
end
  +
  +
function WelcomeHome:IsShowOnScreen(info)
  +
return self.db.profile.showOnScreen
  +
end
  +
  +
function WelcomeHome:ToggleShowOnScreen(info, value)
  +
self.db.profile.showOnScreen = value
  +
end
  +
</pre>
  +
  +
=== Locale-enUS.lua ===
  +
  +
<pre>local L = LibStub("AceLocale-3.0"):NewLocale("WelcomeHome", "enUS", true)
  +
  +
-- Chat commands
  +
L["welcomehome"] = true
  +
L["wh"] = true
  +
  +
L["Welcome Home!"] = true -- default message
  +
  +
L["Message"] = true
  +
L["The message to be displayed when you get home."] = true
  +
L["<Your message>"] = true -- usage
  +
  +
L["Show in Chat"] = true
  +
L["Toggles the display of the message in the chat window."] = true
  +
  +
L["Show on Screen"] = true
  +
L["Toggles the display of the message on the screen."] = true
  +
</pre>

Revision as of 23:28, 19 October 2008

Introduction

Getting Started

Since this is a tutorial about using the Ace3 libraries, I will start by showing you what we need to do to get a barebones addon loaded into the game.

Every addon is stored in a folder underneath the WoW main folder called <WoW>\Interface\Addons. Each addon goes in its own folder named after the addon. So to get started we will create a new folder called “WelcomeHome”.

When WoW finds a folder in the Addons directory, it looks inside that folder for a table of contents (TOC) file that has the same name as the folder. This TOC file contains a manifest of all the other files that make up the addon and is used by WoW to load your addon.

Let’s go ahead and create a barebones TOC file called WelcomeHome.toc:

## Interface: 30000
## Title: Welcome Home
## Notes: Displays a welcome message when you get to your home zone.
## Author: Your Name Here
## Version: 0.1
## X-Category: Interface Enhancements 

Core.lua

These are the basic metadata associated with your addon. For the X-Category option, it is recommended that you pick one of the Ace2 Categories to be consistent with the rest of Ace. There are many other attributes you can include in your TOC file, for a more comprehensive list see The TOC Format on wowwiki.com.

Then create an empty text file called Core.lua in the same directory and start up WoW. After logging in, you should be able to click the “Addons” button in the lower left and see that your addon is being recognized by the game. Go ahead and make sure that it is enabled before exiting out of that screen and then out of the game.

Step 1 complete! We are in the game. But we aren’t doing anything yet. That is next.

Bringing Ace3 to the Party

Since this tutorial is about learning to create addons using Ace3, it is time for us to go ahead and bring those libraries into our addon. Ace3 uses a concept called “Embedded Libraries” that allows mod developers to have the libraries in their codebase without actually duplicating the code when it is loaded along side other mods that use the same libraries.

This means you need to get local copies of the libraries you intend to use. At this point the easiest way to do this is to download them from the Ace SVN files mirror and put them on your system.

Open a browser and go to http://files.wowace.com/. Download Ace3. This is the most recent build of the Ace libraries. Unzip it into a folder called Libs in your Addon's directory. Then go ahead and delete all of the new folders and files except these:

  • AceAddon-3.0
  • AceConfig-3.0
  • AceConsole-3.0
  • AceDB-3.0
  • AceEvent-3.0
  • AceGUI-3.0
  • CallbackHandler-1.0
  • LibStub

There are lots of Ace3 libraries for you to use, but for this tutorial we only need some of them. For more information on these libraries, please visit the Ace3 page.

You should now have a folder structure something like this:

<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> WelcomeHome
 <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> Libs
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceAddon-3.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceAddon-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceAddon-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfig-3.0
     <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigCmd-3.0
     <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigDialog-3.0
     <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigDropdown-3.0
     <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigRegistry-3.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConfig-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConfig-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConsole-3.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConsole-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConsole-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceDB-3.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceDB-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceDB-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceEvent-3.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceEvent-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceEvent-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceGUI-3.0
     <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> widgets
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceGUI-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceGUI-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> CallbackHandler-1.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> CallbackHandler-1.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> CallbackHandler-1.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> LibStub
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> LibStub.lua
 <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> Core.lua
 <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> WelcomeHome.toc

You now have the necessary Ace3 libraries in your addon's Libs folder, but if you were to launch WoW right now, they wouldn't be loaded. We need to tell WoW to load the files when it loads your addon and for that we're going to use a file called embeds.xml. So create a new file with the text below and save it as "embeds.xml" in the same folder as your .TOC file.

<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="Libs\LibStub\LibStub.lua"/>
<Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
<Include file="Libs\AceGUI-3.0\AceGUI-3.0.xml"/>
<Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/>
<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/>
<Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/>
<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
</Ui>

The first three lines are just some xml-voodoo, just copy and paste. Then it's a line each for all the libraries we want to load. It is vital that we load LibStub first, since all the other libs depend on that. Then CallbackHandler that is used by several of our libraries and AceGUI needs to be loaded before AceConfig. Otherwise the order isn't important. Notice that it's <Script /> to load lua files and <Include /> to load xml files.

Now let's update the .TOC file to include the embeds.xml. We include it above Core.lua to ensure that the libraries are loaded and available before the code in Core.lua is executed.

## Interface: 20400
## Title: Welcome Home
## Notes: Displays a welcome message when you get to your home zone.
## Author: Your Name Here
## Version: 0.1
## SavedVariables: WelcomeHomeDB
## OptionalDeps: Ace3
## X-Embeds: Ace3
## X-Category: Interface Enhancements

embeds.xml

Core.lua

I've included a few other things in the TOC that aren't actually required yet (e.g. SavedVariables), but we will need all of that eventually. The OptionalDeps part is there in case a user of your addon comes along later and chooses to use a standalone Ace3 instead of using the embedded libraries in your Libs folder and the X-Embeds is there to tell updaters what libs it needs to download separately if the user don't want to use the embedded libs.

Saying Hello

This section is going to move a little faster than we have been moving. Start by opening Core.lua in a text editor and typing in the following code:

WelcomeHome = LibStub("AceAddon-3.0"):NewAddon("WelcomeHome", "AceConsole-3.0")

function WelcomeHome:OnInitialize()
    -- Called when the addon is loaded
end

function WelcomeHome:OnEnable()
    -- Called when the addon is enabled
end

function WelcomeHome:OnDisable()
    -- Called when the addon is disabled
end

This is the basic starting structure of an Ace3 addon. The first line creates an instance of the AceAddon class using the NewAddon() operator. Since we will also be printing to the chat window and accepting slash commands, I've gone ahead and included the mixin for AceConsole-3.0. Among other things, the AceConsole-3.0 mixin will get us access to the Print method.

Mixins are a concept common in many object oriented languages that allow you to bring additional functionality into your class. (For more on mixins, see the mixins page on Wikipedia.)

Following that are three method overrides. The first is executed only once when the UI loads and the next two are executed when the addon is enabled and disabled. (You can enable and disable Ace addons using /ace commands.)

It is important to note that OnEnable is called at the beginning if the addon is enabled when the UI loads. You will often have to choose which of these two method overrides to use. Generally, you should use the OnInitialize for those things that you don't need to undo and redo if the addon is disabled and enabled. Another important part of this decision has to do with what other things you will need and when you can be sure they are ready. More on this later...

Just to demonstrate that the addon is loading, we will have the addon print "Hello World!" to the chat window by adding one line of code to the OnEnable method.

function WelcomeHome:OnEnable()
    self:Print("Hello World!")
end

Go ahead and restart WoW and enter in to the game. Once you are in, you should be able to scroll up in the chat window and find your message. You are now the proud author of the WoW version of Hello World!.

Before we go any further, go back over to your code and remove the print message. It is generally considered bad Ace form for your addon to toss a bunch of text into the chat window just because it has loaded. Too many addons do this already and there are other ways ("/ace list") to find out what Ace addons have loaded. You can also remove the OnDisable method since we won't be using it.

Responding to Events

So, as I said, this addon will give a welcome message to the player when they arrive in their home zone. How will we know they are in their home zone? Simple, we will respond to one of the ZONE_CHANGED events which the game fires when the player enters a new zone.

I’m sure you are asking yourself “Events? What the heck are those?” It turns out that like many other programming environments, WoW is event driven. Events are raised when things happen in the game and you can fire events when things happen in your code. In fact, without events, your addon would never even know what to do and when to start doing things. Eventually, everything your addon does is in response to an event.

Looking at the events list, it appears that ZONE_CHANGED will be the one we want, so let's hook that one by making a few changes to our Core.lua file.

Before we can subscribe to events, we need to get event support into our addon by including another mixin into our addon. We need to change the line where we create our addon to include the AceEvent-3.0 mixin. Change your first line as shown here:

WelcomeHome = LibStub("AceAddon-3.0"):NewAddon("WelcomeHome", "AceConsole-3.0", "AceEvent-3.0")

Once we've done that, we will use the RegisterEvent method that is now available to us to subscribe to the ZONE_CHANGED event. We will replace the OnEnable override with the following code:

function WelcomeHome:OnEnable()
    -- Called when the addon is enabled
    self:RegisterEvent("ZONE_CHANGED")
end

We also need to add in the event handler for that event by adding the following code to our Core.lua file:

function WelcomeHome:ZONE_CHANGED()
    self:Print("You have changed zones!")
end

You might be wondering if we should call UnregisterEvent from our OnDisable override, but we don't have to do that because AceEvent-3.0 does it for us.

Now head back into the game and have your character leave the area he/she is presently in. You should see your message go by down in the chat area when you change zone.

Reloading Your Addon Without Restarting WoW

Before we go any further, I want to show you a trick that will come in handy for you as you work on your addons.

Leave the game up and running, but switch over to your text editor. Change the event handler as follows...

function WelcomeHome:ZONE_CHANGED()
    self:Print("This is a different message!")
end

Make sure you save your changes and then go back over to the game and switch zones. Which message did you see? The old one.

Why? Because WoW doesn't automatically reload your addon just because you made a change. One way to get your addon reloaded is to restart the game. Ugh. much better way to get it reloaded is by using the ReloadUI() command from the command line using the /script chat command:

/script ReloadUI()

Your screen will freeze for a minute while the UI reloads itself and all your other addons reload, but when it comes back up you should now be able to switch zones and see the new message.

From now on you can use this technique to reload your mod when you have made changes.

Note: if you have AceConsole-2.0 loaded with any of your addons or if you have the standalone version of Ace3, you can use the following command to reload your UI:

/rl

Working with the WoW API

We now have a method that we know will get called when the player changes zones. But what zone is he in? Where does he keep his hearthstone set?

If we head back over to the World of Warcraft API page and look in the Character Functions section, we will see the answer to the second question is a function called GetBindLocation. This function returns the subzone name (e.g. "Tarren Mill") that contains the Inn where your hearthstone is set.

Next we need to figure out how to find what subzone we are in. If we look in the Location Functions section of the API docs, we will find a function called GetSubZoneText that looks about right. It returns either an empty string (if you aren't in a subzone) or the name of the subzone you are in.

We should be able to simply compare these two values in our event handler to decide whether we are home or not:

function WelcomeHome:ZONE_CHANGED()
    if GetBindLocation() == GetSubZoneText() then
        self:Print("Welcome Home!")
    end
end

That's it! We should now have a functional addon doing what we want. Reload your UI and test it out.

Chat Commands and Configuration

But there are still lots of interesting things we can do. Let's start by adding support for the out-of-the-box slash commands by creating an options table at the top of the file and registering it with AceConfig-3.0:

WelcomeHome = LibStub("AceAddon-3.0"):NewAddon("WelcomeHome", "AceConsole-3.0", "AceEvent-3.0")

local options = {
    name = "WelcomeHome",
    handler = WelcomeHome,
    type = 'group',
    args = {
    },
}

function WelcomeHome:OnInitialize()
    -- Called when the addon is loaded
    LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options, {"welcomehome", "wh"})
end

At this point the options table is empty and we haven't provided any commands. For a complete rundown of the structure of the options table, visit the AceConfig docs.

If you reload the UI and type /welcomehome or /wh, you should now see a help message in your chat box that prints the Addon name, description and available commands.

Now lets add a command that lets the user change the text that is displayed by updating the options table:

local options = { 
    name = "WelcomeHome",
    handler = WelcomeHome,
    type = "group",
    args = {
        msg = {
            type = "input",
            name = "Message",
            desc = "The message to be displayed when you get home.",
            usage = "<Your message>",
            get = "GetMessage",
            set = "SetMessage",
        },
    },
}

This will define a new slash command called msg that takes a text argument and uses the functions named to get and set the underlying variables. Let's quickly write those methods:

function WelcomeHome:GetMessage(info)
    return self.message
end

function WelcomeHome:SetMessage(info, newValue)
    self.message = newValue
end

Note: You can find more information to the info argument here. Using the info argument is an advanced topic, for beginners it's enough to know that there is such an argument, so just ignore it until you get more familiar with AceConfig-3.0.

Then reload your UI and type /wh to see the command. Notice that at this point there is no value for the message string. That is because we didn't provide a default. We can fix that by adding the following line right after the call to RegisterOptionsTable:

WelcomeHome.message = "Welcome Home!"

The final step is to change the print line in our event handler to use the new message:

function WelcomeHome:ZONE_CHANGED()
    if GetBindLocation() == GetSubZoneText() then
        self:Print(self.message)
    end
end

Play around with it for a little while and see how it works.

GUI and Blizzard Interface Options

But why stop at chat commands? All cool addons have a Graphical User Interface these days so WelcomeHome shouldn't be any different! For this we need to handle our chat commands slightly different.

function WelcomeHome:OnInitialize()
    -- Called when the addon is loaded
    LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options)
    self:RegisterChatCommand("welcomehome", "ChatCommand")
    self:RegisterChatCommand("wh", "ChatCommand")
    WelcomeHome.message = "Welcome Home!"
end

Here we use the AceConsole method RegisterChatCommand to make /welcomehome and /wh call our ChatCommand method instead. In this function we can do whatever we want. And we want AceConfigDialog to open a GUI for our options if there's no other input, otherwise we'll let AceConfigCmd handle the input like it would before. This way we have both a GUI and chat commands (using the same options table) so our users can use whatever they please.

function WelcomeHome:ChatCommand(input)
    if not input or input:trim() == "" then
        LibStub("AceConfigDialog-3.0"):Open("WelcomeHome")
    else
        LibStub("AceConfigCmd-3.0").HandleCommand(WelcomeHome, "wh", "WelcomeHome", input)
    end
end

Blizzard Interface Options

When Blizzard remade their Interface Options with patch 2.4 they introduced the ability for addons to add their options to the "Addons" tab. Doing this with Ace3 is really easy. All we need to do is call the AceConfigDialog-3.0 method AddToBlizOptions. This method returns a frame we need to specify later to open the Blizzard Interface Options at the page for our options, so we save it in self.optionsFrame.

function WelcomeHome:OnInitialize()
    -- Called when the addon is loaded
    LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options)
    self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("WelcomeHome", "WelcomeHome")
    self:RegisterChatCommand("wh", "ChatCommand")
    self:RegisterChatCommand("welcomehome", "ChatCommand")
    WelcomeHome.message = "Welcome Home!"
end

Now we can replace

LibStub("AceConfigDialog-3.0"):Open("WelcomeHome")

in our ChatCommand method with:

InterfaceOptionsFrame_OpenToFrame(self.optionsFrame)

to make our chat commands open the Interface Options instead.

Making the Message More Prominent

Now that we have the addon working, let's tweak it a bit. Let's add a way to display the message somewhere that is a bit more prominent. To do this we will show the message in a frame called UIErrorsFrame and add some new options to let the user decide what they want.

Note: I'm going to assume that you are starting to figure this out and can figure out where this code goes.

First the new options table:

local options = {
    name = "WelcomeHome",
    handler = WelcomeHome,
    type = "group",
    args = {
        msg = {
            type = "input",
            name = "Message",
            desc = "The message text to be displayed",
            usage = "<Your message here>",
            get = "GetMessage",
            set = "SetMessage",
        },
        showInChat = {
            type = "toggle",
            name = "Show in Chat",
            desc = "Toggles the display of the message in the chat window.",
            get = "IsShowInChat",
            set = "ToggleShowInChat",
        },
        showOnScreen = {
            type = "toggle",
            name = "Show on Screen",
            desc = "Toggles the display of the message on the screen.",
            get = "IsShowOnScreen",
            set = "ToggleShowOnScreen"
        },
    },
}

Then the new default values:

WelcomeHome.showInChat = false
WelcomeHome.showOnScreen = true

Implementations of the new command's get/set methods:

function WelcomeHome:IsShowInChat(info)
    return self.showInChat
end

function WelcomeHome:ToggleShowInChat(info, value)
    self.showInChat = value
end

function WelcomeHome:IsShowOnScreen(info)
    return self.showOnScreen
end

function WelcomeHome:ToggleShowOnScreen(info, value)
    self.showOnScreen = value
end

And finally the new event handler method:

function WelcomeHome:ZONE_CHANGED()
    if GetBindLocation() == GetSubZoneText() then
        if self.showInChat then
            self:Print(self.message)
        end

        if self.showOnScreen then
            UIErrorsFrame:AddMessage(self.message, 1.0, 1.0, 1.0, 5.0)
        end
    end
end

Note: You can learn more about ScrollingMessageFrame:AddMessage on wowwiki.com.

Saving Configuration Between Sessions

One thing you may have noticed is that your settings about where to show the message aren't persisted between sessions. When you logout and back in, you will have the default settings again. This isn't ideal. We should be saving these settings somehow.

WoW provides a way for you to do this called Saved Variables, but there is an Ace way to do it called AceDB-3.0. We already have the library listed in our TOC file, and now it's time to use it.

local defaults = {
    profile = {
        message = "Welcome Home!",
        showInChat = false,
        showOnScreen = true,
    },
}

function WelcomeHome:OnInitialize()
    -- Called when the addon is loaded
    self.db = LibStub("AceDB-3.0"):New("WelcomeHomeDB", defaults, "Default")

    LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options)
    self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("WelcomeHome", "WelcomeHome")
    self:RegisterChatCommand("wh", "ChatCommand")
    self:RegisterChatCommand("welcomehome", "ChatCommand")
end

(In this example we associate these variables with the Ace3 profile, but you also can associate them with the char, class, realm or account. See AceDB-3.0 API Documentation for more information.)

The string "WelcomeHomeDB" refer to the SavedVariables definition in the TOC file. If you don't specify anything for the third argument all characters using the addon will get their own profile. Here we specify "Default". This means that unless you change profile manually all character will share the same "Default" profile.

After that we need to replace all of the references to the old variables (e.g. self.message or WelcomeHome.message) with self.db.profile.<variablename> (e.g. self.db.profile.message). This will require us to change the get/set methods referenced by our options table and our event handler.

Here are the new command get/set methods:

function WelcomeHome:GetMessage(info)
    return self.db.profile.message
end

function WelcomeHome:SetMessage(info, newValue)
    self.db.profile.message = newValue
end

function WelcomeHome:IsShowInChat(info)
    return self.db.profile.showInChat
end

function WelcomeHome:ToggleShowInChat(info, value)
    self.db.profile.showInChat = value
end

function WelcomeHome:IsShowOnScreen(info)
    return self.db.profile.showOnScreen
end

function WelcomeHome:ToggleShowOnScreen(info, value)
    self.db.profile.showOnScreen = value
end

And here is the new event handler:

function WelcomeHome:ZONE_CHANGED()
    if GetBindLocation() == GetSubZoneText() then
        if self.db.profile.showInChat then
            self:Print(self.db.profile.message);
        end

        if self.db.profile.showOnScreen then
            UIErrorsFrame:AddMessage(self.db.profile.message, 1.0, 1.0, 1.0, 5.0)
        end
    end
end

As you can see, it was just a simple substitution of the old variable with the new AceDB variable. Reload your UI and nothing should change. Except now if you change any of the settings, they will be persisted across restarts.

This whole exercise was a bit silly, of course, because in a real world addon you would start out using AceDB for your configuration data, but now you know what it is for and why you should be using it.

Localizing Your Strings with AceLocale

One thing a lot of new addon developers (especially those in the U.S.) forget about is how many people will be wanting to use their addon in a non-english version of the game client. This means that there should be a nice easy way for them to localize the strings in your addon into their language. (Or even better, submit the localized strings to you so you can include them in your next release.)

With Ace3, we do this using the AceLocale-3.0 library. This library is not a mixin like the other libraries we've used so far, so the way we use it is a bit different.

Adding AceLocale-3.0 to the Project

Let's start by grabbing the AceLocale-3.0 folder from the package that we downloaded earlier and putting it into our Libs folder. We also should create our first localization database file. Create an empty file called Locale-enUS.lua and save it in the same folder as your TOC file.

At this point our folder structure should look like this:

<html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> WelcomeHome
 <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> Libs
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceAddon-3.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceAddon-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceAddon-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfig-3.0
     <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigCmd-3.0
     <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigDialog-3.0
     <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigDropdown-3.0
     <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConfigRegistry-3.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConfig-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConfig-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceConsole-3.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConsole-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceConsole-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceDB-3.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceDB-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceDB-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceEvent-3.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceEvent-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceEvent-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceGUI-3.0
     <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> widgets
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceGUI-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceGUI-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> AceLocale-3.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceLocale-3.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> AceLocale-3.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> CallbackHandler-1.0
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> CallbackHandler-1.0.lua
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> CallbackHandler-1.0.xml
   <html><img src="/mediawiki/images/a/a4/Folder.png" alt="Image:Folder.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> LibStub
     <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:Folder.png" /></html> LibStub.lua
 <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> Core.lua
 <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> embeds.xml
 <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> Locale-enUS.lua
 <html><img src="/mediawiki/images/b/b0/File.png" alt="Image:File.png" width="16" height="16" longdesc="/wiki/Image:File.png" /></html> WelcomeHome.toc

Then, we need to update our embeds.xml file to reference these new files as shown here:

<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="Libs\LibStub\LibStub.lua"/>
<Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
<Include file="libs\AceGUI-3.0\AceGUI-3.0.xml"/>
<Include file="libs\AceConfig-3.0\AceConfig-3.0.xml"/>
<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/>
<Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/>
<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
<Include file="Libs\AceLocale-3.0\AceLocale-3.0.xml"/>
</Ui>

And our .TOC file:

## Interface: 20400
## Title: Welcome Home
## Notes: Displays a welcome message when you get to your home zone.
## Author: Your Name Here
## Version: 0.1
## SavedVariables: WelcomeHomeDB
## OptionalDeps: Ace3
## X-Embeds: Ace3
## X-Category: Interface Enhancements

embeds.xml

Locale-enUS.lua
Core.lua

Notice that the localization file comes before the Core.lua file. This is important or your localization database will not be initialized before it is used by the code in your addon.

Since we have updated our TOC file, you will need to restart WoW to get these changes loaded into the game. The ReloadUI trick won't work this time. Once you've done that, go ahead and login and make sure everything is still working.

Building the Localization Database

Now that we have a place to put the localized strings for the US English (enUS) version of our Addon, we need to pass through the source code looking for strings that should be localizable. (In your future addon work, I would recommend going ahead and doing this step at the beginning of your development instead of waiting until the end as we did here.)

After taking a pass through Core.lua, I added the following lines to Locale-enUS.lua:

local L = LibStub("AceLocale-3.0"):NewLocale("WelcomeHome", "enUS", true)

-- Chat commands
L["welcomehome"] = true
L["wh"] = true

L["Welcome Home!"] = true -- default message

L["Message"] = true
L["The message to be displayed when you get home."] = true
L["<Your message>"] = true -- usage

L["Show in Chat"] = true
L["Toggles the display of the message in the chat window."] = true

L["Show on Screen"] = true
L["Toggles the display of the message on the screen."] = true

This file has two parts. The first part creates a new AceLocale instance with the name "WelcomeHome". "enUS" is the language code for US English and true as the third argument tells AceLocale that this is the default locale. The second part is the actual translations. Having the values be true makes them the same as their keys. This means that in our code, when we ask for the localized version of "Welcome Home!", we will get "Welcome Home!", or another string depending on the language.

Using the Localized Strings in the Addon

To use AceLocale in our addon now we ask AceLocale for an instance with translations and it will give us a table for the current locale or the default if the it don't have a translation for the language the user is playing in.

Now, add the following line to the top of Core.lua (be sure you add it above the options table definition).

local L = LibStub("AceLocale-3.0"):GetLocale("WelcomeHome")

Now we can use this anywhere we need a translated string by putting L[ ] around it. For example the first part of our options table now becomes this:

local options = {
    name = "WelcomeHome",
    handler = WelcomeHome,
    type = "group",
    args = {
        msg = {
            type = "input",
            name = L["Message"],
            desc = L["The message to be displayed when you get home."],
            usage = L["<Your message>"],
            get = "GetMessage",
            set = "SetMessage",
        },

    ...etc...

This process is pretty straightforward, so I won't bother showing all of it here. You can see the final source files at the end of the article to see if you got them all in your addon.

Using AceLocale is a simple way to get good localization support in your addon.


Mixed-in variables

If you want to use text elements mixed with variables for different output you can also use functions in your locale table. So the word or text element order does not matter in your script and translations will sound more natural.

-- enUS/enGB:
L['We heartly welcome player X.'] = function(X)
  return 'We heartly welcome player ' .. X;
end
-- deDE:
L['We heartly welcome player X.'] = function(X)
  return 'Wir hei\195\159en Spieler ' .. X .. ' herzlich Willkommen';
end
-- script.lua:
self:Print(L['We heartly welcome player X.'](playername));

Conclusion

You have started from nothing and created a localizable addon that uses chat commands, responds to events and provides feedback to the user in a couple of different ways. There are a number of interesting things you can do to this addon if you want to keep going. Some examples:

  • Integrate it with FuBar and provide a configuration menu in addition to slash commands
  • Provide different messages when you zone into other areas (e.g. Welcome to The Badlands, Home of Uldaman!")

Hopefully that helps you get started with Ace3 development and leads you down the road to successful addon authoring. Best of luck!

Join the Ace Community

As you get more interested in Ace3 development, you should consider getting involved in the Ace community at wowace.com. They have a Subversion server for version control, a web-based forum, an IRC channel and a great wiki full of documentation and help.

You also should consider publishing your Ace3 addon at wowinterface.com in their Ace section.

Final Source Files

Here is the final contents of WelcomeHome.toc, embeds.xml, Core.lua and Locale-enUS.lua in case you want to cheat and go right to the end.

WelcomeHome.toc

## Interface: 20400
## Title: Welcome Home
## Notes: Displays a welcome message when you get to your home zone.
## Author: Your Name Here
## Version: 0.1
## SavedVariables: WelcomeHomeDB
## OptionalDeps: Ace3
## X-Embeds: Ace3
## X-Category: Interface Enhancements 

embeds.xml

Locale-enUS.lua
Core.lua

embeds.xml

<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.blizzard.com/wow/ui/
..\FrameXML\UI.xsd">
<Script file="Libs\LibStub\LibStub.lua"/>
<Include file="Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml"/>
<Include file="Libs\AceAddon-3.0\AceAddon-3.0.xml"/>
<Include file="Libs\AceGUI-3.0\AceGUI-3.0.xml"/>
<Include file="Libs\AceConfig-3.0\AceConfig-3.0.xml"/>
<Include file="Libs\AceConsole-3.0\AceConsole-3.0.xml"/>
<Include file="Libs\AceDB-3.0\AceDB-3.0.xml"/>
<Include file="Libs\AceEvent-3.0\AceEvent-3.0.xml"/>
<Include file="Libs\AceLocale-3.0\AceLocale-3.0.xml"/>
</Ui>

Core.lua

WelcomeHome = LibStub("AceAddon-3.0"):NewAddon("WelcomeHome", "AceConsole-3.0", "AceEvent-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("WelcomeHome")


local options = {
    name = "WelcomeHome",
    handler = WelcomeHome,
    type = "group",
    args = {
        msg = {
            type = "input",
            name = L["Message"],
            desc = L["The message to be displayed when you get home."],
            usage = L["<Your message>"],
            get = "GetMessage",
            set = "SetMessage",
        },
        showInChat = {
            type = "toggle",
            name = L["Show in Chat"],
            desc = L["Toggles the display of the message in the chat window."],
            get = "IsShowInChat",
            set = "ToggleShowInChat",
        },
        showOnScreen = {
            type = "toggle",
            name = L["Show on Screen"],
            desc = L["Toggles the display of the message on the screen."],
            get = "IsShowOnScreen",
            set = "ToggleShowOnScreen",
        },
    },
}

function WelcomeHome:OnInitialize()
    -- Called when the addon is loaded
    self.db = LibStub("AceDB-3.0"):New("WelcomeHomeDB", defaults, "Default")

    LibStub("AceConfig-3.0"):RegisterOptionsTable("WelcomeHome", options)
    self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("WelcomeHome", "WelcomeHome")
    self:RegisterChatCommand("wh", "ChatCommand")
    self:RegisterChatCommand("welcomehome", "ChatCommand")
end

function WelcomeHome:OnEnable()
    -- Called when the addon is enabled
    self:RegisterEvent("ZONE_CHANGED")
end

function WelcomeHome:ZONE_CHANGED()
    if GetBindLocation() == GetSubZoneText() then
        if self.db.profile.showInChat then
            self:Print(self.db.profile.message)
        end

        if self.db.profile.showOnScreen then
            UIErrorsFrame:AddMessage(self.db.profile.message, 1.0, 1.0, 1.0, 5.0)
        end
    end
end

function WelcomeHome:ChatCommand(input)
    if not input or input:trim() == "" then
        InterfaceOptionsFrame_OpenToFrame(self.optionsFrame)
    else
        LibStub("AceConfigCmd-3.0").HandleCommand(WelcomeHome, "wh", "WelcomeHome", input)
    end
end

function WelcomeHome:GetMessage(info)
    return self.db.profile.message
end

function WelcomeHome:SetMessage(info, newValue)
    self.db.profile.message = newValue
end

function WelcomeHome:IsShowInChat(info)
    return self.db.profile.showInChat
end

function WelcomeHome:ToggleShowInChat(info, value)
    self.db.profile.showInChat = value
end

function WelcomeHome:IsShowOnScreen(info)
    return self.db.profile.showOnScreen
end

function WelcomeHome:ToggleShowOnScreen(info, value)
    self.db.profile.showOnScreen = value
end

Locale-enUS.lua

local L = LibStub("AceLocale-3.0"):NewLocale("WelcomeHome", "enUS", true)

-- Chat commands
L["welcomehome"] = true
L["wh"] = true

L["Welcome Home!"] = true -- default message

L["Message"] = true
L["The message to be displayed when you get home."] = true
L["<Your message>"] = true -- usage

L["Show in Chat"] = true
L["Toggles the display of the message in the chat window."] = true

L["Show on Screen"] = true
L["Toggles the display of the message on the screen."] = true