A Complete Newbie’s Guide to Apollo and Houston

This tutorial will function both as a simple tutorial for those who may not have any experience with WildStar’s addon system, AND as a way of testing our syntax highlighting plugins for Lua and how well tutorial posting works in this theme.

Before we dive into any real addons that have sprung up during the WildStar beta (or even any addons with a real use), it would be worthwhile to cover a bit of how WildStar handles addons, and the process by which create them. If you’re already familiar with addon development, much of this may be redundant; I apologize. The final form of the addon will also be included as a .zip file at the end of the tutorial post.

Apollo and Houston

What is Apollo?

You may have seen the term “Apollo” thrown around in relation to WildStar before. Either in Twitter conversations between addon developers, in interviews with Bitwise (Jon Wiesman, Carbine’s lead client developer and the creator of the addon system) or in forum threads. And more than once, I’ve had someone ask “what the heck is ‘Apollo’, anyway?”

Lua is a language which can be embedded in just about anything, but if that’s all you do then the Lua programs won’t be very interesting. If I can add things, subtract them, perform calculations and so on but have no way to actually interact with the host program… well, it’s not very useful. So you have to inject your own APIs into the Lua virtual machine. These APIs will provide functions that call back into the hosting program.

In the case of WildStar, that API which the WildStar client provides to the Lua VM is called “Apollo”. All communication between your addon and the game client will be handled by Apollo.

In many games, either the base UI is written separately from the addon system, or the game developer has access to functionality that addon developers don’t have. Unusually, Carbine holds themselves to the same restrictions that addon authors have; anything they can do as a UI addon, we can as well. Bitwise refers to this as “peer level functionality”.

There are a few exceptions; user-provided addons cannot be used before you’re in the game world, so we can’t replace the login screen or character creation, or anything—like the options menu—which runs outside of the game instance. But within the game itself, we have the same sandbox to play in that Carbine’s own UI developers have.

What is Houston?

Houston is the development environment for WildStar addons. It’s used in-house by Carbine’s own developers, as well as by the third-party addon developers. Houston provides a way to access the resources encoded in WildStar’s asset library—all the sprites, forms and stock addons—as well as to create new addons, design forms and import assets like images and sounds into your own addon.

WildStar's "Houston" editor.

Houston, we have an IDE.

Houston is included with a stock installation of WildStar; there’s no separate download to pick up from somewhere on Carbine’s website. The Houston.exe file can be found in the Client or Client64 directory under your WildStar installation. However, keep in mind that the game cannot be patched while Houston is running, as Houston needs to have the game asset files open (and Houston itself is updated as part of any patch).

Once Houston’s open, you can either load an addon that’s already on disk, load a stock Carbine addon from within the game to look at, or create a new addon.

The Addon

Getting Started

When you choose to make a new addon, Houston will present you with a dialog asking you to provide some information about your addon.

The "New Addon" dialog.

The “New Addon” dialog.

We’ll turn on the slashcommand and timer options; all they do is generate some boilerplate code for us, but it provides a convenient sample we can work with.

When you click OK, you’ll find that Houston has created the project for you, and it contains two files: NASASample.lua and NASASample.xml. There is actually also a third file, toc.xml, which represents the addon bundle itself and is what Houston actually has ‘open’.

Anatomy of an Addon

Let’s double-click on NASASample.lua, to open that in the Lua editor.

Houston's Lua editor.

Houston’s Lua editor.

You’ll notice that the Lua editor shows an entry for each function in each Lua file of the project, giving you a quick way to skip to any of those functions.

Let’s break down the standard addon functions we’re using here.

  • new is called to create an instance of the addon class.
  • Init is called to set up an instance of the class.
  • OnLoad is called when the addon is loading. Every addon must support this.

There are some additional standard functions an addon may optionally provide for additional functionality:

  • OnConfigure can be implemented if the addon wants to have a button in the WildStar configuration menu.
  • OnSave and OnRestore can be implemented to save and load data, which we will cover in the AngryBars tutorial.

You will notice in our sample addon, there are a handful of other functions already provided for us:

  • OnNASASample has been automatically generated for the /nasample command we provided in the addon creation step.
  • OnTimer has been automatically generated for the timer we requested in the addon creation step.
  • OnOK and OnCancel have been automatically generated for the OK and Cancel buttons of the default form that Houston has created for us.

