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

Custom roll parsing and a Repeating Section

February 27 (2 years ago)

Hello everyone,

I have been experimenting on this since some time now and I am having troubles figuring IF it's possible to do what I am thinking.

I am trying to create a custom character sheet for a game called Fabula Ultima, in this game you have four attributes that are represented by a dice, the higher the dice, the better you are, I was able to do this with four select.

Now, weapons in the game works like this, the attack roll is for example ATTR1 + ATTR2 + Modifier so, if ATTR1 = 1d8 and ATTR2 = 1d6 the roll is 1d8+1d6+1, I was able to create a repeating where you can add your weapons, add the name, specify the modifier and use a button to roll like this:

<button type="roll" name="roll_weapon" value="/em attacks with @{weaponname}: [[@{attr1}+@{attr2}+@{modatt}]]"></button>

This is actually working but here comes the first problem, I'd love to let the character to choose which are the two attributes to use for a weapon, since not all weapons use attr1 and attr2, but I cannot figure if it's possible to use two select and use them to refer to the character attributes

Now comes the damage part of the problem, in this game once you have hit the target with your attack, the damage is made of the maximum of the two rolls plus damage modifier, I believe here comes the Custom Roll Parsing magic but again, I need to be able to create the roll for the startRoll function considering what I've said before, the two dices need to depend from the choices made from the weapon added in the repeating.

Is this possible?

Any suggestion?

Thank you very much in advance for any answer and best regards

Fra

February 28 (2 years ago)

+watching this thread


Hi Francesco,

I am in a similar place exploring Custom Roll Templates and Custom Roll Parsing. I don't have the expertise to give you advice, unfortunately. But I am pretty sure that this can be accomplished in a couple of different ways. I will be watching the replies. 


Good luck, my friend.

-Sean

February 28 (2 years ago)

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

Yes, this is all possible.

Your first question about allowing the user to select which attribute to use, doesn't strictly require CRP, but I'll answer using CRP since your second question does require it.

For your second question, this does indeed require CRP.

Here's a rough example of how I'd do both of these:

HTML

<label>
  Attr1
  <input type="text" name="attr_attribute_1">
</label>
<label>
  Attr2
  <input type="text" name="attr_attribute_2">
</label>
<label>
  Attr3
  <input type="text" name="attr_attribute_3">
</label>
<label>
  Attr4
  <input type="text" name="attr_attribute_4">
</label>

<fieldset class="repeating_attacks">
  <button type="action" name="act_roll">Roll</button>
<label>
  weapon attribute 1
  <select name="attr_option_1">
    <!--
      Having a default option that has no value is
      important because repeating sections have issues
      setting their default values from selects
      -->
    <option value="" data-i18n="select one" selected=''></option>
    <option value="@{attribute_1}" data-i18n="attribute 1"></option>
    <option value="@{attribute_2}" data-i18n="attribute 2"></option>
    <option value="@{attribute_3}" data-i18n="attribute 3"></option>
    <option value="@{attribute_4}" data-i18n="attribute 4"></option>
  </select>
</label>
<label>
  weapon attribute 2
  <select name="attr_option_2">
    <option value="" data-i18n="select one" selected=''></option>
    <option value="@{attribute_1}" data-i18n="attribute 1"></option>
    <option value="@{attribute_2}" data-i18n="attribute 2"></option>
    <option value="@{attribute_3}" data-i18n="attribute 3"></option>
    <option value="@{attribute_4}" data-i18n="attribute 4"></option>
  </select>
</label>
<label><span>bonus</span><input type="number" name="attr_bonus"></label>
<label><span>damage</span><input type="number" name="attr_damage"></label>
</fieldset>
<!-- Very basic roll template to demo -->
<rolltemplate class="sheet-rolltemplate-demo">
  attack: {{roll}} damage: {{computed::damage}}
</rolltemplate>

Sheetworker

const baseAttributes = ["attribute_1","attribute_2","attribute_3","attribute_4"];
  // Parses out the section name, row id, and attribute/button name
  const parseTriggerName = function(string){
    let match = string.replace(/^clicked:/,'').match(/(?:(repeating_[^_]+)_([^_]+)_)?(.+)/);
    match.shift();
    return match;
  };

  on('clicked:repeating_attacks:roll',(event) => {
    const [section,rowID] = parseTriggerName(event.triggerName);
    // Assemble the row string so we can use it easily later
    const row = `${section}_${rowID}`;
    // Get all the attributes we need
    // Note the async tag here. This will allow us to await 
    // startroll instead of using the callback syntax.
    getAttrs([`${row}_option_1`,`${row}_option_2`,`${row}_bonus`,`${row}_damage`,...baseAttributes],async (attributes) => {
      // Assemble our roll string
      const rollString = `&{template:demo} {{roll=[[${attributes[`${row}_option_1`]} + ${attributes[`${row}_option_2`]} + ${attributes[`${row}_bonus`]}]]}} {{damage=[[${attributes[`${row}_damage`]}[base damage]]]}}`;
      // Send the roll to the parser
      const results = await startRoll(rollString);
      // Extract the roll result so we can work with it more easily.
      const roll = results.results.roll;
      // An empty object to hold the calculations/adjustments we're going to do
      const computedObj = {};

      // The dice values of the roll are in an array stored in the property `dice`
      // figure the highest die used in the roll
      const highDie = Math.max(...roll.dice);
      // Convert the attribute value of damage into a number type, default to zero if it isn't a valid number.
      const damage = +attributes[`${row}_damage`] || 0;
      // Add the high die and the damage together, and store
      // it in the damage property of computedObj
      computedObj.damage = highDie + (damage);

      // Tell roll20 that the roll is ready to be displayed in chat
      finishRoll(results.rollId,computedObj);
    })
  })
