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.