Let’s take a quick look into a few of the functions as we flow through the system. Let’s start with Init.

31
32
33
function NASASample:Init()
    Apollo.RegisterAddon(self)
end

The first thing you’ll notice is that the function is named ‘NASASample:Init’. The : is a special character in Lua, and basically ensures a hidden parameter of ‘self’ at the beginning of the function which will always have the object that function is on.

RegisterAddon takes a single Lua object, which is used as the main class of the addon. That object is what OnLoad, OnConfigure, OnSave, OnRestore and all the other stock Apollo functions will be called on. By calling RegisterAddon, we have actually set the addon up in Apollo.

OnLoad is where most of the setup of an addon is usually done:

39
40
41
42
43
44
45
46
47
48
49
function NASASample:OnLoad()
    -- Register handlers for events, slash commands and timer, etc.
    -- e.g. Apollo.RegisterEventHandler("KeyDown", "OnKeyDown", self)
    Apollo.RegisterSlashCommand("nasample", "OnNASASampleOn", self)
    Apollo.RegisterTimerHandler("OneSecTimer", "OnTimer", self)
 
    -- load our forms
    self.wndMain = Apollo.LoadForm("NASASample.xml", "NASASampleForm", nil, self)
    self.wndMain:Show(false)
 
end

You can see that Houston has already filled out several things for us:

  • Apollo.RegisterSlashCommand registers a new command the player can use. It takes three parameters: a command (nasample), a function name (“OnNASASampleOn”) and a Lua object on which that function will be called (self). In this case, if you typed “/nasample” as a command the chat input box, the OnNASASample function on this class would be called.
  • Apollo.RegisterTimerHandler takes three parameters: a timer name (“OneSecTimer”), a function name (“OnTimer”) and a Lua object on which that function will be called (self). In this case, “OneSecTimer” is a special timer which fires once per second, so we don’t need to create a timer and set the duration.
  • Apollo.LoadForm will load a form—Apollo’s UI elements—from the UI XML file specified. It takes four parameters: a filename or an xmlDoc (in this case “NASASample.xml”), the name of the form in that file (“NASASampleForm”), the parent form to load this form as a child of or nil if it just sits at the top UI layer, and the object on which all control callbacks will be made. It returns an object representing that form; in this case, we’re storing that as the ‘wndMain’ parameter on our addon class.

The next line, you will notice, has another of those : characters mentioned earlier. “self.wndMain” is the form we just stored off, and “Show” is a function on that form. Calling “self.wndMain:Show(false)” ensures the form is not visible.

Similarly, the OnNASASample function calls self.wndMain:Show(true), making the window visible. The OnOK and OnCancel functions hide the window once again.

The last function we have is the OnTimer function:

63
64
65
function NASASample:OnTimer()
    Print("NASASample:OnTimer()")
end

This is a simple one. Print() is a global function which just outputs the string onto the “Debug” channel of the game’s chat system. Debug is a special channel you cannot talk on, and which is only local to your client, but which can be added to a tab or hidden like any other channel. In this case, it will print the string “NASASample:OnTimer()” every time the timer function we registered is called, once a second.

This isn’t a terribly interesting addon. Let’s spice it up slightly.

The Form Editor

Basics

Going back to the project tab and double-clicking on NASASample.xml will open a new tab in Houston, loading that file into the form editor.

Much like the Lua editor, the form editor breaks down an XML file into each of the forms it contains, and the hierarchy of UI elements contained within.

In this case, Houston has created a dialog window for us which contains three buttons (a close button in the upper right corner, and OK and Cancel buttons in the lower right) and a title.

If we double-click on our NASASampleForm, it will be loaded into the form editor for us, and the properties dialog for that form opened:

Houston's form editor.

Houston’s form editor.

One of the first things that becomes clear is that Apollo positions everything in a slightly different way: each point (left, top, right, bottom) is defined as an anchor and an offset.

For anchors on left and right, ‘0’ means the left side of the parent container and ‘1’ means the right. For top and bottom, an anchor of ‘0’ means the top and ‘1’ means the bottom. You can also have named anchors on other forms, to allow them to be positioned relative to each other, but for now we’re just going to care about those.

