Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

Help with conditional roll macro (possibly using CRP)?

December 20 (2 years ago)

Edited December 21 (2 years ago)
Seraaron
Sheet Author

So I'm trying to do something that (I think) should be fairly simple, but I've been banging my head against the wall with this for hours now.

The rules (in pseudo-code) that I'm trying to implement in a roll button for a custom sheet are as follows:

let total = @{dice} + ?{bonus}
if [total] > 0
    then
        roll = [total]d6kh1
    else
        roll = 2d6kl1

In english: if the total number of dice is greater than 0, then roll that total and keep the highest; if it's equal to or less than 0 then roll 2 dice instead and keep the lowest.

I've tried implementing this using an inline conditional and a pseudo-reuse inside my roll template as follows:

{{roll=[[{{[[(@{${name}-dice}?{Boons / Curses|NA,+0|+2D,+2|+1D,+1|−1D,-1|−2D,-2})]],-1}>0}*([[[[(@{${name}-dice}?{Boons / Curses|NA,+0|+2D,+2|+1D,+1|−1D,-1|−2D,-2})]]d6sd]]-[[2d6sd]]]])+$[[3]]]]}}

but it's a horrible mess, and very difficult to debug. It's possible there's just a missed bracket or something, because the results it's spitting out when i test the roll button are almost right (just with a whole bunch of gobeldigoop inbetween), but I feel like there must be an easier way than this! Plus, on top of that, there's a secondary rule that I'd like to be able to account for too. So normally you only care about the result of the highest die in your roll, but if you rolled multiple 6s then you 'crit', and I'd like to be able to highlight that somehow. Currently I have no idea how to implement that.

I've heard about this new custom roll parsing feature, but I'm so bad with javascript. I don't know why this is, but it's my cryptonite. I look at js, and it's just noise to me. Even though I can program in plenty of other languages just fine. If this were python, c#, or rust, I'd be flying.

Anyway... so can CRP or some other kind of sheetworker help me with this conditional roll?

(The system is ICON btw, if anyone is familiar or wants to help me and wants to read the rules in more detail, the game is free).

December 21 (2 years ago)

