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] Repeating list not re-ordering?

1533217994
Missingquery
Pro
Sheet Author
API Scripter
Okay, so I have two repeating sections on a given character, and I have a variable: let StrengthsA = getAttrByName(attacker.id, "repeating_weapons_$0_Strengths") Now, when I test my script, regardless of the ordering of these two repeating sections, the script always gets the repeating section that was created first, and not the actual firstmost repeating section. Is there something I'm doing wrong here, or is the repeating sections reordering broken or something? The script in question can be found in its entirety here .
1533239801
GiGs
Pro
Sheet Author
API Scripter
Accessing the order of repeating sections can be tricky. The Wiki covers it extremely briefly ( getSectionIDs ). There's a function in that section that will give you the sectionIDs in their actual displayed order, but the syntax confuses me and i don't know how to use it in practice. 
1533261229
Missingquery
Pro
Sheet Author
API Scripter
I've used that one before, though that's a Sheetworkers only thing and doesn't really help here. ^^ I've also tried storing the ID of the firstmost section as an attribute and passing the value into the API for use, though that method was incredibly buggy and inconsistent.
1533262117
The Aaron
Pro
API Scripter
Here's a script I wrote that will resolve a $# reference to the right sorted order, so say you asked for repeating_inventory_$2_item_weight, it would resolve to the right one based on sorting: on('ready',function(){ "use strict"; var attrLookup = function(character,name,caseSensitive){ let match=name.match(/^(repeating_.*)_\$(\d+)_.*$/); if(match){ let index=match[2], attrMatcher=new RegExp(`^${name.replace(/_\$\d+_/,'_([-\\da-zA-Z]+)_')}$`,(caseSensitive?'i':'')), createOrderKeys=[], attrs=_.chain(findObjs({type:'attribute', characterid:character.id})) .map((a)=>{ return {attr:a,match:a.get('name').match(attrMatcher)}; }) .filter((o)=>o.match) .each((o)=>createOrderKeys.push(o.match[1])) .reduce((m,o)=>{ m[o.match[1]]=o.attr; return m;},{}) .value(), sortOrderKeys = _.chain( ((findObjs({ type:'attribute', characterid:character.id, name: `_reporder_${match[1]}` })[0]||{get:_.noop}).get('current') || '' ).split(/\s*,\s*/)) .intersection(createOrderKeys) .union(createOrderKeys) .value(); if(index<sortOrderKeys.length && _.has(attrs,sortOrderKeys[index])){ return attrs[sortOrderKeys[index]]; } return; } return findObjs({ type:'attribute', characterid:character.id, name: name})[0]; }; on('chat:message',function(msg){ if('api' === msg.type && msg.content.match(/^!resolve-repeating\b/) ){ let args=_.rest(msg.content.split(/\s+/)), who = getObj('player',msg.playerid).get('displayname'); if(args.length){ _.chain(msg.selected) .map((o)=>getObj('graphic',o._id)) .reject(_.isUndefined) .filter((t)=>t.get('represents').length) .map(t=>{ return {token:t,character:getObj('character',t.get('represents'))};}) .reject(o=>_.isUndefined(o.character)) .map(o=>{ o.attrs=_.reduce(args,(m,a)=>{ let attr=attrLookup(o.character,a,false); m[a]=(attr ? attr.get('name') : '[MISSING]'); return m; },{}); return o; }) .map(o=> `<div><h3>${o.character.get('name')}</h3><ul>${_.map(o.attrs,(a,n)=>`<li><b>${n}</b>-<code>${a}</code></li>`).join('')}</ul></div>`) .tap(out=>{ sendChat('',`/w ${who} ${out.join('')}`); }); } else { sendChat('',`/w ${who} <b>No tokens selected</b>`); } } }); });
1533273579