In this case, our anchors are (0,0,0,0), and the offsets are (61,52,656,847). This means the left edge of the form will be 61 units past the left edge of the screen (as the screen is our parent container here), the right edge 656 units from the left edge of the screen, the top of our form 52 units from the top of the screen, and the bottom 847 units from the top of the screen.

But what if we want the dialog to never be taller than our screen? Let’s change the bottom to be an anchor point of 1 (the bottom of the screen), and the offset to be -50.

A smaller dialog!

A smaller dialog!

Now our dialog fits within the small preview I was using to take these screenshots, but would get taller as the screen got taller.

“Title” isn’t a very interesting name for the addon’s form, either. Let’s go change that. Double-click the ‘Title’ element, and change the Text value:

"My First Addon"

“My First Addon”

Okay, so we’ve seen how we can change the attributes of an element on the form. But how does it actually do things with the addon itself? Let’s take a look at our OK button to see. Double-click on the OkButton element, and then click on the ‘Events’ tab:

I want to push the shiny, red, CANDY-LIKE button... ok, fine, this one's green, but still.

I want to push the shiny, red, CANDY-LIKE button… ok, fine, this one’s green, but still.

The ButtonSignal event has been bound to “OnOK”. ButtonSignal is one of many potential events you can add handlers for; in this case, ButtonSignal is sent whenever a button is clicked. OnOK, you will notice, is the name of a function back in our Lua handler.

Looking back at this line:

46
    self.wndMain = Apollo.LoadForm("NASASample.xml", "NASASampleForm", nil, self)

You’ll notice the ‘self’ on the end there. That object is dealing with all event handlers from the form we loaded there. Since it was our addon class, when you click on the OK button, it will call “OnOK” on our addon class.

This may seem a bit much, but it can be very useful in complex addons where you may need to break down form handlers, timers and such not into smaller classes for easy maintenance.

So, let’s make our dialog slightly more interesting. We’ll select the NASASampleForm again, then go up and click on the “Window” icon to add a new generic form element as a child of our NASASampleForm. We’ll name it “Icon” and set the anchors to (0,0,1,1) and the offsets to (21,66,-21,-91). Since it is contained within the NASASampleForm, the anchors refer to the boundaries of the form, and we’ll end up with a nice little container.

Since we’ve named it Icon, let’s pick a graphic to put in. You can click the “…” next to the Sprite field, and you’ll find yourself in WildStar’s sprite library. This will let you browse through all the sprites that are available, and enter a search pattern to filter them on.

In this case, let’s enter CRB_DEMO and filter on that. We’ll find a number of assets which are used for the trade show demos:

So many sprites!

So many sprites!

We’ll pick the spellslinger and click ‘OK’. The image hasn’t appeared yet, however, so we’ll need to click into the ‘Styles’ tab and check ‘Picture’ to let the form editor know that, yes, this particular element contains a picture. Now, we have a Spellslinger:

Because 'slingers are the best class. #unbiased

Because ‘slingers are the best class. #unbiased

Now we’re going to add one more element. So we’ll go back to add another generic element, one called Text. This time, we’ll set the anchors to (0,0,0,0) and the offsets to (317,119,565,442). We’ll get a skinny box next to the spellslinger. Because it’s somewhat narrow, we’ll go to the “TextFlags” tab and check “DT_WORDBREAK” to turn on the word-break flag, which means text will wrap onto multiple lines in the element:

Our "Text" element.

Our “Text” element.

Now our dialog is done, but we can still make this a little more interesting.

Accessing Form Elements

Let’s flip back to the Lua editor, and go to our “OnNASASample” function. We’re going to add some to the function:

58
59
60
61
62
63
64
65
function NASASample:OnNASASampleOn()
	local drPlayer = GameLib.GetPlayerUnit()
	local strName = drPlayer and drPlayer:GetName() or "player"
 
	self.wndMain:FindChild("Text"):SetText("Well, shoot, " .. strName .. "! Y'all made an addon!")
 
	self.wndMain:Show(true) -- show the window
end

So, now, before we show the window, we’re calling ‘GetPlayerUnit()’ on the GameLib library, which will return an object representing the WildStar “Unit” for the character. Units are anything within the world; players, mobs, props, harvestable resources, lore objects and so on. If, by some chance, you’ve managed to call this function without being fully loaded into the world, GameLib.GetPlayerUnit() would return nil.

Once we have the player unit, we’re going to get the name or, if the player happens to be nil, we’re going to use the placeholder string “player”.

