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 .
×

I need a RepeatingSum Function but for combining Strings

I'm looking for a way to have a function like RepeatingSum except to combine strings. In my game, we have attack options that can have different effects and characters can have 2 used at once. So I want the characters to be able to use the checkbox in each repeating attack option field to select which ones they will use. I've used the repeatingSum function to add up the attack and damage bonuses, but I don't know how to set it up for text. <input type="number" name="attr_AAOAttackBonus" value= "555"  disabled="true"> <input type="hidden" value="Murder Machine" name="attr_ActiveAttackOption"> <input type="number" name="attr_AAODamageBonus" value="444" disabled="true"> <input type="hidden" value="I murdered him Avy." name="attr_ActiveAttackOptionEffects"> <fieldset class="repeating_attackoptions">                 <table>                     <tr>                         <td><input type="checkbox" name="attr_AttackOptionUsed"></td>                         <td><input type="text" class="weaponInfo" name="attr_AttackOptionName"></td>                         <td><input type="number" class="weaponInfo" value=0 name="attr_AttackOptionAttackBonus"></td>                         <td><input type="number" class="weaponInfo" value=0 name="attr_AttackOptionDamageBonus"></td>                     </tr>                     <tr class="label">                         <td>use</td>                         <td>Attack Option</td>                         <td>Attack Bonus</td>                         <td>Damage Bonus</td>                     </tr>                     <tr>                         <td colspan=4>                             <textarea type="text" name="attr_AttackOptionEffects"></textarea>                         </td>                     </tr>                     <tr class='label'>                         <td></td>                         <td>Effects</td>                     </tr>                 </table>             </fieldset> So how could I get "repeatedText" for the selected Names and Effects of the attack options?
1608788566

Edited 1608875050
Oosh
Sheet Author
API Scripter
Someone else probably has a much better function, but you can try this. I'm pretty useless with the sheet & HTML side of things & I can't actually test this in the sheet sandbox, but...... // Similar input to repeatingSum: // Destinations: a single attribute or array of attributes to output to // Section: section name without the 'repeating' // Fields: Names of repeating attributes to grab values from // Check: Name of repeating checkbox to check if row is active // JoinString: character with which to join attribute values, defaults to ', ' (i.e. 'dagger, bow, sword') // ** Number of destinations -- Number of fields ratio: ** // -- 1 destination, or less destinations than fields: the function will join all values, row by row, // into a single Attribute, (the first or only entry in [destinations]), // -- more than one destination, and equal number of fields and destinations: function will proceed from // left to right, combining the values from each field into each destination attribute. For example: // repeatingString(['outputName','outputType'], section_name, ['name','type']) // would result in the attribute 'outputName' containing all the 'repeating_sectionname_rowid_name' values // and an attribute 'outputType' containing all the 'repeating_sectionname_rowid_type' values const repeatingString = (destinations, section, fields, check, joinString = ', ') => { if (!Array.isArray(destinations)) destinations = [destinations.replace(/\s/g, '').split(',')]; if (!Array.isArray(fields)) fields = [fields.replace(/\s/g, '').split(',')]; fields.push(check); getSectionIDs(`repeating_${section}`, idArray => { const attrArray = idArray.reduce((m, id) => [...m, ...(fields.map(field => `repeating_${section}_${id}_${field}`))], []); getAttrs([...attrArray], v => { let regexCheck = new RegExp(`repeating_${section}_(.*)_${check}`,'i'); let activeRows = []; for (let attr in v) { if (attr.match(regexCheck) && v[attr] != 0) activeRows.push(attr.match(regexCheck)[1]); } fields.pop(); const output = {}; let destinationSingle = (destinations.length === 1 || destinations.length < fields.length) ? true : false; for (let i=0; i < destinations.length; i++) { let outArray = []; for (let key in v) { let label = key; let regexField = new RegExp(`${fields[i]}`,'i') let regexRow = new RegExp(`repeating_${section}_(.*)_`,'i') if ( ((destinationSingle) || label.search(regexField) !== -1) && (!label.match(regexCheck)) && (label.match(regexRow) && activeRows.includes(label.match(regexRow)[1]))) outArray.push(v[key]); } output[destinations[i]] = outArray.join(joinString); } setAttrs(output); }); }); }; It's not nearly as elegant as repeatingSum(), but I don't fully understand how checkboxes & values work on the sheet so I did this the long way around. Hopefully it works. I'm used to the API, rather than the sheetworker helper functions. Then I think you want to call the function with something like this: repeatingString(['combineNames','combineEffects'], 'attackoptions', ['AttackOptionName','AttackOptionEffects'], 'AttackOptionUsed'); That will set two Attributes, combineNames will be something like 'Dagger, Bow, Shortsword', and combineEffects would be something like 'Stabby, Ranged Attack, Slashing'. Obviously I made up the Names & Effects, no idea what the Attributes will really hold. You can also supply a fifth argument to change the way the strings are joined. If it's only ever 2, you could use: repeatingString(['combineNames','combineOptions'], 'attackoptions', ['AttackOptionName','AttackOptionEffects'], 'AttackOptionUsed', ' & '); to join them with an ampersand instead. Could also remove the .join() and setAttrs() from the end of the function and just have it return the arrays if you would prefer to manipulate them yourself before setting an Attribute from them.
1608800146
GiGs
Pro
Sheet Author
API Scripter
What text attributes do you want to extract from each row (the exact attribute names), what attributes are they saved to, and how are they combined? Should each be separated by a ". " for example?
1608807935
Oosh
Sheet Author
API Scripter
GiGs!!!! Where have you been? I've been busy giving awful advice to people in the Character Sheet section, because you suddenly stopped posting.
1608808571
GiGs
Pro
Sheet Author
API Scripter
I've been quite sick (not Covid), but I'm back now. Hehe, you've been giving good advice, as I've seen while doing my best to catch up on threads.
1608809035
Oosh
Sheet Author
API Scripter
Funnily enough, I was just about to start a WHERE IS GIGS thread when I opened the laptop. Glad you're back, I can go back to my free learning course each time you post here :) Speaking of threads, I'll stop derailing this one...
The two fields I need are these, AttackOptionName and AttackOptionEffects If I had to choose one separator, I would probably want them separated with a ", " I would prefer to be able to pass in a short string so I could use ". " or " and " when preferred, but either would work.
Sorry, I replied earlier and it didn't go through. Apparently when I tried to quote in the reply, it blocked the response. I have no idea why. Maybe I need permissions or something for that to work.
1608863660
GiGs
Pro
Sheet Author
API Scripter
I got a lot of notifications of updates to this thread, so I'm guessing the system detected that you tried to send a message, but then didnt display it for some reason.
1608864003
GiGs
Pro
Sheet Author
API Scripter
Nicholas E. said: The two fields I need are these, AttackOptionName and AttackOptionEffects If I had to choose one separator, I would probably want them separated with a ", " I would prefer to be able to pass in a short string so I could use ". " or " and " when preferred, but either would work. I might not have been precise enough with my earlier question. Do you want the AttackOptionName values saved to the ActiveAttackOption input, and the Effects one saved to ActiveAttackOptionEffects? If players can have up to 2 options selected, what should happen when they try to select a third, or fourth? You might need the sheet worker to block one, use a combination of sheet worker and CSS to hide the checkboxes when 2 options are selected. How are you planning to use the values in the ActiveAttackOption and ActiveAttackOptionEffects attaributes? I'm wondering if you can have only 2 options selected, wouldnt it be better to have an attribute for each of them (attack1name, attack2name, etc) to make them easy to use and arrange with your rolltemplate display. You could have a sheet worker that updates those 
1608875218