Edited 1533349056
Missingquery
Pro
Sheet Author
API Scripter
Okay, so I tried to repurpose the above script for use in my script's context, but it doesn't seem to be working. Here's how I used it: var attrLookup; on('ready',function(){ "use strict"; attrLookup = function(character,name,caseSensitive){ let match=name.match(/^(repeating_.*)_\$(\d+)_.*$/); if(match){ let index=match[2], attrMatcher=new RegExp(`^${name.replace(/_\$\d+_/,'_([-\\da-zA-Z]+)_')}$`,(caseSensitive?'i':'')), createOrderKeys=[], attrs=_.chain(findObjs({type:'attribute', characterid:character.id})) .map((a)=>{ return {attr:a,match:a.get('name').match(attrMatcher)}; }) .filter((o)=>o.match) .each((o)=>createOrderKeys.push(o.match[1])) .reduce((m,o)=>{ m[o.match[1]]=o.attr; return m;},{}) .value(), sortOrderKeys = _.chain( ((findObjs({ type:'attribute', characterid:character.id, name: `_reporder_${match[1]}` })[0]||{get:_.noop}).get('current') || '' ).split(/\s*,\s*/)) .intersection(createOrderKeys) .union(createOrderKeys) .value(); if(index<sortOrderKeys.length && _.has(attrs,sortOrderKeys[index])){ return attrs[sortOrderKeys[index]]; } return; } return findObjs({ type:'attribute', characterid:character.id, name: name})[0]; }; }); //... on('chat:message', function(msg) { if (msg.type != 'api') return; var parts = msg.content.split(' '); var command = parts.shift().substring(1); function ReorderRepeating(token, attrname){ let who = getObj('player',msg.playerid).get('displayname') _.chain(token) .map((o)=>getObj('graphic',o._id)) .reject(_.isUndefined) .filter((t)=>t.get('represents').length) .map(t=>{ return {token:t,character:getObj('character',t.get('represents'))};}) .reject(o=>_.isUndefined(o.character)) .map(o=>{ o.attrs=_.reduce(attrname,(m,a)=>{ let attr=attrLookup(o.character,a,false); m[a]=(attr ? attr.get('name') : '[MISSING]'); return m; },{}); return o; }) .map(o=> `<div><h3>${o.character.get('name')}</h3><ul>${_.map(o.attrs,(a,n)=>`<li><b>${n}</b>-<code>${a}</code></li>`).join('')}</ul></div>`) .tap(out=>{ sendChat('',`/w ${who} ${out.join('')}`); }); } // Don't run if it's any other command if (command == 'combat') { //... let StrengthsA = getAttrByName(attacker.id, "repeating_weapons_$0_Strengths") || ""; let StrengthsB = getAttrByName(defender.id, "repeating_weapons_$0_Strengths") || ""; let WeaknessA = getAttrByName(attacker.id, 'weaknesses'); let WeaknessB = getAttrByName(defender.id, 'weaknesses'); ReorderRepeating(selectedToken, "repeating_weapons_$0_Strengths"); Which doesn't do anything and only returns in the chat: (From ): ??? 0 - [MISSING] r - [MISSING] e - [MISSING] p - [MISSING] a - [MISSING] t - [MISSING] i - [MISSING] n - [MISSING] g - [MISSING] _ - [MISSING] w - [MISSING] o - [MISSING] s - [MISSING] $ - [MISSING] S - [MISSING] h - [MISSING] I can only assume I either messed up somewhere while porting it over into the script context, or I'm not using it correctly. Help? Also, if it's not too much of a bother, I would love an in-depth explanation of how this works, since Regex and Underscore.js stuff flies completely over my head.
1533349023
Missingquery
Pro
Sheet Author
API Scripter
Any further help would still be appreciated!
1533349366
GiGs
Pro
Sheet Author
API Scripter
Aaron's the most likely able to help you with this, and he's a way at GenCon right now. 
1533351887
Missingquery
Pro
Sheet Author
API Scripter
Ah, gotcha. That's fine!
1533390972
The Aaron
Pro
API Scripter
Yeah, sorry. GenCon is eating all my time.  In brief, Riley explained how the repeating groups sorting worked sufficiently that I was able to write the above. I’ll have to go into more detail about it later and try to understand more thoroughly what the problem you’re trying to solve is. Sorry for the delay!
1533412115