Then we’re going to find the child of our main form named “Text”, and set the text value of that form element to be the string we’ve provided, including the character name. And then, as before, we’re going to show the window.

Let’s save our addon (File, Save All) and load up WildStar.

Using Our Addon

After you manage to tear yourself away from Jeff Kurtenacker’s awesome title theme long enough to actually log your character in, you’ll quickly notice something: your chat window is full of a bunch of lines saying “[Debug] NASASample:OnTimer()”, one appearing every second.

Whups, we forgot to remove that sample timer. Let’s go back into the Lua file, and we can remove the entire OnTimer function, as well as the RegisterTimerHandler call in OnLoad. Once that is done, type “/reloadui” into your chat input area and hit enter. Your UI will be refreshed, the addons reloaded, and you’ll see the timer has cleared up.

Now that we’re not flooding our chat log, you’ll want to enter that command we registered. Type “/nasample” into the chat log, and you’ll notice that it shows as a valid command. Hit enter, and our dialog will show up:

Success!  Pax cheers your efforts.  Also your taste in class images.

Success! Pax cheers your efforts. Also your taste in class images.

As you can see, our simplistic little form contains the text we set, including the character name. Pax is thrilled to get some recognition.

Congratulations! You’ve made a simple addon, and learned a bit of how to make your way around Houston, probably in the space of about 15-20 minutes. While it’s not a terribly interesting or complicated addon, the knowledge you’ve gained here will help in building considerably more complicated addons, both on your own and in following along with more complicated tutorials on here.

Please feel free to provide commentary on this tutorial; we’re still feeling out the format and process, and if there are parts that needed to be clearer, it’s best to tweak things now with the simplest possible tutorial!

NASASample.zip (2kb)

Bookmark the permalink.

12 Comments

  1. Looking good!

    I’d make the content div a little wider (maybe adapt to the screen size?) so that here is less horizontal scrolling on code blocks when you have long lines (this doesn’t happen much in this article, but I expect with more complex examples you will end up with longer lines also).

  2. Looking good, indeed!

    One thing that I would recommend doing is having the option of seeing the entire code so far after each section: what’s new, what’s replaced, what’s deleted, etc. — this, in theory, should help Lua newbies understand why and where you’re writing the new code.

    You could argue that this is for people who already know Lua — but what better way to learn Lua than make something that works — in a game?

  3. I guess you already know this, but the recent API’s have made it hard for noobs like me to follow your tutorial. OnLoad now has only one line. There is a new Function called GetAsyncLoadStatus. And OnNASASample has been replaced with OnNASASAmpleON. Rather than throw GetAsyncLoadStatus away, I’m going to try and use it and modify your instruction accordingly. Wish me luck.

    • Yeah, unfortunately I need to update this for the current API. However, things should still work if entered as described, even if the auto generated template doesn’t match anymore. 🙂

  4. Now I’ve learned that the code currently generated by Houston (API 7) may not be the way to go. There’s a whole discussion over on the beta forums. Look’s like I’m just unlucky trying to learn this stuff when things aren’t set up to well for noobs like me. But I’m still learning stuff. This was a great tutorial.

  5. Hi! i have a questions: if possible (with this addon) to gain localization language file for insert italian subtitles? And how I find these file for translated in italian subtitles?

    • Unfortunately, no; the localization files currently cannot be modified by addons that I’m aware of. You can access the localization files (via Apollo.GetString) but not add new languages. 🙁

  6. Is it possible to extract images using Houston?

  7. This has been very helpful for a start, thank you! Sucks a bit, that we don’t have a nice documentation and instead have to debug/log to find out what functions there are.

  8. Well speaking as a complete newbie to LUA and still a console window programmer (few months experience).

    I found this pretty easy to follow.

    It helped me:
    – familiarize myself with the interface
    – understand and use the : operator
    – understand positioning

    Most importantly of all it got rid of my fears that learning LUA enough to make some kind of add-on would be some horribly long process with a steep learning curve.

    If it is or isn’t doesn’t matter, it gave me the motivation to learn.

  9. Cerwi Alexis [Jabbit EU]

    Thanks very much! It’s a really good tutorial for showing us newbies Houston and getting an addon up. From here, THE SKY IS THE LIMIT.

Leave a Reply

Your email address will not be published. Required fields are marked *