UI Scripting Manual - User Interface Walkthroughs & Tutorials
Lately there's been a rather... large... influx of players into the WoW community, including myself, many of whom have some coding experience and wish to test their skills and create UI AddOns for World of Warcraft. As with any programming language and interface, there's a bit of a learning curve, so hopefully this will help reduce that (and the headache of digging through various resources for that perfect function) ;)
1. Preparing for the Plunge
a. Weapon of Choice
The first order of business is to get a general understanding of what’s going on here. But in order to do that, it might be best if we armed ourselves with an arsenal of tools to use throughout our coding (modding) saga.
I’m sure at some point we’ve all been on the search for the holy grail of editors (or IDE’s as a slightly more specific term :P), which is often a rather frustrating and long search. (Hell, I have yet to find my “perfect” editor for many of the languages I code in). Anyway, since our scripting language is LUA, we would probably do best with an editor that’s geared towards it. So here’s a list of possibilities, choose well… http://blua.sourceforge.net/
B:Lua – This looks like it is going to be our true holy grail right here. Emil is cooking up a fully featured IDE for the single purpose
of coding for the World of Warcraft UI. Even better, it’s java, so regardless of what
operating system you use, you’re going to benefit from this application! Unfortunately, however, he’s not quite done with his first version. So keep your eye on this one, it’s going to make things a hell of a lot easier on us! So big thanks in advance, Emil! http://www.ideais.com.br/luaeclipse/
Lua Eclipse – here’s another java IDE to use. I haven’t used this particular editor yet, so I can’t comment very much. It is based on the Eclipse platform, which is an extremely feature-rich framework for any
IDE, so I’m sure it is a great editor to use. (might be a bit bulky, however) http://editplus.com/
EditPlus – Here’s a great notepad replacement, and currently my LUA coding
application of choice. It’s got all the basic features you need, and even has a syntax scheme for LUA code (http://www.editplus.com/files/lua2.zip
). However, there’s a 30 day evaluation period with EditPlus, and then you need to pay for it.
Those are just three suggestions – and I think once B:Lua is released, there’s not much of a choice left – I’m switching! :)
b. We Have Our Weapon, but What Shall We Target?
Once you have your editor, that’s only the beginning. We’re going to want a few tools specific to WoW as well. The first of these is WinMPQ, which you can find here: http://shadowflare.gameproc.com/dwnload.html#WinMPQ
Grab the first set of files (you’ll probably need both of the runtime file packs – it’s
a visual basic 4 application). With this, you will be able to dig around in the game’s various MPQ, or MoPaQ, files. MPQ’s are Blizzard’s proprietary format to store game files (think of it as a glorified zip file). If you’re interested in the nitty gritty, as well as the history behind MPQ’s, I suggest you check out “Inside MoPaQ” (http://www.campaigncreations.org/starcraft/inside_mopaq/index.htm
With WinMPQ, you can now extract the contents of the interface and patch MoPaQ’s, (stored in the data folder in your WoW install) which contain everything you’ll ever need (and more) for your World of Warcraft interface modding saga. Interface.MPQ stores the base interface, and thus a majority of the interface files that you can use as examples. Patch.MPQ stores the contents of *all* the patches up to present, and basically is an override for all the base MPQ’s.
So, open up WinMPQ, as we’re going to pull out all the base interface code for you to use as an example. Plus I’ll show you how to keep up-to-date with any changes made by patches. So, with WinMPQ open, click on “open” and then select Interface.MPQ (again, in the data directory of your WoW install) Scroll down a ways, and you’ll start to see files in a FrameXML folder (there’s also a GlueXML folder, but you really don’t need to worry about that – I’ll explain in a bit), so select all the FrameXML files and extract (hit the button) them somewhere outside of your WoW folder.
We’re going to do the same for Patch.MPQ, so open that up too. You’ll notice that Patch.MPQ stores any files updated by our patches, and not just interface files. No matter, we’re only interested in anything under Interface\FrameXML – so grab all those and extract them to the same place as where you previously extracted the FrameXML files from Interface.MPQ (older files will be overwritten). So now you’ve got an up-to-date copy of FrameXML to base your code off of and to use as a reference!
(as a side note, you might want to extract the music, all in
mp3 format to a folder somewhere as it makes for some relaxing music to code to :P)
2. Target Acquired
Now that we’re all ready, it’s time to make your first AddOn!
But, I think first, you need to understand where files are located, and which ones do what. So, let’s take a look at our World of Warcraft install. Inside it are various folders, but the one we’re going to be mucking about in is the Interface folder (create it if it doesn’t exist). Inside the Interface folder are (or will be once you start modifying things) three folders:
FrameXML: This contains all the Blizzard-supplied interface code. The bulk of what you interact with is stored here (though the art is stored elsewhere).
GlueXML: This contains all of the “out of game” interface, such as login,
server selection, character creation, etc. You really shouldn’t need to play around with this, nor want to.
AddOns: This is where you come in! Every player-made module is placed in here.
Now, inside the AddOns folder, each module has its own folder and a table of contents inside that.
3. Commence the jiggling!
a. Initial Setup
So we’re at the point where we’ve gotta do the obligatory Hello World. So start off by making a folder called hello_world under your AddOns folder. Aka, you need it to be placed here:
World of Warcraft/Interface/AddOns/hello_world
Inside this folder, create a file called hello_world.toc. This is your table of contents, and subsequently where you specify what files that WoW should load. A basic Table of Contents looks like this:
## Interface: 4114
## Title: Hello World
## Notes: The obligatory hello world script – WoW-style!
So this needs some explaining. The first line is a relatively new addition to the UI coding scene. Basically, every time Blizzard puts out a new patch, the “current” version number is going to change. If your script does not have the latest version, it *will not* be loaded (It would be nice if we could specify “version equal to or greater”). This is why whenever there’s a patch, all the AddOns break.
Note that you can find the latest version by extracting the file: Interface\FrameXML\FrameXML.toc from Patch.MPQ and opening it in your favorite editor.
I think Title and Notes are pretty self-explanatory. As for OptionalDeps, you can list off any “optional dependencies” that your AddOn may have (you list off other AddOns, separated by spaces). The same goes for Dependencies, except these aren’t optional :P I believe, but I am not sure (I haven’t really tested it) that your AddOn will *not* load if its dependencies don’t exist. In any case, it’s a good idea to stick in any dependencies, optional or not, to help anyone trying to read your code (including yourself :P).
Past the initial declarations, you place a list of xml files to be loaded *in the order that they are written*, one file per line. Note you can also store these in sub-folders to keep things a bit cleaner (for instance you could have the line “core/hello_world.xml” if you wanted a core folder in your AddOn)
b. Adding Some Meat
Now for the fun part! Let’s start out with something pretty simple here, nothing fancy. Create a new file called hello_world.xml (which you specified in the table of contents), and open it up in your favorite editor. Stick this in it: Unfortunately, I can't post the xml as it breaks the post - so you can find the source here (check out the file hello_world.xml) http://wow.nevir.net/tutorial/hello_world/
Oh dear, it just got a bit more complex… So, you can safely ignore all the junk up top (the <Ui…. tag), but pay attention to the line: <Script file="hello_world.lua"/>. This tells WoW to load the file, hello_world.lua, which is where the body of your AddOn will reside.
Now… every part of the interface is contained within a “Frame” Here we have a frame to simply handle any events our script should be aware of, but you also stick all your buttons, windows, etc within frames too (more on this in a bit). In our frame tag, we define a name – this *must* be a unique identifier for this frame. It is recommended that you prefix the name with the name of your module, followed by an underscore. So in our case, we’re calling this our “core” frame, and thus prefixing it by hello_world to make sure that no other module steals this frame name.
Inside our frame, we have a <Script> tag, which really is the events handling section of our frame. In here you can have various events, but <OnLoad> and <OnEvent> are going to be the ones that you probably use the most often.
As its name indicates, any code inside the <OnLoad> tag is executed when your AddOn loads (this occurs after you choose a character to log in, but before the loading screen ends – in other words, your AddOn does *not* load for the login screen, etc).
In our OnLoad event, we register “this” for an event called “VARIABLES_LOADED”. “this” is the frame that we currently are in, so “this” really refers to “hello_world_core” (frames are variables/objects of sorts). The colon ( : ) after signifies that we are running a function that is inherit to that frame, in this case, the function RegisterEvent, which tells the game to notify your frame whenever the “VARIABLES_LOADED” event happens.
This leads us to the <OnEvent> tag. A variable by the name of “event” is *always* defined here, and contains the name of the event that is being triggered. So we check to make sure that the event fired is the one we want, namely VARIABLES_LOADED, and then perform some operations when it is fired.
Now, this brings me to another important point, so pay attention. Recently, the guys over at Blizzard gave us an awesome new feature: saved variables. However, in order to implement this, things got a tad more confusing. You can use the function RegisterForSave(“variable_name”) to specify that a variable is to be saved across game sessions. However, if you set a value to that variable before the VARIABLES_LOADED event is fired, it could be overwritten by the saved value.
In other words, set your default settings before VARIABLES_LOADED is fired, and then make any changes/initializations *after* it is fired.
Finally, in our case, when VARIABLES_LOADED is fired, we call the function hello_world_initialize(). Ah crap, but we haven’t written it yet :(
c. Fleshing it All Out
So, this brings us to the actual coding that goes on around here. Create a file called hello_world.lua. Here’s what we’re going to stick in it. Nothing fancy:
-- add our very first chat command!
SlashCmdList["HELLOW"] = hello_world_command;
SLASH_HELLOW1 = "/hellow";
SLASH_HELLOW2 = "/hw";
-- this function handles our chat command
I’m not going to comment on the actual syntax used, as the actual LUA documentation describes it far better than I could ever hope to. You can read about that here: http://www.lua.org/manual/5.0/
Bear in mind that the input/output library, operating system library (and possibly the coroutine and reflexive debug system - I haven't gotten around to testing them yet) are *not* included in the blizzard UI. So basically you've got access to the math, string, and table functions.
For a list of Blizzard-defined functions and events, you can check them out here, courtesy of the excellent effort put forth by the Cosmos guys (and many untold hours of research): http://www.cosmosui.org/texts/BlizzardCommands.xml
Anyway, back to our code. In our initialization function, we define a chat command. This may seem strange, and indeed, it is – as we are modifying the “table” (these are basically associative arrays) SlashCmdList. The explanation of *how* this works exactly is out of the scope of this article, just for now, take my word – this adds a chat command where you can type either /hellow <message>, or /hw <message> - which will then call the function “hello_world_command” when a user types in said command.
Which is why we defined the function hello_world_command to handle our chat command (one variable is passed to all chat command functions, which is everything typed after the initial command, the <message>).
So in a nutshell, this simple AddOn adds two new chat commands. To test them, start up the game (if you already have it running, simply type “/console reloadui” into your chat to reload the interface). Once you log in as one of your characters, try typing:
/hellow Why hello there!
You should get a popup window with “Why Hello there!” Joy!
4. Finishing up…. For now…
So that’s it for our rudimentary example. I’ll whip up some more advanced examples (perhaps with some real interface components, like…. buttons!
and… and… animation
!) when I am slightly less exhausted, and slightly more sane.
But if you made it this far, I applaud you! That’s a lot of reading.
For the overzealous lot, here’s a bit of homework. Read through the LUA manual (found at http://www.lua.org/manual/5.0/
), and skim over the Blizzard-defined functions (found at http://www.cosmosui.org/texts/BlizzardCommands.xml
). Finally, in your copy of the Interface folder (where you extracted FrameXML) – check out the file:
This handles the message() function you saw earlier, as well as a few other things. (you may want to check out Fonts.xml as well, and see if you can find a better font for the ScriptErrors frame – perhaps a smaller one, and maybe a different color, that’s easier on the eyes)
The latest copy of the WoW API Docs can be found at: http://www.wowwiki.com/Interface_Customization
There are currently 0 Comments for this article.
To add a comment type it in the box below.