Edited 1608875373
Oosh
Sheet Author
API Scripter
The one I posted was no good, then? :(
1608875959
GiGs
Pro
Sheet Author
API Scripter
It should be tried! My feeling is that the approach of putting the two names in one attribute, and two effects in another attribute, is a clunky way to handle this, and I'm going back to first principles, trying to figure out exactly what OP is trying to achieve and see if there's a more elegant way to handle it.
Do you want the AttackOptionName values saved to the ActiveAttackOption input, and the Effects one saved to ActiveAttackOptionEffects? Yes, those are the two variables I want the combined strings saved as. The attack options are limited to 2, so saving them to two variables can work if that is more efficient. If players can have up to 2 options selected, what should happen when they try to select a third, or fourth? You might need the sheet worker to block one, use a combination of sheet worker and CSS to hide the checkboxes when 2 options are selected. We are not blocking it at all, and leaving that to the players right now. Blocking it would be good. Only one character type can do this (2 when we release an expansion), and the rest can only choose one. How are you planning to use the values in the ActiveAttackOption and ActiveAttackOptionEffects attaributes? I'm wondering if you can have only 2 options selected, wouldnt it be better to have an attribute for each of them (attack1name, attack2name, etc) to make them easy to use and arrange with your rolltemplate display. Right now, we are planning on having this be the last row of the roll template {{@{ActiveAttackOption} Effect=@{ActiveAttackOptionEffects} }}
Also, we are using the repeated fields because they can have 20 attack options, but they can only use one or two on each attack. I'm unsure if that was clear or not.
1608964703

Edited 1608965601
GiGs
Pro
Sheet Author
API Scripter
For curiousity, I tested Oosh's approach, and it works. Copy the code below into your script block. Note that the sheet worker below the script is needed to make it work. // Similar input to repeatingSum: // Destinations: a single attribute or array of attributes to output to // Section: section name without the 'repeating' // Fields: Names of repeating attributes to grab values from // Check: Name of repeating checkbox to check if row is active // JoinString: character with which to join attribute values, defaults to ', ' (i.e. 'dagger, bow, sword') // ** Number of destinations -- Number of fields ratio: ** //    -- 1 destination, or less destinations than fields: the function will join all values, row by row, //          into a single Attribute, (the first or only entry in [destinations]), //    -- more than one destination, and equal number of fields and destinations: function will proceed from //          left to right, combining the values from each field into each destination attribute. For example: //          repeatingString(['outputName','outputType'], section_name, ['name','type']) //          would result in the attribute 'outputName' containing all the 'repeating_sectionname_rowid_name' values //          and an attribute 'outputType' containing all the 'repeating_sectionname_rowid_type' values const repeatingString = (destinations, section, fields, check, joinString = ', ') => {     if (!Array.isArray(destinations)) destinations = [destinations.replace(/\s/g, '').split(',')];     if (!Array.isArray(fields)) fields = [fields.replace(/\s/g, '').split(',')];     fields.push(check);     getSectionIDs(`repeating_${section}`, idArray => {         const attrArray = idArray.reduce((m, id) => [...m, ...(fields.map(field => `repeating_${section}_${id}_${field}`))], []);         getAttrs([...attrArray], v => {             let regexCheck = new RegExp(`repeating_${section}_(.*)_${check}`,'i');             let activeRows = [];             for (let attr in v) {                 if (attr.match(regexCheck) && v[attr] != 0) activeRows.push(attr.match(regexCheck)[1]);             }             fields.pop();             const output = {};             let destinationSingle = (destinations.length === 1 || destinations.length < fields.length) ? true : false;             for (let i=0; i < destinations.length; i++) {                 let outArray = [];                 for (let key in v) {                     let label = key;                     let regexField = new RegExp(`${fields[i]}`,'i')                     let regexRow = new RegExp(`repeating_${section}_(.*)_`,'i')                     if ( ((destinationSingle) || label.search(regexField) !== -1)                         && (!label.match(regexCheck))                         && (label.match(regexRow) && activeRows.includes(label.match(regexRow)[1]))) outArray.push(v[key]);                 }                 output[destinations[i]] = outArray.join(joinString);             }             setAttrs(output);         });      });  }; on('change:repeating_attackoptions:attackoptionname change:repeating_attackoptions:attackoptioneffects change:repeating_attackoptions:attackoptionused', () => {     repeatingString(['ActiveAttackOption','ActiveAttackOptionEffects'], 'attackoptions', ['AttackOptionName','AttackOptionEffects'], 'AttackOptionUsed', ' & '); }); PS: in the original html there seems to be an issue: <input type="number" name="attr_AAOAttackBonus" value= "555"  disabled="true"> <input type="hidden" value="Murder Machine" name="attr_ActiveAttackOption"> <input type="number" name="attr_AAODamageBonus" value="444" disabled="true"> <input type="hidden" value="I murdered him Avy." name="attr_ActiveAttackOptionEffects"> The two disabled="true"  sections there should be readonly , since sheet workers cant update disabled inputs. In my testing, i changed the two hidden inputs to text, so i could see the script working. You'll probably want to change those two values to value="" ; to ensure when nothing is checked in the repeating section, you dont get a silly output. This approach has the problem of not really formatting the output very well for your needs, when you have more than one thing checked, but you'll see that as you play with it. That's not an issue with Oosh's approach, but the method you've chosen to format the data.
1608966644
GiGs
Pro
Sheet Author
API Scripter
Nicholas E. said: Right now, we are planning on having this be the last row of the roll template {{@{ActiveAttackOption} Effect=@{ActiveAttackOptionEffects} }} Imagine you could have each attack listed separately. You could have output like {{attack1=Murder machine Effect= I murdered him Avy.  }} {{attack2= Second Attack  Effect= Ouch I bet that hurt }} Or even {{Murder machine=I murdered him Avy. }} {{Second Attack=Ouch I bet that hurt}} What I'm suggesting here is setting up your rolltemplate to accept inputs in a format that makes the data more presentable, and then it easy to present the information. You could have a sheet worker, that scans the section and outputs the entire data in the above format to one attribute, which owuld effectively look like <input type="hidden" name="attr_attackdetails" value="{{attack1=Murder Machine Effect= I murdered him Avy.  }} {{attack2=Second Attack Effect=Ouch I bet that hurt }}"> The value above would normally be "" - this is what it would look like after the sheet worker runs. You'd need to refine your rolltemplate - looking into the allprops feature, which allows you to print out all supplied properties even ones you hadnt planned for would make this work pretty well. It also would allow for handling more than 2 attacks if needed. Then a sheet worker could be build to give the output your rolltemplate needs based on which attacks are checked.
1608966751
GiGs
Pro
Sheet Author
API Scripter
Oosh said: Funnily enough, I was just about to start a WHERE IS GIGS thread when I opened the laptop. Glad you're back, I can go back to my free learning course each time you post here :) Speaking of threads, I'll stop derailing this one... Derailing a thread with praise for me is perfectly acceptable! Thanks for validation :)
Thank you both so much. These forums have saved me repeatedly through this process.