- Action Buttons and the MacroBar
- Callbacks and Promises with startRoll
- Computed Properties and CRP Roll Templates
- Using Queries in Custom Roll Parsing
- Changing Attributes with Custom Roll Parsing
- startRoll and Analysing Your Dice
- The CRP Roll String And Many Buttons
- The Structure of a Custom Roll Parsing Function
- Introduction to 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!