I have a confession to make... I play a lot of World of Warcraft.
Twice a week, a few hours after dinner, me and 19 other guys and gals team up for 4 hours to beat our heads against the latest and most difficult bosses the game has to offer. My team generally ranks somewhere inside the top 100 teams in the US, even finishing as high as US #42 two dungeons ago. I'm proud of that, as finishing in the US Top 100 is considered quite good for an 8 hour / week team. Many of the teams we compete with play together for 12 or even 16 hours / week. The very very best groups quite literally play 16 hours per day, 7 days a week, not relenting from these grueling hours until the final boss of the dungeon is defeated.
If you're interested (let's be honest, you're not), each time a new dungeon is released the whole ordeal these top teams go through is live-streamed on Twitch in a pretty popular event known as the Race to World First.
For these top teams, it can take upwards of ~10-14 days of this insane schedule to finish the dungeon. For us mortals, it takes months.
I know what you're thinking, and you're right. I am pretty cool π.
β What this post is not about
I'd love to talk your ear off about "Theorycrafting" (the art of figuring out, via some pretty complicated math, the best way to play a particular class), my incredible teammates, or how great it feels when finally, after literally hundred of attempts, all 20 people on the team "click" for that one perfect attempt and succeed to defeat the final boss of a dungeon. The problem-solving, teamwork, and coordination required to take 20 nerds sitting behind desks around the country and mold them into a coherent unit capable of executing some pretty sophisticated strategies is something magical and unique about WoW. I've never found anything like it in any other game. Believe me, I've tried. That teamwork, comradery, and sense of accomplishment is the primary reason I keep logging on every week. Unfortunately, this post isn't really about any of those things.
βοΈ What this post is about
I love to code. I find my work incredibly enjoyable and rewarding, and after the workday ends I often hack on a hobby project for a few hours in the evening trying to learn something new. So you can imagine when I, as a new player (noob) to WoW, discovered that I could write code that would assist my teammates and I in our quest to defeat the toughest bosses the game has to offer, I was pretty darn excited. Let me tell you about a little program called WeakAuras.
WeakAuras bills itself as:
"A powerful and flexible framework that allows the display of highly customizable graphics on World of Warcraft's user interface."
Essentially, WeakAuras helps you, the player, write code that responds to a variety of events that the game's UI publishes. These "customizable graphics" can be things like:
- Little icons that count down to when a particular event during a boss encounter is going to happen
- Utilities that remind you to equip certain pieces of gear or add helpful labels to your map
- Fully-fledged programs that dynamically make and display decisions about which players to assign to handle certain jobs during a boss encounter
WeakAuras is so powerful, that the "very very best groups" have gone as far as to hire (for real money!) dedicated programmers whose sole job is to quickly, on-the-fly write them custom WeakAuras during the aforementioned "Race to World First".
WeakAuras has been around for years now, and there is an entire ecosystem built around it. One of the tools in that ecosystem, an Electron / Vue "package manager" app called WeakAuras Companion, I contributed to back in it's early days.
This all might sound like cheating, but WoW's UI has always allowed "add-ons" to modify it. WoW's development team has publicly stated that when designing bosses, they now design around the knowledge that players will create clever WeakAuras to help them defeat the boss. They design the bosses to be more challenging and mechanically complex than they otherwise might have. At times, when players have crafted WeakAuras that were deemed too powerful, the WoW development team has reined the add-on back in by restricting code access to problematic APIs.
π¨βπ» Examples
If you're reading this blog, you're probably interested in the code, so let's get into that...
WoW's UI API is written in Lua. If you aren't familiar, Lua is a great little scripting language built on top of C, designed to be embedded into larger pieces of software. You can even, thanks to web assembly, run it in the browser. It has some quirks that take some getting used to (Lua's Arrays start at index 1 π), but by and large it's great at what it does and can be found everywhere in computer game UIs.
As alluded to earlier, much like JavaScript, WoW's UI API is event driven. Your code registers listeners for certain events and then reacts to them as they occur. The APIs exposed by the WoW UI, however, can be a bit clunky. This is where WeakAuras comes in. It has a GUI for non-programmers, but at it's core it is a complex wrapper around the WoW UI API, abstracting away many of the pain points of interacting with the base APIs.
The following examples are not intended to serve as a WeakAuras tutorial. I've intentionally changed some things and left other things out to make this all easier to understand.
Let's finally jump into some examples. An individual WeakAura can be broken down into a few functions:
-
init
- A place to initialize any state you want when the WeakAura first loads. -
trigger
- Receives some event based arguments and tells the WeakAura whether or not it should display on the screen by returning a boolean value. -
display
- Returns a value that will actually be displayed on the screen.
There are more, but for the purposes of this post these are enough. The following is a simple example WeakAura that utilizes these functions to count the number of times a specific event has happened in an encounter and displays it on the player's screen.
function()
-- aura_env is a non-reactive table scoped to your WeakAura
-- that you can write data you want to be preserved
-- between renders to. think `React.useRef()`
aura_env.count = 0
end
function(event, subvent)
if event == "COMBAT_EVENT"
and subevent == "SPELLCAST_SUCCEEDED"
then
aura_env.count = aura_env.count + 1
end
return aura_env.count > 0
end
function()
return string.format(
"Event has happened %s time(s).",
aura_env.count
)
end
If you have a React background, this pattern is probably immediately recognizable to you. You can draw the following parallels between these functions and parts of the old React component lifecycle:
-
init
-componentDidMount
-
trigger
-componentWillReceiveProps
-
display
-render
They aren't perfect matches, but it's pretty close.
There's a problem with the above example, though. It isn't particularly useful. Perhaps, on a specific boss, you want to rotate and remind 3 of your players to take some action (eg. cast a powerful healing spell) to save the group each time the boss attacks with a powerful ability. Below, I've adapted the code to do that.
function()
aura_env.count = 0
aura_env.currentPlayer = nil
aura_env.playerRotation = {
"Lisa",
"Milo",
"Chris",
}
end
-- spellId is just a made up argument here that would
-- identify a particular ability we wanted to watch
-- out for
function(event, subevent, spellId)
if event == "COMBAT_EVENT"
and subevent == "SPELLCAST_SUCCEEDED"
and spellId === 123456
then
aura_env.count = aura_env.count + 1
aura_env.currentPlayer =
aura_env.playerRotation[aura_env.count % 3]
end
return aura_env.currentPlayer ~= nil
end
function()
return string.format(
"Save the group %s!",
aura_env.currentPlayer
)
end
You could even change that last trigger
function to only show the text on the screen of the person whose turn it currently is using some built-ins WeakAuras provides you.
function(event, subevent, spellId)
-- ...rest of trigger fn
return aura_env.currentPlayer ~= nil
and aura_env.currentPlayer == WeakAuras.me
end
Bring it back now
I titled this post "How I coded my way to victory in World of Warcraft", and hopefully you understand now what I meant by that. I have created dozens of WeakAuras over the years to help my team succeed. Further, I've created hundreds of WeakAuras for both me personally, and at the request of teammates, to help us succeed as individuals.
Even though I promised you that this post was most certainly not about a few things earlier, I've changed my mind here at the end. I've picked up quite a few valuable and transferable skills playing World of Warcraft, and I feel it would be remiss to not highlight them. And, no, I don't mean a new programming language I probably wouldn't have been exposed to otherwise. While that's certainly cool, I think that when the sun sets on WoW and the game is no longer around, the really important things I'll have taken with me are some of the lessons I learned about:
- how to be a good teammate
- cooperative problem-solving to achieve a common goal
- how to succeed in a large team with many diverse backgrounds and personalities
Of course, I'll also take with me all the friends I made along the way.
Just for fun, here's the final 1.5 minutes of top team Limit achieving a world first final boss kill last year. If you watch closely, you'll notice a lot of WeakAuras on the screens of the featured players.
WARNING explicit language and lots of "nerdscreams"
Edit 1/16: A previous version of this post incorrectly linked to fengari. Thanks darkwiiplayer.
Top comments (4)
Minor nitpick: Fengari isn't written in web assembly, it's actually just a port of the LuaΒ VM fromΒ C to JavaScript.
There is another project called wasmoon that compiles the original C code to web assembly though, so it's not like you were wrong π
Thank you! I made a correction :)
What a fascinating read! Your journey through coding and gaming is truly inspiring. Itβs incredible to see how you blended your passion for programming with your love for World of Warcraft. As a fellow WoW enthusiast, I can relate to the thrill of tackling challenging content and finding innovative ways to overcome obstacles.
Speaking of overcoming challenges, I recently discovered the WoW Delves Carry service (wowvendor.com/shop/wow/delves/), and it has been a game-changer for my gaming experience. Delves can be pretty daunting, especially when trying to gather a competent team or simply having limited time to invest in grinding. This service allows me to dive straight into the action without worrying about the grind, letting me enjoy the story and mechanics of the game more fully.
I find that using services like this not only saves time but also enhances the overall enjoyment of the game. It frees me up to focus on other aspects, like exploring new zones or tackling harder raids with friends. Your post reminds me of how much we can benefit from both technology and community in making our gaming experiences richer. Keep up the great work!
Finally got around to reading this. This is great! Really don't know the first thing about WoW, but I appreciate the tie-ins to React here to illustrate how these functions were being used. You're the man!