World.of.Warcraft.Programming.A.Guide.and.Reference.for.Creating.Wo W.Addons

World.of.Warcraft.Programming.A.Guide.and.Reference.for.Creating.WoW.Addons

User Manual:

Open the PDF directly: View PDF PDF.
Page Count: 1443

DownloadWorld.of.Warcraft.Programming.A.Guide.and.Reference.for.Creating.Wo W.Addons
Open PDF In BrowserView PDF
A Guide and Reference for Creating WoW Addons

Whitehead • Roe

Your secret weapon against
the Lich King’s wrath
World of Warcraft has entered a new dimension. Take command of it by modifying the interface
with your own unique addons. Whether your goal is to enhance addons you already use, to
enlarge your arsenal by creating some all-new features, or to immerse yourself in the programming
as well as the game, this book will be your oracle. Learn the arcane languages of Lua and XML
and master the craft of addon creation!

®

•
•
•
•

Understand the anatomy of an addon
Work with frames, widgets, and other graphical elements
Explore basic and advanced functions and control structures
Track damage with CombatTracker, and respond to the combat log
and threat information
• Track, filter, and sort your inventory by writing BagBuddy,
a fully functional addon
• Create slash commands, custom graphics, scroll frames, and
dropdown menus

Who are you?

®

James Whitehead II, aka Cladhaire, is the creator of PerfectRaid, Clique, TomTom, and LightHeaded, and coauthor of Hacking
World of Warcraft.
Rick Roe, aka the crazy goblin tinker Gazmik Fizzwidget, created Feed-O-Matic, FactionFriend, and some other addons so
awesome that Blizzard rolled their functionality into its new UI.

Front cover art © Scott Johnson/FrogPants Studios

Apprentice programmers with no prior experience — learn Lua and XML in Part I
Journeymen who have done some programming — skim Part I and start with the details about addon creation in Part II
Master programmers with addons already in their arsenal — dive right into Advanced Addon Techniques in Part III

Visit our Web site at www.wiley.com/compbooks
$49.99 US • $59.99 CAN
Computer Graphics / Game Programming

James Whitehead II
Rick Roe

World of Warcraft®
Programming

World of Warcraft®
Programming
A Guide and Reference for Creating
WoW Addons
Second Edition

James Whitehead II
Rick Roe

Wiley Publishing, Inc.

World of Warcraft® Programming: A Guide and Reference for Creating WoW Addons, Second Edition
Published by
Wiley Publishing, Inc.
10475 Crosspoint Boulevard
Indianapolis, IN 46256

www.wiley.com
Copyright © 2010 by Wiley Publishing, Inc., Indianapolis, Indiana
Published simultaneously in Canada
ISBN: 978-0-470-48128-8
Manufactured in the United States of America
10 9 8 7 6 5 4 3 2 1
No part of this publication may be reproduced, stored in a retrieval system or transmitted in any form or by any means,
electronic, mechanical, photocopying, recording, scanning or otherwise, except as permitted under Sections 107 or 108
of the 1976 United States Copyright Act, without either the prior written permission of the Publisher, or authorization
through payment of the appropriate per-copy fee to the Copyright Clearance Center, 222 Rosewood Drive, Danvers,
MA 01923, (978) 750-8400, fax (978) 646-8600. Requests to the Publisher for permission should be addressed to the
Permissions Department, John Wiley & Sons, Inc., 111 River Street, Hoboken, NJ 07030, (201) 748-6011, fax (201) 748-6008,
or online at http://www.wiley.com/go/permissions.
Limit of Liability/Disclaimer of Warranty: The publisher and the author make no representations or warranties with
respect to the accuracy or completeness of the contents of this work and specifically disclaim all warranties, including
without limitation warranties of fitness for a particular purpose. No warranty may be created or extended by sales or
promotional materials. The advice and strategies contained herein may not be suitable for every situation. This work
is sold with the understanding that the publisher is not engaged in rendering legal, accounting, or other professional
services. If professional assistance is required, the services of a competent professional person should be sought. Neither
the publisher nor the author shall be liable for damages arising herefrom. The fact that an organization or Web site is
referred to in this work as a citation and/or a potential source of further information does not mean that the author or the
publisher endorses the information the organization or Web site may provide or recommendations it may make. Further,
readers should be aware that Internet Web sites listed in this work may have changed or disappeared between when this
work was written and when it is read.
For general information on our other products and services please contact our Customer Care Department within the
United States at (877) 762-2974, outside the United States at (317) 572-3993 or fax (317) 572-4002.
Wiley also publishes its books in a variety of electronic formats. Some content that appears in print may not be available
in electronic books.
Library of Congress Control Number: 2009933378
Trademarks: Wiley and the Wiley logo are trademarks or registered trademarks of John Wiley & Sons, Inc. and/or its
affiliates, in the United States and other countries, and may not be used without written permission. All other trademarks
are the property of their respective owners. Wiley Publishing, Inc. is not associated with any product or vendor mentioned
in this book.

About the Authors

James Whitehead II is the author of a number of popular addons for World
of Warcraft, including LightHeaded, TomTom, Clique, PerfectRaid, and many
other small but useful addons. He has been an active member of both the WoW
UI and Lua communities since the World of Warcraft Beta began and has been
writing addons ever since. When he actually has time to play the game, you
can find him playing one of his many characters on the Emerald Dream (EU)
server.
Jim currently resides in Oxford, England where he is pursuing his DPhil
(PhD) in Computer Science at the Computing Laboratory. In his spare time he
enjoys all things rowing, hacking on his Android phone, knitting, crocheting,
and spending time with his friends.
Rick Roe—known in the WoW UI community as the zany goblin tinker
Gazmik Fizzwidget—is the author of several popular addons including
Feed-O-Matic and FactionFriend, as well as TrackMenu and a couple of
others so useful that Blizzard made them obsolete by rolling their functionality
into the default UI. When not slaving away for their goblin master, Rick’s alter
egos can often be found adventuring on Cenarius US.
Rick currently resides in Vancouver, Washington, with his wife and cats.
His time outside of Azeroth is split between working to finish a computer
science degree at Washington State University and building Mac and iPhone
applications as an independent software developer.

v

About the Technical Editors

Daniel Stephens—more widely known in the WoW addon community as
Iriel—was the Blizzard WoW UI Forum’s first MVP. He has been helping
others develop addons for several years, creating a few of his own along the
way. His addons include DevTools (recently rolled into the base WoW UI)
and he has made significant contributions to secure handlers and a number of
other utilities. As somewhat of an altaholic, he has characters spread all over
the realms, but considers Silver Hand (US) his original WoW home.
Daniel lives in the San Francisco bay area with his wife, cats, and camera. He
spends his ‘‘not free’’ time doing systems design, architecture, and occasionally
development work.
Esteban Santana Santana, known online as MentalPower, is both Lead Developer for the Auctioneer AddOns Package and one of the Administrators
of Norganna’s AddOns. He’s been part of the WoW UI community since
mid-2005 and has helped many people via the IRC channels and the various
game and UI-related forums. When he logs into World of Warcraft, you can
find him on the US-Alleria realm trying to level his various characters on the
Emerald Dream guild.
Esteban currently resides in Carolina, Puerto Rico, and is a jack-of-all-trades
IT person for Liberty Cablevision. In his spare time, he enjoys thrashing his
buddies in a good game of Rock Band on the XBox 360.

vi

Credits

Acquisitions Editor
Scott Meyers
Contributing Author
Nevin Flanagan
Project Editor
Maryann Steinhart

Vice President and Executive
Group Publisher
Richard Swadley
Vice President and Executive
Publisher
Barry Pruett

Technical Editors
Daniel Stephens
Rick Roe
Esteban Santana Santana

Associate Publisher
Jim Minatel

Production Editor
Rebecca Anderson

Proofreaders
Josh Chase and Nelson Kim, Word
One

Copy Editor
Kim Cofer
Editorial Director
Robyn B. Siesky

Project Coordinator, Cover
Lynsey Stanford

Indexer
J & J Indexing

Editorial Manager
Mary Beth Wakefield

Cover Image
Scott Johnson, FrogPants Studios
LLC

Associate Director of Marketing
David Mayhew

Cover Designer
Michael E. Trent

Production Manager
Tim Tate
vii

Acknowledgments

James and Lee Whitehead, thank you for walking alongside your children as
we journey down the winding road of life; we’re blessed to have the two of
you in our lives. Michelle Hastings, thank you for being such a role model
of strength and determination for your little brother.
Robert Whitehead, thank you for always being yourself and making sure
I don’t stray far from who I am. Gregory Whitehead, thank you for being there
for me whenever I need to ‘‘geek’’ out, I don’t know many people that can get
as excited as I do about silly things. Tom Harper, thank you for what you give
me every single day. Everything about you makes me feel like the luckiest
person alive.
Jamie Anderson, Edward Wilman, Amelia Earl, Rhianedd Jewell, Erika
Nitsch, Daniel Jordan, and all my other friends at Oxford, thank you for
keeping me busy, for helping me make excuses, and for being such a bad
influence.
To Karen Hobson and everyone at WowInterface who have put up with me
for five years now, thank you for all of your efforts in organizing all three
books. To Mike, Kevin, Tom, Jacob, Sam, and everyone at Blizzard, thank you
for creating such an amazing game and supporting us in our documentation
efforts. To everyone at Wiley who helped bring these books into existence,
thank you for your efforts to provide resources for the WoW user interface
community. To Rick, thank you for stepping in when we needed you the most;
your work has been instrumental in making this book what it is today. To
Daniel, Nevin, and Esteban, thank you for all of your help in shaping the edges
of this edition.
Finally, thank you to the World of Warcraft user interface community for
everything you do.
— Jim
ix

x

Acknowledgments

I’d first like to thank my coauthor, Jim, for offering me the opportunity to
‘‘graduate’’ from tech editing on the first edition to authoring on this second
version. Crazy as the schedules and deadlines may have been, I’m still happier
having been able to write my part instead of worrying about mucking with
someone else’s work in order to satisfy my nitpicky tendencies. Thanks also
for all your infrastructure work—without your website and database I’d have
been a scribe without paper.
To Daniel and Esteban fell the unenviable task of performing the role I did
on the first edition—catching all the silly code errors and obtuse explanations
we dumb authors make—and with it my sympathy and gratitude. I can but
hope I haven’t made your work too hard.
Thanks to Karen for playing den mother to the rowdy WoW UI community
and giving us all a place to hook up; if it weren’t for your efforts I’d never
have found my way into this project. Thanks as well to Scott, Maryann, and
everyone at Wiley for making the project happen!
Thanks (again) to Daniel and Jim not just for your work on the book but for
providing development tools without which my tasks would’ve been a whole
lot harder. And of course, thanks to my family, Karen, Doug, and Brad, for
putting up with me for a couple decades and making me the person I am,
and to my wonderful wife Anne: I can’t imagine life without you, much less
without the loving support you give for whatever crazy ideas I set myself to.
Finally, a very special thank you to Mike, Jacob, Sam, and Tom at Blizzard,
without whose patience and willingness to answer oblique questions outside
a normal work schedule we wouldn’t have been able to figure out several
important chunks of the API we’re documenting. Next time I’m in SoCal, your
drinks are on my tab.
— Rick

Contents at a Glance

Introduction

xxxvii

Part I

Learning to Program

1

Chapter 1

Programming for World of Warcraft

3

Chapter 2

Exploring Lua Basics

13

Chapter 3

Basic Functions and Control Structures

39

Chapter 4

Working with Tables

53

Chapter 5

Advanced Functions and Control Structures

77

Chapter 6

Lua Standard Libraries

91

Chapter 7

Learning XML

111

Part II

Programming in World of Warcraft

123

Chapter 8

Anatomy of an Addon

125

Chapter 9

Working with Frames, Widgets, and Other Graphical
Elements

143

Chapter 10 Saving Time with Frame Templates

171

Chapter 11 Exploring the World of Warcraft API

187

Chapter 12 Interacting with Widgets

207
xi

xii

Contents at a Glance
Chapter 13 Responding to Game Events

243

Chapter 14 Tracking Damage with CombatTracker

267

Part III

283

Advanced Addon Techniques

Chapter 15 Taking Action with Secure Templates

285

Chapter 16 Binding Keys and Clicks to Addon Code

309

Chapter 17 Creating Slash Commands

337

Chapter 18 Responding to Graphic Updates with OnUpdate

351

Chapter 19 Altering Existing Behavior with Function Hooking

359

Chapter 20 Creating Custom Graphics

373

Chapter 21 Responding to the Combat Log and Threat Information

387

Chapter 22 Creating Scroll Frames

413

Chapter 23 Creating Dropdown Menus

431

Chapter 24 Scanning and Constructing Tooltips

451

Chapter 25 Taking Protected Action in Combat

463

Chapter 26 Creating Unit Frames with Group Templates

501

Part IV

537

Reference

Chapter 27 API Reference

539

Chapter 28 API Categories

1025

Chapter 29 Widget Reference

1121

Chapter 30 Events Reference

1277

Part V

1303

Appendixes

Appendix A Best Practices

1305

Appendix B Utilizing Addon Libraries

1329

Appendix C Tracking History Using Version Control Systems

1339

Appendix D Addon Author Resources

1349

Index

1357

Contents

Introduction

xxxvii

Part I

Learning to Program

1

Chapter 1

Programming for World of Warcraft
Customizing the User Interface
What Is an Addon?
What Can Addons Do?
Exploring Your AddOns Directory
Blizzard Addons
Custom Addons
Creating Your First Addon: HeyThere
Creating Files and Directories
Loading and Testing the Addon
Summary

3
3
4
4
7
8
10
10
10
11
12

Chapter 2

Exploring Lua Basics
Downloading and Installing Lua
Downloading and Installing WowLua
Using Lua on the Web
Downloading and Installing a Lua Interpreter
Microsoft Windows
Mac OS X
Using the Lua Interpreter
Running Commands
Understanding Error Messages

13
14
14
15
16
16
16
17
18
18

xiii

xiv

Contents

Chapter 3

Using History to Make Changes
Quitting the Interpreter
Microsoft Windows
Mac OS X

19
19
19
19

Working with Numbers
Basic Arithmetic Operations
Scientific Notation
Hexadecimal Notation
Understanding Floating Point
Understanding Values and Variables
Exploring Values and Their Types
Primitive Types
Using the type() Function
Using Variables
Valid Variable Names
Assigning Variables
Assigning Multiple Variables
Comparing Values
Working with Strings
Comparing Strings
Concatenating Multiple Strings
Converting Numbers to Strings
Converting Strings to Numbers
Quoting Strings
Single Quote (’)
Double Quote (’’)
Bracket Quote ([[ ]])
Escaping Special Characters
Getting a String’s Length
Boolean Values and Operators
Using the and Operator
Using the or Operator
Negation Using the not Operator
Understanding the nil Value
Exploring Scope
Blocks
Chunks
Summary

20
20
21
21
22
23
23
23
23
24
25
25
26
26
27
27
28
28
29
29
29
30
30
31
32
33
33
34
34
35
35
36
37
37

Basic Functions and Control Structures
Using Functions
Creating a Function
Local Functions

39
39
39
40

Contents
Function Arguments and Returns
Converting Celsius to Fahrenheit
Empty Arguments
No Return Values
Functions as Lua Values

Chapter 4

41
41
42
42
42

Making Decisions with the if Statement
Simple Conditionals
Complex Expressions
Extended Conditionals
Displaying a Personalized Greeting
Repeating Actions with the while Statement
Computing Factorials
Differences Between while and repeat
Looping with the Numeric for Statement
Computing Factorials
Evaluation of Loop Conditions
Variable Scope in for Loops
Summary

43
43
44
44
45
46
46
47
48
50
50
50
51

Working with Tables
Storing Data Using Tables
Creating and Indexing Tables
Clearing an Element from a Table
Shortcuts for String Keys
Creating Populated Tables
Using Tables as Arrays
Creating an Array
Getting the Length of an Array
Adding Elements to an Array
Removing Elements from an Array
Sorting the Elements of an Array
Using Tables as Namespaces
Creating a Namespace of Utility Functions
Adding Functions to a Namespace
Storing an Existing Function
Defining a New Function
Object-Oriented Programming with Tables
Creating a Non-Object-Oriented Counter
Using Tables as Simple Objects
Using : to Call Object Methods
Defining Functions Using :
Making a Better Counter

53
53
54
54
55
55
56
57
57
58
60
61
61
62
62
62
63
63
63
64
65
66
67

xv

xvi

Contents
Extending Tables with Metatables
Adding a Metatable
Defining Metamethods
Defining Basic Arithmetic Using ___add, ___sub, ___mul,
and ___div
Defining Negation Using ___unm
Creating Meaningful Output with ___tostring
Concatenating Tables Using ___concat
Exploring Fallback Tables with ___index
Catching Creation of Keys with ___newindex
Bypassing Metatables
value = rawget(tbl, key)
rawset(tbl, key, value)
Summary

68
68
69

Chapter 5

Advanced Functions and Control Structures
Multiple Return Values
Converting Hex to RGB
Assigning Multiple Values
Missing Return Values?
Multiple Return Values in World of Warcraft
Using a Dummy Variable
Using the select() Function
Accepting a Variable Number of Arguments
Declaring a Vararg Function
Using select() with . . .
Generic for Loops and Iterators
Syntax of Generic for
Traversing the Array Part of a Table
Traversing an Entire Table
Clearing a Table
Using Other Iterators
Sorting an Array of Table Data
Define Example Data
Default Sort Order
Creating a Comparison Function
Creating a More Complex Sort Function
Summary

77
77
77
78
79
79
80
81
81
82
83
84
84
85
86
86
87
87
88
88
88
89
90

Chapter 6

Lua Standard Libraries
Table Library
str = table.concat (table [, sep [, i [, j]]])
table.insert (table, [pos,] value)
max = table.maxn (table)

91
92
92
92
93

70
71
71
72
72
74
75
75
76

76

Contents
value = table.remove (table [, pos])
table.sort (table [, comp])

93
93

String Utility Functions
Formatting New Strings
Pattern Matching
Character Classes
Pattern Items
Pattern Captures
Pattern Anchors
Pattern Examples
Pattern Matching Functions
string.gmatch(s, pattern)
string.gsub(s, pattern, repl [, n])
string.match(s, pattern [, init])
string.find(s, pattern [, init [, plain]])
Math Library
World of Warcraft Additions to Lua
Function Aliases
Summary

94
95
98
98
100
101
102
102
102
103
103
104
104
105
108
109
110

Chapter 7

Learning XML
XML as a Markup Language
XML’s Relationship to HTML
Components of XML
XML Tags
XML Elements
XML Attributes
XML Entities
Creating Well-Formed XML
Validating an XML Document
Example Schema Definition
Example XML Document
Exploring the Schema
XML in World of Warcraft
Using a GradientType
Exploring Blizzard’s XML User Interface Customization Tool
Summary

111
111
112
112
113
113
113
114
114
115
115
116
116
117
118
119
121

Part II

Programming in World of Warcraft

123

Chapter 8

Anatomy of an Addon
Exploring an Addon’s Files and Folders
Table of Contents (.toc) File
## Interface:

125
125
125
126

xvii

xviii Contents
## Title:
## Notes:
## Dependencies:, ## RequiredDeps:
## OptionalDeps:
## LoadOnDemand:
## LoadsWith:
## DefaultState:
## LoadManager:
## SavedVariables:
## SavedVariablesPerCharacter:

Chapter 9

127
128
128
129
129
129
130
130
130
131

X-Label Directives
Addon Categories
XML Files
Lua Script Files
Media Files
Music
Graphics
Localizing Your Addons
Valid Locales
Reasons for Providing Localization
Encouraging Users to Contribute
Implementing Localization
Add a File for Each Locale
Create a Global Table Containing the Base Strings
Using the Localization Table
Adding New Locales
Handling Partial Translations
Introducing Frames, Widget Scripts, and Events
Frames, FontStrings, and Textures
Displaying Text with FontStrings
Showing Graphics and Colors with Textures
Anchoring Objects On-Screen
Responding to Interaction with Widget Scripts
Responding to Game Events
Loading of an Addon
Summary

131
131
132
133
133
133
133
134
135
135
136
136
136
136
137
137
138
138
138
139
139
139
139
139
141
142

Working with Frames, Widgets, and Other Graphical
Elements
Introducing BagBuddy
Creating an Addon Skeleton
Creating a Frame
Parenting

143
143
144
144
145

Contents
Giving Objects Sizes
Absolute Dimensions
Relative Dimensions
Anchoring Objects
Sticky Anchors
SetAllPoints
Anchor Examples
Using Lua to Create Frames

Adding Layers of Textures and Font Strings
Layering Frames and Graphics
Frame Strata
Frame Levels
Graphical Layers
BagBuddy Frame Design
Finding Graphics
TexBrowser AddOn
ArtBrowser on Wowprogramming.com
Adding Textures
Defining BagBuddy’s Background Textures
Coloring Textures
Using Solid Colors
Creating a Gradient
Adding the Portrait Texture
Creating Textures in Lua
Creating Text using FontStrings
Further Customization
Using Font Definitions
Creating FontStrings in Lua
Understanding Object Visibility
Finding Existing Frames
Summary
The Code
Chapter 10 Saving Time with Frame Templates
Understanding Templates
Advantages of Using Templates
Naming Elements Using $parent
Setting Keys Using the parentKey Attribute
Creating a Template for BagBuddy’s Item Buttons
Setting Button Textures
Creating New Frames with Your Template
Exploring Font Definitions
Altering a Font Definition

146
146
146
147
148
148
148
149

150
150
150
151
152
153
155
155
155
155
157
158
158
159
160
162
164
165
165
166
166
167
167
168
171
171
173
173
174
174
175
176
177
178

xix

xx

Contents
Investigating UIPanelTemplates
UIPanelButtonTemplate
UIPanelCloseButton
UIPanelScrollBarTemplate
InputBoxTemplate
UICheckButtonTemplate
TabButtonTemplate
UIRadioButtonTemplate
Summary
The Code

179
180
180
181
181
182
183
183
183
184

Chapter 11 Exploring the World of Warcraft API
Understanding the WoW API
Normal APIs
Library-like APIs
FrameXML Functions
Protected Functions
Unit Functions Up Close
Querying Item Information for BagBuddy
Scanning Bags with the Container API
Querying Detailed Item Information
Item Identifiers
Using the Item API
Writing a Bag Scanner
Sorting the Player’s Inventory
Displaying the Inventory
Testing the Update Function
Finding the Right API Functions
Exploring the API Categories
Examining the FrameXML Code
Looking at Another Addon
Asking for Help!
Summary
The Code

187
187
188
188
189
189
190
193
193
194
195
197
198
199
199
200
201
201
202
203
203
203
204

Chapter 12 Interacting with Widgets
Making BagBuddy’s Buttons Interactive
Setting Frame Scripts via XML
Using the function Attribute
Setting Frame Scripts Using Lua
Showing Item Tooltips Using OnEnter and OnLeave
Adding Clickable Buttons to BagBuddy
Introducing the OnClick Handler

207
207
208
209
209
210
212
212

Contents
Creating a Close Button Using Templates
Creating Clickable Filter Buttons
Creating the Filter Buttons Dynamically
Adding Custom Tooltips
Making the Filter Buttons Clickable
Updating the Results

Navigating Multiple Pages
Adding XML Definitions for Buttons and Status Text
Writing OnClick Handlers for Navigation Buttons
Altering the Update Function for Pages
Enabling and Disabling Navigation Buttons
Creating and Updating Status Text
Final Changes to Support Navigation
Adding a Name Filter to BagBuddy
Creating an EditBox
Filtering by Name
Exploring Widget Types
Button
CheckButton
ColorSelect
EditBox
GameTooltip
MessageFrame
Minimap
Model
ScrollingMessageFrame
ScrollFrame
SimpleHTML
Slider
StatusBar
Summary
The Code
Chapter 13 Responding to Game Events
Understanding Events
Registering for Events
Responding to Events with OnEvent
Query Events
Tracking Changes to Inventory for BagBuddy
Examining the BAG_UPDATE Event
Tracking New Inventory Items
Writing a New Sorting Function
Altering BagBuddy_Update

213
214
216
217
217
218

219
220
221
221
222
223
224
224
225
226
227
227
228
228
229
229
229
229
230
231
231
231
232
232
233
233
243
243
244
244
246
246
246
246
248
248

xxi

xxii

Contents
Adding an OnEvent Handler
Cleaning Up
Adding a Slash Command

Storing Data with SavedVariables
Registering a New Saved Variable
Saved Variables and ADDON_LOADED
Using Items from BagBuddy
Finding the Right Event Using /eventtrace
Summary
The Code

249
250
251

251
252
252
253
254
255
255

Chapter 14 Tracking Damage with CombatTracker
Defining Specifications
CombatTracker User Experience
Finding the Right Game Events
PLAYER_REGEN_DISABLED
PLAYER_REGEN_ENABLED
UNIT_COMBAT
Creating the Addon’s Skeleton
Defining CombatTracker’s XML Frame
Defining a Backdrop
Adding a Font String
Testing CombatTrackerFrame
Adding Script Handlers to CombatTrackerFrame
Adding Functions to CombatTracker.lua
CombatTracker_OnLoad(frame)
CombatTracker_OnEvent
PLAYER_REGEN_ENABLED
PLAYER_REGEN_DISABLED
UNIT_COMBAT
CombatTracker_UpdateText()
CombatTracker_ReportDPS()
Testing CombatTracker
Frame Dragging
Right-Click Reporting: Part I
Testing Combat Tracking
Right-Click Reporting: Part II
Summary
Part III
Advanced Addon Techniques

267
267
267
268
268
268
269
269
270
271
272
272
273
275
275
276
276
277
277
277
278
278
279
279
280
280
281
283

Chapter 15 Taking Action with Secure Templates
Why Are Secure Templates Necessary?
Protected Frames
Controlling Secure Frames Using Attributes

285
285
286
288

