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);
})
})