February 28 (2 years ago)

Thank you very much Scott!

I am having some troubles completely understanding how this custom roll parsing works, even after having read the wiki and some forum posts (Oosh ones for example) and it looks like there is a lot of trial and error learning involved :D For example, the parseTriggerName you use to identify the repeating row is something that I was completely missing, but thanks to your very kind reply I am understanding more and more.

I'll try to implement your suggestion and see if I can make it work, but by the look of it it should be smooth, I'll let you know as soon as possible, meanwhile I thank you very much and wish you a great day.

Best regards

Fra

February 28 (2 years ago)

Hello Scott,

thanks to your code I was able to make it work and understand a lot more of how things work, but I had parsing error on the roll template and I had to tinker a bit to make it work, I see you used a lot of ${something} in your code and I believe that was the problem, this is my version that is actually working, can you please give it a quick check and tell me if this is the intended way of writing CRP or if there was indeed some parsing error to fix? I am trying to learn the proper way of doing things.

Thank you in advance

Fra


on('clicked:repeating_weapons:roll',(event) => {
							const [section,rowID] = parseTriggerName(event.triggerName);
							// Assemble the row string so we can use it easily later
							const row = section + '_' + rowID;
							
							console.log(row);
							
							// Get all the attributes we need
							// Note the async tag here. This will allow us to await 
							// startroll instead of using the callback syntax.
							getAttrs([row + '_weaponname',row + '_option_1',row + '_option_2',row + '_bonus',row + '_damage',...baseAttributes],async (attributes) => {
								// Assemble our roll string
								//console.log("ok1");
								
								//console.log(attributes);
								
								const rollString = "&{template:weapon} {{name=" + attributes[row + '_weaponname'] + "}} {{roll=[[" + attributes[row + '_option_1'] + " + " + attributes[row + '_option_2'] + " + " + attributes[row + '_bonus'] + "}]]}} {{damage=[[" + (attributes[row + '_damage'] || 0) + "]]}}";
								
								console.log(rollString);
								
								// Send the roll to the parser
								const results = await startRoll(rollString);
								
								//console.log("ok2");
								
								// Extract the roll result so we can work with it more easily.
								const roll = results.results.roll;
								// An empty object to hold the calculations/adjustments we're going to do
								const computedObj = {};

								// The dice values of the roll are in an array stored in the property `dice`
								// figure the highest die used in the roll
								const highDie = Math.max(...roll.dice);
								// Convert the attribute value of damage into a number type, default to zero if it isn't a valid number.
								const damage = parseInt(attributes[row + '_damage'] || "0");
								// Add the high die and the damage together, and store
								// it in the damage property of computedObj
								computedObj.damage = highDie + (damage);

								// Tell roll20 that the roll is ready to be displayed in chat
								finishRoll(results.rollId,computedObj);
							})
						})
February 28 (2 years ago)
GiGs
Pro
Sheet Author
API Scripter

I think the problem is more one of presentation, than inherent complexity. No offence to Scott and Oosh - their examples are not meant for beginners. But there's no excuse for the official documentation from Roll20, which reads to me almost like they were trying to make it look more complicated than it is. I know I struggled when first reading the initial article, and I know my way around javascript.

Though to be fair, when jumping in to something like CRP, and you also have to learn repeating sections, rolltemplates, and more. There's a big learning curve there, so don't be too hard on yourself.

February 28 (2 years ago)

Thank you GiGs,

before writing here I have searched through google a lot and also read the documentation, but it's really hard for beginners, as you have said (and thankfully I am a developer, can't think about a "normal" person trying this out).

As of now my custom character sheet is coming out pretty well, later I'll need to figure out how to handle custom crit conditions but I think I saw something around and I'll be able to do something.

My only concern now is the question I have posted to Scott, have I done well with my worker by removing the various ${} or the problem was somewhere else?

Thank you again for your kind words, I foresee a pretty hard road in front of me for this custom sheet, but having a great community and kind people like you and Scott is what makes me continue the travel.

Best Regards

Fra

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

so, to explain the ${} constructions, they are template literals. This is the same end result as what you are doing with the +'s, but results in more readable code (at least in my opinion). The important difference between string concatenation like you are doing and template literals is that template literals use backticks (`) instead of single or double quotes (' or "). You can read the mdn page linked in this post to learn all the syntax of them.

February 28 (2 years ago)

This thread is gold. Thank you. :)

February 28 (2 years ago)


Scott C. said:

so, to explain the ${} constructions, they are template literals. This is the same end result as what you are doing with the +'s, but results in more readable code (at least in my opinion). The important difference between string concatenation like you are doing and template literals is that template literals use backticks (`) instead of single or double quotes (' or "). You can read the mdn page linked in this post to learn all the syntax of them.

Holy cow, today I have learned something new and for this I thank you very much, my javascript really needs to be updated a bit, I tough that the backticks was an error in the copy/paste, now your example works like a charm, I owe you some beers Scott

Thank you again and best regards


February 28 (2 years ago)
GiGs
Pro
Sheet Author
API Scripter


Scott C. said:

but results in more readable code (at least in my opinion).

Total agreement. In my experience, template literals look more confusing the first couple of times you use them, but they do very quickly become more readable than previous code, and very simple to use - and flexible, too.