- Anatomy of a Sheet Worker
- Events, and watching Attributes
- Variables – How to Name Things
- Arithmetic in Sheet Workers
- What If? in Sheet Workers
- JavaScript Objects
- Getting Loopy With JavaScript
- Logging in the Browser Console
- Strings, Arrays, and Loops
- Asynchronicity and Things to Avoid With Loops
- Changes and the eventInfo Object
- Action Buttons
- setAttrs and Saving Attributes
- Castle Falkenstein Design – Sheet Workers
- The Perils of Sheet Worker Functions
- The Script Block and Identifying Characters
- Arrays and Dropdowns
- Undefined and Other Error Values
- The Ternary Operator – The One-Line If
- Template Literals
- Functions and the Fat Arrow
- Strings in Sheet Workers
- A Sheet Worker Reprise
In this post, we shall do two things with strings – 1) show how to get the middle nickname, and use the first name if it doesn’t exist; 2) create a template for rollbutton values, and building them from string fragments. If it’s a bit vague, you should review part 1. There’ll be other posts in this series that might be worth reviewing too, but they’ll be mentioned as needed.
While these are fairly narrow concepts, the techniques displayed can be used in many different ways.
First, Why?
Let’s say you have a roll like this:
<button type="roll" name="roll_attack" value="&{template:default} {{name=@{character_name} }} {{attack=[[1d20+@{BAB} + @{strength} + ?{modifier|0} }}></button>
Code language: HTML, XML (xml)
The printed name might be very long if someone has named themselves, John ‘The Sword of Rivendell” Cavendish. We could use the worker from the previous post to grab the first name and save it in a first_name attribute. Then this might look like this:
<button type="roll" name="roll_attack" value="&{template:default} {{name=@{first_name} }} {{attack=[[1d20+@{BAB} + @{strength} + ?{modifier|0} }}></button>
Code language: HTML, XML (xml)
But then this specific character name would be shown as “John” which is not that exciting and won’t please anyone. What if we could create a worker to extract the nickname and use that?
We’ll also use console logging to explore what is happening and fix our sheet worker as we go.
Getting the Nickname
We could have fields on the character sheet where people enter their first name, last name, and nickname, and then their name is displayed as a product of those. Like ‘@{first_name} “@{nick_name}” @{last_name}’. But a problem here is that those quotes are displayed even if the nickname is absent. There are ways around that, but in this method we will be working backwards – starting from the full name, and extracting the nickname if it exists.
The Three Types of Quote (Yes, Three)
First, we have to figure out how to find the nickname. Typically, players will use quotes like these ” to surround a nickname. But JS supports three types of quotation mark: “, ‘, and ` (the last are called backticks, and are usually found on the key to the left of the number 1 key).
Backticks have a very important use, which we’ll get to shortly, but any of these three quotes are valid in character names. But an extra complication is that names are strings, and the standard surrounding character for strings is ” (let name = “Jon Smith”), and second quote ends the string. So what happens when you have a name like Fred “The Knife” Smith? Let’s find out with console.log:
So, in our HTML, we create an input for testing, where we can enter any value in the input.
<input type="text" name="attr_character_name" value="">
Code language: HTML, XML (xml)
Then we’ll create a worker that will log the output, over to the right.
on('change:character_name', function() {
getAttrs(['character_name'], function(values) {
const print = values.print;
console.log({print});
});
});
Code language: JavaScript (javascript)
Now we can enter different names to see what the output looks like:
Names Tried:
- Jon Smith
- Jon ‘Mack the Knife’ Smith
- Jon “Mack the Knife” Smith
- Jon “Mack the Knife’ Smith
Log Output:
There are several things to note here. The first two go as expected. In the third, JS changes the enclosing quotes behind-the-scenes, using ‘ instead of ” to enclose the name so it’s not broken. And in the final name, where mismatched quotes were used, JS used the third type of quote to enclose the string. (It also does something else if all three quotes are used, but we’ll ignore that for this function.)
Remember the enclosing quotations are not part of the string – they just tell JS where the string starts and ends. So we now know that players can enter pretty much anything and JS will cope. But now we have to build our function under the assumption that players will use any type of quote!
Use the browser log to analyse what exactly new sheet workers and functions are doing!
Finding Where the Nickname Starts
We can use indexOf or split from the last post to try to find the nickname, but face a conundrum: exactly which type of quotes is a player using? We need to work through all three. A loop would be handy here.
We build a new worker to take all three types of quotes, loop through them and create a new name attribute in an array (names).
If the quote is not found, the name array will be one item (“Jon Smith”), but if there is a quote it will be three items (“Jon”, “Mack the Knife”, and “Smith”).
The if statement in the middle uses the break command to exit the for loop the instant a matching name is found. Otherwise, it will be overwritten.
Using the name Jon “Mack the Knife” Smith, here is the output:
on('change:character_name', function() {
getAttrs(['character_name'], function(values) {
const print = values['character_name'];
const quotes = ["'", '"', "`"];
let names = [];
for(quote of quotes) {
names = print.split(quote);
console.log({quote, names});
if(names.length > 1) {
break;
}
}
});
});
Code language: JavaScript (javascript)
It might be hard to read that console log at first, but see how t shows the type of quote and the name (whether it is an array with one or more parts).
It shows each iteration – in the first, it used ‘. There were no matches, so it returned the full name. Then it tried “, found a match, split the name into three parts and ended the for loop. You can use break; to enter a for loop early, so there was no check against ` – the loop had finished.
Thus, we can end the worker’s code like this:
let nick_name = names(0); // set the default of the first item in names
if (names.length > 1) {
nick_name = names(1); // test if names is bugger than 1 item, then take the second one
}
setAtts({
nick_name: nick_name
});
Code language: JavaScript (javascript)
Array.length tells you how deep the array is – are there 1, 2, 3, or more items. But remember, JS counting starts at 0 for other things. So the first item is [0] when the length might be 1. This is just one area of potential confusion we have to deal with.
Special Cases
If there is no nickname it will print the full name, “Jon Smith”. Which is fine.
But maybe we just want the first name – then we could split again on spaces (but only if length is 1 – we don’t want to do this on names where a nickname has been found).
What then of characters with just one name, like “Magnus”? We can use an if statement for testing if spaces exist, or just use the split method which will return the original name if the splitting character is not found.
But then, what if the player accidentally uses mismatched quotes, or you decide to allow symbols other than quotes?
All of these problems are solvable with a bit of care, but I’ll leave you to find those solutions.
In This Post
In this post we learned that:
- Roll20 stores everything – Everything! – as a String.
- Numbers might not be Numbers – be careful of Strings!
- There are three types of quotation mark.
- Replacing the contents of a String is easy
- Looping through an Array is also easy
- We can break strings apart in several ways – see substring and split, and more below.
You can do a lot with strings. We have only scratched the surface here. There will be a post soon on Template Literals which really take strings to the next level (and are a great feature to use in logging).
String Properties
The topic of Strings is too big for a single post. Here’s a list of String properties and functions it’s handy to know. Only some of them have been used here, but most are easy to use once you know the basics.
- .charAt
- indexOf, indexLastOf
- .length
- .substring
- .replace, .replaceAll
- .toUpperCase(), .toLowercase()
- .includes
- .startsWith, .endsWith
- Using escape characters (\)
- Using regex (shudder)
- .join (to convert arrays into strings)