Documents & Non-Documents
In the FVTT world, understanding the concept of documents and non-documents is crucial. Generally, documents are data structures that are stored permanently in the game world, while everything else is lost when the page is reloaded.
In Practice
In the last article, we discussed the actor
and token
objects accessible as macro arguments.
- Actors are documents.
- Tokens are placeables (non-documents); their document is the TokenDocument.
To clarify, when you access token
in a macro, you are accessing the visual representation of the token on the scene, not its actual data, which is stored in the TokenDocument.
To access the token's data, you need to reference the TokenDocument inside the token.document
property.
Example (logging the token document to the console):
console.log(token.document);
Interacting with the Documents
As established, documents are data structures stored in the world's database. Changing this data will reflect in the game world but not vice versa.
Assigning Values in JavaScript
In JavaScript, you can assign values to variables using the =
operator. For example:
let name = "John";
Variables are memory areas where you can store data to reference later. For example:
let name = "John";
console.log(name);
The output of this code will be John
.
There are two types of variables in JavaScript: let
and const
. The let
keyword declares a variable that can be reassigned, while the const
keyword declares a variable that cannot be reassigned.
let name = "John";
name = "Jane";
console.log(name);
The output of this code will be Jane
.
const name = "John";
name = "Jane";
console.log(name);
The output of this code will be an error, as the name
variable cannot be reassigned.
Assigning Values to Documents
While you can assign values to variables in JavaScript, this method won't work for documents. Assigning values to documents will not change the data stored in the game world; it will only change it for your client until you refresh the page.
Example (changing the name of the token):
token.document.name = "Jane";
This will NOT work, as the token's name will not change in the game world.
Updating Documents
The correct way to update a document is to use the update
method. This method takes an object as an argument and updates the document with the values from the object.
Example (updating the name of the token):
token.document.update({ name: "Jane" });
This will update the token's name in the game world for all users.
You can update multiple properties at once by passing an object with multiple properties.
Example (updating the name and size of the token):
token.document.update({
name: "Jane",
width: 2,
height: 2,
});
Asynchronous Operations
When a document is updated, the data needs to travel through the network to the server and back to the client. For this reason, the data is not immediately updated in the game world.
Let's see an example of an asynchronous operation:
console.log(token.document.name); // Output: John
token.document.update({ name: "Jane" });
console.log(token.document.name); // Output: John
Even though we updated the token's name, the name is still John
in the game world because the data is not immediately updated.
Waiting for the update to complete before logging the name will give us the correct output:
await token.document.update({ name: "Jane" });
console.log(token.document.name); // Output: Jane
The await
keyword is used to wait for the update to complete before continuing the code.
Remember that any operation that interacts with the world database will be asynchronous, so you will need to use await
to wait for the operation to complete if you need to interact with the new data.
Example: Changing a Token's Image
Let's say we want to change the image of a token, for example, if it's a shapeshifter.
token.document.update({ "texture.src": "my/new/image.webp" });
This will change the selected token's image in the game world for all users.
Advanced: IF Statements
Let's say we want our macro to toggle between two images based on the current image. We can use what we've learned so far and an IF statement to achieve this.
const currentImage = token.document.texture.src;
const humanForm = "assets/images/human.webp";
const shapeshifterForm = "assets/images/shapeshifter.webp";
if (currentImage === humanForm) {
token.document.update({ "texture.src": shapeshifterForm });
} else {
token.document.update({ "texture.src": humanForm });
}
This will change the selected token's image based on its current image.
IF Statements in JavaScript
In JavaScript, an IF statement is used to execute a block of code if a certain condition is true. The basic syntax of an IF statement is as follows:
if (condition) {
// code to execute if the condition is true
}
Here's an example of an IF statement in JavaScript:
let age = 25;
if (age >= 18) {
console.log("You are an adult");
}
The else
keyword can be used to execute a block of code if the condition is false:
let age = 15;
if (age >= 18) {
console.log("You are an adult");
} else {
console.log("You are a minor");
}
With the knowledge acquired so far, you can already create a lot of useful macros, in the next chapter we will learn how to grab various documents from the macro and perform various update actions on them.