Edited 1533524603
Missingquery
Pro
Sheet Author
API Scripter
Hey, it's not a problem at all! Take your time. Edit: Nevermind, actually, I think I might have the problem figured out. Sorry for the inconvenience!
1533608464

Edited 1533609072
Missingquery
Pro
Sheet Author
API Scripter
Actually, scratch that, I'm having another problem. The function seems to be struggling to get some values, which is pretty weird. I've modified the function so it returns the contents of the object (using it as a substitute for getAttrByName) but I can't figure out where the problem is. Here's all the relevant code: var attrLookup = function(character,name,caseSensitive){ let match=name.match(/^(repeating_.*)_\$(\d+)_.*$/); let returnval; if(match){ let index=match[2], attrMatcher=new RegExp(`^${name.replace(/_\$\d+_/,'_([-\\da-zA-Z]+)_')}$`,(caseSensitive?'i':'')), createOrderKeys=[], attrs=_.chain(findObjs({type:'attribute', characterid:character.id})) .map((a)=>{ return {attr:a,match:a.get('name').match(attrMatcher)}; }) .filter((o)=>o.match) .each((o)=>createOrderKeys.push(o.match[1])) .reduce((m,o)=>{ m[o.match[1]]=o.attr; return m;},{}) .value(), sortOrderKeys = _.chain( ((findObjs({ type:'attribute', characterid:character.id, name: `_reporder_${match[1]}` })[0]||{get:_.noop}).get('current') || '' ).split(/\s*,\s*/)) .intersection(createOrderKeys) .union(createOrderKeys) .value(); if(index<sortOrderKeys.length && _.has(attrs,sortOrderKeys[index])){ returnval = attrs[sortOrderKeys[index]]; if (returnval != undefined){ return returnval.get("current") } else { return "" } } } returnval = findObjs({ type:'attribute', characterid:character.id, name: name})[0]; log(returnval); log(findObjs({ type:'attribute', characterid:character.id, name: name})[0]) log(character.get("name")); log(character.id) log(name); log("---") if (returnval != undefined){ return returnval.get("current") } else { return "" } }; ... let WNameA = attrLookup(attacker,"repeating_weapons_$0_WName",false) || "Empty"; Which logs something like: undefined //returnval undefined //pure obj "A" //character name "-LIDxNUsJqC5bMDBDRQo" //logs character id "repeating_weapons_$0_WType" //name; I assume this is where the problem is, since findObjs doesn't like this notation iirc "---" //divider Upon closer inspection, it seems like there's an issue with the match checking. In the meantime, my hotfix will just be changing the findObjs at the end to getAttrByName, but I'd love to get some help with this as well. It seems to be inconsistent whether or not it works from character to character.
1533649846
The Aaron
Pro
API Scripter
Hmm..  The  repeating_weapons_$0_WType should not be passed to findObjs(), it should instead detect that it's a repeating group ordered index and find all attributes for that character, then match them against the repeating group's pattern, build an ordered lookup, and then select the item based on the index in the original name.  I'll have to do some testing, but if you dump out the attrs variable and the sortOrderKeys variable just above if(index<sortOrderKeys.length ... it should help with understanding the function.  The way that ordering repeating groups works is that when you reorder them, it creates a hidden pseudo-attribute named _reporder_repeating_foo (where foo is the name of the group) containing a comma separated value list of row ids in the sorted order.  That list is only changed on reordering, and not on creating, so it is definitive for things that were sorted, and anything unsorted is just tacked on in creation order.  If no sorting has ever occurred, the hidden pseudo-attribute won't even exist.