Mod:A Guide to Roll20 Mod
From Roll20 Wiki
This is about a Roll20 feature exclusive to Pro-subscribers (and often to players in a Game created by a Pro-subscriber). If you'd like to use this feature, consider upgrading your account. |
In this guide I will attempt to explain what the API is, using examples give the user a basic idea of how to use the API, and lay down some best practices for its use. This guide is not here to teach you basic programming concepts; but it will cover some “advanced” ideas that will be necessary for your code to not interfere with other scripts.
Contents |
This is a work in progress please keep that in mind. |
What is the API?
What we as the community refer to as the API is actually a complicated scripting system of many different parts.
Here is some terminology I will for each part of what we call the API:
- The Environment or Sandbox – You can think of this as a virtual machine that runs the code you enter into the scripting window.
- The API (Application programming interface) – The actual API is a JavaScript library that gives access to the inner workings of Roll20.
- Roll20 Object - The objects that make up the Roll20 app can all be accessed using the API. The campaign is a object and so is that elf token and the character sheet it is attached to.
Prior Knowledge
To understand this guide I expect you to have a basic understanding of general programming, without this there is no point in reading any more of this guide.
To start learning about general programming and more specifically JavaScript please check out codecademy.
If at any point in this guide you don't understand something and it hasn't been explained it is because it is a simple programming concept and you should follow the link above to learn more.
Requirements
To do anything with the API at this time you must be aPro subscriber. The API is now live on the production servers.
Roll20 provides a built-in script editor you can use, including smart tabs and color-coding. However, you're welcome to use another text editor and paste the script into your campaign. Some good editors include:
Part One: Events
The most fundamental part of the API is its events. Events give you the ability to execute code when something happens in Roll20.
Ready
Probably the most used event is "ready", it is called when the campaign has finished loading and is ready for use. The "ready" event is often used as an entry point for scripts as it is always called unless something stops the campaign from loading, and once called you know that all Roll20 objects that existed at the time of the campaign loading are accessible.
Here is an example of using the "ready" event to make a simple "Hello World" script:
on("ready", function() { log("Hello World!"); });
First off we have the on
function it is part of the API and takes two parameters, a string with the name of the event you wish to hook and a handle function. In this example we write the handle function in line but the following code would work just as well.
var hello = function() { log("Hello World!"); }; on("ready", hello);
Then we have the log function, this prints to the "API Output Console" below the script editor and it takes a string of what to print.
So when run we get the output:
Spinning up new sandbox... "Hello World!"
Best Practices/Tips and Tricks
Below are some of the most important best practices. the API Best Practices wiki page is a more complete list of recommended best practices, Tips and Tricks.
Classes
Classes are a part of OOP that let you encapsulate and reuse code, classes are defined and then an instance of the class is created in a way all the Roll20 objects are instances of a class, a token is a class and then you would have an instance for your elf token for example.
Here is a simple example of a class:
function myclass() { this.doSomething = function() { log("I did something!"); }; } on("ready", function() { var test = new myclass(); test.doSomething(); });
First thing you will notice is I have defined a function. This is because JavaScript doesn't have classes, but its functions can act like them. So we define myclass and we give it the function doSomething that will write "I did something!" to the log. Then in the "ready" event we create an instance of myclass called test, then we call the doSomething function.
The above examples are standard classes, it is possible to create something similar to a static class (a class that only has one instance). Here is an example:
var myclass = { doSomething:function() { log("I did something!"); }, doSomethingElse:function() { log("I did something else!"); } }; on("ready", function() { myclass.doSomething(); });
Namespaces
It cannot be stressed how important namespaceing is to ensure scripts act the way they should. Namespaceing is a way to encapsulate code and will prevent unexpected errors when the user uses multiple scripts.
Here we have the same example as in the classes section:
function myclass() { this.doSomething = function() { log("I did something!"); }; } on("ready", function() { var test = new myclass(); test.doSomething(); });
And here we have the same code changed to use namespaces:
var myNamespace = myNamespace || {}; myNamespace.myclass = function() { this.doSomething = function() { log("I did something!"); }; }; on("ready", function() { var test = new myNamespace.myclass(); test.doSomething(); });
On the first line we define our namespace using a little trick I picked up in an article on elegantcode.com, it will define the namespace only once, making it safe to define the same namespace in all your scripts. Technically speaking JavaScript doesn't have namespaces so all we have done is create an object in the global namespace and then add all our classes and functions to it.
We can also use a namespace hierarchy like so:
var myRootNamespace = myRootNamespace || {}; myRootNamespace.myNamespace = myRootNamespace.myNamespace || {}; myRootNamespace.myNamespace.myclass = function() { this.doSomething = function() { log("I did something!"); }; }; on("ready", function() { var test = new myRootNamespace.myNamespace.myclass(); test.doSomething(); });
The only problem is the code for a namespace hierarchy can get a bit bulky so you may want to use a function like the one in the elegantcode.com article I linked before.