Contents xxiii
Using Secure Templates
Defining Behaviors for Action Buttons
Casting a Spell
Looking Under the Hood
Specifying Units to Affect
Other Types and Their Uses
Making Simple Choices
Working with Modified Attributes
Delegating Attribute Responsibility
Choosing an Action by Hostility
Applying Action Buttons in Practice
Modifying an Existing Frame
A Complex Action Button
Understanding Taint and Working Safely Around Secure
Code
Enabling Taint Logging
Execution Taint
Variable Taint
Creeping Taint
Summary

288
289
289
290
291
291
296
296
298
298
299
299
300
302
303
304
305
307
308

Chapter 16 Binding Keys and Clicks to Addon Code
Defining Bindings in XML
Creating Your Own Binding Actions
Binding Keys to Actions
Building a Simple Binding UI
Defining Basic Behaviors
Using SetBinding()
Working with Existing Bindings
Displaying an Action’s Bindings
Understanding Binding Storage
Binding Keys to Secure Actions
Working with Click Bindings
Creating Secure Bindings in XML
Summary
The Code
BindingTest
ClickBindingTest

309
310
312
314
315
318
321
324
325
326
327
328
329
329
330
330
334

Chapter 17 Creating Slash Commands
Creating Basic Slash Commands
Tokenizing Strings
Tokenizing with Patterns
Setting Up the Patterns

337
337
339
341
341

xxiv

Contents
Preparing for the Tokenization
Parsing the Formula

Using a Command Table
Summary
The Code
SlashCalc

342
343

345
347
347
347

Chapter 18 Responding to Graphic Updates with OnUpdate
Understanding Graphic Updates
Delaying Code Using OnUpdate
Grouping Events to Avoid Over-Processing
Grouping Multiple Events
Repeating Code with OnUpdate
Considering Performance with OnUpdate Scripts
Summary

351
351
352
354
355
356
357
357

Chapter 19 Altering Existing Behavior with Function Hooking
What Is Function Hooking?
Modifying Return Values
Using a Variable Argument Function
Using Utility Functions capture() and release()
Hooking Widget Scripts
Hooking a Function Securely
Hooking Scripts Securely
Deciding When to Hook
Understanding the Hook Chain
You Can’t Rely on Order
There Is No ‘‘Unhook’’
Hooking Hits Performance
Finding Alternatives
Designing an Addon: MapZoomOut
Creating a Timer Frame
Initial Setup
Create the Function Hook
Writing the Timer Code
Final Setup
Testing MapZoomOut
Summary
The Code
MapZoomOut

359
359
360
361
361
362
364
365
365
365
366
366
366
367
367
368
368
369
369
370
370
370
371
371

Chapter 20 Creating Custom Graphics
Common Rules for Creating Graphics
The GIMP

373
373
374

Contents
Create a New Image
Adding Graphical Components
Saving Textures

374
375
376

Adobe Photoshop
Create a New Image
Adding Graphical Components
Creating an Alpha Channel
Saving an Image
Paint Shop Pro
Creating a New Image
Adding Graphical Components
Creating an Alpha Channel
Saving an Image
Testing Your Texture
No Button Appears
A Green Box Appears
XML Texture Definition
Lua Texture Definition
Summary

376
376
377
377
378
379
380
380
381
382
383
384
384
384
385
385

Chapter 21 Responding to the Combat Log and Threat Information
Understanding the Combat Log
Event Arguments
Combat Sub-Events
Combat Event Prefix
Bit Fields and Spell Schools
Combat Event Suffix
Spell-Only Suffixes
Special Combat Events
Unit GUIDs
Format of GUIDs
Unit Flags
COMBATLOG_OBJECT_TYPE_MASK
COMBATLOG_OBJECT_CONTROL_MASK
COMBATLOG_OBJECT_REACTION_MASK
COMBATLOG_OBJECT_AFFILIATION_MASK
COMBATLOG_OBJECT_SPECIAL_MASK
Using CombatLog_Object_IsA
Writing CombatStatus
Creating the Basic Addon Structure
Initializing CombatStatus
Updating Pet Mappings
Storing Damage and Healing Information
Taking ‘‘Snapshots’’ of Damage and Healing

387
387
387
388
389
389
390
393
395
396
397
398
398
398
399
399
399
400
401
401
402
405
405
407

xxv

xxvi

Contents
Writing an OnUpdate Function
Responding to Events
COMBAT_LOG_EVENT_UNFILTERED
PARTY_MEMBERS_CHANGED
UNIT_PET
PLAYER_REGEN_DISABLED
PLAYER_REGEN_ENABLED
Creating the Frame Display
Updating the Frame Display
Future Additions

Summary

408
408
409
409
409
409
410
410
410
412

412

Chapter 22 Creating Scroll Frames
Using Scroll Frames
Adding a Scroll Child
Manipulating a ScrollFrame
Adding Scroll Bars
Creating Faux Scroll Frames
Adding Scroll Bars
Scrolling with the Mouse Wheel
Problems with Slider Precision
Summary
The Code
ScrollFrameTest
MacroIconTest

413
414
415
416
417
419
422
423
423
424
424
424
426

Chapter 23 Creating Dropdown Menus
Creating a Basic Dropdown
Adding a Toggle Button
Creating a Dropdown Frame
Initializing the Dropdown
Adding Buttons to the Dropdown
Calling UIDropDownMenu_Initialize()
Toggling the Dropdown Menu
Testing the Dropdown
Creating Multilevel Dropdowns
Adding Functionality to Dropdowns
Customizing Text Elements
Function Menu Items
CheckButton Menu Items
ColorPicker Menu Items
Using Dropdowns for Selection
Automating Menu Creation with EasyMenu
Creating Dynamic Menus
Summary

431
431
432
433
433
433
434
434
435
436
437
438
440
440
441
443
445
447
449

Contents xxvii
Chapter 24 Scanning and Constructing Tooltips
Understanding the Tooltip System
Different Types of Tooltips
Contextual Tooltips
Static Tooltips
Tooltip Contents
Custom Text in a Tooltip
Game Element Tooltips
Adding Information to the Tooltip
Loading the Tooltip with Item Information
Getting Information from Tooltips
Accessing Individual Tooltip Lines
Checking Soulbound Status
Using Global Strings for Localization
Replacing a Script Instead of Hooking a Script
Summary

451
451
452
452
453
453
453
455
458
458
460
460
461
461
461
462

Chapter 25 Taking Protected Action in Combat
Snippets: The Basis of Secure Action
How Can Addon Code Be Secure?
Writing a Snippet
Secure Handler Frames
Handler Template Reference
Integrating a Click Handler with a Secure Action Button
Preserving State and Controlling Information
Private Global Environments
Secure API Functions
The control Object
Frame Handles
Allowed Actions
Additional or Changed Actions
Wrapping Frame Scripts
A Simple Click Wrapper
Using a Post-Hook
Script Wrapper Reference
Triggered Changes
State Drivers
State Responders
Responding to Show/Hide
Responding to Attribute and State Changes
State Conditionals
Target Specifiers and Unit Conditions
State Variables
Unit Conditions
General Conditions

463
463
463
464
464
466
468
473
474
475
476
477
479
479
482
483
484
485
486
486
487
487
488
490
491
492
492
493

xxviii Contents
Summary
The Code
BlessedMenus

496
496
496

Chapter 26 Creating Unit Frames with Group Templates
Configuring a SecureGroupHeader
Configuration Options
Initial Configuration Function
Creating SquareUnitFrames
Constructing the Template
Creating a Header Template
Setting Name and Status Bars
Nudging Frame Levels
Responding to Events and Clicks
Targeting the Unit on Left-Click
Moving the Header
Health Update Events
Power Update Events
Responding to Name Changes
Enhancing SquareUnitFrames
Highlighting Units on Mouseover
Showing the Targeted Unit
Displaying Threat Levels
Showing Dead Players
Displaying Unit Names
Adding Pets to SquareUnitFrames
Creating a SecureGroupPetHeaderTemplate
Summary
The Code
SquareUnitFrames

501
501
502
505
506
506
508
509
511
511
511
512
513
514
516
516
516
517
518
519
521
523
526
526
526
526

Part IV

537

Reference

Chapter 27 API Reference
API Reference Conventions
Function Signatures
Optional Arguments
Argument Choices
Argument and Return Listings
Common API Flags
API Meta-Types
1nil
actionID
ah-list-type

539
539
539
540
540
540
541
542
542
542
543

Contents
anchorPoint
arenaTeamID
auraFilter
backdrop
bitfield
binding
chatMsgType
colorString
containerID
containerSlotID
frameStrata
glyphIndex
GUID (Globally Unique IDentifier)
Players
NPCs
Pets
Vehicles
GUID Example
Hyperlink
player
playerGM
glyph
spell
enchant
quest
talent
achievement
trade
item
inventoryID
itemID
itemLocation
itemQuality
itemString
justifyH
justifyV
layer
macroID
powerType
rollID
spellbookID
spellID
standingID
unitID

API Reference

543
543
543
544
544
545
545
545
546
546
546
547
547
547
547
548
548
548
549
549
549
549
550
550
550
550
551
551
552
552
553
553
553
554
554
554
554
554
554
555
555
555
555
555

556

xxix

xxx

Contents
Chapter 28 API Categories
Achievement Functions
Action Functions
ActionBar Functions
Addon-related Functions
Arena Functions
Auction Functions
Bank Functions
Barbershop Functions
Battlefield Functions
Blizzard Internal Functions
Buff Functions
CVar Functions
Calendar Functions
Camera Functions
Channel Functions
Chat Functions
Class Resource Functions
Client Control and Information Functions
Combat Functions
CombatLog Functions
Companion Functions
Complaint Functions
Container Functions
Currency Functions
Cursor Functions
Debugging and Profiling Functions
Duel Functions
Equipment Manager Functions
Faction Functions
GM Survey Functions
GM Ticket Functions
Glyph Functions
Guild Bank Functions
Guild Functions
Hyperlink Functions
In-game Movie Playback Functions
Inspect Functions
Instance Functions
Inventory Functions
Item Text Functions
Item Functions

1025
1025
1027
1028
1028
1029
1030
1031
1032
1032
1035
1035
1035
1036
1040
1041
1043
1045
1045
1046
1046
1047
1047
1047
1048
1049
1051
1052
1052
1053
1054
1054
1054
1055
1056
1059
1060
1060
1061
1061
1063
1063

Contents
Keybind Functions
Keyboard Functions
Knowledge-base Functions
Limited Play Time Functions
Locale-specific Functions
Looking For Group Functions
Loot Functions
Lua Library Functions
Mac Client Functions
Macro Functions
Mail Functions
Map Functions
Merchant Functions
Modified Click Functions
Money Functions
Movement Functions
Multi-cast Action
NPC ‘‘Gossip’’ Dialog Functions
Objectives Tracking Functions
Party Functions
Pet Stable Functions
Pet Functions
Petition Functions
Player Information Functions
PvP Functions
Quest Functions
Raid Functions
Recruit-a-friend Functions
Secure Execution Utility Functions
Skill Functions
Social Functions
Socketing Functions
Sound Functions
Spell Functions
Stance/Shapeshift Functions
Stat Information Functions
Summoning Functions
Talent Functions
Targeting Functions
Taxi/Flight Functions
Threat Functions
Tracking Functions
Trade Functions

1065
1065
1066
1067
1067
1068
1069
1070
1072
1073
1074
1075
1076
1078
1078
1079
1080
1080
1081
1082
1083
1083
1085
1085
1088
1089
1094
1095
1095
1096
1096
1097
1098
1099
1101
1101
1103
1103
1104
1105
1105
1106
1106

xxxi

xxxii Contents
Trade Skill Functions
Trainer Functions
Tutorial Functions
UI/Visual Functions
Unit Functions
Utility Functions
Vehicle Functions
Video Functions
Voice Functions
Zone Information Functions

Chapter 29 Widget Reference
Widget Types
UIObject
ParentedObject
ScriptObject
Region
VisibleRegion
LayeredRegion
FontInstance
FontString
Texture
Frame
Button
CheckButton
ColorSelect
Cooldown
GameTooltip
Minimap
Model
PlayerModel
DressUpModel
TabardModel
MovieFrame
ScrollFrame
SimpleHTML
Slider
StatusBar
Font
MessageFrame
ScrollingMessageFrame
EditBox
AnimationGroup

1107
1108
1110
1110
1110
1113
1115
1116
1117
1119

1121
1121
1121
1122
1122
1124
1129
1130
1131
1135
1138
1145
1164
1170
1172
1175
1176
1192
1195
1201
1202
1202
1204
1206
1208
1215
1219
1221
1222
1225
1231
1238

Contents xxxiii
Animation
Path
ControlPoint
Rotation
Scale
Translation
Alpha

1243
1248
1250
1251
1252
1253
1254

Widget Scripts

1255

Chapter 30 Events Reference

1277

Part V

1303

Appendixes

Appendix A Best Practices
General Programming
Use Meaningful Variable Names
Variable Naming Exceptions
Use Named Constants Instead of Literals
Organize for Easier Maintenance
Rework Repetitive Code
Break Apart Long Functions
Use Consistent Programming Style
Lua Tips
Use Local Variables
Minimize Unnecessary Garbage
How to Reduce Garbage
Recyclable Objects
Recycle Tables
Other Fine-tuning Optimizations
Check Expected Conditions First
Exploit Shortcut Evaluation
Use Tables as a Logic Structure
Cache Frequently Accessed Values
The WoW Environment
Use What You’re Given
Localize with Global Strings
Avoid Deprecated Systems
Global Widget Handler Arguments
bag and slot Attributes on item Type Action Buttons
Avoiding Common Mistakes
Adding Files While WoW Is Running
Entering | into the Chat Edit Box
‘‘Missing’’ Frames
Ignoring Logs\FrameXML.log
Not Checking API Returns

1305
1305
1306
1307
1307
1308
1308
1309
1309
1310
1310
1311
1312
1317
1318
1319
1319
1320
1321
1322
1323
1323
1323
1324
1324
1325
1325
1325
1326
1326
1326
1326

xxxiv Contents
Requesting Data Before PLAYER_LOGIN
Conflicting or Existing Anchor Points

1327
1327

Appendix B Utilizing Addon Libraries
What Is an Addon Library?
How Do Libraries Work?
Standalone Libraries
Advantages
Disadvantages
Embedded Libraries
Embedded Library Load Process
Manually Versioning an Embedded Library
Versioning Using LibStub
Using a Library
Ace3
Portfolio
Dongle
PeriodicTable
BossIDs
LibHealComm
LibSharedMedia
Other Library Resources

1329
1329
1330
1330
1331
1331
1332
1332
1333
1334
1335
1335
1336
1336
1336
1336
1336
1336
1337

Appendix C Tracking History Using Version Control Systems
Subversion
Terminology
Layout of a Repository
Obtaining Subversion
Command Primer
svn checkout  [path]
svn update [path]
svn add 
svn commit [path]
svn status
svn log [path]
svn diff [path]
Creating a Local Repository
Git and Mercurial
Terminology
Obtaining Git
Obtaining Mercurial (hg)
Typical Usage
Git
Mercurial

1339
1339
1340
1340
1341
1341
1341
1342
1342
1342
1342
1343
1343
1343
1344
1344
1345
1345
1345
1345
1347

Contents xxxv
Appendix D Addon Author Resources
Community Websites
World of Warcraft Forums
WowProgramming Forums
WoWInterface Forums
WowAce Forums
Curse Forums
Elitist Jerks
IncGamers UI Customization Forums
Internet Relay Chat (IRC)
#wowuidev on irc.freenode.net
#wowace on irc.freenode.net
#wowprogramming on irc.freenode.net
Distributing and Supporting Your Addon
WoW-Specific Hosting
WoWInterface
CurseForge and WowAce
IncGamers
Other Hosting Solutions
Google Code
Sourceforge
Personal Web Hosting

1349
1349
1349
1350
1350
1350
1350
1350
1351
1351
1351
1351
1351
1352
1352
1352
1353
1353
1353
1353
1354
1355

Index

1357

Introduction

Since World of Warcraft (WoW) was released on November 23, 2004, it has
been one of the most popular video games ever created. The game currently
boasts more than eleven million subscribers; it seems that everyone knows
someone who plays. World of Warcraft is an extremely immersive environment
that allows you to customize your character, explore new worlds, and group
with friends without requiring an enormous time commitment. Some players
spend four to six hours a night raiding with their guilds trying to defeat the
latest and greatest monster. Others prefer player-to-player combat, spending
time in the Arena or Battlegrounds trying to improve their standing. Some
players just enjoy playing the game with a group of friends when they have
spare time. World of Warcraft has something to offer each of these players,
and that’s probably one of the primary reasons for its success.
One aspect of the game that reaches each of these play styles is user
interface customization in the form of addons. For those players who are
technically inclined or simply can’t accept things being anything less than
perfect, Blizzard has opened up its user interface to allow players to customize
and change its overall functionality. Addons can be as simple as changing the
colors of health bars or adding a new slash command to do random emotes, or
as complicated as providing complex statistical analysis of a server’s economy.
Beyond opening up this world of customization, Blizzard continues to provide
enhancements and support for the user interface community in a way that no
other game developer has done.
The user interface community has grown immensely over the past few
years, and shows no signs of stopping. This book was written to give the
reader the tools necessary to create custom modifications to the World of Warcraft user interface, including an introduction to the languages, terminology,
and structure of addon creation. There are thousands of addons out there
xxxvii

xxxviii Introduction

waiting to be written, and this book provides you with the skills necessary to
realize them.

Who This Book Is For
This book is designed to be useful to novice addon users who want to learn
how to tweak existing addons, to budding addon authors looking to add more
exciting features to their repertoire, and to advanced addon developers as a
reference to the extremely complex WoW UI system. The only assumptions
made throughout the book are that you are familiar with World of Warcraft
and have an interest in programming. Readers who have had exposure to
programming languages in any form will find many of the concepts presented
to be familiar.
The reader with little to no prior programming experience should initially
focus on the first section of the book, which teaches Lua, the programming
language that is used to write addons. Although readers with no programming
experience will learn enough to create and modify addons, they may want to
pursue more general programming lessons from another source to supplement
the material presented.
For readers with prior programming experience, the first few chapters
will be very easy. The examples can be used to pick up the basic rules of
the Lua programming language rather quickly. If you are already familiar
with high-level scripting languages such as Python or JavaScript, you can
easily skim the first few chapters and move right into the second section,
which covers the basics of addon creation itself. These chapters detail how
the WoW addon system works for the author, and lead you through writing
your first addon.
Addon authors may want to skip directly to the third section of the book. Its
chapters introduce specific concepts and walk through the creation of working
example addons that use the concepts. Some of the more obscure difficult
systems (such as secure snippets, dropdown menus, and state headers) are
explored in depth.
In addition, the fourth section of the book contains an extremely comprehensive reference to the WoW API, including events and widgets.

How This Book Is Organized
This book is divided into four parts that introduce increasingly complex topics.
Part I is an introduction to Lua and XML, bringing you up to speed with the
languages needed to create addons.

Introduction xxxix

Part II discusses the way addons are built and the basics behind the frame
system in World of Warcraft. In this part you create your first addon and
become familiar with the WoW API.
Part III of the book guides you through some of the more advanced topics
by creating a number of addons from start to finish.
Finally, Part IV is a comprehensive reference to the entire API, including
functions, widgets, events, and secure templates.

What’s on the Website
Every few months, Blizzard releases a new patch for World of Warcraft that
may introduce new content, fix existing bugs, or even drastically change game
mechanics. As a result, the material covered in this book will change from time
to time. To help combat this problem, the authors have created a companion
website for the book at http://wowprogramming.com. While we do not expect
sweeping changes to the core concepts, the details of any specific changes will
be listed on the website, including information about how those changes affect
the material in this book. Besides serving as a glorified errata repository, the
website also has online versions of all the references included in the book.

From Here
The World of Warcraft user interface community is a very exciting place
with endless possibilities for customization and realization of ideas. World of
Warcraft is a fun game in its own right; the capability to use it as a development
platform for addons that can help users and enhance their game experience is
an extra bonus that each of us can enjoy. So Enjoy!

Part

I
Learning to Program

In This Part
Chapter
Chapter
Chapter
Chapter
Chapter
Chapter
Chapter

1: Programming for World of Warcraft
2: Exploring Lua Basics
3: Basic Functions and Control Structures
4: Working with Tables
5: Advanced Functions and Control Structures
6: Using the Lua Standard Libraries
7: Learning XML

CHAPTER

1
Programming for World
of Warcraft

World of Warcraft (WoW) was released Nov. 23, 2004, and very quickly became
the model for Massively Multiplayer Online Role Playing Games (MMORPG).
Providing an intuitive user interface and a low barrier to success the game
is currently played by more than 11 million users, including their friends,
co-workers, and families. WoW has something enjoyable for those players
who spend six hours a night raiding with their guilds, the cubicle warriors
who play for half an hour a day on their lunch breaks, and a large range of
individuals in between.
Beyond killing monsters and questing for glory, there is another side to
World of Warcraft, a game within a game. Blizzard has provided an extremely
powerful system for creating third-party addons and writing macros, and
users have been taking advantage of the open system since the beta test for
the game. This book is designed to introduce you to the world of customizing
World of Warcraft and show you how to create custom addons.

Customizing the User Interface
The World of Warcraft game client consists of two major parts: the game world
and the user interface. The game world is the three-dimensional space in which
your character resides. This includes the buildings and terrain, other players
and enemies, and interactive objects such as herbs, mining veins, mailboxes,
and signposts. The game world also includes some non–three-dimensional
objects, namely the character names and titles, and the numbers that show the
damage your character has done. These elements are not accessible through
the scripting interface and cannot be modified.
3

4

Part I

■

Learning to Program

The user interface comprises the other elements in the client, including
the action buttons, unit frames, maps, and options windows. Addons can be
written to add or modify existing elements to add functionality or to show
information in a different way.

What Is an Addon?
An addon is a collection of files inside a named directory within the World of
Warcraft directory. These files are loaded by the game’s scripting system and
executed within the client to make some modification to the user interface.
This definition of addons does not include any third-party executables that
are run outside the game (those sorts of programs are normally prohibited by
WoW’s terms of service).
The average addon consists of individual components that work together to
create a final product, possibly including:
A table of contents file that identifies the addon and the files to be loaded
Media files, such as graphics and sounds
Lua scripts that define the behavior of the addon
XML files that define the visual elements of the addon
The first part of this book is designed to introduce you to the Lua programming language and the XML markup that is specific to World of Warcraft.
These skills are an important part of writing addons effectively. If you are
already proficient in Lua and XML, you can skip ahead to Part II of the
book, which covers the use of the World of Warcraft API in creating addons;
however, you will likely find the material in Part I worthwhile.

What Can Addons Do?
Addons typically fall into one or more of the following categories:
Displaying additional information, such as the sale price of quest rewards
(Figure 1-1), or approximately how many more of a given spell you can
cast without running out of power (Figure 1-2).
Changing the display of interface elements, such as the combat text
information (Figure 1-3), or making the auction house interface easier to
navigate (Figure 1-4).
Providing new ways for the player to take action (targeting units, casting
spells) within the game, such as replacement unit frames (Figure 1-5) or
alternate action buttons (Figure 1-6).

Chapter 1

■

Programming for World of Warcraft

Figure 1-1: Valuation showing sell price for items

Figure 1-2: Dr. Damage displaying number of possible casts

Figure 1-3: MikScrollingBattleText displaying combat information

5

6

Part I

■

Learning to Program

Figure 1-4: Auctioneer displaying auction listings in a compact form

Figure 1-5: Grid unit frames showing the status of a raid

Figure 1-6: Bartender4 with ButtonFacade_Serenity providing alternate action buttons

Prior to the release of the Burning Crusade expansion pack to World of
Warcraft, there were several addons that Blizzard deemed against the spirit
and intention of the game. These addons were later disabled and changes were

Chapter 1

■

Programming for World of Warcraft

made to the scripting system to prevent their use. As a result the following
actions are unavailable to addons:
Automatic character movement
Automatic target selection
Automatic selection and use of spells or items
Real-time communication with external programs
In the past, Blizzard has been asked about the limits of the scripting/macro
system. Its response has been that it is interested in ‘‘smart players,’’ not ‘‘smart
buttons.’’ In other words, addons and macros can work to display information
to the users or allow them to access functionality in an easier way, but should
not be used to make automatic decisions.
In addition, addons are forbidden from doing anything that would otherwise
be against the World of Warcraft ‘‘Terms of Use,’’ which you can find at
http://worldofwarcraft.com/legal/termsofuse.html.

Exploring Your AddOns Directory
As mentioned previously, all addons must exist within a subdirectory under
your World of Warcraft directory. Depending on what operating system you
are using and how you have installed the game, this directory may exist in
one of a few places (see Table 1-1). If you happen to be running Windows
Vista, the location of your installation will depend on how the computer has
been configured and where the game was installed. During the installation of
Wrath of the Lich King or patch 3.0.2 you should have been asked to move the
game to option #3. If you agreed to this change, you may have two versions of
World of Warcraft, with the old one not being used any more.
Table 1-1: Default World of Warcraft Installation Directory
OPERATING SYSTEM

DEFAULT INSTALLATION DIRECTORY

Microsoft Windows 98, 2000,
or XP

C:\Program Files\World of Warcraft

