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 .
Page Count: 1443 [warning: Documents this large are best viewed by clicking the View PDF Link!]
- World of Warcraft® Programming, Second Edition
- About the Authors
- About the Technical Editors
- Credits
- Acknowledgments
- Contents at a Glance
- Contents
- Introduction
- Part I: Learning to Program
- Part II: Programming in World of Warcraft
- 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
- Part III: Advanced Addon Techniques
- Chapter 15: Taking Action with Secure Templates
- Chapter 16: Binding Keys and Clicks to Addon Code
- Chapter 17: Creating Slash Commands
- Chapter 18: Responding to Graphic Updates with OnUpdate
- Chapter 19: Altering Existing Behavior with Function Hooking
- Chapter 20: Creating Custom Graphics
- Chapter 21: Responding to the Combat Log and Threat Information
- Chapter 22: Creating Scroll Frames
- Chapter 23: Creating Dropdown Menus
- Chapter 24: Scanning and Constructing Tooltips
- Chapter 25: Taking Protected Action in Combat
- Chapter 26: Creating Unit Frames with Group Templates
- Part IV: Reference
- Part V: Appendixes
- Index
James Whitehead II
Rick Roe
A Guide and Reference for Creating WoW Addons
Whitehead • Roe
$49.99 US • $59.99 CAN
Computer Graphics / Game Programming
Visit our Web site at www.wiley.com/compbooks
Front cover art © Scott Johnson/FrogPants Studios
®
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, fi lter, and sort your inventory by writing BagBuddy,
a fully functional addon
• Create slash commands, custom graphics, scroll frames, and
dropdown menus
Who are you?
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
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.
®
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
10987654321
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 Devel-
oper 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
Technical Editors
Daniel Stephens
Rick Roe
Esteban Santana Santana
Production Editor
Rebecca Anderson
Copy Editor
Kim Cofer
Editorial Director
Robyn B. Siesky
Editorial Manager
Mary Beth Wakefield
Associate Director of Marketing
David Mayhew
Production Manager
Tim Tate
Vice President and Executive
Group Publisher
Richard Swadley
Vice President and Executive
Publisher
Barry Pruett
Associate Publisher
Jim Minatel
Project Coordinator, Cover
Lynsey Stanford
Proofreaders
Josh Chase and Nelson Kim, Word
One
Indexer
J&JIndexing
Cover Image
Scott Johnson, FrogPants Studios
LLC
Cover Designer
Michael E. Trent
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 satisfymynitpickytendencies.Thanksalso
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,andBrad,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 Advanced Addon Techniques 283
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 Reference 537
Chapter 27 API Reference 539
Chapter 28 API Categories 1025
Chapter 29 Widget Reference 1121
Chapter 30 Events Reference 1277
Part V Appendixes 1303
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 3
Customizing the User Interface 3
What Is an Addon? 4
What Can Addons Do? 4
Exploring Your AddOns Directory 7
Blizzard Addons 8
Custom Addons 10
Creating Your First Addon: HeyThere 10
Creating Files and Directories 10
Loading and Testing the Addon 11
Summary 12
Chapter 2 Exploring Lua Basics 13
Downloading and Installing Lua 14
Downloading and Installing WowLua 14
Using Lua on the Web 15
Downloading and Installing a Lua Interpreter 16
Microsoft Windows 16
Mac OS X 16
Using the Lua Interpreter 17
Running Commands 18
Understanding Error Messages 18
xiii
xiv Contents
Using History to Make Changes 19
Quitting the Interpreter 19
Microsoft Windows 19
Mac OS X 19
Working with Numbers 20
Basic Arithmetic Operations 20
Scientific Notation 21
Hexadecimal Notation 21
Understanding Floating Point 22
Understanding Values and Variables 23
Exploring Values and Their Types 23
Primitive Types 23
Using the type() Function 23
Using Variables 24
Valid Variable Names 25
Assigning Variables 25
Assigning Multiple Variables 26
Comparing Values 26
Working with Strings 27
Comparing Strings 27
Concatenating Multiple Strings 28
Converting Numbers to Strings 28
Converting Strings to Numbers 29
Quoting Strings 29
Single Quote (’) 29
Double Quote (’’) 30
Bracket Quote ([[ ]]) 30
Escaping Special Characters 31
Getting a String’s Length 32
Boolean Values and Operators 33
Using the and Operator 33
Using the or Operator 34
Negation Using the not Operator 34
Understanding the nil Value 35
Exploring Scope 35
Blocks 36
Chunks 37
Summary 37
Chapter 3 Basic Functions and Control Structures 39
Using Functions 39
Creating a Function 39
Local Functions 40
Contents xv
Function Arguments and Returns 41
Converting Celsius to Fahrenheit 41
Empty Arguments 42
No Return Values 42
FunctionsasLuaValues 42
Making Decisions with the if Statement 43
Simple Conditionals 43
Complex Expressions 44
Extended Conditionals 44
Displaying a Personalized Greeting 45
Repeating Actions with the while Statement 46
Computing Factorials 46
Differences Between while and repeat 47
Looping with the Numeric for Statement 48
Computing Factorials 50
Evaluation of Loop Conditions 50
Variable Scope in for Loops 50
Summary 51
Chapter 4 Working with Tables 53
Storing Data Using Tables 53
Creating and Indexing Tables 54
Clearing an Element from a Table 54
Shortcuts for String Keys 55
Creating Populated Tables 55
Using Tables as Arrays 56
Creating an Array 57
Getting the Length of an Array 57
Adding Elements to an Array 58
Removing Elements from an Array 60
Sorting the Elements of an Array 61
Using Tables as Namespaces 61
Creating a Namespace of Utility Functions 62
Adding Functions to a Namespace 62
Storing an Existing Function 62
Defining a New Function 63
Object-Oriented Programming with Tables 63
Creating a Non-Object-Oriented Counter 63
Using Tables as Simple Objects 64
Using : to Call Object Methods 65
Defining Functions Using : 66
Making a Better Counter 67
xvi Contents
Extending Tables with Metatables 68
Adding a Metatable 68
Defining Metamethods 69
Defining Basic Arithmetic Using ___add, ___sub, ___mul,
and ___div 70
Defining Negation Using ___unm 71
Creating Meaningful Output with ___tostring 71
Concatenating Tables Using ___concat 72
Exploring Fallback Tables with ___index 72
Catching Creation of Keys with ___newindex 74
Bypassing Metatables 75
value = rawget(tbl, key) 75
rawset(tbl, key, value) 76
Summary 76
Chapter 5 Advanced Functions and Control Structures 77
Multiple Return Values 77
Converting Hex to RGB 77
Assigning Multiple Values 78
Missing Return Values? 79
Multiple Return Values in World of Warcraft 79
Using a Dummy Variable 80
Using the select() Function 81
Accepting a Variable Number of Arguments 81
Declaring a Vararg Function 82
Using select() with ... 83
Generic for Loops and Iterators 84
Syntax of Generic for 84
Traversing the Array Part of a Table 85
Traversing an Entire Table 86
Clearing a Table 86
Using Other Iterators 87
Sorting an Array of Table Data 87
Define Example Data 88
Default Sort Order 88
Creating a Comparison Function 88
Creating a More Complex Sort Function 89
Summary 90
Chapter 6 Lua Standard Libraries 91
Table Library 92
str = table.concat (table [, sep [, i [, j]]]) 92
table.insert (table, [pos,] value) 92
max = table.maxn (table) 93
Contents xvii
value = table.remove (table [, pos]) 93
table.sort (table [, comp]) 93
String Utility Functions 94
Formatting New Strings 95
Pattern Matching 98
Character Classes 98
Pattern Items 100
Pattern Captures 101
Pattern Anchors 102
Pattern Examples 102
Pattern Matching Functions 102
string.gmatch(s, pattern) 103
string.gsub(s, pattern, repl [, n]) 103
string.match(s, pattern [, init]) 104
string.find(s, pattern [, init [, plain]]) 104
Math Library 105
World of Warcraft Additions to Lua 108
Function Aliases 109
Summary 110
Chapter 7 Learning XML 111
XML as a Markup Language 111
XML’s Relationship to HTML 112
Components of XML 112
XML Tags 113
XML Elements 113
XML Attributes 113
XML Entities 114
Creating Well-Formed XML 114
Validating an XML Document 115
Example Schema Definition 115
Example XML Document 116
Exploring the Schema 116
XML in World of Warcraft 117
Using a GradientType 118
Exploring Blizzard’s XML User Interface Customization Tool 119
Summary 121
Part II Programming in World of Warcraft 123
Chapter 8 Anatomy of an Addon 125
Exploring an Addon’s Files and Folders 125
Table of Contents (.toc) File 125
## Interface: 126
xviii Contents
## Title: 127
## Notes: 128
## Dependencies:, ## RequiredDeps: 128
## OptionalDeps: 129
## LoadOnDemand: 129
## LoadsWith: 129
## DefaultState: 130
## LoadManager: 130
## SavedVariables: 130
## SavedVariablesPerCharacter: 131
X-Label Directives 131
Addon Categories 131
XML Files 132
Lua Script Files 133
Media Files 133
Music 133
Graphics 133
Localizing Your Addons 134
Valid Locales 135
Reasons for Providing Localization 135
Encouraging Users to Contribute 136
Implementing Localization 136
Add a File for Each Locale 136
Create a Global Table Containing the Base Strings 136
Using the Localization Table 137
Adding New Locales 137
Handling Partial Translations 138
Introducing Frames, Widget Scripts, and Events 138
Frames, FontStrings, and Textures 138
Displaying Text with FontStrings 139
Showing Graphics and Colors with Textures 139
Anchoring Objects On-Screen 139
Responding to Interaction with Widget Scripts 139
Responding to Game Events 139
Loading of an Addon 141
Summary 142
Chapter 9 Working with Frames, Widgets, and Other Graphical
Elements 143
Introducing BagBuddy 143
Creating an Addon Skeleton 144
Creating a Frame 144
Parenting 145
Contents xix
Giving Objects Sizes 146
Absolute Dimensions 146
Relative Dimensions 146
Anchoring Objects 147
Sticky Anchors 148
SetAllPoints 148
Anchor Examples 148
Using Lua to Create Frames 149
Adding Layers of Textures and Font Strings 150
Layering Frames and Graphics 150
Frame Strata 150
Frame Levels 151
Graphical Layers 152
BagBuddy Frame Design 153
Finding Graphics 155
TexBrowser AddOn 155
ArtBrowser on Wowprogramming.com 155
Adding Textures 155
Defining BagBuddy’s Background Textures 157
Coloring Textures 158
Using Solid Colors 158
Creating a Gradient 159
Adding the Portrait Texture 160
Creating Textures in Lua 162
Creating Text using FontStrings 164
Further Customization 165
Using Font Definitions 165
Creating FontStrings in Lua 166
Understanding Object Visibility 166
Finding Existing Frames 167
Summary 167
The Code 168
Chapter 10 Saving Time with Frame Templates 171
Understanding Templates 171
Advantages of Using Templates 173
Naming Elements Using $parent 173
Setting Keys Using the parentKey Attribute 174
Creating a Template for BagBuddy’s Item Buttons 174
Setting Button Textures 175
Creating New Frames with Your Template 176
Exploring Font Definitions 177
Altering a Font Definition 178
xx Contents
Investigating UIPanelTemplates 179
UIPanelButtonTemplate 180
UIPanelCloseButton 180
UIPanelScrollBarTemplate 181
InputBoxTemplate 181
UICheckButtonTemplate 182
TabButtonTemplate 183
UIRadioButtonTemplate 183
Summary 183
The Code 184
Chapter 11 Exploring the World of Warcraft API 187
Understanding the WoW API 187
Normal APIs 188
Library-like APIs 188
FrameXML Functions 189
Protected Functions 189
Unit Functions Up Close 190
Querying Item Information for BagBuddy 193
Scanning Bags with the Container API 193
Querying Detailed Item Information 194
Item Identifiers 195
Using the Item API 197
Writing a Bag Scanner 198
Sorting the Player’s Inventory 199
Displaying the Inventory 199
Testing the Update Function 200
Finding the Right API Functions 201
Exploring the API Categories 201
Examining the FrameXML Code 202
Looking at Another Addon 203
Asking for Help! 203
Summary 203
The Code 204
Chapter 12 Interacting with Widgets 207
Making BagBuddy’s Buttons Interactive 207
Setting Frame Scripts via XML 208
Using the function Attribute 209
Setting Frame Scripts Using Lua 209
Showing Item Tooltips Using OnEnter and OnLeave 210
Adding Clickable Buttons to BagBuddy 212
Introducing the OnClick Handler 212
Contents xxi
Creating a Close Button Using Templates 213
Creating Clickable Filter Buttons 214
Creating the Filter Buttons Dynamically 216
Adding Custom Tooltips 217
Making the Filter Buttons Clickable 217
Updating the Results 218
Navigating Multiple Pages 219
Adding XML Definitions for Buttons and Status Text 220
Writing OnClick Handlers for Navigation Buttons 221
Altering the Update Function for Pages 221
Enabling and Disabling Navigation Buttons 222
Creating and Updating Status Text 223
Final Changes to Support Navigation 224
Adding a Name Filter to BagBuddy 224
Creating an EditBox 225
Filtering by Name 226
Exploring Widget Types 227
Button 227
CheckButton 228
ColorSelect 228
EditBox 229
GameTooltip 229
MessageFrame 229
Minimap 229
Model 230
ScrollingMessageFrame 231
ScrollFrame 231
SimpleHTML 231
Slider 232
StatusBar 232
Summary 233
The Code 233
Chapter 13 Responding to Game Events 243
Understanding Events 243
Registering for Events 244
Responding to Events with OnEvent 244
Query Events 246
Tracking Changes to Inventory for BagBuddy 246
Examining the BAG_UPDATE Event 246
Tracking New Inventory Items 246
Writing a New Sorting Function 248
Altering BagBuddy_Update 248
xxii Contents
Adding an OnEvent Handler 249
Cleaning Up 250
Adding a Slash Command 251
Storing Data with SavedVariables 251
Registering a New Saved Variable 252
Saved Variables and ADDON_LOADED 252
Using Items from BagBuddy 253
Finding the Right Event Using /eventtrace 254
Summary 255
The Code 255
Chapter 14 Tracking Damage with CombatTracker 267
Defining Specifications 267
CombatTracker User Experience 267
Finding the Right Game Events 268
PLAYER_REGEN_DISABLED 268
PLAYER_REGEN_ENABLED 268
UNIT_COMBAT 269
Creating the Addon’s Skeleton 269
Defining CombatTracker’s XML Frame 270
Defining a Backdrop 271
Adding a Font String 272
Testing CombatTrackerFrame 272
Adding Script Handlers to CombatTrackerFrame 273
Adding Functions to CombatTracker.lua 275
CombatTracker_OnLoad(frame) 275
CombatTracker_OnEvent 276
PLAYER_REGEN_ENABLED 276
PLAYER_REGEN_DISABLED 277
UNIT_COMBAT 277
CombatTracker_UpdateText() 277
CombatTracker_ReportDPS() 278
Testing CombatTracker 278
Frame Dragging 279
Right-Click Reporting: Part I 279
Testing Combat Tracking 280
Right-Click Reporting: Part II 280
Summary 281
Part III Advanced Addon Techniques 283
Chapter 15 Taking Action with Secure Templates 285
Why Are Secure Templates Necessary? 285
Protected Frames 286
Controlling Secure Frames Using Attributes 288
Contents xxiii
Using Secure Templates 288
Defining Behaviors for Action Buttons 289
Casting a Spell 289
Looking Under the Hood 290
Specifying Units to Affect 291
Other Types and Their Uses 291
Making Simple Choices 296
Working with Modified Attributes 296
Delegating Attribute Responsibility 298
Choosing an Action by Hostility 298
Applying Action Buttons in Practice 299
Modifying an Existing Frame 299
A Complex Action Button 300
Understanding Taint and Working Safely Around Secure
Code 302
Enabling Taint Logging 303
Execution Taint 304
Variable Taint 305
Creeping Taint 307
Summary 308
Chapter 16 Binding Keys and Clicks to Addon Code 309
Defining Bindings in XML 310
Creating Your Own Binding Actions 312
Binding Keys to Actions 314
Building a Simple Binding UI 315
Defining Basic Behaviors 318
Using SetBinding() 321
Working with Existing Bindings 324
Displaying an Action’s Bindings 325
Understanding Binding Storage 326
Binding Keys to Secure Actions 327
Working with Click Bindings 328
Creating Secure Bindings in XML 329
Summary 329
The Code 330
BindingTest 330
ClickBindingTest 334
Chapter 17 Creating Slash Commands 337
Creating Basic Slash Commands 337
Tokenizing Strings 339
Tokenizing with Patterns 341
Setting Up the Patterns 341
xxiv Contents
Preparing for the Tokenization 342
Parsing the Formula 343
Using a Command Table 345
Summary 347
The Code 347
SlashCalc 347
Chapter 18 Responding to Graphic Updates with OnUpdate 351
Understanding Graphic Updates 351
Delaying Code Using OnUpdate 352
Grouping Events to Avoid Over-Processing 354
Grouping Multiple Events 355
Repeating Code with OnUpdate 356
Considering Performance with OnUpdate Scripts 357
Summary 357
Chapter 19 Altering Existing Behavior with Function Hooking 359
What Is Function Hooking? 359
Modifying Return Values 360
Using a Variable Argument Function 361
Using Utility Functions capture() and release() 361
Hooking Widget Scripts 362
Hooking a Function Securely 364
Hooking Scripts Securely 365
Deciding When to Hook 365
Understanding the Hook Chain 365
You Can’t Rely on Order 366
There Is No ‘‘Unhook’’ 366
Hooking Hits Performance 366
Finding Alternatives 367
Designing an Addon: MapZoomOut 367
Creating a Timer Frame 368
Initial Setup 368
Create the Function Hook 369
Writing the Timer Code 369
Final Setup 370
Testing MapZoomOut 370
Summary 370
The Code 371
MapZoomOut 371
Chapter 20 Creating Custom Graphics 373
Common Rules for Creating Graphics 373
The GIMP 374
Contents xxv
Create a New Image 374
Adding Graphical Components 375
Saving Textures 376
Adobe Photoshop 376
Create a New Image 376
Adding Graphical Components 377
Creating an Alpha Channel 377
Saving an Image 378
Paint Shop Pro 379
Creating a New Image 380
Adding Graphical Components 380
Creating an Alpha Channel 381
Saving an Image 382
Testing Your Texture 383
No Button Appears 384
A Green Box Appears 384
XML Texture Definition 384
Lua Texture Definition 385
Summary 385
Chapter 21 Responding to the Combat Log and Threat Information 387
Understanding the Combat Log 387
Event Arguments 387
Combat Sub-Events 388
Combat Event Prefix 389
Bit Fields and Spell Schools 389
Combat Event Suffix 390
Spell-Only Suffixes 393
Special Combat Events 395
Unit GUIDs 396
Format of GUIDs 397
Unit Flags 398
COMBATLOG_OBJECT_TYPE_MASK 398
COMBATLOG_OBJECT_CONTROL_MASK 398
COMBATLOG_OBJECT_REACTION_MASK 399
COMBATLOG_OBJECT_AFFILIATION_MASK 399
COMBATLOG_OBJECT_SPECIAL_MASK 399
Using CombatLog_Object_IsA 400
Writing CombatStatus 401
Creating the Basic Addon Structure 401
Initializing CombatStatus 402
Updating Pet Mappings 405
Storing Damage and Healing Information 405
Taking ‘‘Snapshots’’ of Damage and Healing 407
xxvi Contents
Writing an OnUpdate Function 408
Responding to Events 408
COMBAT_LOG_EVENT_UNFILTERED 409
PARTY_MEMBERS_CHANGED 409
UNIT_PET 409
PLAYER_REGEN_DISABLED 409
PLAYER_REGEN_ENABLED 410
Creating the Frame Display 410
Updating the Frame Display 410
Future Additions 412
Summary 412
Chapter 22 Creating Scroll Frames 413
Using Scroll Frames 414
Adding a Scroll Child 415
Manipulating a ScrollFrame 416
Adding Scroll Bars 417
Creating Faux Scroll Frames 419
Adding Scroll Bars 422
Scrolling with the Mouse Wheel 423
Problems with Slider Precision 423
Summary 424
The Code 424
ScrollFrameTest 424
MacroIconTest 426
Chapter 23 Creating Dropdown Menus 431
Creating a Basic Dropdown 431
Adding a Toggle Button 432
Creating a Dropdown Frame 433
Initializing the Dropdown 433
Adding Buttons to the Dropdown 433
Calling UIDropDownMenu_Initialize() 434
Toggling the Dropdown Menu 434
Testing the Dropdown 435
Creating Multilevel Dropdowns 436
Adding Functionality to Dropdowns 437
Customizing Text Elements 438
Function Menu Items 440
CheckButton Menu Items 440
ColorPicker Menu Items 441
Using Dropdowns for Selection 443
Automating Menu Creation with EasyMenu 445
Creating Dynamic Menus 447
Summary 449
Contents xxvii
Chapter 24 Scanning and Constructing Tooltips 451
Understanding the Tooltip System 451
Different Types of Tooltips 452
Contextual Tooltips 452
Static Tooltips 453
Tooltip Contents 453
Custom Text in a Tooltip 453
Game Element Tooltips 455
Adding Information to the Tooltip 458
Loading the Tooltip with Item Information 458
Getting Information from Tooltips 460
Accessing Individual Tooltip Lines 460
Checking Soulbound Status 461
Using Global Strings for Localization 461
Replacing a Script Instead of Hooking a Script 461
Summary 462
Chapter 25 Taking Protected Action in Combat 463
Snippets: The Basis of Secure Action 463
How Can Addon Code Be Secure? 463
Writing a Snippet 464
Secure Handler Frames 464
Handler Template Reference 466
Integrating a Click Handler with a Secure Action Button 468
Preserving State and Controlling Information 473
Private Global Environments 474
Secure API Functions 475
The control Object 476
Frame Handles 477
Allowed Actions 479
Additional or Changed Actions 479
Wrapping Frame Scripts 482
A Simple Click Wrapper 483
Using a Post-Hook 484
Script Wrapper Reference 485
Triggered Changes 486
State Drivers 486
State Responders 487
Responding to Show/Hide 487
Responding to Attribute and State Changes 488
State Conditionals 490
Target Specifiers and Unit Conditions 491
State Variables 492
Unit Conditions 492
General Conditions 493
xxviii Contents
Summary 496
The Code 496
BlessedMenus 496
Chapter 26 Creating Unit Frames with Group Templates 501
Configuring a SecureGroupHeader 501
Configuration Options 502
Initial Configuration Function 505
Creating SquareUnitFrames 506
Constructing the Template 506
Creating a Header Template 508
Setting Name and Status Bars 509
Nudging Frame Levels 511
Responding to Events and Clicks 511
Targeting the Unit on Left-Click 511
Moving the Header 512
Health Update Events 513
Power Update Events 514
Responding to Name Changes 516
Enhancing SquareUnitFrames 516
Highlighting Units on Mouseover 516
Showing the Targeted Unit 517
Displaying Threat Levels 518
Showing Dead Players 519
Displaying Unit Names 521
Adding Pets to SquareUnitFrames 523
Creating a SecureGroupPetHeaderTemplate 526
Summary 526
The Code 526
SquareUnitFrames 526
Part IV Reference 537
Chapter 27 API Reference 539
API Reference Conventions 539
Function Signatures 539
Optional Arguments 540
Argument Choices 540
Argument and Return Listings 540
Common API Flags 541
API Meta-Types 542
1nil 542
actionID 542
ah-list-type 543
Contents xxix
anchorPoint 543
arenaTeamID 543
auraFilter 543
backdrop 544
bitfield 544
binding 545
chatMsgType 545
colorString 545
containerID 546
containerSlotID 546
frameStrata 546
glyphIndex 547
GUID (Globally Unique IDentifier) 547
Players 547
NPCs 547
Pets 548
Vehicles 548
GUID Example 548
Hyperlink 549
player 549
playerGM 549
glyph 549
spell 550
enchant 550
quest 550
talent 550
achievement 551
trade 551
item 552
inventoryID 552
itemID 553
itemLocation 553
itemQuality 553
itemString 554
justifyH 554
justifyV 554
layer 554
macroID 554
powerType 554
rollID 555
spellbookID 555
spellID 555
standingID 555
unitID 555
API Reference 556
xxx Contents
Chapter 28 API Categories 1025
Achievement Functions 1025
Action Functions 1027
ActionBar Functions 1028
Addon-related Functions 1028
Arena Functions 1029
Auction Functions 1030
Bank Functions 1031
Barbershop Functions 1032
Battlefield Functions 1032
Blizzard Internal Functions 1035
Buff Functions 1035
CVar Functions 1035
Calendar Functions 1036
Camera Functions 1040
Channel Functions 1041
Chat Functions 1043
Class Resource Functions 1045
Client Control and Information Functions 1045
Combat Functions 1046
CombatLog Functions 1046
Companion Functions 1047
Complaint Functions 1047
Container Functions 1047
Currency Functions 1048
Cursor Functions 1049
Debugging and Profiling Functions 1051
Duel Functions 1052
Equipment Manager Functions 1052
Faction Functions 1053
GM Survey Functions 1054
GM Ticket Functions 1054
Glyph Functions 1054
Guild Bank Functions 1055
Guild Functions 1056
Hyperlink Functions 1059
In-game Movie Playback Functions 1060
Inspect Functions 1060
Instance Functions 1061
Inventory Functions 1061
Item Text Functions 1063
Item Functions 1063
Contents xxxi
Keybind Functions 1065
Keyboard Functions 1065
Knowledge-base Functions 1066
Limited Play Time Functions 1067
Locale-specific Functions 1067
Looking For Group Functions 1068
Loot Functions 1069
Lua Library Functions 1070
Mac Client Functions 1072
Macro Functions 1073
Mail Functions 1074
Map Functions 1075
Merchant Functions 1076
Modified Click Functions 1078
Money Functions 1078
Movement Functions 1079
Multi-cast Action 1080
NPC ‘‘Gossip’’ Dialog Functions 1080
Objectives Tracking Functions 1081
Party Functions 1082
Pet Stable Functions 1083
Pet Functions 1083
Petition Functions 1085
Player Information Functions 1085
PvP Functions 1088
Quest Functions 1089
Raid Functions 1094
Recruit-a-friend Functions 1095
Secure Execution Utility Functions 1095
Skill Functions 1096
Social Functions 1096
Socketing Functions 1097
Sound Functions 1098
Spell Functions 1099
Stance/Shapeshift Functions 1101
Stat Information Functions 1101
Summoning Functions 1103
Talent Functions 1103
Targeting Functions 1104
Taxi/Flight Functions 1105
Threat Functions 1105
Tracking Functions 1106
Trade Functions 1106
xxxii Contents
Trade Skill Functions 1107
Trainer Functions 1108
Tutorial Functions 1110
UI/Visual Functions 1110
Unit Functions 1110
Utility Functions 1113
Vehicle Functions 1115
Video Functions 1116
Voice Functions 1117
Zone Information Functions 1119
Chapter 29 Widget Reference 1121
Widget Types 1121
UIObject 1121
ParentedObject 1122
ScriptObject 1122
Region 1124
VisibleRegion 1129
LayeredRegion 1130
FontInstance 1131
FontString 1135
Texture 1138
Frame 1145
Button 1164
CheckButton 1170
ColorSelect 1172
Cooldown 1175
GameTooltip 1176
Minimap 1192
Model 1195
PlayerModel 1201
DressUpModel 1202
TabardModel 1202
MovieFrame 1204
ScrollFrame 1206
SimpleHTML 1208
Slider 1215
StatusBar 1219
Font 1221
MessageFrame 1222
ScrollingMessageFrame 1225
EditBox 1231
AnimationGroup 1238
Contents xxxiii
Animation 1243
Path 1248
ControlPoint 1250
Rotation 1251
Scale 1252
Translation 1253
Alpha 1254
Widget Scripts 1255
Chapter 30 Events Reference 1277
Part V Appendixes 1303
Appendix A Best Practices 1305
General Programming 1305
Use Meaningful Variable Names 1306
Variable Naming Exceptions 1307
Use Named Constants Instead of Literals 1307
Organize for Easier Maintenance 1308
Rework Repetitive Code 1308
Break Apart Long Functions 1309
Use Consistent Programming Style 1309
Lua Tips 1310
Use Local Variables 1310
Minimize Unnecessary Garbage 1311
How to Reduce Garbage 1312
Recyclable Objects 1317
Recycle Tables 1318
Other Fine-tuning Optimizations 1319
Check Expected Conditions First 1319
Exploit Shortcut Evaluation 1320
Use Tables as a Logic Structure 1321
Cache Frequently Accessed Values 1322
The WoW Environment 1323
Use What You’re Given 1323
Localize with Global Strings 1323
Avoid Deprecated Systems 1324
Global Widget Handler Arguments 1324
bag and slot Attributes on item Type Action Buttons 1325
Avoiding Common Mistakes 1325
Adding Files While WoW Is Running 1325
Entering |into the Chat Edit Box 1326
‘‘Missing’’ Frames 1326
Ignoring Logs\FrameXML.log 1326
Not Checking API Returns 1326
xxxiv Contents
Requesting Data Before PLAYER_LOGIN 1327
Conflicting or Existing Anchor Points 1327
Appendix B Utilizing Addon Libraries 1329
What Is an Addon Library? 1329
How Do Libraries Work? 1330
Standalone Libraries 1330
Advantages 1331
Disadvantages 1331
Embedded Libraries 1332
Embedded Library Load Process 1332
Manually Versioning an Embedded Library 1333
Versioning Using LibStub 1334
Using a Library 1335
Ace3 1335
Portfolio 1336
Dongle 1336
PeriodicTable 1336
BossIDs 1336
LibHealComm 1336
LibSharedMedia 1336
Other Library Resources 1337
Appendix C Tracking History Using Version Control Systems 1339
Subversion 1339
Terminology 1340
Layout of a Repository 1340
Obtaining Subversion 1341
Command Primer 1341
svn checkout <url>[path] 1341
svn update [path] 1342
svn add <path>1342
svn commit [path] 1342
svn status 1342
svn log [path] 1343
svn diff [path] 1343
Creating a Local Repository 1343
Git and Mercurial 1344
Terminology 1344
Obtaining Git 1345
Obtaining Mercurial (hg) 1345
Typical Usage 1345
Git 1345
Mercurial 1347
Contents xxxv
Appendix D Addon Author Resources 1349
Community Websites 1349
World of Warcraft Forums 1349
WowProgramming Forums 1350
WoWInterface Forums 1350
WowAce Forums 1350
Curse Forums 1350
Elitist Jerks 1350
IncGamers UI Customization Forums 1351
Internet Relay Chat (IRC) 1351
#wowuidev on irc.freenode.net 1351
#wowace on irc.freenode.net 1351
#wowprogramming on irc.freenode.net 1351
Distributing and Supporting Your Addon 1352
WoW-Specific Hosting 1352
WoWInterface 1352
CurseForge and WowAce 1353
IncGamers 1353
Other Hosting Solutions 1353
Google Code 1353
Sourceforge 1354
Personal Web Hosting 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 War-
craft 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 compre-
hensive 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 1: Programming for World of Warcraft
Chapter 2: Exploring Lua Basics
Chapter 3: Basic Functions and Control Structures
Chapter 4: Working with Tables
Chapter 5: Advanced Functions and Control Structures
Chapter 6: Using the Lua Standard Libraries
Chapter 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
4PartI■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 program-
ming 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 5
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
6PartI■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 7
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\<username>\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
8PartI■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 mod-
ular 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 9
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
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 11
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
willtrytoloaditifitisenabled.
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.
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
createdyourfirstaddonandtestedin-gametoensureitworkedcorrectly.
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
thedownloadlinktogetthelatest 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 15
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.
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 appli-
cation 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 17
Figure 2-4: LuaforMacOSXdiskimage
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
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!“)
Youshouldseethefollowingoutput:
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 19
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+Dtoinsert
the end-of-file character and end the session immediately. You can also use
Ctrl+C to kill the session.
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 4as 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 21
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
thesecondnumber(theein 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 under-
standing 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).
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 23
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,buttheyare
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
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 5is number,asyou’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.Asyousaw
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
Avariable 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 25
>print(x + y)
6
In this example, you take the name xandbindittothevalue4; and bind the
name yto the value 2. You then call the print function, and instead of using
the numbers 4and 2, you use the names xand 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,andwhile.Avariable
name is also case-sensitive, so the character ais 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,andyoucanprint
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:
>x=4
>y=x
>print(y)
4
>x=3
>print(y)
4
26 Part I ■Learning to Program
The first line simply binds the value 4to the identifier x. The second line,
however, assigns the value bound to the identifier xto identifier y. This means
quite literally that both xand yare names for the same value (the number 4).
As a result, when you run x=3, you simply change that binding, leaving
yintact.
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 ■Exploring Lua Basics 27
> print(1 == 1)
true
> print(1 < 3)
true
> print(1 > 3)
false
> print(2 <= 2)
true
> print(1 >= 3)
false
> print(1 ~= 3)
true
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“)
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“,sothe
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 4to 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 29
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.
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 (’’)
Thedoublequote(“) 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 31
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.
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 ¨
ager. 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 33
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.
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 35
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,whichisoftypenil.Youcanusethis
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,usingthe== operator.
You can also check the type of the value, to see if that is nil.Besuretonote
the difference between the value nil and the string “nil“,becausethetype()
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.
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,andlocal, you’re doing something
fairly simple here. You assign the value 7to 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 ias 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,certainLua
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 37
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 introduc-
tion 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.
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
becauseeachlineofcodeisinitsownscope,butyoumayfindthetechnique
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 41
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.
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, andusedaskeysintables(tablesare
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 43
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 <boolean expression> 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
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 expres-
sions so long as the expression evaluates to a Boolean value. That allows you
to combine multiple conditions using the logical operators (and,or)intoa
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,andthename 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 <boolean expression> then
-- if part
elseif <boolean expression> then
-- elseif part
elseif <boolean expression> 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.
Usingthisformofif/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 45
if <first condition> then
-- do something
end
if <second condition> 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]: ?
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 <boolean expression> 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 xis computed by multiplying
all of the numbers from 1to xtogether. Thus, 3factorial is 1*2*3.Ifa
function factorial() is defined, you can simply type print(factorial(9))
Chapter 3 ■Basic Functions and Control Structures 47
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 <boolean expression>
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,
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(),thenum 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 9and 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 49
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:
>fori=1,3,1do
>> 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
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,3example can thus be written as follows:
for i = 1, 3 do
print(i)
end
Computing Factorials
The for loopcanbeusedtomakethefactorial() 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
>fori=1,upper do
>> print(i)
>> upper = upper + 1
>> end
1
2
3
This example doesn’t loop forever because loop conditions are only evalu-
ated 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
>fori=1,3doprint(i) end
Chapter 3 ■Basic Functions and Control Structures 51
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 iin 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.
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 pro-
gramming languages (arrays, records, dictionaries,hashtables).InLua,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.Youcan
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 55
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,
...
}
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 compi-
lation 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,duetosome
similarities they share with arrays in other programming languages. More
specifically, the part of a table that has integer keys starting at 1is referred to
as the array part of the table.
Chapter 4 ■Working with Tables 57
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] = value1,
[2] = value2,
[3] = value3,
...
}
Inthefirstcaseyoucanomitthekeynamesentirely,andjustprovidea
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.Althoughnil 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
Thesamelengthoperator(#) 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
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:
>fori=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 59
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)
fori=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,whichinsertsitafterthe
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 61
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
wouldbesortedinthesameway.
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()
62 Part I ■Learning to Program
When functions are grouped together in this manner, they are said to be
part of a namespace,inthiscase,thetable 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 63
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
fori=1,numdo
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
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 decre-
mented, 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
counteranobjectwithtwomethods,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 65
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() butpassitthecounter2 object:
>print(counter.get(counter2))
15
>print(counter.get == counter2.get)
true
This should be no surprise because you’re just moving and copying refer-
ences 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
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),youcancallcounter: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 program-
ming 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:
>print(counter:get())
0
>counter:inc()
>counter:inc()
>print(counter:get())
2
Chapter 4 ■Working with Tables 67
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 call-
ing 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())
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 69
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
Ametamethod 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.
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
fori=1,#ado
table.insert(result, a[i])
end
-- Copy table b in second
fori=1,#bdo
table.insert(result, b[i])
end
return result
end
To simplify the function, the arguments have been named aand b.Thefirst
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
>fori=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 71
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:
> unm_test = -tbl1
> for i = 1, #unm_test do print(i, unm_test[i]) end
1 gamma
2 beta
3 alpha
> unm_test = -tbl1 + tbl2
> for i = 1, #unm_test do print(i, unm_test[i]) end
1 gamma
2 beta
3 alpha
4 delta
5 epsilon
6 zeta
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
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 73
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.
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 75
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>
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.
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 79
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
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 under-
score 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 81
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
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 ineffi-
cient, 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 83
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
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 compu-
tation 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 <expression> do
-- body of for loop
end
Chapter 5 ■Advanced Functions and Control Structures 85
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 <expression>, 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.
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();justpassitthetableanduseitaspartofagenericfor 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 ele-
ments 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 imple-
mented. 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(),youcanclearthe
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 87
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.
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 89
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 inthecomparisonhavechanged):
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
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
beascomplexasyouneed,aslongasitreturnstrue 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
aregroupedattheendofthechapter.
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
iis 1, and the default for jis the length of the table. If iis greater than j,it
returns the empty string.
> tbl = {“alpha“, “beta“, “gamma“}
> print(table.concat(tbl, “:“))
alpha:beta:gamma
> print(table.concat(tbl, nil, 1, 2))
alphabeta
> print(table.concat(tbl, “\n“, 2, 3))
beta
gamma
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 xat the end of table t.
Chapter 6 ■Lua Standard Libraries 93
> 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.
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
returns its length.
The empty string ““
has length 0.
Embedded zeros are
counted, so
“a\000bc\000“
has length 5.
> print(string.len(“Monkey“))
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 95
FUNCTION DESCRIPTION EXAMPLE(S)
string.rep(s, n) Returns a string that is
the concatenation of n
copies of the string s.
> print(string.rep(“Hello“, 3))
HelloHelloHello
> test = “foo“
> 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.
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
percentsignandthetypespecifier.Thefollowing 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 speci-
fied for strings, the resulting string will be cut off at this number of
characters.
Chapter 6 ■Lua Standard Libraries 97
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)
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 selec-
tion to craft these messages.
The English format string is “%s’s %s is removed.“, and the German for-
mat 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
canbeusedtorepresentanyletter.
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 99
Table 6-3: Character Classes
CLASS MATCHES
x(where xis not one of the
magic characters
ˆ $()%.[]*+-?)
The character xitself.
.(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 xis 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.
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 101
PATTERN MATCHES
%n For nbetween 1and 9; matches a substring equal to the
nth captured string (see the section later in this chapter on
captures).
%bxy,wherexand y
are two distinct
characters
Strings that start with x, end with y, and where the xand y
are balanced. This means that if you read the string from
left to right, counting +1foranxand -1 for a y, the ending
yis 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.
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
canbeusedtomakeapatternmoreexplicit.
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
mayonlyworkinspecificcases.
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 103
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 sin
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
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,withnbetween
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).
Herearesomeexamples:
> 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.Ifpattern specifies no captures, the whole
match is returned. A third, optional numerical argument—init—specifies
where to start the search; its default value is 1and 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 swhere this occurrence starts and ends; otherwise, it returns nil.Athird,
Chapter 6 ■Lua Standard Libraries 105
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
eraisedtothexpower.
> 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
106 Part I ■Learning to Program
Table 6-8: (continued)
FUNCTION DESCRIPTION EXAMPLE
math.fmod(x, y) Returns the remainder
of the division of xby
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 ■Lua Standard Libraries 107
FUNCTION DESCRIPTION EXAMPLE
a program. For
example, this function
canbeusedalongwith
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
1andm. When called
with two numbers m
and n, returns a
pseudo-random integer
between and including
mand n.
math.randomseed
(x)
The pseudo-random
number generator used
by Lua takes an initial
seed and generates a
sequence of numbers
basedonthatseed.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
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]]])
ThesefunctionsareavailableintheWowLuaaddon,ontheWebLua
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 109
> 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,incaseyouseeitincodefromolderaddons.
> 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 cur-
rent 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 inter-
preter 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.
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(),andstring.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
<html>
<head>
<title>My Document</title>
</head>
<body>
<h1>Heading One</h1>
<p>
This text is <strong>bold</strong>.
</p>
</body>
</html>
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 <strong> 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:
<addressbook name=“Personal“>
<entry>
<firstname>Alice</firstname>
<lastname>Applebaum</lastname>
<phone>+1-212-555-1434</phone>
<address>
114 Auburn Street
Apt 14
Atlanta, GA
</address>
</entry>
</addressbook>
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,
XMLisacousinoftheHTMLstandardthatisgeneralizedformultipleuses,
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 113
XML Tags
An XML tag is an identifier that begins and ends with angle brackets, such
as <tag>. The tags are case-sensitive, so <Tag> is a different tag name than
<tag>. A closing tag is the same as an opening tag, but has a forward slash
immediately after the open bracket, such as </tag>. 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 <entry></entry> 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 <tag /> or <tag/>.
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:
<tag attribute=“value“></tag>
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 rela-
tionship, attributes describe something specific about the element, such as the
name of the element. The addressbook element has the name Personal,soit
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.
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 (>)inadocument.
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 entirelywithin another element. This disallows
something like <b>Some <i>Text</b></i>,becausethe<i> 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 115
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:
<xs:schema
xmlns:xs=“http://www.w3.org/2001/XMLSchema“>
<xs:element name=“addressbook“ type=“AddressBook“/>
<xs:complexType name=“AddressBook“>
<xs:sequence>
<xs:element name=“name“ type=“xs:string“/>
<xs:element name=“phone“ type=“xs:string“/>
<xs:element name=“address“ type=“xs:string“/>
</xs:sequence>
</xs:complexType>
</xs:schema>
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,creatinganew
<addressbook> 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.
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:
<addressbook
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“
xsi:noNamespaceSchemaLocation=“addressbook.xsd“>
<name>Alice Applebaum</name>
<phone>+1-212-555-1434</phone>
<address>
114 Auburn Street
Apt 14.
Atlanta, GA
</address>
</addressbook>
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 <addressbook> to appear in the
exact order shown. If you were to swap the order of <name> and <phone>,
the document would no longer validate. To add the elements in any order,
as long as you include them all, you can change the <xs:sequence> and its
matching close tag to read <xs:all>.
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 117
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 auto-
matically 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:
<xs:simpleType name=“ORIENTATION“>
<xs:restriction base=“xs:NMTOKEN“>
<xs:enumeration value=“HORIZONTAL“/>
<xs:enumeration value=“VERTICAL“/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name=“ColorFloat“>
<xs:restriction base=“xs:float“>
<xs:minInclusive value=“0.0“/>
118 Part I ■Learning to Program
<xs:maxInclusive value=“1.0“/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name=“ColorType“>
<xs:attribute name=“r“ type=“ColorFloat“ use=“required“/>
<xs:attribute name=“g“ type=“ColorFloat“ use=“required“/>
<xs:attribute name=“b“ type=“ColorFloat“ use=“required“/>
<xs:attribute name=“a“ type=“ColorFloat“ default=“1.0“/>
</xs:complexType>
<xs:complexType name=“GradientType“>
<xs:sequence>
<xs:element name=“MinColor“ type=“ColorType“/>
<xs:element name=“MaxColor“ type=“ColorType“/>
</xs:sequence>
<xs:attribute name=“orientation“ type=“ORIENTATION“ default=“HORIZONTAL“/>
</xs:complexType>
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,whichmustbea
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,andb(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 <MinColor> tag and a <MaxColor> tag, both of type ColorType.
Additionally, this tag can take an orientation attribute, described earlier.
Using a GradientType
Assuming there is a <Gradient> tag with the type GradientType defined
somewhere, the following would be a valid usage of this schema:
<Gradient orientation=“VERTICAL“>
<MinColor r=“1.0“ g=“0.0“ b=“0.3“ a=“1.0“/>
<MaxColor r=“0.0“ g=“0.0“ b=“0.0“ a=“1.0“>
</Gradient>
Chapter 7 ■Learning XML 119
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
<Gradient> 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.Thiswebsite
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).
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 121
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.
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 127
.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.
128 Part II ■Programming in World of Warcraft
## Notes:
## Notes: Greet other players
The ## Notes directive gives you the capability to provide a longer descrip-
tion of your addon. This field can also be localized to provide a different
description depending on client locale in the same way as ## Title,andmay
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 dependenciesofanaddonbeforetryingtoload
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 129
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 1or a 0,where1means the addon is LoD capable,
and 0means 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
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 respon-
sibility 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 131
## 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 direc-
tive 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 simi-
lar types of addons together when listing or displaying them. Here’s the list of
categories:
Action Bars Frame Modification Priest
Auction Guild Quest
Audio Healer Raid
Battlegrounds/PvP Hunter Rogue
(continued)
132 Part II ■Programming in World of Warcraft
NONSTANDARD METADATA DIRECTIVES (continued)
Buffs Interface Enhancements Shaman
Caster Inventory Tank
Chat/Communication Library Tradeskill
Combat Mage UnitFrame
Compilations Mail Warlock
Data Export Map Warrior
Development Tools Miscellaneous
Druid Paladin
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 <Script
file=“SomeFile.lua“/> tag. Each XML file should contain a top-level <Ui>
element.
To validate your XML document, you will also need to include the schema
information in the <Ui> element. Here’s an example:
<Ui xmlns=“http://www.blizzard.com/wow/ui/“
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance“
xsi:schemaLocation=“http://www.blizzard.com/wow/ui/
http://wowprogramming.com/FrameXML/UI.xsd“>
This may need a bit of explanation. The xlmns attribute defines the name-
space that the document is hoping to conform to. It must match the namespace
declared in the given schema. In this case, it is a value defined by Blizzard. The
xmlns:xsi attribute tells the validating program to which schema instance the
schema will conform. Finally, xsi:schemaLocation is a pair of strings, the first
being the name of a namespace and the second being the location where that
schema can be found.
The example specifies that the http://www.blizzard.com/wow/ui/ schema
can be found in the online document http://wowprogramming.com/FrameXML/
UI.xsd. This is a file that is kept updated on the website for validation
purposes. Instead of using the online version, you could specify the path to
the UI.xsd file on your local machine. The location of this file depends on your
specific installation, but it will be unpacked when you use the User Interface
Customization Tool.
Chapter 8 ■Anatomy of an Addon 133
As your XML files are loaded, any errors will appear in the Logs\
FrameXML.log file in your base World of Warcraft installation. If your addons
aren’t behaving properly, it’s a good idea to check this file to ensure there
wasn’t an error in validating or parsing your code.
Lua Script Files
HelloFriend.lua
The TOC file can list any number of Lua files that exist somewhere under-
neath the addons directory. Each of these files is loaded, parsed, and then
executed by the game client in the order listed in the TOC file. Because each file
is run independently, local variables defined in one file will not be available
in another file; if you need to share data between different files you should
ensure that you use global variables of some sort.
Media Files
Addons can include custom graphics, sounds, and fonts to be displayed (or
played) within the game client, providing a different visual style or audio
cues. These files are included within the addon directories themselves, and are
addressed by full pathname from the WoW directory.
Music
Assume you have a file called CreepySound.mp3 included as part of an addon
called Goober that resides in the following location:
World of Warcraft\Interface\Addons\Goober\CreepySound.mp3
It can then be played by running the following command in-game (remember
to escape the backslash character because Lua uses it as the escape character):
/run PlaySoundFile(“Interface\\Goober\\CreepySound.mp3“)
The WoW client can natively play MP3 files as well as WAV files. Converting
files to these formats can be accomplished through a number of tools freely
available on the Internet.
Graphics
WoW accepts two graphics formats when loading textures for frames. In
addition to being in the right format, each graphic must meet the following
basic requirements to be loaded:
1. The file’s width and height must be greater than or equal to 2, and smaller
than 1024 pixels.
2. The height and width of the file must be a power of two, although they
need not be the same.
134 Part II ■Programming in World of Warcraft
For example, a file that is 32 x 64 is acceptable, whereas a file that is 512 x 400
is not (because the height is not a power of two). In addition, the file can, and
should, contain an alpha channel, something that is particular to the specific
graphics editing software you are using.
More information on creating and editing custom graphics for addons is
available in Chapter 20.
Following is a look at the two primary graphics formats used in WoW: BLP2
and TGA.
BLP2 Format
If you have extracted the Blizzard Interface Art using the User Interface
Customization Tool (as shown in Chapter 7), you may have noticed that all the
files that were created have a .blp extension. That file format was created by
Blizzard, and has been used in both Warcraft III and World of Warcraft. Even
though Blizzard provides a way to extract the files, there is still no official tool
that can convert these files. The companion website for the book provides a
way to convert these graphics to the PNG format, which is easier to view and
edit; see Chapter 7 for more information.
The only time this book deals with .blp files is if any original game art is
altered, in which case the texture is provided in the .tga format instead.
TGA Format
Wikipedia defines a .tga file as a Truevision Advanced Raster Graphics
Adapter (TARGA) file. This is a simple graphics file format that can be used to
store color images, including transparency information. TGA files never use
lossless compression, which means the image is not degraded as a result of
saving the image, as happens with JPG files. Most modern graphics editors can
save to this file format natively, and Chapter 20 provides an extensive tutorial
on creating files to be used in the game.
Localizing Your Addons
Localization as it relates to addon development is the process of converting
the text and icons used in the application to a format that is meaningful for
users from other regions of the world, who may speak different languages.
WoW boasts more than 10 million subscribers, many of them coming from
regions in Europe and Asia. You may see the word ‘‘localization’’ abbreviated
to L10n, which stands for ‘‘L’’ followed by 10 other letters, followed by an
‘‘n.’’ Similarly, you may see I18n as an abbreviation for ‘‘Internationalization.’’
However you call it, localization makes your addons more accessible.
Chapter 8 ■Anatomy of an Addon 135
Valid Locales
Blizzard provides a number of game locales with World of Warcraft. Table 8-1
shows a list of the current valid game locale codes. For each of these
languages, Blizzard has translated each in-game message and string so they
are meaningful to users from that region.
Table 8-1: Valid Client Locales
LOCALE CODE CORRESPONDING LANGUAGE
deDE German
enUS American English
enGB British English
esES Spanish
esMX Spanish (Mexico)
frFR French
koKR Korean
ruRU Russian
zhCN Simplified Chinese
zhTW Traditional Chinese
Although there is technically an enGB locale, the game will never display
that locale anywhere (in particular, GetLocale() will not return it).
Reasons for Providing Localization
When users play the game in their native language, it’s often easier for them
to make split-second decisions if they aren’t trying to read an entirely different
language as part of a custom addon. Imagine if you were a native Spanish
speaker who played the game in Spanish, but had addons that displayed your
information in English. Even if you’re a fluent reader of both languages, your
brain may find difficulty in switching between the two quickly.
From a purely practical standpoint, why not provide support for localization
in your addon? Most users are willing and able to help authors with the addon
localization, and if the addon is organized well, it can be a very easy task to
keep localizations up to date.
136 Part II ■Programming in World of Warcraft
Encouraging Users to Contribute
More often than not, users will approach authors with localization files, but
the author can take some steps to ensure the addon is easy to localize. This
typically means the following:
1. Include a dedicated localization file with no other addon logic, including
a set of constants of a lookup table to be used instead of string constants.
2. Provide information in the readme.txt file for your addon and the
addon’s website on how users can contact you to help with localization.
3. Provide comments about what a specific message means so it can easily be
translated. Although the word ‘‘speed’’ means only one thing in English,
it may translate to different words depending on the language.
Implementing Localization
Because localization implementations are simply different means of structuring
Lua programs, there are countless ways to do it. This section describes one
particular way to implement localization.
When working with non-English locales, you should ensure that your
editors work properly with UTF-8 markup. Most modern text editors work
just fine, but older, less-featured editors may show garbage instead of the
correct markup.
Add a File for Each Locale
Begin by adding a new localization file for each locale for which you have
translations. If you don’t have any translations to begin with, simply create a
file for the ‘‘base’’ locale in which you’ve developed the addon. For my addons,
this means adding a Localization.enUS.lua file to my directory structure.
Add the file to the top of the .toc file to ensure that it’s loaded first.
Create a Global Table Containing the Base Strings
Create a new global table called MyAddonLocalization in the Localization
.enUS.lua file, replacing MyAddon with the name of your addon. For instance,
if your addon is named BurgerDoodle, your global table would be called
BurgerDoodleLocalization.
You can use full strings or tokens to add the base translations to this file.
Using Full Strings
The following is a set of table definitions that takes the entire string to be
translated and uses it as both the key and the value. The reason for this will
become apparent later.