Using Queries in Custom Roll Parsing

This series will show you complex examples of existing systems, using techniques we have already learned. This post is more general, looking at a very useful but often overlooked skill – how to place queries (like yes/no questions, or any kind of branching) in the worker.

Before we get started, you might want to refresh your memory on how if statements work in Roll20.

Yes/No Questions

Let’s say your character sheet has a bunch of resource, and when players use them they check off a box to show they have been used. But a certain points in play, these bioxes can all be refreshed (set to zero) with a sheet worker like this:

on('clicked:refresh', () => {
  const boxes = {};
  boxes.powers = 0;
  boxes.health = 0;
  setAttrs(boxes)
});Code language: JavaScript (javascript)

This character sheet has two special powers, named powers and health. It could be any number of boxes. Whenever the refresh button is clicked, any checkboxes are used and they are reset to a value of zero.

The sheet author recognises players could very easily click that button by mistake (or just out of curiousity) and the value of those boxes is erased. Wouldn’t it be nice if there was a hesitation built in?

on('clicked:refresh', () => {
  startRoll("!{{template:default}}{{ask=![[?{Are You Sure?|No,0|Yes,1}]]}}", question => {
    const query = question.results.ask.result;
    if(query) {
      const boxes = {};
      boxes.powers = 0;
      boxes.health = 0;
      setAttrs(boxes)
    }
  });
});Code language: JavaScript (javascript)

Here we create a simple yes/No query. First, we use his roll: !{{template:default}}{{ask=![[?{Are You Sure?|No,0|Yes,1}]]}}. This has several elements:

Any line beginning with ! is not printed to chat. That symbol usually indicates that whatever follows is a scripting command, but here we hijack it just to make sure the message is silent.

Then the message uses a rolltemplate – this is because startRoll doesn’t record the results unless a rolltemplate is used. lines beginning /roll are no good for this purpose. So even though the message will not be printed to chat, we still need a rolltemplate.

And then there is the query – ask. This is the dropdown that will appear to the user when they click the button. And we save that value in the query variable. The if statement checks if it is 0 or any other value (in this case 1), and only proceeeds if the value is 1.

So when the player clicks the button, they get a Yes/No question, and those boxes are only cleared if they click yes.

The GM could further modify this so a message is sent to chat whenever the query is run. Here is one way to do that.

on('clicked:refresh', () => {
  startRoll("!{{template:default}}{{ask=![[?{Are You Sure?|No,0|Yes,1}]]}}", question => {
    const query = question.results.ask.result;
    if(query) {
      const boxes = {};
      boxes.powers = 0;
      boxes.health = 0;
      const roll_string = "&{template:default} {{name=@{character_name} clears Resource Boxes}}";
      setAttrs(boxes,{silent:true},startRoll(roll_string, roll => {
         finishRoll(roll.rollId);
      });
    }
  });
});Code language: JavaScript (javascript)

This uses the ability for setAttrs to run other functions after it has completed. It also demonstrates how you can nest startRoll commands inside each other.

You could just as easily swap the final order:

on('clicked:refresh', () => {
  startRoll("!{{template:default}}{{ask=![[?{Are You Sure?|No,0|Yes,1}]]}}", question => {
    const query = question.results.ask.result;
    if(query) {
      const roll_string = "&{template:default} {{name=@{character_name} clears Resource Boxes}}";
      startRoll(roll_string, roll => {
         const boxes = {};
         boxes.powers = 0;
         boxes.health = 0;
         setAttrs(boxes);
         finishRoll(roll.rollID)
      });
    }
  });
});Code language: JavaScript (javascript)

Or even

on('clicked:refresh', () => {
  startRoll("!{{template:default}}{{ask=![[?{Are You Sure?|No,0|Yes,1}]]}}", question => {
    const query = question.results.ask.result;
    if(query) {
      const roll_string = "&{template:default} {{name=@{character_name} clears Resource Boxes}}";
      startRoll(roll_string, roll => {
         const boxes = {};
         boxes.powers = 0;
         boxes.health = 0;
         setAttrs(boxes, {silent:true}, finishRoll(roll.rollID));
      });
    }
  });
});Code language: JavaScript (javascript)

As is normal with programming, there are many ways to write the same function.

Other Queries

You can use this technique to perform different kind of branching. For instance, if you want to decide which startRoll function to run you might have the roll_string being set inside the query. For example:

on('clicked:which_dice', () => {
  startRoll("!{{template:default}}{{ask=![[?{What Dice Are you Rolling?|1d6|1d8|1d10|1d12|1d20}]]}}", question => {
    const query = question.results.ask.result;
    const expression = question.results.ask.expression;
    const roll_string = `&{template:default} {{name=@{character_name} rolls ${expression}}} {{${expression} roll=[[${query}]]}}`;
    startRoll(roll_string, roll => {
        finishRoll(roll.rollId));
    });
  });
});Code language: JavaScript (javascript)

Here’s an example which asks for a die then rolls that die. The player will see a query like this:

And everyone will see a roll in chat like this:

Conclusion

Using queries and nesting multiple startRolls can open up interesting possibilities. Have fun!

Series Navigation<< Changing Attributes with Custom Roll ParsingComputed Properties and CRP Roll Templates >>

Leave a Reply

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