So you'd like to learn more about creating a WoW AddOn... We've thrown together a more advanced tutorial to familiarize yourself with some key concepts in the WoW UI framework.
Show Me The Money!
For those of you wishing you had a way to keep track of the total amount of money you've ever accumulated on your character, today's your lucky day.
"Show Me The Money!" is an AddOn designed to do just that - it adds any cash you gain to a persistent total across game sessions for each unique character. The intention of this tutorial is to demonstrate the use of key functionality within the World of Warcraft interface framework: OnLoad, OnEvent, OnUpdate, and saved variables. However, since this AddOn is a bit more complex than the one we went through up above, copy the "ShowMeTheMoney" directory from "Blizzard Interface Tutorial" to "Interface\AddOns" to see how it functions in the game. To see the AddOn in action just go to a vendor and sell something! It would also be helpful to have the associated files viewable for reference while reading through this tutorial. The code itself has more inline commentary to clarify what each bit of code does, whereas here we will go over key concepts.
There are four neccesary files for this tutorial:
ShowMeTheMoney.toc
ShowMeTheMoney.xml
ShowMeTheMoney.lua
CashMoney.tga
ShowMeTheMoney.toc
For this AddOn, we've got a fairly straightforward .toc file. One particular thing of note, however, is a new field called "SavedVariablesPerCharacter". This is a comma separated list of global variables that are automatically saved and restored with this AddOn.
## Interface: 20000
## Title: Show Me The Money!
## Notes: An advanced tutorial
## Dependencies:
## SavedVariablesPerCharacter: SHOWMETHEMONEY_MONEY
ShowMeTheMoney.lua
ShowMeTheMoney.xml
ShowMeTheMoney.xml
The best way to tackle building an add on is first determining what it is you want the AddOn to look like. Once you get the looks down, it's easy to add the functionality piece by piece, testing it each step of the way. Once the XML is built, you can actually load your AddOn into the game and use the
"/script ReloadUI();" command every time you save an update to your files. If there are syntax errors in loading, you can just edit, fix, and reload. Keep in mind, however, that ReloadUI(); will not load new files, only existing ones.
Let's take a closer look at the key elements within the ShowMeTheMoney.xml.
Since the XML can be somewhat complex, let's move down chunk by chunk and discuss what's being called within it.
<Frame name="ShowMeTheMoneyFrame" toplevel="true" parent="UIParent" movable="true"
hidden="false">
<Size x="148" y="30"/>
<Anchors>
<Anchor point="TOPLEFT" relativeTo="Minimap" relativePoint="BOTTOMLEFT">
<Offset x="-3" y="-10"/>
</Anchor>
</Anchors>
This is the frame that contains our AddOn. It is essentially an empty frame, since it houses an interior frame that accepts game events. We will utilize this frame to handle mouse events while our interior frame is hidden, since a frame will only detect mouse movement if it is visible. It is a top-level frame, because it is a container, and its parent is UIParent, the parent of all top-level frames. Yes, this frame is movable (since we want it to move our event-driven frame). We also have established the overall size of this AddOn's frame, and where it should render within the game screen (in this case, just below the Minimap).
Note: Frames work together in a number of ways. It can inherit the features and settings of a parent frame or have a relative attachment to another frame. Many frames within the World of Warcraft UI are constructed this way. Poke around in the extracted files (specifically UIParent.xml) to see a few examples.
Within the container frame, we then define our active frame. We will need to establish look and feel (any artwork drawn in to create our box), our active elements, and related scripts. For this AddOn, we're going to utilize a pre-created Tooltip look and feel, the format for which was obtained from the WoW Interface source files. We also want to have a little animation that plays on each cash event that our AddOn registers, so we'll define and position that texture here as well.
Note: Whenever a frame, texture, or fontstring is defined in XML, its initial attributes are defined and, if named, a global variable with that name is created to represent that object in Lua.
<Frames>
<Frame name="ShowMeTheMoney" setAllPoints="true" hidden="true">
<Backdrop bgFile="Interface\Tooltips\UI-Tooltip-Background"
edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true">
<EdgeSize>
<AbsValue val="16"/>
</EdgeSize>
<TileSize>
<AbsValue val="16"/>
</TileSize>
<BackgroundInsets>
<AbsInset left="5" right="5" top="5" bottom="5"/>
</BackgroundInsets>
</Backdrop>
<Layers>
<Layer level="BACKGROUND">
<Texture name="CashMoney" file="Interface\AddOns\ShowMeTheMoney\CashMoney">
<Size x="16" y="16"/>
<Anchors>
<Anchor point="RIGHT" relativePoint="LEFT"/>
</Anchors>
</Texture>
</Layer>
</Layers>
Now that we've got the look, we need the functionality. Since we're working with Money related events, we'll utilize another portion of the pre-existing UI for our functionality: the MoneyFrame. If you'd like to learn more about the MoneyFrame, browse through
MoneyFrame.xml and
MoneyFrame.lua in the "Blizzard Interface Data\FrameXML" directory to familiarize yourself with some of the concepts. For this AddOn, we'll take advantage of the "SmallMoneyFrameTemplate" which is typically viewed in the player's Backpack. It is within this frame that we'll call our functions defined within
ShowMeTheMoney.lua.
Note: There is a call to set a Money Type within our script, and that's because MoneyFrame.lua will complain if no type is set, so we set a pre-defined type.
<Frames>
<Frame name="$parentMoneyFrame" inherits="SmallMoneyFrameTemplate" hidden="false">
<Size x="0" y="30"/>
<Anchors>
<Anchor point="TOPRIGHT"/>
</Anchors>
<Scripts>
<OnLoad>
self.staticMoney = 0;
MoneyFrame_SetType("STATIC");
</OnLoad>
</Scripts>
</Frame>
</Frames>
<Scripts>
<OnLoad>
ShowMeTheMoney_OnLoad(self);
</OnLoad>
<OnEvent>
ShowMeTheMoney_OnEvent(self, event, ...);
</OnEvent>
<OnUpdate>
ShowMeTheMoney_OnUpdate(self, elapsed);
</OnUpdate>
</Scripts>
The following code is all related to the UI interaction - fading the window in and out, on Mouse Enter and Leave respectively, and the ability for the user to reposition it wherever they like. To do these things, we'll use some pre-defined functions within
UIParent.lua.
</Frame>
</Frames>
<Scripts>
<OnEnter>
UIFrameFadeRemoveFrame(ShowMeTheMoney);
UIFrameFlashRemoveFrame(ShowMeTheMoney);
UIFrameFadeIn(ShowMeTheMoney, 0.1);
</OnEnter>
<OnLeave>
UIFrameFadeRemoveFrame(ShowMeTheMoney);
UIFrameFlashRemoveFrame(ShowMeTheMoney);
UIFrameFadeOut(ShowMeTheMoney, 0.25);
</OnLeave>
<OnMouseDown>
self:StartMoving();
</OnMouseDown>
<OnMouseUp>
self:StopMovingOrSizing();
</OnMouseUp>
</Scripts>
</Frame>
Now that we've familiarized ourselves with the XML and how it's written, let's take a look at the supporting
.lua that drives this beast. There are four key elements to understand within the WoW UI: AddOn variables, OnLoad, OnEvent, and OnUpdate. We will utilize all of these components in our AddOn.
One thing to note is that our primary functions (OnLoad, OnEvent, OnUpdate) are passing through pre-defined local variables. These are variables present in each of the supporting functions driven by the SCRIPT tags in the XML. self refers to the frame the code is referencing, event is a trigger that the code is listening for (which will be dealt with later on in this tutorial), and elapsed refers to the time interval between each rendered frame.
OnLoad
This is the script that fires when the frame is loaded. In the case of our AddOn, we're initilizing elements of our animation, setting our backdrop color, and registering the events neccesary for the remainder of our functionality.
function ShowMeTheMoney_OnLoad()
ShowMeTheMoney_Animate(SHOWMETHEMONEY_ANIM_NUMFRAMES);
self:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g,
TOOLTIP_DEFAULT_COLOR.b);
self:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r,
TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b);
self:RegisterEvent("ADDON_LOADED");
self:RegisterEvent("PLAYER_ENTERING_WORLD");
self:RegisterEvent("PLAYER_MONEY");
end