The CRP Roll String And Many Buttons

This post focusses on the creation and manipulation of the roll string, which can then be used to manage a lot of action buttons from just a few or one action button.

Recall that the startRoll function requires a roll string, like this:

startRoll('/roll 3d6', roll => {Code language: JavaScript (javascript)

or when using a roll template, like this:

startRoll('&{template:default} {{name=A 3d6 Roll}} {(roll=[[3d6]]}}', roll => {Code language: JavaScript (javascript)

That roll string is sent to chat. It’s exactly like the value of a roll button, which might look like this:

<button type="roll" name="roll_example" value="/roll 3d6"></button>Code language: HTML, XML (xml)

But since this happens inside a sheet worker, you can put the roll string in a variable, like this:

const roll_string = '&{template:default} {{name=A 3d6 Roll}} {(roll=[[3d6]]}}';
startRoll(roll_string, roll => {Code language: JavaScript (javascript)

This means you construct the roll string in parts, like this complete example:

on('clicked:example', () => {
  getAttrs(['an_input_containing_title', 'the_roll'], values => {
     const title = values.an_input_containing_title;
     const roll = values.the_roll;
     const roll_string = `&{template:default} {{name=${title} }} {(roll=[[${roll}]]}}`
     startRoll(roll_string, roll => {
       finishRoll(roll.rollID);
     });     
  });
});
const roll_string = '&{template:default} {{name=A 3d6 Roll}} {(roll=[[3d6]]}}';
Code language: JavaScript (javascript)

If those backticks (`) and ${} brackets are confusing, it is worth refreshing your memory on template literals.

What this shows is that you can construct the roll string from parts. That will come in very handy.

Managing Action Buttons

Now we’ll look at the many ways of ifdentifying and constructing a roll string. Imagine you had six attributes, str, dex, con, int, wis, and cha, and you wanted to create a roll for each of them. We’ll look at different ways you might approach this. All of these can be useful in different situations – use whichever approach suits you best.

1. Fixed Strings

The simplest approach is to have a fixed roll string, and a separate action button for each roll. This can be tedious to write, but doesn’t need much coding knowledge. You’d simply write out the sheet worker for each, like this:

on('clicked:str', () => {
  startRoll('&{template:default} {{name=STR Roll}} {{str=[[@{str}]]}} {{roll=[[1d20+@{str}]]}}', roll => {
    finishRoll(roll.rollID);
  });
});
on('clicked:dex', () => {
  startRoll('&{template:default} {{name=DEX Roll}} {{dex=[[@{dex}]]}} {{roll=[[1d20+@{dex}]]}}', roll => {
    finishRoll(roll.rollID);
  });
});Code language: JavaScript (javascript)

2. Universal Sheet Workers

Over on the roll20 wiki, I coined the phrase Universal Sheet Workers to describe a way of using code to automate the creation of many sheet workers so you’d have to do a lot less writing. You can use basically the same method with Custom Roll Parsing. First, you’d create an array of the attribute rolls, then use that array to loop through and create the roll workers.

const stats = ['str', 'dex', 'con', 'int', 'wis', 'cha'];
stats.forEach(stat => {
  on(`clicked:${stat}`, () => {
    startRoll('&{template:default} {{name=${stat.toUpper()} Roll}} {{${stat}=[[@{${stat}}]]}} {{roll=[[1d20+@{${stat}}]]}}', roll => {
      finishRoll(roll.rollID);
    });
  });
});Code language: JavaScript (javascript)

You have to be careful of brackets, and often get things like @{${stat}}. This takes advantage of the template literal ability to insert variables in strings. so if ${stat} equals str, the above becomes @{str}, exactly what we want.

This creates a separate sheet worker for each variable in the stats array, via the forEach loops. If you need to, refresh your memory with Getting Loopy.

3, Using eventInfo

Each sheet worker starts with what i call the event line, and can include the eventInfo object like this:

on('clicked:my_button, eventInfo => {Code language: JavaScript (javascript)

As described earlier, the eventInfo oject can include a bunch of properties. the triggerName property tells you what triggered that button. In the above case, eventInfo.triggerName would equal clicked:my_button. You’d need some way of removing clicked: and then you have the name of the button. So imagine you did this:

const stats = ['str', 'dex', 'con', 'int', 'wis', 'cha'];
const stat_changes = stats.map(stat => `clicked:${stat}`).join(' ');
on(stat_changes, eventInfo => {
  const trigger = eventInfo.triggerName.replace('clicked:','');
  startRoll('&{template:default} {{name=${trigger.toUpper()} Roll}} {{${trigger}=[[@{${trigger}}]]}} {{roll=[[1d20+@{${trigger}}]]}}', roll => {
    finishRoll(roll.rollID);
  });
})Code language: JavaScript (javascript)

This is very similar to the previous function. First, create an array of the button names, and make sure they match the attributes for those rolls.

Then create an event listener, which will look like clicked:str clicked:dex clicked:con etc. You could type that in manually, but here we have used cde – since we altready have the array of button names.

Then we get the button name from eventInfo using the trigger variable. And from there it is the same.

This uses just a single sheet worker, which might appeal to efficiency afficianados. Each will have different situations where it is best.

4. Using getAttrs

Sometimes you need to grab a value from the character sheet. It’s not enough to let the roll do it with something like @{stat}. You can use getAttrs in custom roll parsing (and we’ll see some more elaborate ways of using it in the future. But for now, let’s say you want to grab the values of those stats and insert them instead of using @{str}, @{dex} and so on. Here’s how you might do it.

const stats = ['str', 'dex', 'con', 'int', 'wis', 'cha'];
const stat_changes = stats.map(stat => `clicked:${stat}`).join(' ');
on(stat_changes, eventInfo => {
  const trigger = eventInfo.triggerName.replace('clicked:','');
  getAttrs([trigger], values => {
    const score = +values[trigger] || 0;
    startRoll('&{template:default} {{name=${trigger.toUpper()} Roll}} {{${trigger}=[[${score}]]}} {{roll=[[1d20+${score}]]}}', roll => {
      finishRoll(roll.rollID);
    });
  });
});Code language: JavaScript (javascript)

This example uses both the trigger and getattrs. Your example might use just one or the other, but you should be able to see how the getAttrs value is inserted.

5. The Object Method

This method, first suggested by Scott C, is the most flexible method. First, you build a JavaScript object which includes key/value pairs. The key is the name of the button, the value is part or all of the roll. Then you sheet worker uses the trigger to identify the name of the button, and gets thwets the rollstring from the object. Here’s an example:

const my_rolls = {
  str: '&{template:default} {{name=STR Roll}} {{str=[[@{str}]]}} {{roll=[[1d20+@{str}]]}}',
  dex: '&{template:default} {{name=DEX Roll}} {{str=[[@{dex}]]}} {{roll=[[1d20+@{dex}]]}}',
  con: '&{template:default} {{name=CON Roll}} {{str=[[@{con}]]}} {{roll=[[1d20+@{con}]]}}',
  int: '&{template:default} {{name=INT Roll}} {{str=[[@{int}]]}} {{roll=[[1d20+@{int}]]}}',
  wis: '&{template:default} {{name=WIS Roll}} {{str=[[@{wis}]]}} {{roll=[[1d20+@{wis}]]}}',
  cha: '&{template:default} {{name=CHA Roll}} {{str=[[@{cha}]]}} {{roll=[[1d20+@{cha}]]}}',
};

const stats = ['str', 'dex', 'con', 'int', 'wis', 'cha'];
const stat_changes = stats.map(stat => `clicked:${stat}`).join(' ');
on(stat_changes, eventInfo => {
  const trigger = eventInfo.triggerName.replace('clicked:','');
  const roll_string = my_rolls[trigger];
  startRoll(roll_string, roll => {
    finishRoll(roll.rollId);
  });
});Code language: JavaScript (javascript)

The big advantage of this process is that different rolls don’t need to be built the same way. You can have many different types of rolls in the same object.

You could also include just part of a roll in the object. There’s a lot of duplication in this example, so you’d probably want to streamline the code here. But again, if there are a lot of sdifferent kinds of rolls in the rolls object, you might want them to be listed in full like this, to make your workers easier to read.

6. The htmlAttributes Property

The eventInfo object also includes a htmlAttributes object which includes a lot of useful properties. However, if the button is fired in chat the htmlAttributes property is empty. So it’s often going to be useless, and lead to buttons failing to work properly. For that reason, I don’t describe it here – but if you want to explore eventInfo.htmlAttributes, go for it.

A Note on Repeating sections

When buttons are fired from inside a repeating section, and using the triggerName property, you can hit a problem. The trigger includes the row index, and that can be $0, $1, etc if using the macro index. If there’s demand for it, I might write a post detailing how to deal with that.

Conclusion

In this post, we’ve seen how to build a roll string, and how to build sheet workers that work for many action buttons. This is a very useful ability, and will feature heavily in future posts.

Series Navigation<< The Structure of a Custom Roll Parsing FunctionstartRoll and Analysing Your Dice >>

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.