Microsoft Windows Vista
(option #1)

C:\Program Files\World of Warcraft

Microsoft Windows Vista
(option #2)

C:\Users\\AppData\Local\
VirtualStore\Program Files\World of
Warcraft

Microsoft Windows Vista
(option #3)

C:\Users\Public\Games\World of
Warcraft

Mac OS X

/Applications/World of Warcraft

7

8

Part I

■

Learning to Program

If you have launched World of Warcraft previously, there should be an
Interface directory within and an AddOns directory below that. This is where
all addons are stored.

Blizzard Addons
Much of the functionality in the default user interface is implemented via modular addons that are loaded only when needed by the user. When the player
visits an auctioneer, for instance, the game loads the Blizzard_AuctionUI
addon.
Having the addons in separate load-on-demand modules allows addon
authors to easily override the default functionality (such as replacing the
auction house interface rather than just changing it). In addition, the modularity
speeds up load times when starting the game. Table 1-2 describes the existing
Blizzard addons.
Table 1-2: Blizzard Load-on-Demand Addons
ADDON NAME

PURPOSE

Blizzard_AchievementUI

Explore the achievements your character can
complete and those he has already
completed.

Blizzard_ArenaUI

Display unit frames for enemy units in arena
PVP.

Blizzard_AuctionUI

Search for items available for sale, as well as
posting new items up for auction.

Blizzard_BarbershopUI

Customize the facial features and hair
style/color for your character.

Blizzard_BattlefieldMinimap

Display a smaller version of the world map,
including the PVP objectives.

Blizzard_BindingUI

Customize the keyboard bindings made
available by the default and custom
interfaces.

Blizzard_Calendar

Display a calendar that shows the various
scheduled game events and allows players
to create their own events.

Blizzard_CombatLog

Present combat information in a linear
combat log that can be filtered and colored
via options.
Continued

Chapter 1

■

Programming for World of Warcraft

Table 1-2: (continued)
ADDON NAME

PURPOSE

Blizzard_CombatText

Show various combat events in moving text
in the user interface, customizable via the
options screens.

Blizzard_DebugTools

Provide slash commands and utility functions
that are useful to addon developers.

Blizzard_GMChatUI

Provide a chat window for communication
with game masters.

Blizzard_GMSurveyUI

Allow the user to fill out a survey that has
been sent by Blizzard following a GM
interaction.

Blizzard_GlyphUI

Inscribe glyphs into your spellbook in order
to customize your spells.

Blizzard_GuildBankUI

Add and remove items and gold from your
guild’s bank.

Blizzard_InspectUI

Inspect another player to view his
equipment, combat stats, and talents.

Blizzard_ItemSocketingUI

Socket gems into an item.

Blizzard_MacroUI

Edit global and character-specific macros.

Blizzard_RaidUI

Display unit frames for the members in your
raid.

Blizzard_TalentUI

Assign talent points and explore the various
talent trees.

Blizzard_TimeManager

Show a clock on the minimap and provide a
simple in-game timer.

Blizzard_TokenUI

View the various currency tokens that your
character has earned.

Blizzard_TradeSkillUI

Explore the various recipes that are
associated with a given tradeskill.

Blizzard_TrainerUI

Purchase skills available from a trainer.

Each of these directories contains a single file that has the addon’s name
and a .pub extension. As far as we can tell, this is some sort of signature
used by the game to verify the authenticity of the addon. Addons that are
written by Blizzard are given a special ‘‘secure’’ flag that allows them to take

9

10

Part I

■

Learning to Program

protected actions, something that is covered in Chapter 8. The code for the
addons is actually stored in the data files for the game and can’t be directly
replaced.

Custom Addons
If you have downloaded any custom addons they will sit alongside the
Blizzard addons in your Interface\AddOns directory inside subdirectories.
Unlike the official addons, these addon directories actually contain the files
that are necessary to load and run the addon. The organization and contents
of these files varies depending on the addon. Each author has his or her own
preferences and style and these differences are reflected in the way the addon
is packaged and the way the code is written. Although we provide some
recommendations for writing and packaging your addons, you are free to
develop a style that works best for you.

Creating Your First Addon: HeyThere
Before you delve into Lua and XML, take a look at a very simple addon
example so you’ll have an idea of how the system works. To complete the
example you need to know how to create a new directory on your computer.
You also need to be familiar with a text editor that saves files without special
formatting. On Windows, for example, you could use Notepad to edit files; on
Mac OS X, the built-in Text Editor program is sufficient.

Creating Files and Directories
First create a new directory that will contain the addon. Navigate to your
Interface\AddOns directory and create a new directory inside called HeyThere.
Open your text editor and type the following into the file:
## Interface: 30300
## Title: Hey There!
## Notes: Provides slash commands to greet other players
HeyThere.lua

Save this file in the new directory as HeyThere.toc. Open a new file in the
editor and add the following:
SLASH_HEYTHERE1 = “/hey“
SLASH_HEYTHERE2 = “/heythere“
SlashCmdList[“HEYTHERE“] = function(self, txt)
if UnitExists(“target“) then

Chapter 1

■

Programming for World of Warcraft

SendChatMessage(“Hello “ .. UnitName(“target“), “SAY“)
else
SendChatMessage(“Hey there everybody!“)
end
end

Save this file as HeyThere.lua in the same directory and close the text editor.
Don’t worry right now about what any of this code does; it’s just an example
addon to get you familiar with creating files and directories. You’ll learn what
the code does later in the book.

Loading and Testing the Addon
If you have World of Warcraft open, you must close it so it can recognize the
new addon. Once you’ve re-opened the game client, log in to your account and
stop at the character selection screen. In the bottom left of the screen should
be a button named AddOns. Click it and a window similar to one shown in
Figure 1-7 opens. The window shows that WoW recognizes your addon and
will try to load it if it is enabled.

TIP You may find it useful to create a character on a server that is different from
your main server for addon development. This allows you to easily change which
addons are enabled and disabled without affecting the characters with which you
normally play.

Figure 1-7: Addon selection screen showing your new addon

Ensure that the addon is enabled by checking the box to the left of the
addon name. Click Okay to exit the addon selection screen and enter the game.

11

12

Part I

■

Learning to Program

This addon adds two new slash commands that allow you to greet people in
the world. You can type either /heythere or simply /hey and depending on
whether you have something targeted your character will display one of two
messages (see Figure 1-8).

Figure 1-8: HeyThere greeting with (left) and without (right) a target

If for some reason you do not see the addon in the addon selection list,
ensure that you’ve created the files and directories correctly. The layout should
be as follows:
Interface\AddOns\HeyThere
Interface\AddOns\HeyThere\HeyThere.toc
Interface\AddOns\HeyThere\HeyThere.lua

If you get an error or have any other issues, double-check that you’ve typed
everything correctly in each of the files. Alternatively, download the addon
from this chapter’s section of the website at http://wowprogramming.com/
chapters/01 and compare it to the version you have created.

Summary
This chapter introduced you to the addon system for World of Warcraft. The
specific limitations and capabilities of the system were listed, along with a
description of the addons that Blizzard has included with the game. You
created your first addon and tested in-game to ensure it worked correctly.
Chapter 2 introduces you to the basics of the Lua programming language,
used extensively when creating addons.

CHAPTER

2
Exploring Lua Basics

Lua is a powerful, lightweight, embedded scripting language that is used
in several large software projects, including WoW. Lua is a fairly small
programming language, and you may find some similarities to other languages
you already know. Lua is most often compared to Python because both are
relatively easy for a non-programmer to use when compared to languages
such as C or Java.
This chapter serves as a general introduction to the Lua programming
language. If you have prior experience with Lua or have extensive experience
using other programming languages, you may want to skim this chapter
and run through some of the interactive exercises. Although these examples
should be easy to understand without you needing to run them, we strongly
encourage you to download a Lua interpreter so you can run through the
examples on your own. In addition, an interpreter allows you to easily explore
the language to increase your overall understanding of concepts.
ON THE WEB
You can read more about the Lua programming language at www.lua.org.
The website contains a large amount of reference material, including an online
version of Programming in Lua, a book entirely about the Lua programming
language.

13

14

Part I

■

Learning to Program

Downloading and Installing Lua
You have three easy ways to obtain a Lua interpreter:
1. Download WowLua, an addon the authors have written that gives you
an interactive Lua interpreter within World of Warcraft.
2. Visit the book’s website at http://wowprogramming.com/utils/weblua
to use an interactive Lua interpreter in your web browser.
3. Download a Lua interpreter to your computer, so it can be run locally
without access to the Internet or WoW.
The first option enables you to run a Lua interpreter directly within World of
Warcraft. This is useful if you want to spend your time in the game watching
things. The second allows you to run Lua without needing to download
anything, so it will work even on computers where you can’t install software.
The third option allows you to work with Lua when you’re not connected to
the Internet, which also can be useful.
Any of these options will work for the examples in the first part of this book,
so feel free to choose the ones that work best for you.

Downloading and Installing WowLua
We have created a version of the Lua interpreter that runs as an addon within
World of Warcraft. This is the simplest way to install a Lua interpreter for
anyone with experience using addons. It also has the advantage of letting you
work within the game, allowing you to test your work on-the-fly, experiment
with the default UI and other addons, and still be able to chat with your friends
and guild.
Navigate to http://wowprogramming.com/utils/wowlua-addon and click
the download link to get the latest version of the WowLua addon. This
downloads a .zip file to your computer. Once you save the file, you can
extract it using your favorite compression utility or by double-clicking it on
a standard Windows XP or Mac OS X machine. A single folder called WowLua
will be extracted. Place the folder in the Interface\AddOns folder underneath
your World of Warcraft installation.
You can verify that the addon is installed properly by clicking the Addons
button in the bottom-left corner of your character selection screen. You should
see the addon listed in a fashion similar to that shown in Figure 2-1.

Chapter 2

■

Exploring Lua Basics

Figure 2-1: WowLua in the addon listing

Select a character and log in to the game. Type either /lua or /wowlua into
the chat box to open the WowLua window (see Figure 2-2). You can close the
window by clicking the X button in the top-right corner, or by pressing the
Esc key.

Figure 2-2: WowLua interactive interpreter

Using Lua on the Web
For those people who don’t want to run these examples within WoW and
have access to an Internet connection, we’ve created a simple webpage that
serves as a Lua interpreter over the Web, called WebLua. Simply browse to
http://wowprogramming.com/utils/weblua to begin.
The webpage requires JavaScript to function, so ensure you have it enabled
in your web browser.

15

16

Part I

■

Learning to Program

Downloading and Installing a Lua Interpreter
If you prefer to download an interpreter so you can work offline, packages are
available for both Microsoft Windows and Mac OS X.

Microsoft Windows
You can download the interpreter for Microsoft Windows at http://
wowprogramming.com/utils/lua/windows. The package doesn’t require any
installation; you can simply place it anywhere that is convenient for you.
Extract the ZIP file to a new folder and place it where you can easily find
it again.
To launch the Lua interpreter, go to the files you’ve extracted and
double-click the icon for the interpreter. This opens a window that looks
something like that shown in Figure 2-3. You can also create a shortcut to this
file from which you can launch the interpreter.

Figure 2-3: Lua running on Microsoft Windows

Mac OS X
You can find a package that can be used to install a Lua interpreter for Mac
OS X at http://wowprogramming.com/utils/lua/macosx. The download is
a standard disk image that can be mounted on your system. To mount it,
navigate to the disk image and double-click it. The disk image contains a
package (selected in Figure 2-4) that you can run to install Lua on your system.
Double-click the package to install Lua on your system.
To launch the Lua interpreter, you need to open Terminal. This is an application normally located under Applications  Utilities. A window appears,
so you can type lua and press Enter to actually open the Lua interpreter.
Figure 2-5 shows a terminal window with Lua running.

Chapter 2

■

Exploring Lua Basics

Figure 2-4: Lua for Mac OS X disk image

Figure 2-5: Lua running on Mac OS X

Using the Lua Interpreter
When you launch your interpreter for the first time, you are greeted with
something similar to the following:
Lua 5.1.4
>

Copyright © 1994-2007 Lua.org, PUC-Rio

The first line contains the version string of the particular Lua interpreter
you are using. As long as you are using a version that begins with 5.1 you

17

18

Part I

■

Learning to Program

should be okay. The second line of output is the prompt, where you can type
commands to be run.

Running Commands
The Lua interpreter is interactive, enabling you to input commands and
receive a response, like a conversation between two friends. You will receive
instant feedback with any errors in your code, allowing you to tinker with the
language to see how it works.
Type the following command at the prompt (you only need to type the part
after the >, shown in bold):
> print(“Hello Azeroth!“)

You should see the following output:
Hello Azeroth!
>

This simple command takes the text string Hello Azeroth! and sends it to
the function print(), which outputs the string to your window. You examine
the nitty-gritty details of what this actually means later in this chapter.

NOTE For the purposes of this chapter, consider a function to be a process that
you can give information, and have it complete some task. In this case, you feed a
string to the function, which prints it to the output window.

Understanding Error Messages
Inevitably, you will make a typo and get an error from Lua when running a
command. The error messages are usually human-readable and will tell you
where the problem occurred. Type the following command at the prompt
(note that you’re intentionally misspelling the word print):
> prnit(“Hello Azeroth!“)

The response, a typical error message in Lua, is similar to this:
stdin:1: attempt to call global 'prnit’ (a nil value)
stack traceback:
stdin:1: in main chunk
[C]: ?
>

The first line gives you the error message and the line number on which
the error occurred. In this case, Lua says that you tried to call a global prnit,
which is a nil value. In layman’s terms, it means you tried to call a function
that doesn’t exist.

Chapter 2

■

Exploring Lua Basics

The rest of the error message is called a stack traceback, which tells you
where the error occurred. This will be useful when you begin calling functions
from other functions.

Using History to Make Changes
Depending on the Lua interpreter you are using, you may be able to view
the recent command-line history (the last few commands you’ve given the
interpreter) by pressing the Up and Down arrow keys. Test this now by
pressing the Up arrow key on your keyboard while in your Lua interpreter.
(This always works in WowLua, but may not work correctly if you are using
a standalone version of Lua.)
If it worked correctly, you should see the last line you typed (prnit(“Hello
Azeroth!“)) and your cursor should be at the end of the line. If it didn’t work,
you may instead see something similar to the following:
> ˆ [[A

That simply means your specific interpreter doesn’t handle history.
Although you may find this inconvenient at times, it certainly shouldn’t
hamper your capability to run through the examples correctly.

Quitting the Interpreter
When you are done running code in the interpreter, in most cases, you can
simply close the window. However, if you started the interpreter from a
command line and want to return to it, you can use one of the following
methods, depending on your operating system.

Microsoft Windows
On a Microsoft Windows system, Lua can be closed by pressing Ctrl+Z. This
inserts the following into your interpreter:
> ˆZ

In Windows this inserts a special character that means end-of-file, and it
causes the interpreter to quit. You can also press Ctrl+C to outright kill your
session.

Mac OS X
Mac OS X is a UNIX-based system, so the end-of-file character is different
from that of the Windows systems. At your prompt, press Ctrl+D to insert
the end-of-file character and end the session immediately. You can also use
Ctrl+C to kill the session.

19

20

Part I

■

Learning to Program

Working with Numbers
Almost every language has a way to calculate numeric values, and Lua is no
different. Type the following into your interpreter:
> print(2 + 2)

As expected, you will see 4 as the response followed by another prompt.
Although it may not be the most convenient calculator, the interpreter does
enable you to perform calculations in your programs.

Basic Arithmetic Operations
Table 2-1 shows Lua’s basic arithmetic operators. You can use any of them to
compute a value.
Table 2-1: Lua Arithmetic Operators
OPERATION

IN LUA

EXAMPLE

RESULT

Addition

+

> print(4 + 4)

8

Subtraction

-

> print(6 - 10)

-4

Multiplication

*

> print(13 * 13)

169

Division

/

> print(10 / 2)

5

Exponentiation

ˆ

> print(13 ˆ 2)

169

Modulo

%

> print(8 % 3)

2

Unary Negation

-

> print(- (4 + 4))

-8

In addition to these operators, you can use parentheses to group expressions
together to make more complex expressions. Consider the following example:
> print(2 * (1 + 2 + 3) ˆ 3)
432

If you run this command with no parentheses in the expression itself, you
get an entirely different answer:
> print(2 * 1 + 2 + 3 ˆ 3)
31

Parentheses are used to make an expression explicit and ensure that it
is evaluated properly. In the second case, the exponentiation operator is

Chapter 2

■

Exploring Lua Basics

processed first, followed by the multiplication operator and then the addition
operator, giving you the equivalent formula:
> print(2 + 2 + 27)

When in doubt, make your math explicit so it is easier to read when review
is needed in the future.

Scientific Notation
Occasionally, you’ll encounter an extremely large number such as
10,000,000,000,000,000 (10 ˆ 15). Rather than type it out each time with zeros
or use parentheses to make sure it’s being calculated correctly inside another
formula, you can use scientific notation. Lua may automatically display large
numbers using scientific notation if printing them would be unwieldy. Run
the following commands:
> print(10 ˆ 15)
1e+015
> print(10 ˆ -15)
1e-015

As you can see, Lua converts the numbers to scientific notation for the
purpose of printing them. Conveniently, you can also write numbers in
this fashion, which takes the first number and multiplies it by 10 raised to
the second number (the e in between can be capitalized or lowercase). For
example:
> print(1.23456e5)
123456
> print(1.23456 * (10 ˆ 5))
123456
> print(1234e-4)
0.1234
> print(1234 * (10 ˆ -4))
0.1234

You may not encounter numbers in scientific notation often, but understanding the output when Lua sends it back to you is important.

Hexadecimal Notation
Lua can natively convert a number in hexadecimal notation to the decimal
value. You can use this as a quick hex-to-decimal conversion, or you may
actually have a need to use hexadecimal notation in your systems. Lua expects
a zero, followed by the letter x, followed by a string of valid hex digits (0–9,
A–F, a–f).

21

22

Part I

■

Learning to Program

> print(0x1)
1
> print(0xF)
15
> print(0x10)
16
> print(0x10a4)
4260

When writing code, you can refer to numbers in this format and Lua will
convert them properly. As you can see from these examples, however, Lua
only responds in decimal or scientific notation, regardless of how the numbers
were input.
> print(2 * 0xF)
30
> print(0x10 ˆ 2)
256

Understanding Floating Point
Every number in a standard Lua system is represented internally as a
floating-point number. For average use this won’t make a difference, but
it can have some odd implications. Here’s a simple (but confusing) example,
which uses some concepts that you won’t examine until later in this section of
the book:
> pointTwo = 1.2 - 1.0
> print(pointTwo < 0.2)
true
> print(pointTwo)
0.2

The number 0.2 cannot be accurately represented as a floating-point number,
so the programming language must do a bit of rounding when calculating
the value, and then again when printing it. The floating-point numbers
in Lua can accurately represent any integer from 10 ˆ –37 through 10 ˆ 37,
so you shouldn’t encounter many of these problems. This rounding can,
however, serve as a source of calculation error when working with the real
numbers.
ON THE WEB
Much information regarding floating-point numbers and the implications
of the format exists out there on the Web. The following resources are all
extremely helpful if you’re interested in exploring the topic further:
(continued)

Chapter 2

■

Exploring Lua Basics

ON THE WEB (continued)
http://wikipedia.org/wiki/Floating point
http://docs.sun.com/source/806-3568/ncg goldberg.html
http://lua-users.org/wiki/FloatingPoint

Understanding Values and Variables
Like most other languages, Lua makes a distinction between values (such
as the string “Hello“ and the number 14) and variables (or references).
Understanding the underlying types of values and the distinction between a
value and a variable can be helpful while programming.

Exploring Values and Their Types
A value is the actual thing that is used, such as the number 17, or the string
“Hello“. The number 14 is a different value from the number 27, but they are
both number values. The string “Hello“ is a value, but it is a different type of
value than the two numbers. (There’s nothing tricky or complex that you need
to understand about values, I promise!)
There are eight primitive types in the Lua programming language and
every value you encounter will have one of them. You’ve already seen two
different types, number and string. The line between a string and number can
occasionally get blurry, such as drawing the distinction between the string “4“
and the number 4, but they remain discrete types.

Primitive Types
Table 2-2 describes Lua’s primitive types. Every value ends up being one of
these types, regardless of where it’s encountered in the language.
You will encounter each of these types through the course of your work, so
you should be aware of them.

Using the type() Function
Within Lua you can use the type() function to determine the type of a given
value, which gives you flexibility when it comes to validation and verification
in your programs. Type the following into your Lua interpreter:
> print(type(5))
number

23

24

Part I

■

Learning to Program

Table 2-2: Lua’s Primitive Types
TYPE

DESCRIPTION

number

All numbers (including hexadecimal numbers and those using
scientific notation) have this type. Examples: 1, 7313, 1e5,
0xFFF1a

string

A sequence of characters. Examples: “Hello“, “Test String“.

boolean

The values true and false are of the boolean type.

function

A function is a collection of statements that can be called, and is
introduced in Chapter 3.

table

A table is a mix between a traditional hash table (dictionary) and
an array.

nil

The value nil is of the special type nil.

thread

A value of the thread type is a coroutine (limited lightweight
thread) that can be used for asynchronous computation.

userdata

Userdata is traditionally a wrapper around some data structure
defined in the host language (usually C).

What you’ve done here is call the type() function with a value of 5, and
call the print() function with that result (you explore functions further in
Chapter 3). The output shows that the type of the value 5 is number, as you’d
expect. Here are some other examples:
> print(type(“Hello Azeroth!“))
string
> print(type(2 * (1 + 2 + 3) ˆ 3))
number
> print(prnit)
nil

In the third example note the misspelling of the variable prnit. As you saw
earlier in this chapter, that is a nil value, so the type () function returns nil as
expected. You can always use the type() function to find out which type your
value is.

Using Variables
A variable can be seen as a temporary name for a specific Lua value, with the
caveat that the same value may have multiple names. An example here will
be more telling than words, so type the following into your interpreter (it may
not have output after each line, so just move on to the next line):
> x = 4
> y = 2

Chapter 2

■

Exploring Lua Basics

> print(x + y)
6

In this example, you take the name x and bind it to the value 4; and bind the
name y to the value 2. You then call the print function, and instead of using
the numbers 4 and 2, you use the names x and y.

Valid Variable Names
A variable’s name or identifier has to start with a letter or an underscore
character. The name cannot contain anything other than letters, numbers, or
the underscore character. In addition, it can’t be any of the keywords that are
reserved by Lua: and, break, do, else, elseif, false, for, function, if, in,
local, nil, not, or, repeat, return, then, true, until, and while. A variable
name is also case-sensitive, so the character a is different from the character A,
meaning that the following are all different identifiers:
MyVariable
myVariable
myvariable

Assigning Variables
Use the assignment operator = to bind a value to a variable name, with the
variable name on the left and the value on the right. Try the following examples
in your Lua interpreter:
> foo = 14
> print(foo)
14
> foo = “Hello!“
> print(foo)
Hello!

The first example binds the value 14 to the identifier foo, and you can print
and use that variable instead of the value itself. The second binds the value
“Hello“ to the identifier, changing what the variable refers to.
Variables can be used on the right-hand side of an assignment operator
as well, and what happens in those situations is different depending on the
context. Try the following in your interpreter:
>
>
>
4
>
>
4

x = 4
y = x
print(y)
x = 3
print(y)

25

26

Part I

■

Learning to Program

The first line simply binds the value 4 to the identifier x. The second line,
however, assigns the value bound to the identifier x to identifier y. This means
quite literally that both x and y are names for the same value (the number 4).
As a result, when you run x = 3, you simply change that binding, leaving
y intact.

TIP The distinction between values and variables can be confusing, especially
when working through more advanced topics. Use the Lua interpreter as a tool to
explore the rules and better understand what’s happening.

Assigning Multiple Variables
On some occasions you will assign more than one variable at a time, and a
convenient short form makes this easier. Run the following example:
> x, y = 3, 5
> print(x * y)
15

The assignment operator allows a list of variables on the left side and a list
of values on the right side. That’ll be a bit more useful when you get into
functions and return values. If there are more variables on the left side than
there are values on the right side, the remaining variables will be set to nil.
If there are more values on the right side, they will just be thrown away.

Comparing Values
In many cases, you will need to compare different values to see how they are
related. Several comparison operators (listed in Table 2-3) can be used.
Table 2-3: Comparison Operators
COMPARISON OPERATOR

EQUIVALENT LUA OPERATOR

equality

==

less than

<

greater than

>

less than, or equal

<=

greater than, or equal

>=

not equal

~=

These operate exactly as you’d expect, but you can play in the Lua interpreter
to better understand them. When you print the result of a comparison, it will
be of the boolean type (that is, true or false).

Chapter 2

> print(1
true
> print(1
true
> print(1
false
> print(2
true
> print(1
false
> print(1
true

■

Exploring Lua Basics

== 1)
< 3)
> 3)
<= 2)
>= 3)
~= 3)

The equality operators (== and ~=) can be used to compare any two values,
but the <, >, <=, and >= operators can only be used with values of the same type,
such as when comparing number to number or string to string; otherwise you
will get an error as follows:
> print(1 < “Hello“)
stdin:1: attempt to compare number with string
stack traceback:
stdin:1: in main chunk
[C]: ?

In other words, whereas the == and ~= operators work for all values, the less
than/greater than (< and >) family of operators is only defined for numbers
and string values.

Working with Strings
You’ve already been introduced to the string type, and you’ve used it to print
“Hello Azeroth!“ in the interpreter. In this section you examine strings in a
bit more detail.

Comparing Strings
The less than (<) and greater than (>) operators can be used on strings, but the
result depends on the way your system internally sorts the different characters.
For single character comparisons, the operator compares the two characters’
order in the character set; for multiple character strings, it compares the order
of the first two differing characters. For example:
> print(“a“ < “b“)
true
> print(“d“ >= “c“)

27

28

Part I

■

Learning to Program

true
> print(“abcd“ < “abce“)
true
> print(“a“ < “A“)
false
> print(“abcd“ < “abcde“)
true
> print(“rests“ < “test“)
true

You may be surprised by the output from the fourth example. In the
standard Lua character set, uppercase English letters precede lowercase letters,
so the string “a“ is actually greater than the string “A“. In the fifth example, the
strings are identical until the first string runs out of characters. At this point,
Lua sees that the second string still has the letter “e“ and returns that the
second is greater. However, in the final example, even though the first string
is longer than the second one, the letter “r“ is less than the letter “t“, so the
whole string “rests“ is less than “test“.

Concatenating Multiple Strings
Strings in Lua are immutable, which means they cannot be changed without
creating an entirely new string. To add to a string, you use the special
concatenation operator (..), which enables you to take two strings and fuse
them together to make a new, larger string. Here are a couple of examples:
> x = “Hello“
> y = “Azeroth“
> print(x .. y)
HelloAzeroth
> foo = “a“ .. “b“ .. “c“ .. “d“ .. “e“
> print(foo)
abcde

Converting Numbers to Strings
As you can imagine, sometimes you will need to convert from numbers to
strings and, in most cases, Lua handles this for you. Try the following:
> print(“Number: “ .. 4)
Number: 4

Lua automatically converts the number 4 to a string, and it’s added to the
string “Number: “. If you need to explicitly convert a number to a string, you
can use the tostring() function, as in the following example:
> x = 17
> foo = tostring(x)
> print(type(foo))
string

Chapter 2

■

Exploring Lua Basics

The tostring() function takes whatever it is given (in this case a number)
and turns it into a string value.

Converting Strings to Numbers
Conversely, you may have a string of digits that you’d like to convert to a
number. Lua’s built-in tonumber() function takes a value and turns it into
a number. If the digits can’t be converted (such as when the string doesn’t
contain a number), the function returns the value nil. Here are some examples:
> x = tonumber(“1234“)
> print(type(x))
number
> print(x)
1234
> x = tonumber(“1e3“)
> print(type(x))
number
> print(x)
1000

Here the strings are converted into numbers, and the results are printed to
the screen. The tonumber() function has a few more tricks up its sleeve that
you can explore in Chapter 7.

Quoting Strings
So far, you’ve used double quotes to create strings, but there are several ways
to construct a string for use in Lua. When programming, it is considered proper
style to use the same type of quote character (as described in the following
sections) unless you have a specific reason for needing to use a different type.
This helps other people read your code without confusion.

Single Quote (’)
You can use the single quote mark (’)—also called a tick or tick mark—to
create a string, and this is standard convention. Nothing really special happens
here unless you need to include a tick mark within your string. Look at the
following examples:
> x = 'Hello’
> print(type(x))
string
> x = 'Isn’t it nice?’
stdin:1: `=´ expected near `it´

The first example works correctly and creates a new string with the text
Hello inside.

29

30

Part I

■

Learning to Program

The second example throws an error that you should explore a bit more.
What Lua sees here is an identifier (x), the assignment operator (=), and a
string (’Isn’). Because Lua doesn’t require any whitespace between most
operations, it immediately starts the next part of the expression, which begins
with the identifier t. The next thing the interpreter sees is another identifier
called it, and doesn’t know what to do with it. In this case, the interpreter
infers that you meant to use the assignment operator and errors out with this
suggestion.
You can get around this by escaping the tick mark that is inside the string to
tell Lua that it’s part of the string instead of the end of it. Here’s an example:
> x = 'Isn\’t it nice?'
> print(type(x))
string

You tackle the details of string escaping shortly.

Double Quote (’’)
The double quote (“) can be used the same way as the single quote, with the
same caveat of needing to escape embedded quote characters. Here are some
examples:
> x = “Hello“
> print(type(x))
string
> x = “Isn’t it nice?“
> print(type(x))
string
> x = “I play the game “World of Warcraft““
stdin:1: '=' expected near 'of'

The second works because the tick mark isn’t being used to delimit the
quote, but the inner quote in the third example needs to be escaped with a
backslash:
> x = “I play the game, \“World of Warcraft\““
> print(type(x))
string

Bracket Quote ([[ ]])
Lua has the concept of a long quote that enables you to include multiple
lines and internal quote characters. These quotes begin with the open bracket
character ([), have any number of equal signs (=), including 0, and then

Chapter 2

■

Exploring Lua Basics

another open bracket ([). The string closes only when it finds the opposite
combination (close brace, equal signs, close brace). Although this may seem
overly complex, it enables you to tailor-make your quote start/end for the
contents inside. Consider the following example:
> x = [[This is a long string, and I can include ' and “]]
> print(x)
This is a long string, and I can include ' and “

This includes no equal signs, which is the typical case. You could instead
include any number of them, if you needed to use the string “]]“ somewhere
in your larger string, as in this example:
> x = [==[This is a long string, and I can include ]], ', and “]==]
> print(x)
This is a long string, and I can include ]], ', and “

You may not find yourself using the [[Some String]] syntax often, but it can
be useful when the string spans multiple lines, or includes quote characters.

Escaping Special Characters
Beyond the ‘ and ‘‘ characters, there are other characters that aren’t necessarily
type-able but need to be included in a string. Try to make a multiline string
and see what happens:
> x = “First line
>> Second line“
stdin:1: unfinished string near '“First line’

Two things happen here: First, the prompt changes to two >> signs instead of
the normal one. This means you have an unfinished block and the interpreter
is waiting for you to finish the expression. Second, you get an error about an
unfinished string. This is a peculiarity in the Lua interpreter, because nothing
you can type on the second line will allow you to complete the expression.
You get around this by using \n, an escaped character that means newline.
Type the following:
> x = “First line\nSecond line“
> print(x)
First line
Second line

When constructing a string, you can include any of the escape sequences
listed in Table 2-4. Not all entries will have an effect when printed inside
World of Warcraft, but you should be aware of what valid sequences exist
because you may find them in preexisting strings.

31

32

Part I

■

Learning to Program

Table 2-4: String Escape Sequences
SEQUENCE

DESCRIPTION

\a

audible bell

\b

backspace

\f

form feed

\n

newline

\r

carriage return

\t

horizontal tab

\v

vertical tab

\\

backslash

\“

double quote

\’

single quote

\xxx

ASCII character ddd

In World of Warcraft, you typically only encounter \n, \\, \“, \’, and \xxx,
because the output widgets in World of Warcraft don’t support the others.

NOTE More often than not, you will find escape codes used in localization
strings for addons. Some locales contain characters that aren’t type-able on all
keyboards, so they are inserted using the \xxx syntax. In the deDE localization for
World of Warcraft, the word Hunter is “J\195\164ger’’, which is typically
displayed as Jäger. Localization is discussed further in Chapter 8.

Getting a String’s Length
There are two ways to obtain the length of a specific string: using the # operator
or using the string.len() function. The length of a string is often used when
validating or parsing strings.
Using the # operator before a string value returns the length as a number, as
shown in the following examples:
> print(#“Hello“)
5
> foo = “This is a test string“
> print(#foo)
21

You can use the built-in function string.len() to accomplish the same feat.
The period in between string and len means that this specific function is a

Chapter 2

■

Exploring Lua Basics

part of the string namespace (which you learn more about in Chapter 4). Type
the following into your interpreter:
> foo = “This is a test string“
> print(string.len(foo))
21

It returns the same value as the # operator because they both use the same
underlying method to calculate the length.

Boolean Values and Operators
The boolean type is relatively simple and only has two possible values— true
or false —but as with many things in programming, there’s more than meets
the eye. Three logical operators can be applied to Boolean values: and, or,
and not.

Using the and Operator
The and operator is true when both of its arguments are true, and false when
either of them is false or doesn’t have a value. Examples help make this clearer:
> print(true and true)
true
> print(true and false)
false
> print(false and false)
false
> print(false and true)
false

This operator has one peculiarity that you might run into, illustrated by the
following example:
> print(false and “Goodbye“)
false
> print(true and “Hello“)
Hello
> print(true and 4)
4

In the first example, Lua evaluates only as much of the expression as
necessary. The and operator is being used, and it encounters a false value, so
it simply returns false without evaluating the rest of the expression (referred
to as short-circuit evaluation). In the last two examples, Lua does something
similar: it encounters a true value, so it returns the second value as its result.
That’s because the expression true and value evaluates to true if and only if
value itself would evaluate to true.

33

34

Part I

■

Learning to Program

Using the or Operator
The or operator is true any time either of its arguments is true. Again, a simple
set of examples should make this clear, because there are only two possible
truth values:
> print(true or true)
true
> print(true or false)
true
> print(false or false)
false
> print(false or true)
true

This operator has a lower precedence than the and operator, so make sure you
are using it correctly, and include parentheses when necessary. For example:
> print(false and false or true)
true
> print(true and false or false)
false

In the first example, even though false and false turns out to be false,
it is part of a larger or statement, so the whole expression evaluates to true.
This isn’t Lua being confusing; it’s the underlying Boolean logic at play.

TIP You can use the behavior of the and and or operators to shorten some of
your expressions if you remember how they are evaluated. These operators allow
you to make the equivalent of the a ? b : c statement in C, using Lua. You will
encounter more useful examples of this later, but here’s a small example:
> print(true and “Hello“)
Hello
> print(false and “Hello“ or “Azeroth!“)
Azeroth!

Negation Using the not Operator
Simply enough, if you need to turn one Boolean value into the other, toggling
it, you can use the not operator:
> print(not true)
false
> print(not false)
true
> print(not 4)

Chapter 2

■

Exploring Lua Basics

false
> print(not “Hello“)
false

Again, because any value in Lua that is not false or nil evaluates to true,
you can even negate those values.

Understanding the nil Value
Earlier in this chapter, you encountered the following error message:
stdin:1: attempt to call global 'prnit’ (a nil value)

nil is a special thing that means lack of value in Lua. This is most often seen

when working with variables and tables (which you learn about in Chapter 4).
Type the following into your interpreter:
> print(SomeEmptyVariable)
nil
> print(type(SomeEmptyVariable))
nil

Because you have not bound the variable SomeEmptyVariable to anything
yet, it holds the special value nil, which is of type nil. You can use this
knowledge to check if a variable is currently unset, as in the following
example:
> print(SomeEmptyVariable == nil)
true
> print(type(SomeEmptyVariable) == “nil“))
true

You can check to see if a value is equivalent to nil, using the == operator.
You can also check the type of the value, to see if that is nil. Be sure to note
the difference between the value nil and the string “nil“, because the type()
function always returns a string.

Exploring Scope
So far, each and every variable you have declared has been a global variable,
meaning it is accessible to all other parts of your program. There is another
type of variable, which is called local, in that its visibility to the rest of the
program is limited in some way. To fully understand the difference between
global and local variables you need to understand the scope (or visibility rules)
of blocks and chunks.

35

36

Part I

■

Learning to Program

Blocks
The best way to illustrate a block is with an example, so type the following
into your Lua interpreter:
> do
>> local i = 7
>> do
>> local i = 10
>> print(“Inside: “ .. i)
>> end
>> print(“Outside: “ .. i)
>> end
Inside: 10
Outside: 7

Apart from the new keywords do, end, and local, you’re doing something
fairly simple here. You assign the value 7 to the variable i, assign the value 10
to a new variable i, and then print each of them out as a string. In this case, the
do keyword tells the interpreter to begin a block, and the end keyword shows
where the block ends. It might make more sense when viewed indented:
do
local i = 7
do
local i = 10
print(“Inside: “ .. i)
end
print(“Outside: “ .. i)
end

By declaring the variable i as local, you’ve limited its scope to the current
block, in this case, the code between within the same do and end keywords.
You can access this variable as much as you like within those boundaries, but
outside that it’s as if the variable doesn’t exist.
In addition to manually creating blocks using do and end, certain Lua
constructs such as for loops, while loops, and function definitions implicitly
begin a new block. You learn more about these constructs in Chapter 3.

NOTE In World of Warcraft, your addons will be competing with any number of
other addons that may use global variables. As a result, it is considered good
practice to use local variables in many cases.

Chapter 2

■

Exploring Lua Basics

Chunks
Earlier in this chapter, you received a stack traceback with an error, and it
may have referenced the main chunk. In Lua, a chunk is either the file being
executed, or the string that is being run. Variables are also limited in scope to
their specific chunk. This means a local variable declared at the top of one file
won’t be accessible in another file (they are different chunks).
In the Lua interpreter, each individual line you type is its own chunk, unless
you wrap it in a block (such as the preceding do . . . end block). For this reason,
the following code will not work:
> local i = 10
> print(i)
nil

This is just a peculiarity of the way the Lua interpreter works. To get around
this, you can use do . . . end to wrap multiple lines of code:
> do
>> local i = 10
>> print(i)
> end
10

Scope and visibility will be more important when you start working with
functions in Chapter 3, but it is important to understand the implication that
almost all variables are global, unless specified otherwise.

Summary
This chapter gave you a very broad introduction to fundamental concepts
central to the Lua programming language, including variables, values, types,
operators, and scope. Chapters 3 through 6 give you a more in-depth introduction to specific aspects of the language, as it relates to World of Warcraft. The
Lua programming language is used extensively outside WoW and a number
of good reference books are available on the language as a whole.

37

CHAPTER

3

Basic Functions and Control
Structures

Chapter 2 showed you the basics of the Lua programming language using the
print() and type() functions, without fully explaining what a function is. In
addition, basic control structures such as loops and conditionals haven’t been
introduced yet.
The first part of this chapter explains the concept of functions and guides
you through creating several of your own. The second half introduces the
basic looping and conditional statements.

Using Functions
A function is a portion of a program that can be used to simplify repeated tasks
or perform complex calculations. When a function is called it may be passed
several arguments, that is, data that the function can use for the duration of its
execution. When a function completes, it can return any number of values to
the calling portion of the program.

Creating a Function
The function keyword is used to create a new function, which can then be
stored in a variable or called directly. A basic function declaration looks like
this (type this into your Lua interpreter):
> hello = function()
>> print(“Hello World!“)
>> end

39

40

Part I

■

Learning to Program

The function constructor begins where you type function() and continues
to the matching end keyword, with the code between these delimiters making
up the body of the function. The Lua interpreter recognizes the new block
of code and indents the prompt to show you’re continuing the same section
of code (until you type the final end). In this case, a function is created that
takes no arguments (more on this in the next section) and prints the string
Hello World before ending. The resulting function value is then assigned to
the variable hello. Test this function by running the following:
> hello()
Hello World!

Now, instead of typing print(“Hello World“) every time you want to print
that string, you can simply type hello() to call the new function. This is an
extremely simple example, but you use the full power of functions as you
move through the examples in this chapter.

Local Functions
The function constructor returns a new Lua value, so it can be assigned to a
local variable the same as any other value. This can be useful when defining
functions that are called within your addons, but need not be exposed for other
addons to call. Local variables are difficult to explore in the Lua interpreter
because each line of code is in its own scope, but you may find the technique
of using local functions useful when working through the rest of this book.
SYNTACTIC SUGAR
Lua provides a different way to define functions that is more conventional and
may be easier to read. Examine the following function definition:
function hello()
print(“Hello World!“)
end

When the Lua interpreter encounters this definition, it is converted into the
definition used in the previous section:
hello = function()
print(“Hello World!“)
end

That is to say, the two definitions end up running the same code in the
interpreter. Functions defined in this manner can be made local by adding the
keyword local before the function constructor, such as:
local function hello()
print(“Hello World“)
end

(continued)

Chapter 3

■

Basic Functions and Control Structures

SYNTACTIC SUGAR (continued)
When the local keyword is used, it is converted to roughly the following
version:
local hello
hello = function()
print(“Hello World“)
end

Function Arguments and Returns
When a function is called, it can be passed any number of arguments to be used
throughout the body of the function. In addition, a function may return any
number of values when it completes. This allows for the creation of dynamic
functions that can operate on values that are passed into the function, rather
than some static formula or process.
Simple and repetitive tasks such as converting degrees Celsius to degrees
Fahrenheit can easily be made into functions that use arguments and return
values.

Converting Celsius to Fahrenheit
The conversion formula given for temperature conversion is ‘‘Multiply the
temperature in degrees Celsius by 1.8 and add 32 to the result.’’ Instead of
performing this conversion with arithmetic each time, a function can be written
that takes a number as an argument and returns the converted value as the
answer. Type the following into your Lua interpreter:
convert_c2f = function(celsius)
local converted = (celsius * 1.8) + 32
return converted
end

Here, a function is created with a single argument, which is named celsius.
The first line of the new function calculates the converted value and the second
line returns it. To see how this works, type the following:
> print(convert_c2f(0))
32
> print(convert_c2f(-40))
-40

When the new function is called, the first argument passed to it (the
number 0) is assigned to a local variable named celsius (corresponding to
the name given in the function). This allows you to define the formula for
conversion without needing to know the specific number you are converting.

41

42

Part I

■

Learning to Program

Empty Arguments
Try the following in your interpreter:
> print(convert_c2f())
stdin:2: attempt to perform arithmetic on local 'celsius' (a nil value)
stack traceback:
stdin:2: in function 'convert_c2f'
stdin:1: in main chunk
[C]: ?

When no value is passed as an argument, the argument gets the value of
nil. The first line of the convert_c2f function tries to multiply celsius by 1.8
and errors out because nil can’t be part of an arithmetic expression. A similar

error will occur if you pass other non-number values into this function.

No Return Values
Not every function you encounter will have a return statement because not
all functions need to return anything. The hello() function defined earlier
in this chapter is one such example. In these cases any assignments or other
expressions involving a call to the function will evaluate to nil. Here’s an
example:
> function hello() print(“Hello World!“) end
> test = hello()
Hello World!
> print(type(test))
nil

Functions as Lua Values
Each function in Lua is just a plain Lua value of the type function. These
values can be compared (using == and ~=), bound to variable names, passed
to functions, returned from functions, and used as keys in tables (tables are
explored in Chapter 4). A Lua value that is treated this way is called a first-class
object, and a language that supports functions in this way is said to have
first-class functions.
Run the following in your interpreter:
> hello = function() print(“Hello World!“) end

This creates a new function called hello. This value can now be compared
in the same way you’d compare any other Lua value.
> print(hello == hello)
true
> hello2 = hello
> print(hello2 == hello)

Chapter 3

■

Basic Functions and Control Structures

true
> hello2()
Hello World!
> hello2 = function() print(“Hello World!“) end
> print(hello2 == hello)
false

In the final lines of the preceding example, a new function is created and
bound to hello2. Even though the new function has the exact same definition
and body as hello, it is actually a distinct function.

Making Decisions with the if Statement
The if statement is the basis for decision making in Lua, and it supports
simple conditionals as well as more complex statements. The syntax of the
most basic if statement looks like this:
if  then
-- do something
end

Simple Conditionals
An if statement can be used to execute a block of code conditionally when
the Boolean expression evaluates to true. To better see this, type the following
into your interpreter:
function conditional_test(num)
print(“You input: “ .. num)
if (num == 7) then
print(“You found the magic number!“)
end
end

This example function prints whatever number it gets passed, but if the
number 7 is passed, it will print an additional special message. Input this
function into your interpreter, and then test it with the following:
> conditional_test(3)
You input: 3
> conditional_test(7)
You input: 7
You found the magic number!
> conditional_test(13)
You input: 13

43

44

Part I

■

Learning to Program

As with other arithmetic and Boolean expressions, the parentheses around
the conditional are not strictly necessary, but they can certainly make code
easier to read.

Complex Expressions
In addition to simple Boolean conditions, Lua supports more complex expressions so long as the expression evaluates to a Boolean value. That allows you
to combine multiple conditions using the logical operators (and, or) into a
single complex condition. The following are all valid conditions (where name
and anonymous_flag are variables):
name
type(name) == “string“
(not anonymous_flag) and (type(name) == “string“)

The first example simply checks to see that the variable name is anything other
than nil or false. The second example checks to verify that the variable name is
a string, and the final example checks to see that the variable anonymous_flag
is either false or nil, and the name variable is a string.

Extended Conditionals
An extended form of the if statement allows you to chain multiple conditions
together, as well as provide a default for when no condition is matched. The
full syntax for the if statement is:
if  then
-- if part
elseif  then
-- elseif part
elseif  then
-- another elseif part
else
-- else part
end

When the interpreter runs this expression, it checks each condition in order,
stopping at the first expression that evaluates to true and running that portion
of the code. If none of the expressions are true, the code in the else section is
run. Not every if statement will include elseif or else options, but they are
always available if you need them.
Using this form of if/elseif/else ensures that only one action in the entire
if statement will be taken. If you were to write it using a series of simple if
statements, more than one may be called, as in the following example.

Chapter 3

■

Basic Functions and Control Structures

if  then
-- do something
end
if  then
-- do something
end

Both types of constructs are useful but you should ensure you are using the
correct one, so your program behaves correctly depending on whether you
need to take one action based on a condition, or evaluate multiple conditions
independently.

Displaying a Personalized Greeting
Conditionals can be used to verify the arguments to a function. For example,
consider a function that takes a name (or nil) and prints out a personalized
greeting. Define this function in your Lua interpreter:
function greeting(name)
if (type(name) == “string“) then
print(“Hello “ .. name)
elseif (type(name) == “nil“) then
print(“Hello friend“)
else
error(“Invalid name was entered“)
end
end

The first condition checks to see if the name argument is a string, in
which case it generates and prints a custom greeting. If the name argument is
nil, meaning nothing was passed into the function, it will print the generic
string Hello friend. Finally, if neither of the previous conditions match, the
function triggers a custom error message using the error() function. Test this
new function in your interpreter:
> greeting(“Frank“)
Hello Frank
> greeting()
Hello friend
> greeting(13)
stdin:7: Invalid name was entered
stack traceback:
[C]: in function 'error'
stdin:7: in function 'greeting'
stdin:1: in main chunk
[C]: ?

45

46

Part I

■

Learning to Program

When the error() function is called, Lua provides the error message
supplied along with a stack traceback. In this case, you can see that the error
was triggered from the greeting() function, which was called from the main
chunk.
The preceding greeting() function could have been written without using
the elseif statement, by using nested if statements, as follows:
function greeting(name)
if (type(name) == “string“) then
print(“Hello “ .. name)
else
if (type(name) == “nil“) then
print(“Hello friend“)
else
error(“Invalid name was entered“)
end
end
end

The nested style is useful in certain situations when you have multiple
conditions but also need to have an else portion for each of them. In general,
use whatever style you consider to be more readable and appropriate for the
given situation.

Repeating Actions with the while Statement
Computers are often used to repeat tasks or simplify complex calculations that
would otherwise require manual repetition. Lua provides the while statement,
which will repeat a block of code as long as a specified condition is met. The
while statement’s syntax is:
while  do
-- body
end

The Boolean expression is evaluated on each and every repetition of the
loop, and the loop will continue as long as the condition evaluates to true.

Computing Factorials
The process of computing a number’s factorial is a good example of something
that is easily automated. The factorial of a number x is computed by multiplying
all of the numbers from 1 to x together. Thus, 3 factorial is 1 * 2 * 3. If a
function factorial() is defined, you can simply type print(factorial(9))

Chapter 3

■

Basic Functions and Control Structures

instead of print(9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1). Define this function
now by typing the following definition into your interpreter:
function factorial(num)
local total = 1
while (num > 1) do
print(“total: “.. total .. “ num: “ .. num)
total = total * num
num = num - 1
end
return total
end

This function includes a print() statement that will show you what the
function does on each iteration of the loop. Before using this code in an addon,
remove that line from the function, or simply comment it out. For now, test
this in your Lua interpreter:
> print(factorial(5))
total: 1 num: 5
total: 5 num: 4
total: 20 num: 3
total: 60 num: 2
120

You can see each step of the loop and how the value is being calculated.
Using debug statements like this can be really handy when writing code, but
you have to remember to remove them before you release the code.

NOTE The condition of a while statement is checked prior to running the loop,
and again on each subsequent run of the loop. This means if the condition is never
met, the body of the while loop is never executed, and Lua just skips past it.

Differences Between while and repeat
The repeat/until loop is a variant of the while loop that has the following
form:
repeat
-- body
until 

The primary difference between the while/do loop and a repeat/until loop
is that the condition of a repeat loop is checked at the end of the computation,
so the loop of the body is always executed at least once. In other words,
the condition in a while statement is checked in order to continue the loop,

47

48

Part I

■

Learning to Program

whereas the condition in a repeat loop is checked in order to exit the loop.
Here’s how you’d define a new factorial function using this construct:
function factorial2(num)
local total = 1
repeat
total = total * num
num = num - 1
until (num < 1)
return total
end

You can verify the results of this function by testing it with a few different
values:
> print(factorial2(1))
1
> print(factorial2(2))
2
> print(factorial2(3))
6
> print(factorial2(5))
120

If you happened to test these two functions with some unexpected value,
such as -3, you should see a difference between the results:
> print(factorial(-3))
1
> print(factorial2(-3))
-3

When running factorial(), the num variable is already less than 1, so
the while body never runs; it simply returns the default value of 1. When
factorial2() is called, the body of the loop happens once, which causes the
different return value of -3.

Looping with the Numeric for Statement
As the preceding factorial function demonstrated, many loops begin at a simple
integer value and then either increment or decrement to some predefined limit.
In the case of factorial(9), the loop starts at 9 and continues until it reaches 1.
Rather than managing this sort of loop yourself, the for statement provides
an easy way to write these loops:
for variablename = start_value, end_value, step_value do
-- body
end

Chapter 3

■

Basic Functions and Control Structures

Table 3-1 explains the different arguments that must be supplied to the for
statement.
Table 3-1: Arguments for Numeric for Loop
ARGUMENT

DESCRIPTION

variablename

A valid variable identifier, the counter variable

start_value

A number, the initial value of variablename

end_value

A number, the end value of the loop

step_value

The number by which to increment the counter after each loop

The following are examples of simple for loops:
> for i = 1, 3, 1 do
>> print(i)
>> end
1
2
3
> for i = 3, 1, -1 do
>> print(i)
>> end
3
2
1

These two loops translate (roughly) to the following code blocks using while
loops:
do
local i = 1
while (i <= 3) do
print(i)
i = i + 1
end
end

and
do
local i = 3
while (i >= 1) do
print(i)
i = i - 1
end
end

49

50

Part I

■

Learning to Program

When the step value in a for loop is not provided, Lua assumes a value of 1
for the loop. The earlier 1, 2, 3 example can thus be written as follows:
for i = 1, 3 do
print(i)
end

Computing Factorials
The for loop can be used to make the factorial() function even more clear.
Type the following definition into your interpreter:
function factorial3(num)
local total = 1
for i = 1, num do
total = total * i
end
return total
end

Rather than manually writing the terminal condition for the while or repeat
loop, you can just provide the for statement with a start value, and an upper
bound. This example uses the variable num, which is evaluated to a number
when the function is run.

Evaluation of Loop Conditions
In a for loop, the end_value and step_value are both calculated once, at the
start of the loop. As a result, variables and expressions can be used for these
values. These values cannot be changed mid-loop; they will have already been
calculated. Consider the following example:
> upper = 3
> for i = 1, upper do
>> print(i)
>> upper = upper + 1
>> end
1
2
3

This example doesn’t loop forever because loop conditions are only evaluated at the start of the loop.

Variable Scope in for Loops
When writing a for loop, remember that the counter variable name you supply
will automatically be made local to that block and won’t be accessible outside
that level:
> i = 15
> for i = 1, 3 do print(i) end

Chapter 3

■

Basic Functions and Control Structures

1
2
3
> print(i)
15

In addition, changes made to the counter variable inside the loop do not
affect the iteration. For example, the assignment to i in the following loop
doesn’t actually advance the loop counter:
> for i = 1, 10 do
>> print(“Loop iteration: “ .. i)
>> i = i + 1
>> end
Loop iteration: 1
Loop iteration: 2
Loop iteration: 3
Loop iteration: 4
Loop iteration: 5
Loop iteration: 6
Loop iteration: 7
Loop iteration: 8
Loop iteration: 9
Loop iteration: 10

If, for some reason, you need to save the control variable’s value, you can
declare a local variable just prior to the for loop, where you can save the
number you need, as in the following example:
upper = 10
do
local max
for i = 1, upper do
max = i
end
print(max)
end

When the loop terminates, max will be 10, which was the last value of the
control variable.

Summary
This chapter introduced you to functions and showed you two different
methods to create functions, using two different syntaxes. Conditionals and
control structures were introduced, enabling you to easily perform repeated
computations. The next chapter explores advanced techniques using functions
and control structures.

51

CHAPTER

4

Working with Tables

Keeping data in variables is handy when working on simple programs, but
larger projects require an easier way to store data. Consider a simple address
book program that enables you to electronically store contact information.
Using variables, that might look something like this:
alice_name = “Alice Applebaum“
alice_phone = “+1-212-555-1434“
alice_address1 = “114 Auburn Street“
alice_address2 = “Apt 14“
alice_city = “Atlanta“
alice_state = “GA“

As you can see, using this method for more than a few simple entries
would be unwieldy. Adding a new entry requires you to create a new variable
name and enter each of the details in a new variable. Computers are all about
automating processes, so there has to be a better way to deal with this.

Storing Data Using Tables
You may be familiar with tables or some analogous object from other programming languages (arrays, records, dictionaries, hash tables). In Lua, tables
are objects that can be used to store other (usually related) values.
To understand how to use tables, it’s important to grasp the concept of an
associative table; which is how tables in Lua are implemented. An associative
table is a collection of values that are each associated with a key. Code can then
request a given key from a table and receive the value that is paired with that
key, if such a pair exists.
53

54

Part I

■

Learning to Program

Creating and Indexing Tables
Create a new table for Alice by running this code:
> alice = {}

In Lua, the table constructor {} creates a new table, in this case an empty
one. Here, the new table has been assigned to the variable alice. You can
index this table using square brackets and a key. Run the following:
>
>
>
>
>
>

alice[“name“] = “Alice Applebaum“
alice[“phone“] = “+1-212-555-1434“
alice[“address1“] = “114 Auburn Street“
alice[“address2“] = “Apt 14“
alice[“city“] = “Atlanta“
alice[“state“] = “Georgia“

Each line here tells Lua to look in the table alice using the provided key,
and set that value to whatever is on the right side of the assignment operator.
These elements can then be accessed later:
> print(alice[“name“])
Alice Applebaum
> print(alice[“address2“])
Apt 14

In each case, a key is matched up with a specific value and stored within the
table. Each of these examples uses a string as the key, but Lua actually allows
any value (except nil) to be used as a key. See this in the following example:
> alice[1] = “Test value“
> alice[2] = 14
> print(alice[1])
Test value
> print(alice[2])
14

Clearing an Element from a Table
When a table is indexed with a key that has not been set, the table will return
the special value nil. Run the following:
> print(alice[“fax“])
nil

This means, quite literally, that there is nothing stored in the table for the
given key. In order to clear a key/value pair from a table, you just assign that
key the value nil. Do this now to clear the two test values set previously:
> alice[1] = nil
> alice[2] = nil

Chapter 4

■

Working with Tables

Shortcuts for String Keys
Lua provides an easier way to index a table by string keys when those strings
are a single identifier. This is extremely useful when working with data tables.
Instead of typing this:
alice[“address1“] = “114 Auburn Street“

you can type the following:
alice.address1 = “114 Auburn Street“

This shortcut method only works when the key begins with a letter or
underscore character and consists of only letters, digits, and underscore
characters. In addition, the key cannot be a reserved Lua keyword (such as
end). All of the following identifiers are considered valid:
myTable.someKey
myTable.someKey12
myTable.some_Key
myTable._someKey
myTable.start

But these will cause an error:
myTable.12someKey
myTable.some-key
myTable.end
myTable.or

This method of indexing a table is only provided as a convenience, and
only works when your keys are in a specific format. You can still access the
‘‘invalid’’ keys using the full bracket notation.

Creating Populated Tables
In addition to using {} to create new empty tables, you can also use it to
create an already populated table. This is accomplished by providing a set of
key/value pairs within the constructor itself, using the following syntax:
myTable = {
[key1] = value1,
[key2] = value2,
...
}

55

56

Part I

■

Learning to Program

Running the following can thus create an equivalent record for Alice:
alice = {
[“name“] = “Alice Applebaum“,
[“phone“] = “+1-212-555-1434“,
[“address1“] = “114 Auburn Street“,
[“address2“] = “Apt 14“,
[“city“] = “Atlanta“,
[“state“] = “Georgia“,
}

You can take advantage of shortcuts for string keys in the constructor too, by
typing someKey instead of [“someKey“]. This shortcut follows the same rules
as dot notation for table indexing. This shortens the example record to:
alice = {
name = “Alice Applebaum“,
phone = “+1-212-555-1434“,
address1 = “114 Auburn Street“,
address2 = “Apt 14“,
city = “Atlanta“,
state = “Georgia“,
}

TRAILING COMMAS IN TABLE CONSTRUCTORS
The last line of each of these table examples has a trailing comma before the
closing brace. The syntax of Lua allows this within tables so it is easier to add
new key/value pairs to the end of the definition. If Lua didn’t allow this and
you forget to add a comma before adding a new line, you would get a compilation error.
When creating new tables in this format, having the trailing comma makes
adding new entries easier, so it is a common practice to include them on every
row.

Using Tables as Arrays
Lua tables have another unique property when they are used with consecutive
integer keys starting at 1. These tables can be used as lists of values and include
library functions for inserting values, removing values, and sorting the list.
Tables used in this manner are typically referred to as arrays, due to some
similarities they share with arrays in other programming languages. More
specifically, the part of a table that has integer keys starting at 1 is referred to
as the array part of the table.

Chapter 4

■

Working with Tables

Creating an Array
You can create a new array using the table constructor in one of the two
following ways (they are equivalent):
tbl = {
value1,
value2,
value3,
...
}
tbl =
[1]
[2]
[3]
...
}

{
= value1,
= value2,
= value3,

In the first case you can omit the key names entirely, and just provide a
comma-separated list of values. As you can see, arrays are just a special case
of tables. Each of the functions covered in this section is only reliable when
dealing with consecutive integer keys starting at 1. Although nil can be used
as a value in a table, it indicates that a value is missing, so care must be taken
to ensure that nil values don’t appear in the array part of a table.
The two types of table constructors can be mixed, so you can define a table
with an array part and key/value pairs at the same time. For example, the
following is a valid table definition that combines the use of all three definition
methods:
class_list = {
“Alice“,
“Bob“,
“Carol“,
class_name = “Foundations of Engineering and Computer Science“,
[“class_code“] = “ECS101“,
}

Getting the Length of an Array
The same length operator (#) that was introduced in Chapter 2 for use on
strings is also used to get the length of the array part of a table. Test this now
with these quick examples:
> tbl = {“alpha“, “beta“, “gamma“, “delta“}
> print(#tbl)
4

57

58

Part I

■

Learning to Program

> tbl = {}
> print(#tbl)
0
> tbl = {
>> “alpha“,
>> “beta“,
>> [“one“] = “uno“,
>> [“two“] = “dos“,
>> “gamma“,
>> }
> print(#tbl)
3

You can see that # only counts the elements in the array part. This operator
can be used to print the table’s elements without your needing to hardcode
the upper limit. For example:
> for i = 1, #tbl do
>> print(tbl[i])
>> end
alpha
beta
gamma

Adding Elements to an Array
Adding an element to an array is as simple as associating the value to the next
integer key in sequence. More generally:
> tbl[#tbl + 1] = “new element“

This is a really tedious and error-prone way to do something relatively
simple. Lua provides a table.insert() library function that makes adding
elements a bit easier. The syntax for table.insert() is:
table.insert(tbl, [pos,] value)

The arguments are as follows:
tbl —The table to alter
pos (optional)—The position at which to add the new element
value —The value to insert

The second parameter being enclosed in brackets indicates that it is optional
and does not need to be included. If the position isn’t included, the new value
will be added to the end of the table.

Chapter 4

■

Working with Tables

Run the following in your interpreter to create a sample table and a function
that will allow you to easily print the contents of the array part of a table that
is passed in as an argument:
tmp = {“alpha“, “beta“, “gamma“}
function print_array(tbl)
for i = 1, #tbl do
print(i, tbl[i])
end
end

To print the current list, use the following command:
> print_array(tmp)
1
alpha
2
beta
3
gamma

To add a new element to the end of the list, call table.insert() with the
table you’d like to alter and the value you’d like to add:
> table.insert(tmp, “delta“)
> table.insert(tmp, “epsilon“)
> print_array(tmp)
1
alpha
2
beta
3
gamma
4
delta
5
epsilon

To insert a new value at a given position, call table.insert() with the
optional second parameter pos, a number that indicates at what position you’d
like to add the element. When you insert a value in this way, all elements after
the given position will be renumbered and moved up.
> table.insert(tmp, 3, “zeta“)
> print_array(tmp)
1
alpha
2
beta
3
zeta
4
gamma
5
delta
6
epsilon

When using the position argument, it’s important to make sure you’re
supplying a valid number. The position should always be between 1, which
inserts the value at the front of the list, and #tmp + 1, which inserts it after the

59

60

Part I

■

Learning to Program

current last element. If you supply a value outside this range, the results are
unpredictable.

Removing Elements from an Array
Lua includes a function to remove elements from a table, and the syntax is
similar to its companion table.insert():
value = table.remove(tbl [, pos])

This function takes up to two parameters:
tbl —The table to alter
pos (optional)—The element to remove from the table

The function signature is written as if it was an assignment. This is shorthand
notation to show that the function also returns something:
value —The value removed from the table

Again, the brackets around pos show that it is an optional parameter. When
a position isn’t included, Lua will remove the last element in the table (that is,
the element at the #tbl position).
To remove the last element of a table, use the following command:
> removed = table.remove(tmp)
> print_array(tmp)
1
alpha
2
beta
3
zeta
4
gamma
5
delta
> print(removed)
epsilon

By simply calling table.remove() with only a table argument, the last
element has been removed and we’re left with the rest of the table.
Here’s how to remove a specific element in a table:
> removed = table.remove(tmp, 3)
> print_array(tmp)
1
alpha
2
beta
3
gamma
4
delta
> print(removed)
zeta

When an element is removed from the middle of the table (including the first
element), all other elements are renumbered and shifted down. This ensures

Chapter 4

■

Working with Tables

that the elements of the array are always numbered properly so the array part
functions all work properly.
Just because a function has return values doesn’t mean you have to do
anything with them. You could just as easily call table.remove(), ignoring
the return value entirely.

Sorting the Elements of an Array
When an array contains basic elements such as strings and numbers that can
be easily compared, there is a standard library function to sort the list. The
syntax of the table.sort() function follows:
table.sort(tbl [, comp])

The second argument to table.sort() is covered in detail in Chapter 5, but
the first argument is the table that you would like to sort. You can call this
function and pass it a table to be sorted as the first argument:
> print_array(tmp)
1
alpha
2
beta
3
gamma
4
delta
> table.sort(tmp)
> print_array(tmp)
1
alpha
2
beta
3
delta
4
gamma

Because the values in this table are strings, they are sorted alphabetically,
in ascending order (this is the default). If the table contained numbers, they
would be sorted in the same way.
A simple sort like this won’t be effective for more complex values (such as
tables), or when the values in an array are mixed (such as strings and numbers).
Chapter 5 will show you how to use the second argument to table.sort()to
custom tailor the sort function for these situations.

Using Tables as Namespaces
You’ve already been introduced to a few functions that are grouped together:
table.insert()
table.remove()
table.sort()

61

62

Part I

■

Learning to Program

When functions are grouped together in this manner, they are said to be
part of a namespace, in this case, the table namespace. Namespaces provide a
logical grouping of functions that are related, collected in a Lua table. Because
tables can hold function values, the preceding functions are also accessible
using:
table[“insert“]()
table[“remove“]()
table[“sort“]()

Creating a new namespace is a matter of writing your new functions,
creating a new table, and setting the appropriate key/value pairs in your table.

Creating a Namespace of Utility Functions
You’ve already written a few utility functions that might be handy to keep
around, such as convert_c2f(). You can create a new namespace to start
storing these functions by defining a new table:
> util = {}

Adding Functions to a Namespace
You have two different ways to add functions to a namespace: by indexing the
table and storing the value of an existing function, or by defining the function
directly as part of the namespace.

Storing an Existing Function
If you’ve closed your previous Lua session, redefine your Celsius to Fahrenheit
conversion function:
function convert_c2f(celsius)
return (celsius * 1.8) + 32
end

Now that you have a function to which you can refer, run the following
code to store it in the util table:
> util.celsius2fahrenheit = convert_c2f

This function can then be accessed directly from the util table:
> print(util.celsius2fahrenheit(0))
32
> print(util.celsius2fahrenheit(-40))
-40

Chapter 4

■

Working with Tables

Defining a New Function
Rather than define a function with a name and then set it as part of the
namespace, you can define the function directly as part of the namespace. Run
the following code:
function util.factorial(num)
local total = 1
for i = 1, num do
total = total * i
end
return total
end

You may recall from Chapter 3 that this method of function definition is
syntactic sugar and is translated by Lua into the following:
util.factorial = function(num)
local total = 1
for i = 1, num do
total = total * i
end
return total
end

Using the first form is often the most convenient way to define functions,
and it makes the code easier to read compared to the alternative methods.
More often than not, when you read a namespace definition, you will see it in
this form; however, as always, feel free to develop your own style.

Object-Oriented Programming with Tables
Tables can also be used for a different type of programming called object-oriented
programming. In this type of programming, data is described as objects, which
contain methods, special functions that act directly on or through that object. Lua
provides some simple mechanisms to enable object-oriented programming,
but does not strictly enforce any particular style of programming.

Creating a Non-Object-Oriented Counter
To illustrate some of the benefits this type of programming provides, run the
following in your interpreter:
-- Create a new scope for local variables
do

63

64

Part I

■

Learning to Program

-- Create a counter that cannot be accessed outside this scope
local counter = 0
-- Global functions to interact with counter
function counter_get()
return counter
end
function counter_inc()
counter = counter + 1
end
end

This block of code makes a simple, one-way counter that can’t be decremented, but can be retrieved and incremented via the counter_get() and
counter_inc() functions. Explore this by running the following in your
interpreter:
> print(counter_get())
0
> counter_inc()
> counter_inc()
> print(counter_get())
2
> counter = counter - 1
stdin:1: attempt to perform arithmetic on global 'counter' (a nil value)
stack traceback:
stdin:1: in main chunk
[C]: ?

You can see that the counter variable is not accessible outside of the created
scope and thus can’t be altered without calling the provided functions. This
code implements a single counter when, in fact, you might need more than
one. Because these functions are tied to a specific counter variable, they are
very limited.

Using Tables as Simple Objects
The following is a different implementation for the simple counter, making the
counter an object with two methods, get and inc. Unlike the first example, the
counter can be altered directly without calling the functions. Run the following
code in your interpreter:
counter = {
count = 0
}
function counter.get(self)

Chapter 4

■

Working with Tables

return self.count
end
function counter.inc(self)
self.count = self.count + 1
end

This program allows you to do the following:
> print(counter.get(counter))
0
> counter.inc(counter)
> print(counter.get(counter))
1

In this implementation, the actual counter variable is stored in a table (which
serves as the object). Each of the functions that interact with this value has an
argument named self, which is expected to be a counter object. You could
make a new counter by running the following:
> counter2 = {
>> count = 15,
>> get = counter.get,
>> inc = counter.inc,
>> }
> print(counter2.get(counter2))
15

Because the functions are just Lua values and they are designed to work on
an argument rather than some magical hidden variable, you can copy them
into your counter. As a matter of fact, the functions will work correctly even if
you call counter.get() but pass it the counter2 object:
> print(counter.get(counter2))
15
> print(counter.get == counter2.get)
true

This should be no surprise because you’re just moving and copying references to the same functions around. Although this implementation is definitely
more convenient than the first attempt, it can be made even easier. Right now,
you have to call the function and pass in the counter object, causing you
to type the object’s name twice. Lua provides a bit of syntactic sugar that
helps you.

Using : to Call Object Methods
In the preceding example you can refer to get and inc as object methods
because they are written to be called within the context of an object. Lua

65

66

Part I

■

Learning to Program

provides a bit of syntactic sugar that makes calling an objects methods easier.
Instead of typing counter.get(counter), you can call counter:get().
Lua translates counter:get() into counter.get(counter), saving you a bit
of typing and making code easier to read. This all happens behind the scenes
and prevents from you having to pass the object in every time you make a
method call.

Defining Functions Using :
You can use the : operator to define functions, making this type of programming even more natural. When this happens, Lua includes an implicit first
argument called self. That’s why you used the variable name self in the
previous example.
Redefine the earlier functions by typing the following into your interpreter:
counter = {
count = 0
}
function counter:get()
return self.count
end
function counter:inc()
self.count = self.count + 1
end

This code is roughly equivalent to the following definition:
counter = {
count = 0
}
function counter.get(self)
return self.count
end
function counter.inc(self)
self.count = self.count + 1
end

Test this new version with the following code:
>
0
>
>
>
2

print(counter:get())
counter:inc()
counter:inc()
print(counter:get())

Chapter 4

■

Working with Tables

COMMON ERRORS
If you attempt to call a method that expects the self argument with a period
instead of a colon, you might get an error similar to this:
stdin:2: attempt to index local 'self' (a nil value)

Most of the time, when you get this error, it means you are accidentally calling a method without passing a first argument, or you used a period where
you meant to use a colon.

Making a Better Counter
The counter program still has room for improvement because the way new
counters are created is relatively clunky. Run the following to define a more
robust counter system:
-- Create a new scope for local variables
do
local function get(self)
return self.count
end
local function inc(self)
self.count = self.count + 1
end
function new_counter(value)
if type(value) ~= “number“ then
value = 0
end
local obj = {
count = value,
get = get,
inc = inc,
}
return obj
end
end

This example provides a single global function called new_counter, which
takes the initial value of the counter as an argument. It returns a new object
containing two methods and the counter value itself. This type of function is
typically called a factory function because it just returns new objects each time
you call it. Run a few tests to ensure the system works properly:
> counter = new_counter()
> print(counter:get())

67

68

Part I

■

Learning to Program

0
> counter2 = new_counter(15)
> print(counter2:get())
15
> counter:inc()
> print(counter:get())
1
> print(counter2:get())
15

Although the implementation may seem a bit more complex than the
previous attempts, creating and manipulating new counters is extremely easy.
Choose whichever implementation makes the most sense in your code.

Extending Tables with Metatables
Each table in Lua is capable of having a metatable attached to it. A metatable is
a secondary table that gives Lua extra information about how that table should
be treated when it is used. For example, by default, when you try to print a
table you are given a string that looks something like table: 0x30d470, which
isn’t extremely readable. Lua provides a way to change this behavior using
metatables and metamethods.

Adding a Metatable
A metatable is nothing more than a table used to store extra information about
the tables to which it is attached. They can be passed around, attached to
multiple tables, and altered at any time. To begin redefining the behavior of
a table, you must create a metatable and attach it to a table object, using the
setmetatable() function. This function takes two arguments:
tbl —The table to alter
mt —The table to attach to tbl

In addition, setmetatable() returns a single argument, the table you passed
in as the first argument. This can be helpful when creating new tables to pass
directly to setmetatable(). Run the following code to create some tables to
play with, and attach the same metatable to each of them:
tbl1 = {“alpha“, “beta“, “gamma“}
tbl2 = {“delta“, “epsilon“, “zeta“}
tbl3 = {}
mt = {}
setmetatable(tbl1, mt)
setmetatable(tbl2, mt)
setmetatable(tbl3, mt)

Chapter 4

■

Working with Tables

You can verify the metatable has been set correctly by using the
getmetatable() function. This function simply takes a table as the first
argument and returns the metatable, or nil if no metatable is attached.
> print(getmetatable(tbl1) == mt)
true

Now that you have an object with a metatable, you can begin redefining the
behavior of the table.

Defining Metamethods
A metamethod is nothing more than a function stored with a specific key
in a metatable. There are several possible metamethods, and they take a
varying number of arguments. Each metamethod begins with two underscore
characters. You can find a full list in the Lua Reference Manual (available online
at http://www.lua.org), but the most frequently used ones are shown in
Table 4-1.
Table 4-1: Relevant Metamethods
METAMETHOD

ARGUMENTS

DESCRIPTION

___add

2

Defines the behavior when used in addition
operations.

___mul

2

Defines the behavior when used in multiplication
operations.

___div

2

Defines the behavior when used in division
operations.

___sub

2

Defines the behavior when used in subtraction
operations.

___unm

1

Defines the behavior when negated (unary minus).

___tostring

1

Defines the behavior when the table is an
argument to tostring(). This also affects the
print() function, which calls tostring()
directly.

___concat

2

Defines the behavior when used with the
concatenation operator (..)

___index

2

Defines the behavior when the table is indexed
with a key that doesn’t exist in that table.

___newindex

3

Defines the behavior when a previously unset key
in the table is being set.

69

70

Part I

■

Learning to Program

Defining Basic Arithmetic Using ___add, ___sub, ___mul, and
___div
Each of the arithmetic metamethods ___add, ___sub, ___mul, and ___div takes
two arguments and can (in theory) return anything you’d like. However, keep
the following in mind:
The result of one operation may be part of a larger arithmetic expression.
If you return a non-number from your metamethod, you should ensure
it is capable of handling further arithmetic.
If you return nil, it will break any arithmetic expression it is a part of, so
it’s best to avoid that.
The following function defines addition between two tables as a new table
with the elements of the first table’s array part, followed by the elements of
the second’s array part. Add the following function to your Lua interpreter:
function mt.___add(a,b)
local result = setmetatable({}, mt)
-- Copy table a in first
for i = 1, #a do
table.insert(result, a[i])
end
-- Copy table b in second
for i = 1, #b do
table.insert(result, b[i])
end
return result
end

To simplify the function, the arguments have been named a and b. The first
line creates a new results table and makes sure to set the metatable correctly;
without this the result might not work in a larger arithmetic expression. The
rest of the function is straightforward, copying the elements of each table to
the new resulting table. Here is a simple test:
> add_test = tbl1 + tbl2
> print(#add_test)
6
> for i = 1, #add_test do print(i, add_test[i]) end
1
alpha
2
beta
3
gamma
4
delta
5
epsilon
6
zeta

Chapter 4

■

Working with Tables

The metamethod correctly handles the addition and creates a new table
with the results of the addition. The other basic arithmetic operations could
be defined in the same way. Instead of returning a table, these functions could
return some meaningful number that can be used as part of a larger formula.

Defining Negation Using ___unm
The unary minus (negation) operator, __unm, expects exactly one argument,
and should return the result of the argument being negated. In these examples,
this will mean reversing the array part of the given table. Run the following
code:
function mt.___unm(a)
local result = setmetatable({}, mt)
-- Reverse through the elements of the array
for i = #a, 1,-1 do
table.insert(result, a[i])
end
return result
end

Test table negation with a few examples:
>
>
1
2
3
>
>
1
2
3
4
5
6

unm_test =
for i = 1,
gamma
beta
alpha
unm_test =
for i = 1,
gamma
beta
alpha
delta
epsilon
zeta

-tbl1
#unm_test do print(i, unm_test[i]) end

-tbl1 + tbl2
#unm_test do print(i, unm_test[i]) end

Creating Meaningful Output with ___tostring
In the current example, it would be useful to print the table and have it display
the elements rather than the unique string Lua provides. You can accomplish
that using the ___tostring metamethod, which takes a single argument (the
table) and should return a string.
Run the following code:
function mt.___tostring(tbl)
local result = “{“
for i = 1, #tbl do

71

72

Part I

■

Learning to Program

if i > 1 then
result = result .. “, “
end
result = result .. tostring(tbl[i])
end
result = result .. “}“
return result
end

Because you know the input will be a table, you start the string with the {
character. This function then loops through each element of the array. If the
loop is beyond the first element, a comma is added to the string to separate
each value. This is done so you don’t have an extra comma at the end of the
output. Then the value itself is concatenated onto the result string. Finally,
when the loop is complete, you close the brace and return the string:
> print(tbl1)
{alpha, beta, gamma}
> print(tbl2)
{delta, epsilon, zeta}
> print(tbl3)
{}

When working with more complex objects, it can be very useful to provide a
meaningful text representation of your data, so the ___tostring metamethod
can be extremely handy.

Concatenating Tables Using ___concat
For these tables, concatenation will end up being the same thing as addition, so
you can simply use that function for the ___concat metamethod, as well. Both
metamethods take in two arguments and return a single result. In addition,
both are typically chained together, so you’ll need to ensure the resulting
object is also capable of concatenation. Run the following test:
> mt.___concat = mt.___add
> print(tbl1 .. tbl2)
{alpha, beta, gamma, delta, epsilon, zeta}

Because the ___tostring metamethod is still active, the resulting table is
converted to string representation, even when printed like this.

Exploring Fallback Tables with ___index
Normally, when a table does not have a value associated with a given key, nil
is returned. That makes sense for run-of-the-mill tables, but at times it is more

Chapter 4

■

Working with Tables

appropriate to take other action instead. The ___index metamethod allows
that to happen, following this procedure:
1. Code tries to access an unassociated key in a table.
2. If the table has an ___index metatable entry that is another table, look
up the same key in that table and return it (or nil if it doesn’t exist).
This may possibly trigger the ___index metamethod of the second table,
making a chain.
3. If the table has an ___index metatable entry that is a function, call the
function with the table and the key as arguments, and return the result.
Example Using Tables

Let’s expand on the previous example by creating a table with an ___index
metamethod that allows for the translation of English phrases into German.
Run the following code:
tbl4 = {[“Night elf“] = “Nachtelf“}
setmetatable(tbl4, mt)
enUS_defaults = {
[“Human“] = “Human“,
[“Night elf“] = “Night elf“,
}
mt.___index = enUS_defaults

This example creates a new table that contains the German localization of
the English phrase Night elf. In addition, there is a default table that contains
the English phrases Human and Night elf. If the answer isn’t found when tb14
is indexed, Lua will look in the metatable’s ___index entry and return that
result. See this in action yourself:
> print(tbl4[“Night elf“])
Nachtelf
> print(tbl4[“Human“])
Human
> print(tbl3[“Night elf“])
Night elf

Because the metatable is shared between the four tables being used in this
exercise, if you access the Night elf or Human key in the table, you will get the
English version of the phrase back. The ___index metatable entry here allows
you to provide partial localization for the German language by displaying the
English words by default when a translation isn’t found.

73

74

Part I

■

Learning to Program

Example Using Functions

Instead of using a table for the ___index entry, you can specify a function
that takes two arguments: the table itself and the key being requested. This
function enables you to add logic to the indexing of tables. Run the following
code, which allows you to avoid having a long table of defaults where the keys
and the values are the same:
defaults_mt = {
___index = function(tbl, key)
if type(key) == “string“ then
print(“Return default value of '“ .. key .. “' for key: “ .. key)
return key
else
return nil
end
end,
}
setmetatable(enUS_defaults, defaults_mt)

Then test it with the following examples:
> print(tbl4[“Night elf“])
Nachtelf
> print(tbl4[“Human“])
Human
> print(tbl4[“Gnome“])
Return default value of 'Gnome' for key: Gnome
Gnome
> print(tbl4[1])
nil

Note that the second to last example prints a message in addition to returning
the value. In fact, a metamethod that is a function can do any number of things.

Catching Creation of Keys with ___newindex
Unlike the ___index metamethod, which is designed to handle keys being
requested from a table, the ___newindex metamethod can be used when a new
key has been set in the table. Specifically, it is called whenever an assignment
is made to a non-existing key in a table.
___newindex takes three arguments:
tbl —The table being indexed
key —The key being used to index the table
value —The value to assign to table[key]

Chapter 4

■

Working with Tables

When this metamethod is set, it is responsible for actually making the
assignment happen. This can be used to stop a value from ever being set in the
first place. Run the following code in your interpreter:
function mt.___newindex(tbl, key, value)
if key == “banana“ then
error(“Cannot set a protected key“)
else
rawset(tbl, key, value)
end
end

The rawset() function here allows you to bypass the metatable (covered
in the next section), to prevent your metamethod from being called again.
As long as this metatable is set, you will be unable to (through conventional
means) set the key [“banana“] in any of the example tables, as shown in the
following:
> tbl1.apple = “red“
> print(tbl1.apple)
red
> tbl1.banana = “yellow“
stdin:3: Cannot set a protected key
stack traceback:
[C]: in function 'error'
stdin:3: in function 
stdin:1: in main chunk
[C]: ?
> print(tbl1.banana)
Return default value of 'banana' for key: banana
banana

Because the metamethod errors instead of setting the new entry, you have a
pseudo-‘‘protected’’ key in your tables.

Bypassing Metatables
When writing functions for the ___index and ___newindex metamethods, it
may be necessary to bypass the metatable when getting or setting a value. This
is accomplished using the rawget() and rawset() functions.

value = rawget(tbl, key)
The rawget() function takes the table to query and the key to look up, and
returns the value of that key in the table without using the metatable for
lookups. When you are writing a function that serves as a metamethod for a
table, it is typically best to use rawget() to access values in that table.

75

76

Part I

■

Learning to Program

rawset(tbl, key, value)
To set a value in a table without hitting the metatable, you can use the
rawset() function. It takes in the table to be altered, the key to use, and the
value to be placed in the table. You will encounter tables with ___newindex
metamethods less frequently than those with ___index metamethods, but it’s
good to understand what tools are available, in case you need them.

Summary
In this chapter you learned how to use Lua tables to store data that can be
easily read and indexed. Arrays were introduced as a special subset of tables
with helper functions to insert/remove and sort array tables. Namespaces
of functions were introduced along with basic object-oriented programming.
Finally, you learned how to extend tables using metatables.
The next chapter introduces you to more advanced features of functions and
control structures.

CHAPTER

5

Advanced Functions and
Control Structures

The functions and control structures introduced in Chapter 3 were relatively
simple but gave you the capability to create nontrivial programs. This chapter
introduces more advanced versions of functions and loops that allow you to
accomplish the following:
Create functions with a variable number of arguments
Return multiple values from a function
Loop through the key/value pairs in the hash part of a table
Sort an array with table data

Multiple Return Values
In Lua, functions are able to return more than one value in a return statement,
which makes accomplishing some tasks more natural. For example, colors
in World of Warcraft are represented as both hexadecimal values (such as
99CCFF) as well as numeric percentages of red, green, and blue (such as 0.6,
0.8, 1.0). As a result, it can be useful to convert the hexadecimal values (which
are widely used on the web) to the decimal equivalents.

Converting Hex to RGB
An example of a hexadecimal string is FFCC99, where the first two characters
represent the value of the color red as a number between 0 and 255 in
hexadecimal. The second set of characters is the value of green, followed by
blue. The string.sub() function can be used to split the string into its three
77

78

Part I

■

Learning to Program

component color strings, whereas the tonumber() function can convert the
string into a number. If the tonumber() function is called with the red part of
the string “FF“, it won’t return a meaningful result:
> print(tonumber(“FF“))
nil

By default, the tonumber() function expects the number to be a decimal
number (that is, in base-10), so it can’t convert this base-16 number. The
second argument of tonumber() specifies the base of the string that is being
converted. In this case:
> print(tonumber(“FF“, 16))
255

Because the output needs to be a number between 0.0 and 1.0, this value
can be divided by 255 to obtain the percentage value. Add a definition for
ConvertHexToRGB() as follows:
function ConvertHexToRGB(hex)
local red = string.sub(hex, 1, 2)
local green = string.sub(hex, 3, 4)
local blue = string.sub(hex, 5, 6)
red = tonumber(red, 16) / 255
green = tonumber(green, 16) / 255
blue = tonumber(blue, 16) / 255
return red, green, blue
end

Test this function with a few sample values:
> print(ConvertHexToRGB(“FFCC99“))
1, 0.8, 0.6
> print(ConvertHexToRGB(“FFFFFF“))
1, 1, 1
> print(ConvertHexToRGB(“000000“))
0, 0, 0

Assigning Multiple Values
To get the results of a function with multiple return values such as
ConvertHexToRGB(), you can use the following syntax:
var1, var2, var3, var4 = someFunction()

This calls someFunction() and assigns the first return to var1, the second
return to var2, and so on. If there are more returns than variables, the extra
returns are just discarded. In the case that there are more variables than
returns, the remaining variables are set to nil.

Chapter 5

■

Advanced Functions and Control Structures

Missing Return Values?
When you are working with multiple return values, a few odd things can
happen. Look at the following example:
> print(ConvertHexToRGB(“FFFFFF“))
1, 1, 1
> print(ConvertHexToRGB(“FFFFFF“), “SomeOtherArgument“)
1, SomeOtherArgument

Where did the other returns go? They were eaten by the following rule:
When a function call with multiple return values is the last argument to another
function, or the last argument in a multiple assignment expression, all of the
return values are passed or used. Otherwise, only the first return value is used or
assigned.
You can see this behavior with the assignment operator in the following
example:
> a, b, c, d = ConvertHexToRGB(“FFFFFF“), “some“, “more“, “arguments“
> print(a, b, c, d)
1, some, more, arguments

Because the call to ConvertHexToRGB() is followed by additional values,
Lua only uses the first return from the function call. There are a few technical
reasons for this limitation, but it should not affect you very often. The exception
to the rule can be seen in the following example:
> a, b, c, d = “first argument“, ConvertHexToRGB(“FFFFFF“)
> print(a, b, c, d)
first argument, 1, 1, 1

TIP When working with multiple return values, you can always wrap the function
call in parentheses to limit it to a single return value, as follows:
> print((ConvertHexToRGB(“FFFFFF“)))
1

Multiple Return Values in World of Warcraft
Several World of Warcraft API functions return multiple values. For example,
the function GetRaidRosterInfo() takes a character’s raid index (a number)
and returns the following information:
The name of the character
The character’s rank in the raid (leader, assistant, and so on)
What subgroup the character is in

79

80

Part I

■

Learning to Program

The character’s level
The character’s class (localized)
The character’s class (capitalized, in English)
The name of the zone the character is currently in
Whether the character is online
Whether the character is dead
If the character is a main tank or main assist
Whether the character is master looter
This function provides a ton of information, but, typically, when you need
one of the items, you need more than one. In this case, it’s more efficient for
the game client to return each of these items every time the function is queried,
rather than having 11 different API functions.
SELECTING SPECIFIC VALUES
Functions with multiple return values provide a unique set of challenges, such
as how to get at values that aren’t at the start of the return list. There are
two easy ways to do this: using dummy variables and using the select()
statement.
Taking the ConvertHexToRGB() example, how could you extract just the
green value?

Using a Dummy Variable
The function is going to return three results regardless of how you call it, but
you can use dummy variables to throw away the results that aren’t interesting.
For example, you may see something that looks like this:
local _, g = ConvertHexToRGB(“FFFFFF“)

Because the underscore character is a valid identifier, it can be used to store
values, but most sane programs choose more valid variable names. The underscore identifier has become somewhat of a de facto standard when you need
to throw away the result of a function call simply because it’s easy to type,
and most likely not already in use, but its use is still considered bad practice.
Instead of using the underscore as a dummy variable, it’s better to give each
variable a meaningful name, and only use those that are necessary. That way if
you ever need to look at that code in the future, you have a hint of what other
information is available. Some situations can’t be handled using this method,
but Lua provides a utility function to compensate.
(continued)

Chapter 5

■

Advanced Functions and Control Structures

SELECTING SPECIFIC VALUES (continued)

Using the select() Function
The select() function was designed to help solve this problem, by allowing
you to choose a specific argument from a given list. This function takes any
number of arguments, the first of which tells the function what to do. When
select() is passed the “#“ string as the first argument, it simply returns
the number of arguments in the second part of the function. If select() is
passed a number value, it returns that argument from the list, followed by
anything after it. After this initial argument, select() takes any number of
arguments, comma separated.
Confused yet? Look at a few examples:
> print(select(“#“, “alpha“, “beta“, “gamma“))
3
> print(select(1, “alpha“, “beta“, “gamma“))
alpha, beta, gamma
> print(select(2, “alpha“, “beta“, “gamma“))
beta, gamma
> print(select(3, “alpha“, “beta“, “gamma“))
gamma
> third = select(3, “alpha“, “beta“, “gamma“)
> print(third)
gamma

If you just need to get a single value from the list, you can assign it directly
to the variable, or wrap the select() call in parentheses so the extra values
are thrown away.
You may find this function useful when working with some of the longer
World of Warcraft API functions, such as GetRaidRosterInfo(). If you only
need a single return, you can isolate it using a call to select().

Accepting a Variable Number of Arguments
Many functions are designed to take a specific number of arguments, such
as the tonumber() function, which takes a string, and optionally, a number
base for the conversion. Other functions make more sense when they accept
a variable number of arguments. Consider a function that calculates the
arithmetic mean of a set of numbers. A simple version of this function that
works with two arguments might look something like this:
function mean(num1, num2)
return (num1 + num2) / 2
end

81

82

Part I

■

Learning to Program

Unfortunately, if you need to compute the mean of three numbers, you
would need to do it manually, call the function twice, or write a new function
that takes three arguments instead. As you can imagine, this is highly inefficient, and Lua provides an easier way to write these types of functions so they
can accept a variable number of arguments.

Declaring a Vararg Function
Functions with a variable number of arguments are called vararg functions for
short, and they use an ellipsis (three periods) in their function declaration to
indicate they take any number of arguments.
In Lua, the ellipsis can only appear as the last argument in a function
declaration. Whenever the ellipsis is then used in the body of the function,
the arguments that were supplied in the vararg slot are substituted. Take the
print() function, which already accepts a variable number of arguments, and
extend it by running the following code:
function test_print(...)
print(“testing“, ...)
end

This function takes in any number of arguments and then passes them to
the print() function, adding its own text to the start of the list. The output
from running this function looks like this:
> test_print(“alpha“, “beta“, 13, “gamma“)
testing, alpha, beta, 13, gamma

When the function is run and Lua encounters the . . . symbol, it replaces it
with the list of arguments that were passed to the function. As a result, it can
be used in the following ways:
-- Pass the arguments to another function
print(...)
-- Assign the arguments to variables
local var1, var2, var3 = ...
-- Construct a new table with the arguments
local tbl = {...}

The preceding example could be used to make a new function called
newtable(), which takes in a set of arguments and makes a new table with
those arguments in the array part of the table:
function newtable(...)
return {...}
end

Chapter 5

■

Advanced Functions and Control Structures

Test this function now:
> tbl = newtable(“alpha“, “beta“, “gamma“)
> for i=1, #tbl do
>> print(i, tbl[i])
>> end
1, alpha
2, beta
3, gamma

Using select() with ...
The select() function makes working with vararg functions very easy,
because it can provide the number of arguments passed, as well as allow you
to easily iterate through them without needing to assign them to variables.
Consider the following function that takes a list of arguments and prints a line
for each argument including the index and the argument itself:
function printargs(...)
local num_args = select(“#“, ...)
for i=1, num_args do
local arg = select(i, ...)
print(i, arg)
end
end

Sample output:
> printargs(“alpha“, “beta“, 13, “gamma“)
1, alpha
2, beta
3, 13
4, gamma

This method lets you avoid creating a new table every single time, and
allows the value nil to be passed as an argument. Remember that the length
operator and the table library functions are only reliable when the array table
does not contain any ‘‘holes’’ in the form of nil values. Run the following
function definitions in your interpreter:
function test1(...)
local tbl = {...}
for i = 1, #tbl do
print(i, tbl[i])
end
end
function test2(...)
for i = 1, select(“#“, ...) do

83

84

Part I

■

Learning to Program

print(i, (select(i, ...)))
end
end

You can see an example of this issue by running the following:
> test1(“alpha“, “beta“, “gamma“, nil)
1, alpha
2, beta
3, gamma
> test2(“alpha“, “beta“, “gamma“, nil)
1, alpha
2, beta
3, gamma
4, nil

The first example stuffs the arguments into a table and then tries to get the
length of the table. Because there is a nil value in the middle of the table,
getting the length could return either two or four. This sort of unpredictability
is specifically why you should use the second example.
In addition the first function needs to create a new table on each call, which
will allocate and use more memory in the long run. The version using select()
has no such hidden cost.

Generic for Loops and Iterators
Chapter 3 introduced the for statement, which allows you to repeat a computation over a series of numbers by supplying a start value, end value, and a
value by which to increment the counter after each loop. Chapter 4 introduced
storing data in both the array part and the hash part of Lua tables. Until this
point there has been no way to loop through the elements of the hash part
of the table, but Lua provides a more generic form of the for statement that,
when combined with an iterator function, allows just that.
Wikipedia defines an iterator as ‘‘an object which allows a programmer
to traverse through all elements of a collection, regardless of its specific
implementation.’’ In Lua specifically, you use an iterator function along with
some extra information to loop through a collection.

Syntax of Generic for
The generic for loop syntax is a bit different than the numeric for loop:
for val1, val2, val3, ... in  do
-- body of for loop
end

Chapter 5

■

Advanced Functions and Control Structures

A generic for loop can return many variables on each iteration (as many as
defined by the iterator function, actually). Immediately after the for keyword,
you supply a list of variable names that are used to store the returns on each
iteration of the loop. The generic loop then determines what to traverse by
evaluating , which should return the following three values:
An iterator function that can be called on each iteration of the loop
state, used by the iterator function on each subsequent call
An initial value for the iterator value
Luckily, unless you plan to write your own iterator functions, you won’t
have to deal with any of this directly. A number of prewritten functions will
create your iterators for you.

Traversing the Array Part of a Table
ipairs() is one such function that allows you to traverse the array part of
a table without using the numeric for loop. Some programmers prefer this
syntax to that of the numeric for loop. Run the following example:
> tbl = {“alpha“, “beta“, “gamma“}
> for idx, value in ipairs(tbl) do
>> print(idx, value)
>> end
1, alpha
2, beta
3, gamma

The ipairs() function takes a table and returns all the information the for
loop requires to traverse the array part of the table, including the iterator
function itself. Each call to the iterator function returns the numeric index of
the element, and the element itself. These variables can be named whatever
you’d like and, as always, are local to the scope of the for loop (meaning they
cannot be accessed outside of that scope).
You can explore the ipairs() function a bit more by running the following
in your interpreter:
> print(ipairs(tbl))
function: 0x300980, table: 0x3072c0, 0
> print(tbl)
table: 0x3072c0

It appears the ipairs() function returns an iterator function, the state (in this
case it’s just the table you passed in), and the initial value for the iterator (0).
There’s no real magic going on here, just a useful function allowing you to
loop through array tables.

85

86

Part I

■

Learning to Program

Traversing an Entire Table
Another function, called pairs(), allows you to traverse a table in its entirety,
including both the array part and the hash table part. The usage is the same as
ipairs(); just pass it the table and use it as part of a generic for loop:
> tbl = {“alpha“, “beta“, [“one“] = “uno“, [“two“] = “dos“}
> for key, value in pairs(tbl) do
>> print(key, value)
>> end
1, alpha
2, beta
one, uno
two, dos

TRAVERSING USING PAIRS()
In the preceding example, the pairs() function seemed to traverse the
table in the order the elements were added to the table, but this is just a
coincidence. The specific order in which elements will be visited is unspecified
by this function, even for numeric keys. If you specifically need to traverse
the table’s numeric elements in order, you should instead use the ipairs()
function, which can guarantee this. The lack of order when using pairs() is
due to the way hash tables are implemented, as a collection of associated
key/value pairs with no internal order.
When using the pairs() function, you must ensure you don’t add any elements to the table. This is because pairs() calls the next() function, which
carries the following warning in the Lua 5.1 Reference Manual:
‘‘The behavior of next is undefined if, during the traversal, you assign any
value to a nonexistent field in the table. You may, however, modify existing
fields. In particular, you may clear existing fields.’’
If you add an element to the table during the traversal, the iteration may
simply not work, it may terminate early, or it may throw an error. It’s important
to keep this in mind when working with an iteration using pairs().
In addition, you may encounter an error if you try to clear a key that was not
previously set by assigning nil to it. This is due to the way tables are implemented. In general you should ensure you only ever assign to keys that existed
prior to the iteration.

Clearing a Table
As stated in the Lua 5.1 Reference Manual for next(), you can clear the
elements of a table while traversing it using pairs(). The following code will
clear a table of all set elements:
for key, value in pairs(tbl) do

Chapter 5

■

Advanced Functions and Control Structures

tbl[key] = nil
end

Because pairs() works for all keys of a table, this is a quick way to ensure
you’ve cleared all elements (in the event you need to re-use a table, for
example). Note that this is different than just running tbl = {}, which would
create a new table entirely, rather than clearing the existing table. You can see
this by printing the value of the table before and after, and verifying that they
are different:
> tbl = {“alpha“, “beta“, “gamma“}
> print(tbl)
table: 0x131800
> tbl = {}
table: 0x131ac0

Using Other Iterators
A number of other functions in Lua can be used to generate iterators that
are extremely useful. The string.gmatch() function can be used with Lua
pattern matching to create iterators over strings, and specific matches within
that string. You learn more about this function and Lua pattern matching in
Chapter 6, but here are some examples:
> for word in string.gmatch(“These are some words“, “%S+“) do
>> print(word)
>> end
These
are
some
words
> for char in string.gmatch(“Hello!“, “.“) do
>> print(char)
>> end
H
e
l
l
o
!

Sorting an Array of Table Data
The built-in table.sort() function only allows you to sort number and string
data by default. Fortunately, table.sort() enables you to pass in a function to
do the actual comparisons between elements, with the library function doing
the overall sort based on the results of your function. This means you can write
your own function to determine which of two tables is bigger when it comes to
sorting.

87

88

Part I

■

Learning to Program

Define Example Data
For the examples in this section you need some sample data to sort. Define the
following in your Lua interpreter:
guild = {}
table.insert(guild, {
name = “Cladhaire“,
class = “Rogue“,
level = 80,
})
table.insert(guild, {
name = “Draoi“,
class = “Druid“,
level = 80,
})
table.insert(guild, {
name = “Deathsquid“,
class = “Deathknight“,
level = 68,
})

Default Sort Order
By default, this list is sorted in the order it was inserted, because it’s using the
array part of the table. Run the following to verify this:
> for idx, value in ipairs(guild) do
>> print(idx, value.name)
>> end
1, Cladhaire
2, Draoi
3, Deathsquid

Rather than print value itself, which would show table: 0x3003a0 instead
of something meaningful, this code indexes the table and prints the value
associated with the key name. This code segment could be altered to print the
class, or the level if so desired.

Creating a Comparison Function
If you try to sort this data using table.sort(), you will get an error because
Lua doesn’t know how to compare table values (to determine what makes one
table less than another).
> table.sort(guild)
attempt to compare two table values

Chapter 5

■

Advanced Functions and Control Structures

stack traceback:
[C]: in function 'sort'
stdin:1: in main chunk
[C]: ?

The table.sort() function takes a second argument specifically for this
purpose, to allow the programmer to define how values should be compared.
This function takes two arguments, and returns true if the first argument is
less than the second argument, and false if the second argument is less than
or equal to the first argument. That means you can sort two tables based on
their member fields, or some other criteria you specify. Write the following
function, which will compare two of the elements based on name:
function sortNameFunction(a, b)
return a.name < b.name
end

Although the function is extremely short, that’s all that is required to sort the
array by name. Pass this function in as the second argument to table.sort():
> table.sort(guild, sortNameFunction)
> for idx, value in ipairs(guild) do
>> print(idx, value.name)
>> end
1, Cladhaire
2, Deathsquid
3, Draoi

To reverse the sort order, just reverse the order of the comparison (note that
the position of b.name and a.name in the comparison have changed):
function sortNameFunctionDesc(a, b)
return b.name < a.name
end

Sort with this new function:
> table.sort(guild, sortNameFunctionDesc)
> for idx, value in ipairs(guild) do print(idx, value.name) end
1, Draoi
2, Deathsquid
3, Cladhaire

Creating a More Complex Sort Function
Assume you’d like to sort the preceding data by level and then by character
name. You can write a function to sort by level, but there’s no way to tell in

89

90

Part I

■

Learning to Program

what order it will put the two level 80 characters. The following comparison
function accomplishes this more complex sort:
function sortLevelNameAsc(a, b)
if a.level == b.level then
return a.name < b.name
end
return a.level < b.level
end

All that is required is a simple check to see if the two levels are the same,
and if they are, to compare the names of the characters. A sort function can
be as complex as you need, as long as it returns true when the first argument
should be sorted less than the second argument:
> table.sort(guild, sortLevelNameAsc)
> for idx,value in ipairs(guild) do print(idx, value.name, value.level) end
1, Deathsquid, 68
2, Cladhaire, 80
3, Draoi, 80

Summary
This chapter introduced the concepts of vararg functions, generic for loops,
iterators, and sorting complex data in arrays. These concepts are relatively
advanced, but come up often when designing and writing a new addon.

CHAPTER

6
Lua Standard Libraries

Throughout the first part of this book, a number of Lua standard library
functions have been introduced and used in code examples. Although this
book does not cover every single Lua function included in the World of
Warcraft implementation of Lua, this chapter introduces you to the some of
the most prevalent functions that you will need when developing addons.
In addition to the Lua standard libraries, this chapter covers some functions
specific to WoW that aren’t really part of the game API itself. These functions
are grouped at the end of the chapter.

NOTE The details in this chapter cover the parts of the Lua API that are most
relevant to WoW. You can find a full reference for Lua online at
http://lua.org/manual/5.1. This manual is also available in print: Lua 5.1
Reference Manual by R. Ierusalimschy, L. H. de Figueiredo, and W. Celes, Lua.org,
August 2006 (ISBN 85-903798-3-3).
In addition, the chief architect of Lua has written an easy-to-read book about the
Lua programming language that covers these (and more) functions in depth. You
can find a version of this book written for an older version of Lua at
http://lua.org/pil. If reading the older version is confusing, you can find the
second edition at many online bookstores: Programming in Lua (second edition)
by Roberto Ierusalimschy, Lua.org, March 2006 (ISBN 85-903798-2-5).

Each function is this chapter is presented with what is called the function’s
signature. A function signature describes what values are returned by the
function, as well as what arguments are taken by the function. For example,
consider the fictional function foo():
someReturn = foo(arg1, arg2)

91

92

Part I

■

Learning to Program

In this example, the function foo() takes two arguments (arg1 and arg2)
and returns a single value someReturn. These signatures can also indicate
optional arguments, by enclosing them in square brackets:
somereturn = foo(arg1 [, arg2])

This notation indicates that the second argument to foo() is optional. When
you see this, you should consult the description of the function and arguments
to determine the behavior of the function because it varies.

Table Library
The table library provides several functions that allow you to easily add
elements, remove elements, and sort array tables. In addition, a utility function
is provided that works outside of the array part of the table, returning the
maximum numeric index used in the table. The former functions all operate
exclusively on the array part of the table, whereas the latter can be used on
any type of table.

str = table.concat (table [, sep [, i [, j]]])
The table.concat() function concatenates all entries of the array part of
a table, with an optional separator string sep. Given an array where all
elements are strings or numbers, it returns table[i]..sep..table[i+1] ...
sep..table[j]. The default value for sep is the empty string, the default for
i is 1, and the default for j is the length of the table. If i is greater than j, it
returns the empty string.
> tbl = {“alpha“, “beta“,
> print(table.concat(tbl,
alpha:beta:gamma
> print(table.concat(tbl,
alphabeta
> print(table.concat(tbl,
beta
gamma

“gamma“}
“:“))
nil, 1, 2))
“\n“, 2, 3))

This function is an easy way to print the elements of the array part of a
table. As you can see, sep can be any string (including the newline character)
because it’s just concatenated with the entries in the table.

table.insert (table, [pos,] value)
The table.insert() function inserts a new element into the array, optionally
at position pos, shifting other elements up to make space, if necessary. The
default value for pos is n+1, where n is the length of the table. Therefore, a call
of table.insert(t,x) inserts x at the end of table t.

Chapter 6

■

Lua Standard Libraries

> tbl = {“alpha“, “beta“, “gamma“}
> table.insert(tbl, “delta“)
> table.insert(tbl, “epsilon“)
> print(table.concat(tbl, “, “))
alpha, beta, gamma, delta, epsilon
> table.insert(tbl, 3, “zeta“)
> print(table.concat(tbl, “, “))
alpha, beta, zeta, gamma, delta, epsilon

max = table.maxn (table)
The table.maxn() function returns the largest positive numerical index of the
given table, or zero if the table has no positive numerical indices. To do its
job, this function does a linear traversal of the entire table. Unlike most table
functions, table.maxn() considers numerical keys instead of integer keys, so
numerical constants and rational numbers are counted as well.
> tbl = {[1] = “a“, [2] = “b“, [3] = “c“, [26] = “z“}
> print(#tbl)
3
> print(table.maxn(tbl))
26
> tbl[91.32] = true
> print(table.maxn(tbl))
91.32

value = table.remove (table [, pos])
The table.remove() function removes an element from the given table, shifting
down other elements to close the space, if necessary. It returns the value of the
removed element. The default value for pos is n, where n is the length of the
table, so a call table.remove(t) removes the last element of table t.
> tbl = {“alpha“, “beta“, “gamma“, “delta“}
> print(table.remove(tbl))
delta
> print(table.concat(tbl, “, “))
alpha, beta, gamma

table.sort (table [, comp])
The table.sort() function sorts the array part of a table by reordering the
elements within the same table. If comp is given, it must be a function that
receives two table elements and returns true when the first is less than the
second (so that not comp(a[i+1],a[i]) will be true after the sort for all i). If
comp is not given, the standard Lua operator < is used instead.

93

94

Part I

■

Learning to Program

This sort algorithm is not stable, which means that elements considered
equal by the given comparison function may have their order changed by
the sort.
> tbl = {“alpha“, “beta“, “gamma“, “delta“}
> table.sort(tbl)
> print(table.concat(tbl, “, “))
alpha, beta, delta, gamma
> sortFunc = function(a,b) return b < a end
> table.sort(tbl, sortFunc)
> print(table.concat(tbl, “, “))
gamma, delta, beta, alpha

String Utility Functions
Lua provides several utility functions for working with and manipulating
strings. Each of these functions is available as object-oriented method calls,
as well as the library calls themselves. For example, the following two calls
accomplish the same thing:
> str = “This is a string“
> print(string.len(str))
16
> print(str:len())
16

Table 6-1 describes the various utility functions and illustrates their use.
Table 6-1: String Utility Functions
FUNCTION

DESCRIPTION

EXAMPLE(S)

string.len(s)

Receives a string and > print(string.len(“Monkey“))
5
returns its length.
The empty string ““
has length 0.
Embedded zeros are
counted, so
“a\000bc\000“
has length 5.

string.lower(s)

Returns the input
string with all
uppercase letters
changed to
lowercase. All other
characters are left
unchanged. The
definition of what an
uppercase letter is
depends on the
current locale.

> test = “Hello World!“
> print(string.lower(test))
hello world!
> printtest:lower())
hello world!

Chapter 6

■

Lua Standard Libraries

FUNCTION

DESCRIPTION

EXAMPLE(S)

string.rep(s, n)

Returns a string that is > print(string.rep(“Hello“, 3))
the concatenation of n HelloHelloHello
> test = “foo“
copies of the string s.
> print(test:rep(3))
foofoofoo

string.reverse(s) Returns a string that is
the string s reversed.

> print(string.reverse(“Test“))
tseT
> test = “Hello World!“
> print(test:reverse())
!dlroW olleH

string.sub(s, i
[, j])

Returns the substring
of s that starts at i and
continues until j; i and
j may be negative. If j
is absent, it is assumed
to be equal to -1
(which is the same as
the string length). In
particular, the call
string.sub(s,1,j)
returns a prefix of s
with length j, and
string.sub(s, -i)
returns a suffix of s
with length i.

> test = “Hello World“
> print(string.sub(test, 1, 3))
Hel
> print(test:sub(1, -1))
Hello World
> print(test:sub(-3, -1))
rld

string.upper(s)

Receives a string and
returns a copy of this
string with all
lowercase letters
changed to uppercase.
All other characters are
left unchanged. The
definition of what a
lowercase letter is
depends on the
current locale.

> test = “Hello World!“
> print(string.upper(test))
HELLO WORLD!
> print(test:upper())
HELLO WORLD!

Formatting New Strings
Throughout the book, you’ve used the concatenation operator to make new
strings and format longer messages. This code to generate longer strings ends
up being extremely difficult to read, and difficult to maintain. Lua provides a
utility function called string.format(formatstring, ...) that will format a
list of arguments according to a defined format.

95

96

Part I

■

Learning to Program

A format string can contain literal characters and special conversion codes
that are used along with the arguments to create the final result. Conversion
codes begin with a percent sign (%) and contain one of the following specifiers
that indicate what type of data the argument should be treated as:
%c —Takes a number argument and formats it as the ASCII character that

corresponds to the number.
%d, %i —Takes a number argument and formats it as a signed integer.
%o —Takes a number argument and formats it as an octal number.
%u —Takes a number argument and formats it as an unsigned integer.
%x —Takes a number argument and formats it as a hexadecimal number,

using lowercase letters.
%X —Takes a number argument and formats it as a hexadecimal number,

using capital letters.
%e —Takes a number argument and formats it as scientific notation, with

a lowercase e.
%E —Takes a number argument and formats it as scientific notation, with

an uppercase E.
%f —Takes a number argument and formats it as a floating-point number.
%g and %G —Takes a number and formats it according to either %e (or %E
if %G is specified) or %f, depending on which is shortest.
%q —Formats a string so it can safely be read back into a Lua interpreter.
%s —Takes a string and formats it according to the supplied options.

Several options can be used in a conversion specification between the
percent sign and the type specifier. The following options can be included, in
this specific order:
1. Sign specification (either a + or a –) that causes a sign to be printed with
any number. By default, the sign is only printed with negative numbers.
2. A padding character (either a space, or a 0) that will be used when
padding the result to the correct string width. By default, any results will
be padded with spaces to meet the correct width, if specified.
3. An alignment specification that causes the result to be left-justified or
right-justified. The default is right-justification, whereas a – character
will make the result left-justified.
4. A width specification that specifies the minimum width of the resulting
string.
5. A precision specification that dictates how many decimal digits should
be displayed when formatting a floating-point number. When specified for strings, the resulting string will be cut off at this number of
characters.

Chapter 6

■

Lua Standard Libraries

Confused yet? More often than not, you’ll only use a very small subset of
these options, but it’s good to understand the abilities and limitations of the
string formatting system. The examples in Table 6-2 should help clarify the
basics of string formatting.
Table 6-2: Example Format Strings
COMMAND

RESULT

string.format(“%%c: %c“, 83)

%c: S

string.format(“%+d“, 17.0)

+17

string.format(“%05d“, 17)

00017

string.format(“%o“, 17)

21

string.format(“%u“, 3.14)

3

string.format(“%x“, 13)

D

string.format(“%X“, 13)

D

string.format(“%e“, 1000)

1.000000e+03

string.format(“%E“, 1000)

1.000000E+03

string.format(“%6.3f“, 13)

13.000

string.format(“%q“, [[“One“, “Two“]])

“\“One\“, \“Two\““

string.format(“%s“, “monkey“)

monkey

string.format(“%10s“, “monkey“)

monkey

string.format(“%5.3s“, “monkey“)

mon

IN WORLD OF WARCRAFT
WoW includes an extra option for string.format() that allows you to
choose a specific argument from the argument list, rather than having them
in consecutive order. This option is not included in standard Lua 5.1, so you
will need to use one of the interpreters provided on the book’s website
(http://wowprogramming.com/utils) to test this. If you are using the
WoWLua addon, it should work correctly.
To select a specific argument, include the number of the argument, followed
by the dollar sign ($), immediately after the percent sign (%). For example:
> print(string.format(“%2$d, %1$d, %d“, 13, 17))
17, 13, 17

The first type identifier is modified to request the second argument, and the
second identifier consumes the second argument to the format string. When
selecting parameters in this way, you can’t skip any and leave them unused.
(continued)

97

98

Part I

■

Learning to Program

IN WORLD OF WARCRAFT (continued)
If you use parameters 1 and 3, you must also use parameter 2. You can mix
parameter selection and normal type identifiers in the same format string
without any issues.
WoW specifically includes this functionality to provide support for multiple
languages. For example, the following string appears in English:
Cladhaire’s Shadow Word: Pain is removed.

In German, the phrase used in this same situation is:
’Shadow Word: Pain’ von Cladhaire wurde entfernt.

As you can see, the order of the arguments is swapped based on the way
the phrase is constructed for German clients. Without parameter selection,
WoW would have to handle each of these cases specifically, which would get
very messy. Instead, the client uses string.format() along with parameter selection to craft these messages.
The English format string is “%s’s %s is removed.“, and the German format string is “’%2$s’ von %1$s wurde entfernt.“. Rather than maintain
a long list of special messages, format strings are used to make the client
consistent.

Pattern Matching
A common theme you will find when writing addons is the need to match
and parse text supplied by the game against a given pattern. Lua provides a
number of utility functions to accomplish these tasks. These functions can use
patterns to describe what to search for when matching against a given string.

Character Classes
Patterns can use any of the character classes described in Table 6-3. Each class
is designed to match a subset of all characters in a specific way. For example,
the character class %s can be used to match any whitespace character, and %a
can be used to represent any letter.
In addition, with any of the character classes that have a percent sign
followed by a letter, the letter can be changed to uppercase to serve as a
shortcut for the complement to the character class. In other words, %S will
match any character that is not a space, and %A will match any character that
is not a letter.
Take a look at some examples. Given the test string “abc ABC 123 !@# \n
\000 %“, Table 6-4 shows what will be matched by a given pattern.

Chapter 6

■

Lua Standard Libraries

Table 6-3: Character Classes
CLASS

MATCHES

x (where x is not one of the
magic characters
ˆ $()%.[]*+-?)

The character x itself.

. (period, or full stop)

Any character.

%a

Any letter.

%c

Any control character.

%d

Any digit.

%l

Any lowercase letter.

%p

Any punctuation character.

%s

Any space character.

%u

Any uppercase letter.

%w

Any alphanumeric character.

%x

Any hexadecimal digit.

%z

The character with representation 0 (for
example,\000).

%x (where x is any
non-alphanumeric character)

Represents the character x. This is the standard
way to escape the magic characters. Any
punctuation character (even the non-magic
ones) can be preceded by a % when used to
represent itself in a pattern. For example, to
include a percent sign in the resulting string you
would include %% in the format string.

[set]

Any characters included in set, which can be
specified as a range of characters by listing the
range with a hyphen (such as A-Z). All classes
defined in this table may also be used as a part
of set, including the other characters, which just
represent themselves. For example, [%w_]
matches all alphanumeric characters plus the
underscore character, and [0-9%l%-] matches
all digits plus the lowercase letters and the character.

[ ˆ set]

The complement of any set (as previously
defined). Therefore, [ ˆ %s] matches any
non-space character.

99

100

Part I

■

Learning to Program

Table 6-4: Example Patterns
PATTERN

STRING MATCHED

“a“

a

“.“

a

“%a“

a

“%c“

\n

“%d“

1

%l“

a

%p

!

%s

space

%u

A

%w

a

%x

a

%z

\000

%%

%

Pattern Items
Each of the character classes previously defined can be used in the pattern
items described in Table 6-5.
Table 6-5: Using Pattern Items
PATTERN

MATCHES

A single character class
followed by a -

Zero or more repetitions of characters in the class. Unlike *,
these repetition items always match the shortest possible
sequence.

A single character class

Any single character in the class.

A single character class
followed by an *

Zero or more repetitions of a character in the class. These
repetition items always match the longest possible
sequence.

A single character class
followed by a +

One or more repetitions of characters in the class. These
repetition items always match the longest possible
sequence.

A single character class
followed by a ?

Zero or one occurrence of a character in the class. This will
always match one occurrence if it is possible to do so.

Chapter 6

■

Lua Standard Libraries

PATTERN

MATCHES

%n

For n between 1 and 9; matches a substring equal to the
nth captured string (see the section later in this chapter on
captures).

%bxy, where x and y
are two distinct
characters

Strings that start with x, end with y, and where the x and y
are balanced. This means that if you read the string from
left to right, counting +1 for an x and -1 for a y, the ending
y is the first y where the count reaches 0. For instance, the
item %b() matches expressions with balanced parentheses.

These pattern items can be very simple to use when you need to match a
specific part of a string in a very general way. Table 6-6 gives a number of
example patterns and the corresponding matches when run against the string
“abc ABC 123 !@# \n \000 %“.
Table 6-6: Example Patterns
PATTERN

STRING MATCHED

%a

a

%a*

abc

%a+

abc

%a-

no string matched

%a-%s

abc

%a?

a

%ba3

abc ABC 123

Pattern Captures
A pattern can contain sub-patterns enclosed in parentheses, called captures.
When a match succeeds, the part of the pattern enclosed in parentheses is
stored (captured) for future use. Captures are numbered according to the
order of their left parenthesis because they can be nested. For instance, in the
pattern “(a*(.)%w(%s*))“, the part of the string matching “a*(.)%w(%s*)“
is stored as the first capture (with number 1); the character matching “.“ is
captured as number 2, and the part matching “%s*“ has number 3.
Additionally, the empty capture () captures the current string position
(a number). For instance, if you apply the pattern “()aa()“ on the string
“flaaap“, there will be two captures, the number 3 and the number 5.

101

102

Part I

■

Learning to Program

Pattern Anchors
A pattern is quite literally a sequence of characters to be matched. Using ˆ at
the beginning of a pattern can match the beginning of a string, whereas using
$ at the end of a pattern can match the end of a string. When used anywhere
else in the pattern, these strings will match their literal equivalent. Anchors
can be used to make a pattern more explicit.

Pattern Examples
Table 6-7 illustrates a number of common requirements for pattern matching
and shows what that pattern might look like. These are general examples and
may only work in specific cases.
Table 6-7: Example Patterns
REQUIREMENT

PATTERN

Match a non-space token in a string.

“%S+“

Match a string beginning with the text MYADDON:
followed by at least one character, capturing the rest of
the string.

“ ˆ MYADDON:(.+)“

Match a number, optionally with a fractional part after a
decimal point, capturing the entire number. The number
can be positive or negative.

“(%-?%d+%.?%d*)“

Match an assignment in the form xxxx=yyyy, where xxxx
is alphanumeric and yyyy contains no spaces, and
capture each individually.

“(%w+)=(%S+)“

Match a single quoted string, such as 'foo' and 'bar'.

“%b’’“

Match the last nonspace token in a string.

“%S+$“

Pattern Matching Functions
Lua provides four functions that accept pattern strings:
string.gmatch(s, pattern)
string.gsub(s, pattern, repl [, n])
string.match(s, pattern [, init])
string.find(s, pattern [, init [, plain]])

Chapter 6

■

Lua Standard Libraries

These functions are also available as object-oriented method calls on the
string itself, as with the utility functions discussed earlier. Each of them
accomplishes a different task for strings, as you’ll see.

string.gmatch(s, pattern)
The string.gmatch(s, pattern) function returns an iterator function that,
each time it is called, returns the next set of captures from pattern over string s.
If pattern specifies no captures, the entire match is produced in each call.
For example, the following loop iterates over all the words from string s,
printing one per line:
> s = “hello world from Lua“
> for word in string.gmatch(s, “%a+“) do
>> print(word)
>> end
hello
world
from
Lua

And here’s an example that collects all sets of key=value pairs from the
given string into a table:
> t = {}
> s = “from=world, to=Lua“
> for k, v in string.gmatch(s, “(%w+)=(%w+)“) do
>> t[k] = v
>> end
> for k,v in pairs(t) do
>> print(k, v)
>> end
to, Lua
from, world

When working with string.gmatch(), remember that the pattern is
designed to potentially match more than one occurrence, so the pattern
shouldn’t be anchored too heavily (in particular, using the ˆ and $ anchors
would make the preceding example work incorrectly).

string.gsub(s, pattern, repl [, n])
The string.gsub(s, pattern, repl [, n]) function returns a copy of s in
which all (or the first n, if given) occurrences of the pattern have been replaced
by a replacement string specified by repl, which may be a string, a table, or a

103

104

Part I

■

Learning to Program

function. string.gsub() also returns as its second value the total number of
matches that occurred.
If repl is a string, its value is used for replacement. The character % works
as an escape character: any sequence in repl of the form %n, with n between
1 and 9, stands for the value of the nth captured substring (see the following
example). The sequence %0 stands for the whole match. The sequence %% stands
for a single %.
If repl is a table, the table is queried for every match, using the first capture
as the key; if the pattern specifies no captures, the whole match is used as
the key.
If repl is a function, this function is called every time a match occurs, with
all captured substrings passed as arguments, in order. If the pattern specifies
no captures, the whole match is passed as the argument.
If the value returned by the table query or by the function call is a string or
a number, it is used as the replacement string; otherwise, if it is false or nil,
there is no replacement (that is, the original match is kept in the string).
Here are some examples:
> print(string.gsub(“hello world“, “(%w+)“, “%1 %1“))
hello hello world world, 2
> print(string.gsub(“hello world“, “%w+“, “%0 %0“, 1))
hello hello world, 1
> print(string.gsub(“hello Lua“, “(%w+)%s*(%w+)“, “%2 %1“))
Lua hello, 1
> lookupTable = {[“hello“] = “hola“, [“world“] = “mundo“}
> function lookupFunc(pattern)
>> return lookupTable[pattern]
>> end
> print(string.gsub(“hello world“, “(%w+)“, lookupTable))
hola mundo, 2
> print(string.gsub(“hello world“, “(%w+)“, lookupFunc))
hola mundo, 2

string.match(s, pattern [, init])
The string.match(s, pattern [, init]) function looks for the first match of
pattern in the string s. If it finds one, the match returns the captures from the
pattern; otherwise, it returns nil. If pattern specifies no captures, the whole
match is returned. A third, optional numerical argument— init —specifies
where to start the search; its default value is 1 and may be negative.

string.find(s, pattern [, init [, plain]])
The string.find(s, pattern [, init [, plain]]) function looks for the
first match of pattern in the string s. If it finds a match, find returns the indices
of s where this occurrence starts and ends; otherwise, it returns nil. A third,

Chapter 6

■

Lua Standard Libraries

optional numerical argument— init —specifies where to start the search; its
default value is 1 and may be negative. A value of true as a fourth, optional
argument, plain, turns off the pattern matching facilities, so the function
does a plain ‘‘find substring’’ operation, with no characters in pattern being
considered ‘‘magic.’’ Note that if plain is given, init must also be given.
If the pattern has captures, then in a successful match the captured values
are also returned, after the two indices.

Math Library
The math library provides an interface to several standard math functions and
constants. Table 6-8 describes some of the more common functions in the math
library. (It is not a full listing of the library; for that, please consult a proper
Lua reference, which includes a full set of trigonometric functions such as
math.cos, math.sin, math.tan, and so on.)
Table 6-8: Math Functions
FUNCTION

DESCRIPTION

EXAMPLE

math.abs(x)

Returns the absolute
value of x.

> print(math.abs(13))
13
> print(math.abs(-13))
13

math.ceil(x)

Returns the smallest
integer larger than or
equal to x.

> print(math.ceil(1.03))
2
> print(math.cell(13))
13
> print(math.cell(17.99))
18

math.deg(x)

Returns the angle x
(given in radians) in
degrees.

> print(math.deg(math.pi))
180
> print(math.deg(math.pi * 2.5))
450

math.exp(x)

Returns the value of the
mathematical constant
e raised to the x power.

> print(math.exp(27))
532048240601.8

math.floor(x)

Returns the largest
integer smaller than or
equal to x.

> print(math.floor(1.03))
1
> print(math.floor(13.0))
13
> print(math.floor(17.99)
17

Continued

105

106

Part I

■

Learning to Program

Table 6-8: (continued)
FUNCTION

DESCRIPTION

EXAMPLE

math.fmod(x, y)

Returns the remainder
of the division of x by
y, rounding the
quotient toward
zero.

> print(math.fmod(14, 3))
2
> (print(math.fmod(14, 2))
0

math.log(x)

Returns the natural
logarithm of x.

> print(math.log(532048240601.8))
27

math.log10(x)

Returns the base-10
logarithm of x.

> print(math.log10(10 ˆ 2))
2

math.max(x, y,
z, ...)

Returns the maximum
value among its
arguments.

> print(math.max(-13, 7, 32))
32

math.min(x, y,
z, ...)

Returns the minimum
value among its
arguments.

> print(math.min(-13, 7, 32, 17))
-13

math.modf(x)

Returns two numbers,
the integral part of x
and the fractional part
of x.

> print(math.modf(10.23))
10, 0.23
> print(math.modf(7/22))
0, 0.31818181818182)

math.pi

The value of the
mathematical constant
pi.

> print(math.pi)
3.1415926535898

math.pow(x, y)

Returns x raised to the
y power. (You can also
use the expression x ˆ y
to compute this value.)

> print(math.pow(2, 10))
1024
> print(math.pow(2, -10))
0.0009765625

math.rad(x)

Returns the angle x
(given in degrees) in
radians.

> print(math.rad(180))
3.1415926535898
> print(math.rad(180) == math.pi)
true
> print(math.rad(450))
7.8539816339745

math.random([m
[, n]])

Generates
pseudo-random
numbers. The numbers
generated may not be
sufficient for statistical
analysis but provide an
easy way to create
pseudo-randomness in

> print(math.random())
7.8263692594256e-06
> print(math.random(100))
14
> print(math.random(10, 20))
18

Chapter 6

FUNCTION

DESCRIPTION

■

Lua Standard Libraries

EXAMPLE

a program. For
example, this function
can be used along with
the
SendChatMessage()
World of Warcraft API
function to allow your
character to make
random sayings based
on certain events.
When called without
arguments, returns a
pseudo-random real
number between 0 and
1 (not including 1).
When called with a
number m, returns a
pseudo-random integer
between and including
1 and m. When called
with two numbers m
and n, returns a
pseudo-random integer
between and including
m and n.
math.randomseed
(x)

The pseudo-random
number generator used
by Lua takes an initial
seed and generates a
sequence of numbers
based on that seed. As
a result, the same initial
seed will always
produce the same
sequence. This function
has been removed from
the Lua implementation
in World of Warcraft,
but is listed here for
completeness.

> math.randomseed(1000)
> print(math.random(100))
1
> print(math.random(100))
54
> print(math.random(100))
61
> -- reset the seed
> math.randomseed(1000)
> print(math.random(100))
1
> print(math.random(100))
54
> print(math.random(100))
61

math.sqrt(x)

Returns the square root
of x. (You can also use
the expression x ˆ 0.5
to compute this value.)

> print(math.sqrt(169)
13
> print(math.sqrt(2))
1.4142135623731
> print(2 ˆ 0.5)
1.4142135623731

107

108

Part I

■

Learning to Program

NOTE Lua doesn’t include a math.round() function because there are so many
possible variations on what it means to ‘‘round’’ a number. http://lua-users.
org/wiki/SimpleRound shows how to implement the following function, which
rounds a number to a given decimal place:
function round(num, idp)
local mult = 10 ˆ (idp or 0)
return math.floor(num * mult + 0.5) / mult
end

World of Warcraft Additions to Lua
Several functions have been added to the Lua implementation in WoW as
utility functions for developers:
strsplit(sep, str)
strjoin(sep, ...)
strconcat(...)
getglobal(name)
setglobal(name, value)
debugstack([start[, count1[, count2]]])

These functions are available in the WowLua addon, on the WebLua
webpage, and in the interpreters that are available for download via the
book’s companion website. They may not be available in Lua distributions
obtained elsewhere.
strsplit(sep, str) takes a given string str and splits it into separate
strings on each occurrence of any character in the separator string sep. This
function returns each individual string (with separator characters removed) to
the caller.
> print(strsplit(“:“, “foo:bar:blah“))
foo, bar, blah
> print(strsplit(“ “, “This is a string“))
This, is, a, string

The strjoin(sep, ...) function takes a list of strings and concatenates
them together with the separator string sep, returning the result.
> print(strjoin(“ “, “This“, “is“, “a“, “test“, “string“))
This is a test string
> print(strjoin(“, “, “alpha“, “beta“, “gamma“))
alpha, beta, gamma

The strconcat(...) function takes a list of strings and concatenates them
together into one long string, which is returned.
> print(strconcat(“This“, “is“, “a“, “test“))
Thisisatest

Chapter 6

■

Lua Standard Libraries

> print(strconcat(“alpha:“, “beta:“, “gamma“))
alpha:beta:gamma

getglobal(name) takes a variable name as a string and returns the so-named
global variable, if it exists. This function is deprecated in World of Warcraft,
meaning it should no longer be used but has not yet been removed. It is
included, along with setglobal, in case you see it in code from older addons.
> greek1, greek2, greek3 = “alpha“, “beta“, “gamma“
> for i=1,3 do
>> print(getglobal(“greek“ .. i))
>> end
alpha
beta
gamma

The setglobal(name, value) function takes a variable name as a string,
along with a corresponding value, and sets the so-named global variable to
the new value.
> print(myVariable)
nil
> setglobal(“myVariable“, 17)
> print(myVariable)
17

The debugstack([start[, count1[, count2]]]) function returns the current calling stack according to three inputs, as described in Table 6-9.
Table 6-9: debugstack Inputs
INPUT

TYPE

DESCRIPTION

start

Number

The stack depth at which to start the stack trace (defaults to 1,
the function calling debugstack)

count1

Number

The number of functions to output at the top of the stack
(default 12)

count2

Number

The number of functions to output at the bottom of the stack
(default 10)

This function only operates correctly in WoW. The standalone Lua interpreter has its own method of providing stack traces.

Function Aliases
In World of Warcraft, many of the library functions have been given shorter
aliases so they are easier to access and type. Table 6-10 contains a full listing of
these aliases.

109

110

Part I

■

Learning to Program

Table 6-10: Global Aliases
ALIAS

ORIGINAL FUNCTION

ALIAS

ORIGINAL FUNCTION

abs

math.abs

gsub

string.gsub

ceil

math.ceil

strbyte

string.byte

cos

math.cos

strchar

string.char

deg

math.deg

strfind

string.find

exp

math.exp

strlen

string.len

floor

math.floor

strlower

string.lower

frexp

math.frexp

strmatch

string.match

ldexp

math.ldexp

strrep

string.rep

log

math.log

strrev

string.reverse

max

math.max

strsub

string.sub

min

math.min

strupper

string.upper

mod

math.fmod

foreach

table.foreach

rad

math.rad

foreachi

table.foreachi

random

math.random

getn

table.getn

randomseed

math.randomseed

sort

table.sort

sqrt

math.sqrt

tinsert

table.insert

format

string.format

tremove

table.remove

gmatch

string.gmatch

Summary
Lua has three major libraries that contain utility functions. The table library
provides ways to insert, remove, and sort array tables; the string library
has a number of useful utilities for tasks such as turning a string into all
lowercase, uppercase, or even reversing the string. In addition to these utility
functions, this chapter introduced the basics of Lua pattern matching and string
formatting using string.format(), string.match(), and string.find().

CHAPTER

7

Learning XML

As mentioned in Chapter 1, you use two languages to build user interfaces for
World of Warcraft. You have already been introduced to Lua, the programming
language that defines the behavior of the interface, but you haven’t yet tackled
eXtensible Markup Language (XML), used to create the graphical frames that
comprise WoW’s user interface. That’s what this chapter is all about.

XML as a Markup Language
A markup language takes text content and adds extra information to the
document, mixing it in with the text itself. The markup typically describes
something about the text itself, such as the structure of the document or how
the text should be displayed on screen. Following are examples of two notable
markup languages, HTML and LaTeX:
HTML


My Document


Heading One

This text is bold.

111 112 Part I ■ Learning to Program LaTeX \documentclass{article} \title{My Document} \begin{document} \maketitle \section{Heading One} This text is \textbf{bold}. \end{document} Each of these examples provides basic information about the structure of the content by creating new headings and sections, and delimiting the actual body of the document. In addition, the and \textbf{} tags are intermixed with the text to indicate that a specific word should be displayed in a bold face font. XML’s Relationship to HTML Whereas HTML is a markup language describing presentation with a minimal amount of structural information, XML is entirely a structural language, describing the relationship between elements but providing no cues about how they should be presented. Consider this example XML document: Alice Applebaum +1-212-555-1434
114 Auburn Street Apt 14 Atlanta, GA
Unlike the earlier HTML example, this has no presentation cues, and most applications wouldn’t know how to display this information. An XML document typically structures information according to some set of rules (such as a schema definition, which you will explore later this chapter). In short, XML is a cousin of the HTML standard that is generalized for multiple uses, and is stricter in its syntax and structure. Components of XML XML is designed to be both human-readable and computer-readable, so it has a strict required structure. An XML document includes tags, elements, attributes, and entities, each of which is discussed in the following sections. Chapter 7 ■ Learning XML XML Tags An XML tag is an identifier that begins and ends with angle brackets, such as . The tags are case-sensitive, so is a different tag name than . A closing tag is the same as an opening tag, but has a forward slash immediately after the open bracket, such as . The XML standard doesn’t define any specific tags, only the rules defining how and when tags should appear. XML Elements Elements are the lowest level of structure and content in an XML document, taking some content and enclosing it in a set of open/close tags. A basic element from the earlier XML example is the section, which defines an XML element with the name entry. An XML element can contain any type of content, including more markup. Elements are governed by the following rules: A nonempty element must begin with an opening tag and end with a closing tag. An element with no content can either be delimited with start/end tags or be a self-closing tag. A self-closing tag has a forward slash immediately before the closing angle bracket, such as or . Again, the XML standard doesn’t really define any element types or tags, but merely describes how the document should be structured so it conforms to the standard. XML Attributes In addition to containing generic content, each XML element can have any number of attributes, which are named values belonging to that element. An attribute is declared in the start tag (or the self-closing tag, if used) like this: Attributes can have any name, but the XML standard requires that all values be quoted using either balanced single quotes or balanced double quotes. This ensures that any program conforming to the XML standards can parse the document. Unlike an element’s content, which describes more of a parent/child relationship, attributes describe something specific about the element, such as the name of the element. The addressbook element has the name Personal, so it can be distinguished easily from any other addressbook that has been defined. The distinction isn’t made through the XML standard but is extremely useful when parsing and validating an XML document. 113 114 Part I ■ Learning to Program XML Entities The XML specification forbids the ampersand (&) and the less-than sign (<) from appearing within an element. In addition it might be confusing to see single quotes (’), double quotes (“) and the greater than sign (>) in a document. To compensate for this, XML provides a number of escaped entities that can be included in the place of these characters. Table 7-1 shows a list of the most common XML entities: Table 7-1: XML Entities CHARACTER EQUIVALENT ENTITY & & < < > > “ " ’ ' Creating Well-Formed XML A well-formed XML document is one that is valid and parsable from a syntactic point of view; that is, it follows all the required rules defined by the standard. Before jumping into the rules for a well-formed document, look at the definitions of root and non-root elements: Root element: A root element is an element that is not nested within another element. The first element in an XML file is the only root element. Non-root element: An element that is nested within another element. For a document to be well formed, it must comply with the following: Any non-empty elements begin with a start tag and end with an end tag. Empty elements may either be delimited with start and end tags or be marked as a self-closing element. All attribute values are quoted with balanced single or double quotes. Tags may be nested, but must not overlap. In particular, each non-root element must be contained entirely within another element. This disallows something like Some Text, because the element is not contained entirely within another element. Checking the syntax of an XML document can be as simple as opening it in your favorite web browser, although more specialized tools are available. Most modern browsers are XML-capable and can tell you which line of the Chapter 7 ■ Learning XML document failed. In addition, you can use the XMLValidate utility on the book’s web page (http://wowprogramming.com/utils/xmlvalidate.) to see whether your document is well formed. Validating an XML Document The XML format itself describes the syntax of the language—that is, the rules that make an XML document well-formed—but doesn’t delve into the semantics, such as what attributes can belong to a given element, and what relationships can exist between given elements. One method of describing the semantics of a given XML document is a schema definition. These definitions can come in a few forms, such as: Document Type Definition (DTD), a format native to XML. XML Schema, a W3C standard for declaring a schema. RELAX NG, a simple schema language available in XML formats as well as a shorter version. World of Warcraft defines its schema using the XML Schema standard. The following section of the chapter focuses on this standard, and how to read it and use it for validating your files. Example Schema Definition The following is a simple XML Schema definition for an address book: The initial line is standard for declaring a schema; it simply points to the standard document for the W3C definition of the XML Schema definition. The second tag defines a new element named addressbook, creating a new tag, and associating it with the named type AddressBook. The rest of the sequence defines what it means to be of type AddressBook, namely a sequence of four different named elements that is simply string content. 115 116 Part I ■ Learning to Program Example XML Document The following is a file that declares its schema to exist in the file addressbook.xsd. Assuming both files are in the same directory, this file can be validated against the schema directly: Alice Applebaum +1-212-555-1434
114 Auburn Street Apt 14. Atlanta, GA
You can use a number of utilities to validate an XML schema on different platforms: XMLNanny (MacOSX), www.xmlnanny.com Microsoft Visual Studio (Windows), www.microsoft.com/express XMLSpy, www.altova.com/xml-editor Decision Soft’s Online XML Validator, http://tools.decisionsoft.com/ schemaValidate Figure 7-1 shows this XML document being validated against the given schema using XMLNanny. In addition to these downloadable tools there is a very simple web-based validator that you can use at http:// wowprogramming.com/utils/xmlvalidate. The document passes the validation step because it’s been structured correctly and the schema has been followed exactly. As a matter of fact, the example schema requires the elements of to appear in the exact order shown. If you were to swap the order of and , the document would no longer validate. To add the elements in any order, as long as you include them all, you can change the and its matching close tag to read . Exploring the Schema One advantage of a strict markup like XML being used for layout is that all the information necessary to write complex layouts is contained within the schema itself. The schema reveals to you all of the valid options for any given tag or attribute. In addition, a number of tools are available to make it easier for you to edit XML files. Chapter 7 ■ Learning XML Figure 7-1: Validating with XMLNanny For example, XMLSpy, Visual Studio, and other XML editors can provide auto-complete when you’re creating a new file, so attribute names are automatically completed, and some editors even give you dropdowns to select the values when they are defined. XML in World of Warcraft The WoW user interface has an incredibly detailed XML schema that dictates exactly what tags, attributes, and values are valid when defining frames. To better understand how everything is structured, you can unpack the latest XML schema following the directions given in Chapter 8. It will extract to the Blizzard Interface Data (enUS)/FrameXML/UI.xsd file under your WoW installation, where enUS is your locale. Here’s an excerpt from the file: 117 118 Part I ■ Learning to Program use=“required“/> use=“required“/> use=“required“/> default=“1.0“/> This excerpt from the WoW XML schema defines a series of types that are used later in the schema, along with attributes and valid values. The first block defines a new type called ORIENTATION. This value is an enumeration, which means it must be one of the listed values, specifically HORIZONTAL or VERTICAL. The second block defines a new type called ColorFloat, which must be a floating-point number. In this case, it must be between the values 0.0 and 1.0 inclusive. Next, a complex type called ColorType is defined; it has three required attributes and one optional attribute. Any element of this type must supply values for r, g, and b (which must conform to the rules for ColorFloat), and may optionally provide a value for a. These correspond to the red, green, blue, and alpha values of a given color. Finally, a complex type GradientType is defined; it takes exactly two items in sequence, a tag and a tag, both of type ColorType. Additionally, this tag can take an orientation attribute, described earlier. Using a GradientType Assuming there is a tag with the type GradientType defined somewhere, the following would be a valid usage of this schema: Chapter 7 ■ Learning XML When used as part of a texture in the game, this appears as a gradient from red to black, with the gradient traveling vertically. This is exactly how the tag should be used. Exploring Blizzard’s XML User Interface Customization Tool Blizzard has provided us with a tool to extract the XML files that comprise the default user interface. To extract it, you must download the User Interface Customization tool from http://www.worldofwarcraft.com/ui. This website contains versions for Microsoft Windows as well as for Mac OS X. Once you’ve downloaded the file, extract the program and run it. On loading, you’ll see the screen shown in Figure 7-2. You have two options: Install Interface Data—Extracts all of the code that defines the default user interface, the XML schema that defines the markup, as well as two tutorial addons with step-by-step descriptions. Install Interface Art—Extracts all the graphics files that are used in the default interface, such as icons, border textures, and so on. If you choose to extract the interface data, the following two subdirectories will be created in your World of Warcraft directory: Blizzard Interface Data (enUS) Blizzard Interface Tutorial If you extract the interface art, the following subdirectory is created: Blizzard Interface Art (enUS) You may find that your directories extract with a different directory name. The enUS in the example stands for U.S. English, the language that the interface files use. If you use a German WoW client, you may instead see deDE, for example. You learn more about localization a little later in the book. The Blizzard Interface Data directory contains two subdirectories, FrameXML and AddOns. The files contained in FrameXML are loaded each time the client starts, whereas the files in the AddOns directory are loaded under certain circumstances (see the listing in Chapter 1 for more information). 119 120 Part I ■ Learning to Program Figure 7-2: User Interface Customization tool BLIZZARD INTERFACE ART Although Blizzard provides a way to extract the art that is used throughout the game, the graphics files are in a proprietary format called BLP2. Blizzard uses that format for its graphics and, unfortunately, no official tools have been released to support it. Foxlit, an enterprising member of the user interface community, has written a web page that can convert these files on demand, and we have the opportunity to host a version of it on the book’s companion website: http://wowprogramming.com/utils/blp2png. Simply upload a BLP file that you’d like converted, and the web page will return a PNG image that can be saved and edited. Remember, however, that World of Warcraft only loads BLP and TGA files, so you’ll have to convert it to TGA after making any changes. Alternatively, you can browse the contents of the interface art directories online at http://wowprogramming.com/utils/artbrowser. Each of the images are hosted in PNG format for you to view and download. Chapter 7 ■ Learning XML Summary XML is a broad specification that allows virtually endless combinations of schemas and structure, but when dealing with World of Warcraft, you focus on a very particular subset defined by the schema. The default user interface uses XML for all of its frame layout and creation, and you can take advantage of this by using Blizzard’s own code to learn more about the system. 121 Part II Programming in World of Warcraft In This Part Chapter 8: Anatomy of an Addon Chapter 9: Working with Frames, Widgets, and Other Graphical Elements Chapter 10: Saving Time with Frame Templates Chapter 11: Exploring the World of Warcraft API Chapter 12: Interacting with Widgets Chapter 13: Responding to Game Events Chapter 14: Tracking Damage with CombatTracker CHAPTER 8 Anatomy of an Addon As discussed in Chapter 1, an addon for World of Warcraft is a collection of text and media files packaged together and loaded to extend the core functionality of the game client. At the more virtual level an addon is also a collection of functions, tables, frames, textures, and font strings. This chapter explains the contents of an addon’s files, and introduces you to the widgets system and the event-based programming system used in WoW. Exploring an Addon’s Files and Folders An addon consists of a table of contents file that defines certain metadata about an addon (such as name, author, version, and a list of files to be loaded), along with XML frame definitions, Lua scripts, and other media files. This section details the actual contents of these files. Table of Contents (.toc) File The one file that must be included in every addon is the table of contents (TOC) file, which must have the same name as the addon’s directory. For example, if an addon’s directory name is MyAddon, it must contain a file called MyAddon.toc. The TOC file provides vital information about the addon (such as title, description, author, and so on) along with a list of files to be loaded by the game client. A sample .toc file might look like this: ## Interface: 30300 ## Title: My Addon Name 125 126 Part II ■ Programming in World of Warcraft ## Author: My Name Here ## Notes: This is my sample addon MyAddon.xml MyAddon.lua Each line beginning with ## contains a definition of some sort of metadata. For example, the ## Title metadata is displayed on the addon selection screen, and ## Notes contains a longer description that is displayed when you mouse over the addon in that list. The lines after the directives are simply a list of files to be loaded by the addon. ## Interface: ## Interface: 30300 The interface version directive (## Interface: 30300 in this example) provides a basic versioning mechanism that the client uses for the addon selection screen. The game client uses this number to verify that an addon is compatible with the current game version. If the version is not compatible, the game will label it with one of two states: Out of date—This state indicates that there has been a patch to the game client since the addon was written. This is strictly just a warning; the addons may work just fine if you check ‘‘Load out of date AddOns’’ at the top of the screen. The version number typically only changes when there is an actual change to the API, so this warning should be heeded. Incompatible—When a major change happens to the game client (such as an expansion pack), the addon selection screen will display this status and will refuse to load the addon. A new version of the addon should be downloaded to ensure it operates correctly in the new API. Figure 8-1 shows two addons, one flagged as out of date and the other as incompatible. TinyPad could be loaded by checking the Load out of date AddOns checkbox, but nothing can force the incompatible addon to load. Figure 8-1: Addon selection screen Just because an addon is listed as out of date doesn’t mean there’s anything particularly wrong with it, only that the game client has been patched since the Chapter 8 ■ Anatomy of an Addon .toc file was last updated. When that happens, it’s a good reminder to update your addons and make sure you’re using the latest versions. This helps you get the latest bug fixes and features, and also makes it easier for the author of the addon to support you. The interface number is generally built from the version number of the WoW client. For example, the interface number for the 3.3.0 client is 30300. However, this does not necessarily change each time there is a patch. If after a WoW patch you’re not sure what interface number to use in building your own addons, you can extract the latest FrameXML files using the User Interface Customization Tool introduced in Chapter 7 and consult the FrameXML.toc file. ADDON SELECTION SCREEN You can access the addon selection screen by clicking the AddOns button at the bottom-left of the character selection screen. This button appears when you have downloaded and installed an addon in the appropriate place. From the selection screen, addons can be enabled and disabled on a per-character or global basis. The global settings work only for a single server, so if your characters are on different servers, you will need to configure them independently. The addon selection screen can be used to browse the addons that are available on a given system, as well as any dependencies they may have. When things go wrong with an addon, checking the addon selection screen to ensure the addon isn’t flagged as ‘‘Out of date’’ or ‘‘Incompatible’’ is a good place to start to ensure the addon is actually being loaded. ## Title: ## Title: Hello Friend When addons are listed in the addon selection screen, they are sorted and shown by their ## Title directive, rather than by the name of the addon’s TOC file or directory. This can be somewhat confusing as you try to determine which directory corresponds to which addon title in game, but these problems are relatively infrequent and easy to resolve. The default value for this option is the name of the addon’s directory. The ## Title directive can be localized, meaning it can display different text depending on which language the user’s client is set to display. To localize it for Spanish language users, for instance, you’d add a hyphen followed by a locale code, such as ## Title-esES: Hola Amigo. When your addon is loaded on a WoW client with that locale, the custom name will be displayed instead of the generic one supplied in the ## Title directive. Localization of addons is covered in more depth later in this chapter. 127 128 Part II ■ Programming in World of Warcraft ## Notes: ## Notes: Greet other players The ## Notes directive gives you the capability to provide a longer description of your addon. This field can also be localized to provide a different description depending on client locale in the same way as ## Title, and may also contain color codes to highlight portions of the text. Figure 8-2 shows the tooltip displayed by the WoWLua addon .toc file. Figure 8-2: WowLua tooltip, generated from ## Title and ## Notes directives ## Dependencies:, ## RequiredDeps: ## Dependencies: Juggernaut, Alpha ## RequiredDeps: Juggernaut, Alpha Occasionally, one addon requires another to be loaded in order to function. For example, certain addons are organized into individual addon plugins, all requiring one central addon. To express this, you give the ## Dependencies or ## RequiredDeps directive a list of comma-separated addon names. The game client will load all required dependencies of an addon before trying to load the addon itself. When an addon is missing a required dependency, or the dependency addon has been disabled, an error message is displayed, as shown in Figures 8-3 and 8-4. You can move your mouse over the addon name to view a list of dependencies and see which ones are missing. Figure 8-3: Addon with dependency disabled Figure 8-4: Addon with dependency missing Chapter 8 ■ Anatomy of an Addon Dependencies also ensure addons are loaded in the proper order, so if Beta relies on Alpha, the client will load Alpha before it loads Beta. This is even true for a long chain of dependencies. ## Dependencies and ## RequiredDeps both work the same for this directive. Addons should obviously try to avoid circular dependencies, because no addon will ever be loaded in that case. ## OptionalDeps: ## OptionalDeps: Juggernaut, Alpha When an addon can interact with another addon, but doesn’t strictly require it to function, it can be listed as an optional dependency using the ## OptionalDeps directive. All this directive does is ensure that the optional dependencies are loaded before this addon, if they are available. This directive takes a comma-separated list of addon names. The names listed must match the .toc file and the directory names of the given addons. ## LoadOnDemand: ## LoadOnDemand: 1 As mentioned in Chapter 1, each of the Blizzard addons is configured to load on demand, meaning that the client will load and initialize the addon in response to some game event. This saves memory and load time by not loading all of the addons each time the player logs in to the game, but only when he needs them. Because not all addons may be written in a way that supports load on demand (LoD), there is a directive that flags an addon as LoD capable. An LoD addon can still use the other directives, and still appears in the addon list, but will not be loaded until explicitly requested by another addon. Many addons use this functionality for their configuration systems, only loading them when the user tries to make a configuration change. This option takes either a 1 or a 0, where 1 means the addon is LoD capable, and 0 means it is not. If this value isn’t supplied in the TOC, it defaults to 0. ## LoadsWith: ## LoadsWith: Blizzard_RaidUI The ## LoadsWith directive can be combined with ## LoadOnDemand to load an addon as soon as another is being loaded. For example, an addon that alters the default Blizzard Raid UI could include ## LoadsWith: Blizzard_RaidUI to be loaded along with the default raid interface. This directive has rather limited use but expands the usefulness of LoD components quite a bit. If 129 130 Part II ■ Programming in World of Warcraft multiple addons are listed, the addon will be loaded as soon as any of those listed finishes loading. ## DefaultState: ## DefaultState: disabled Not all addons are meant to be loaded on each and every character, so this directive enables you to set the default state of an addon. The flag tells the client whether an addon should be checked (enabled) in the addon selection screen by default. As soon as a user overrides this setting by checking or unchecking the addon, the user preference is respected. If not supplied, this value defaults to enabled. ## LoadManager: ## LoadManager: AddonLoader Adding to the complexity (and versatility) of the LoD system is the ## LoadManager directive, which indicates that some other addon will take responsibility for loading this addon. The addon is flagged as LoD as long as the load manager addon is installed and enabled. The most prevalent load manager is called AddonLoader and is available from a few different locations, including: http://wowace.com/projects/addon-loader http://wowinterface.com/downloads/info11476-r77-release.html AddonLoader is used by several addons as a LoadManager. The developer can provide conditions in the TOC that AddonLoader then uses to decide when the addon should be loaded. For example, an addon that is specific to Rogues can be flagged with ## X-LoadOn-Class: Rogue, and it will be loaded for any rogue characters but not for any others. This method requires the developer to add these flags to the TOC file and the user to download AddonLoader, but it provides major benefits when used correctly. You can find documentation about using AddonLoader online at http://www.wowwiki.com/AddonLoader. ## SavedVariables: ## SavedVariables: JuggernautDB The only way an addon can save information between sessions is to define a Lua variable and list the name of the variable in the ## SavedVariables directive in its TOC file. This tells the game to save the contents of that variable out to a file when the game is closed, and read it back in when the game is started up again. The variable can be a string, number, Boolean, or table. Chapter 8 ■ Anatomy of an Addon ## SavedVariablesPerCharacter: ## SavedVariablesPerCharacter: JuggernautDB The ## SavedVariablesPerCharacter: VariableName directive operates in the same way as ## SavedVariables:, except a different file is saved and loaded for each character you log in with. If you log in to character Alice, her settings will be saved separately from those for Bob. Nothing special needs to happen in the addon; it’s all handled automatically by the client. NONSTANDARD METADATA DIRECTIVES Beyond the officially supported metadata tags, you may see any number of other tags included in the .toc file of custom addons. One customary directive is ## Author. This information isn’t displayed by default client, but can be accessed by other addons in-game. ## Author: ArgyleSocks X-Label Directives In addition, custom directives can be defined with an X, followed by a hyphen and then some label. These directives can contain any string of data, limited to roughly 1,000 characters. For example, an addon could include a web address using an ## X-Website directive. Each of the X label directives is localized by the game client, so you can include all of the following and only the correct version will be available through the GetAddOnMetadata() API function: ## X-FAQ-Website: http://www.myaddon.com/faq/ ## X-FAQ-Website-esES: http://www.myaddon.com/faq/esES ## X-FAQ-Website-deDE: http://www.myaddon.com/faq/deDE Addon Categories The addon community has developed a standard set of addon categories that can be included in the metadata for an addon, making it easier to group similar types of addons together when listing or displaying them. Here’s the list of categories: Action Bars Auction Audio Battlegrounds/PvP Frame Modification Guild Healer Hunter Priest Quest Raid Rogue (continued) 131 132 Part II ■ Programming in World of Warcraft NONSTANDARD METADATA DIRECTIVES (continued) Buffs Caster Chat/Communication Combat Compilations Data Export Development Tools Druid Interface Enhancements Inventory Library Mage Mail Map Miscellaneous Paladin Shaman Tank Tradeskill UnitFrame Warlock Warrior You could use one of these categories or define your own set — that’s the beauty of addon metadata. To supply your category, simply use the ## X-Category: CategoryName directive. XML Files HelloFriend.xml A table of contents file can list any number of XML files to be loaded. Markup in these files will be validated against the WoW UI XML schema file as it’s parsed and loaded. XML files can also load Lua scripts using the

Navigation menu