Edited December 21 (2 years ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator

Yes, custom roll parsing can handle all of this for you. I think I remember that you were using the K-scaffold. If that's still the case, then the pug for your action button will look something like this (going to write this for an athletics button):

+roller({name:'athletics',trigger:{triggeredFuncs:['iconRoll']}})

And your iconRoll function will look something like this (note that I've changed the attribute names to be snake_case instead of kebab-case):

const assembleRollField = async (attrName,attributes,sections) => {
  const dice = attributes[`${attrName}_dice`];
  // Ask the user for their boons/curses and convert the response to a number
  const boonCurse = +(await k.extractQueryResult('Boons / Curses|NA,0|+2D,2|+1D,1|−1D,-1|−2D,-2'));
  let totalDice = Math.max(0,dice + boonCurse);
  let rollString = totalDice <= 0 ?
    `[[2d6kl1]]` :
    `[[${totalDice}d6kh1]]`;
  return rollString;
}

// Converts the rollObj into a roll string.
const assembleRollString = (rollObj,rollStart = '&{template:icon}') => {
  return Object.entries(rollObj)
    .reduce((str,[field,value]) => {
      return str + ` {{${field}=${value}}}`;
    },rollStart);
}

/**
 * Function to do icon rolls
 * @param {object} trigger - The trigger that caused the function to be called
 * @param {object} attributes - The attribute values of the character
 * @param {object[]} sections - All the repeating section IDs
 * @param {object} casc - Expanded cascade object
 */
const iconRoll = async function({trigger,attributes,sections,casc}){
  // the async tag above tells javascript that this code will pause at some point (when we use the await keyword).
  // extract the section, rowID, and button from the trigger's name.
  const [section,rowID,button] = k.parseTriggerName(trigger.name);
  // Convert your button's name to a k-scaffold attribute name
  const attrName = button.replace(/-action$/,'').replace(/-/g,'_');
  // I specify my rolls with an object and then use an assembly function to convert them to roll strings. It makes this much easier to debug.
  const rollObj = {
    roll:await assembleRollField(attrName,attributes,sections),
    num6s:'[[0[computed value]]]'
  };

  const rollString = assembleRollString(rollObj);
  const roll = await startRoll(rollString);
  // Object to aggregate our modifications to the roll results
  const computeObj = {};
  // Count the number of 6s rolled by the dice
  computeObj.num6s = roll.results.roll.dice.reduce((total,die) => {
    if(die === 6){
      total++;
    }
    return total;
  },0);
  // Tell roll20 to release the roll to the chat with our changes.
  finishRoll(roll.rollId,computeObj);
};
k.registerFuncs({iconRoll});
December 22 (2 years ago)
GiGs
Pro
Sheet Author
API Scripter

CRP is certainly possible as scott suggests, but might be overkill. It seems to me you could easily have a sheet worker to do your initial pseudo code, and create an attributewit that. then in your roll button, call that attribute for the roll. That way the same rollbutton manages your two roll types easily.

December 22 (2 years ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator

As far as I know, there's no way to handle the multiple six rule without crp.

December 22 (2 years ago)
GiGs
Pro
Sheet Author
API Scripter

I'd missed the use of ?{modifier} there, which suggests its calculated as part of a roll as it's being made. In that case, CRP does like the only way to proceed.

December 31 (2 years ago)
Seraaron
Sheet Author

I've been on holiday but thanks for writing out the full thing in scaffold script. I'll try it out next week and let you know how it goes!

January 06 (2 years ago)

Edited January 06 (2 years ago)
Seraaron
Sheet Author

okay so, I wasn't expecting to be able to put copy and paste your code in and have it work out of the door, but once again I am confounded by js syntax I think... because I've been at it at almost an hour now and I'm no closer to getting this to work. Currently this is my actions PUG:

-
const actions = [
'charm',
'command',
'endure',
'excel',
'sense',
'smash',
'sneak',
'study',
'tinker',
'traverse'
];
- varObjects.actionNames = actions;

section#actions.actions
h1(data-i18n='actions')
each name in actions
.flex-box.space-around
+roller({name, trigger:{triggeredFuncs:['actionRoll']}})
+input({name, value:`${name}`, class:'action-label uppercase bigger', 'aria-level':3, data_i18n:`${name}`, type:'label', readonly:true})
+number({name:`${name}_dice`, disabled:'true', class:'autocalc', value:`(@{${name}-dots}+@{${name}-knack}-@{${name}-burden})`, 'aria-level':5})
+fillLeft({
radioObj:{name:`${name}-dots`},
divObj:{class:'action-radio'},
valueArray:[0,1,2,3,4]
})
+span({style:'width:20px; height:20px'})
+checkbox({name:`${name}-knack`, value:'1', class:'checkbox magenta tick'})
+span({style:'width:20px; height:20px'})
+checkbox({name:`${name}-burden`, value:'1', class:'checkbox'})
+fieldset({name:'action'})
.flex-box.space-around
+roller({name:'custom', trigger:{triggeredFuncs:['actionRoll']}})
+input({name:'name', class:'action-label uppercase bigger', 'aria-level':3, type:'label', 'data-i18n-placeholder':'custom'})
+number({name:'dice', disabled:'true', class:'autocalc', value:'(@{dots}+@{knack}-@{burden})', 'aria-level':5})
+fillLeft({
radioObj:{name:'dots'},
divObj:{class:'action-radio'},
valueArray:[0,1,2,3,4]
})
+span({style:'width:20px; height:20px'})
+checkbox({name:'knack', value:'1', class:'checkbox magenta tick'})
+span({style:'width:20px; height:20px'})
+checkbox({name:'burden', value:'1', class:'checkbox'})

the only solid change I've made is changing the name of your function to 'actionRoll' and changed the number of dice to be snake case.

Possibly the other reason this isn't working is because I have two types of action rolls that the macro needs to apply to. There's the default list of actions that all character have, and then there's a repeating segment for custom actions. I'm assuming that CRP the code you sent doesn't work simply because it's not being called properly by my PUG?? but i have honestly no idea at this point. The other potential problem is that I'm using an autocalc to do a bit of pre-roll maths, but that could possibly be absorbed into the roll function.

Currently, the roller button does nothing when pressed. There isn't even a log in the console

January 06 (2 years ago)

Edited January 06 (2 years ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator

Ah, your dice atttribute is a disabled attribute. You can't (easily) use sheetworkers with disabled attributes. You'll need to switch that to a readonly field and calculate the value with a sheetworker.

Edit: Just added a more explicit warning against using auto-calcs to the wiki pages.

January 06 (2 years ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
For the repeating version of the button, you may need to adjust the code to handle the repeating section. The version I wrote was for only non repeating versions. There's some additional support code you'll need for a repeating roller construct as well, but we can address that once we get the non repeating version working.
January 06 (2 years ago)
Seraaron
Sheet Author

Okay I've just changed the fillLeft name to be ${name}_dice for now and commented out the autocalc. The knacks and burden additions can be worked out later. But this isn't enough to get anything to pop up when the button is pressed, or for anything to log in the console. Is there a way to ping the console with js, just as a sanity check?

January 06 (2 years ago)

Edited January 06 (2 years ago)
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator

hmm, odd. Can you share a link to your full code? I can take a look. Only other thing I can think of is, when you changed the function name, did you update the registration as well?

edit:
e.g. 

k.registerFuncs({iconRoll});

changed to

k.registerFuncs({actionRoll});
January 07 (2 years ago)
Seraaron
Sheet Author

I changed the roll function back to what you posted originally and changed the function call in the roller back to 'iconRoll' just to be sure and still got nothing. Here's the github repo https://github.com/Seraaron/roll20-character-sheets/tree/master/ICON%20RPG%20%5BMassifPress%5D

January 14 (2 years ago)
Seraaron
Sheet Author

Hm.. I think I've figured it out using conditional helper functions in the roll template itself. Using 'RollGreater()', and just making each roll separately but only showing one based on the result of a hidden calculation.