CRP: Criticals on Multiple Dice (HERO and GURPS)

One drawback of Roll20 dice mechanics is that criticals and fumbles are tested for on a per die basis. If yoo roll 1d20 and critical on a 20, that’s easy. But what if you roll 2d6 and critical on 12, or 3d6 and critical on a 3?

There is no way to do this with standard Roll20 dice mechanics. But you can solve it with Custom Roll Parsing. You can also report different qualities of success or failure based on your roll. We’ll showcase both of those in this post.

The Project

In GURPS, you roll 3d6 against your skill, to try to get equal to or under it. The success roll can be modified up or down. Your chance of getting a critical success is based on your chance of success. For 14 or less, it’s a roll of 3-4; for 15, it’s a roll of 5, and for 16 or higher, it’s a roll of 6.

You get a critical failure. or fumble, if you roll an 18, or 10 higher than your chance of success.

If you succeeded, it’s sometimes useful to know how much you succeeded by (your degree of success). Let’s say you need a 14, and roll a 7 – how much did you succeed by?

It is possible to report these using a standard roll template’s logic helpers, but the code is seriously clunky (especially if you want to report margin of success). Remember, you can perform no calculation inside a roll template – you need to build all the calculation into the roll expression, and would probably need something like this:

&{template:gurps} {{roll=[[3d6]]}} {{score=[[@{score}+?{modifier|0} ]] }} {{critical=[[ {[[{@{score}+?{modifier} - 10,6}kl1]], 4}kh1 ]] }} {{fumble= [[{@{score}+?{modifier} +10,18}kl1]]}}

If you wanted to also calculate the margin of effect (which you can’t do normally – you’d need to use the Reusing Rolls technique):

&{template:gurps} [[ @{score}+?{modifier|0} - [[3d6]] ]] {{roll=$[[0]]}} {{score=[[@{score}+?{modifier|0} ]] }} {{critical=[[ {[[{@{score}+?{modifier} - 10,6}kl1]], 4}kh1 ]] }} {{fumble= [[{@{score}+?{modifier} +10,18}kl1]]}} {{margin=$[[1]]}}

And then you’d need to build the roll template with carefully build Logic Helpers, to display the result. Custom Roll Parsing is more elegant. A roll expression for that would look more like this:

&{template:gurps} {{roll=[[3d6]]}} {{score=[[@{score}+?{modifier|0} ]] }} {{effect=[[0]]}}

Now we’ll demonstrate how to do this with Custom Roll Parsing.

The Basic Sheet HTML

A complete sheet might be very complex, but for this specific example we only need a single button.

<button type="action" name="roll_shooting" class="skill">Shooting</button>
<input type="text" name="attr_shooting" value="10">Code language: HTML, XML (xml)

The CRP Sheet Worker

Since every roll on the sheet uses the same technique, we’ll use the event_info method, which lets us use one sheet worker for many action buttons.

    const gurps_skills = ['shooting'];
    const capitalize = word => word[0].toUpperCase() + word.slice(1);
    const clicked = buttons => buttons.map(button => `clicked:${button}`).join(' ');
    on(clicked(gurps_skills), event_info => {
        const skill = event_info.triggerName.replace('clicked:', '');
        const label = capitalize(skill);
        const roll_string = `&{template:gurps} {{title=${label} }} {{roll=[[3d6cf0cs7]]}} {{score=[[@{${skill}}+?{modifier|0} ]] }} {{effect=[[0]]}}`
        startRoll(roll_string, roll => {
            const dice = roll.results.roll.dice;
            const dice_roll = roll.results.roll.result;
            const score = roll.results.score.result;
            const margin = Math.max(score - dice_roll, -1);
            const critical = score > 15 ? 6 : score < 15 ? 4 : 5;
            const fumble = Math.min(score + 10, 18);
            const effect = dice_roll <= critical ? 'Critical' : 
               dice_roll <= score ? 'Success' : 
                  dice_roll < fumble ? 'Failure' : 'Fumble';
            finishRoll(roll.rollId, {
                roll: dice,
                score: margin,
                effect: effect
            });
        });
    });Code language: JavaScript (javascript)

If you’ve read the previous examples in this series, there’ll be no surprises here. But here’s an explanation of some key elements.

The capitalize function turns the first letter of a lower case word to upper case – great for titles.

The clicked function takes an array and turns into ther ‘clicked:something’ string needed in sheet workers.

The event_info.triggerName line gets the name of the button used, and with capitalize we turn that into a visible label.

In the roll string, we have {{roll=[[3d6cf0cs7]]}}. Seeting cf0 and cs7 disables the built in coding for showing criticals and fumbles (this could also be done with CSS but would be harder to illustrate).

Inside startRoll we grab a bunch of numbers we will need, and then in finishRoll save the computed values.

Finally, the code uses ternary operators in place of space-consuming if statements. This one const critical = score > 15 ? 6 : score < 15 ? 4 : 5; could have been written as:

let critical = 5;
if (score > 15) {
   critical = 6;
} else if (score < 15) {
   critical = 4
}Code language: JavaScript (javascript)

And if you prefer writing if statements that way, go ahead! It can be clearer.

The Roll Template

To make all this work, we need a rolltemplate. I’ve used a template very similar to all the preious examples, since that’s quick and easy. It’s very tempting to spend a lot of time to make this look prettier, but that is not the point of this post.

<rolltemplate class="sheet-rolltemplate-gurps">
    <div class="heading">{{title}}</div>
    <div class="results">
        <div class="key">Score</div>
        <div class="value">{{score}}</div>
        <div class="key">Roll</div>
        <div class="value"><span class="dice">
            {{computed::roll}}</span> = {{roll}}
        </div>
        <div class="key">Result</div>
        <div class="value">{{computed::effect}} 
            {{#rollGreater() computed::score 0}}
               ({{computed::score}}}
            {{/rollGreater() computed::score 0}}</div>
    </div>
</rolltemplate>Code language: HTML, XML (xml)
.sheet-rolltemplate-gurps {
    background: white;
    border-radius: 5%;
}
.sheet-rolltemplate-gurps .sheet-heading {
    background: black;
    color: white;
    text-align: center;
}
.sheet-rolltemplate-gurps .sheet-results {
    display: grid;
    grid-template-columns: 49% 49%;
    column-gap: 2%;
    line-height: 1.8em;
}
.sheet-rolltemplate-gurps .sheet-results .sheet-key {
    text-align: right;
}
.sheet-rolltemplate-gurps .sheet-dice {
    letter-spacing: 2px;
}Code language: CSS (css)

Concluding Words

So there we have it. An example of using CRP to calculate criticals and fumbles from multiple dice, and to show text success levels based on a roll.

Series Navigation<< CRP: Fudge and FateCRP: OctaNe and Inspectres >>

Leave a Reply

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