Setting character attributes

1462363219

Edited 1462364513
I am looking for a way to update a lot of character attributes in one go. (I'm playing a druid character in DnD 3.5 and wildshape involves updating around 10 attributes) I have tweaked the !setprop script to take a _characterid and this works but does involve calling !setprop for each attribute I need to change. It sometimes hangs when multiple calls are kicked off in quick succession. Ideally I'm looking for a !setattr function that can take a characterid and then pairs of names and values, setting the "current" value of the named attribute to the new value Something like !setattr -c @{MyName|character_id} Str 27 Dex 13 Con 19 NaturalAC 5 Damage 1d8 Form Bear SR 0 I'm no expert in java script so wondered if there is something already available or if someone more skilled would be prepared to code it. Any help will be most appreciated.
1462386835

Edited 1462387839
Nic
Pro
What's the exact name of each of the attributes you want to change?
1462390384

Edited 1462391031
Nic
Pro
I GIVE UP TRYING TO MAKE THIS EMBED  This should work, assuming the variable names are the same was what you listed in your original post. You call this script by: !wildshape [character name] [value you want to change Str to] [value you want to change Dex to] [value you want to change Con to] [value you want to change NaturalAC to] [value you want to change Damage to] [value you want to change Form to] [value you want to change SR to] so like !wildshape Broheim 27 13 19 5 1d8 Bear 0 to use the numbers you used in the original post.  If someone could make the gisthub thing work that would be swell, since apparently I'm too dumb to figure out how to do it myself. Disclaimer: there's probably a way easier/better way to do this, I'm a JS newb who taught myself by dicking around with other people's scripts so please don't make fun of my awful code.
1462400534

Edited 1468850013
The Aaron
Pro
API Scripter
I likewise threw something together real quick.  It's a small hack that will give you the functionality you want. Here is the grammar for the command: !gsa [-c] <character id> --<attribute name>|<value> [--<attribute name>|<value> ...] !gsa : this is the command for this script -c : You can optionally include this and it will create attributes that don't currently exist. <character id> : replace this with the character id.  Feel free to use @{selected|character_id} or @{character_id} or what have you. --<attribute name>|<value> : you can specify as many of these as you like <attribute name> : replace this with the attribute name | : (vertical pipe) this separates the attribute name from the value.  You can use # to make it easier to include in roll queries. <value> : replace this with the value you want to use.  You can enclose it in " " or ' ' (though you shouldn't need to.) Additionally, inline rolls will be expanded and you can use {{  }} to span multiple lines just as in TokenMod and Powercards. Examples (all of these are equivalent): !gsa @{character_id} --Str|27 --Dex|13 --Con|19 --NaturalAC|5 --Damage|1d8 --Form|Bear --SR|0 !gsa @{character_id} {{ --Str|27 --Dex|13 --Con|19 --NaturalAC|5 --Damage|1d8 --Form|Bear --SR|0 }} !gsa @{character_id} {{ --Str#27 --Dex#13 --Con#19 --NaturalAC#5 --Damage#1d8 --Form#Bear --SR#0 }} !gsa @{character_id} {{ --Str|[[27]] --Dex|[[13d1]] --Con|[[{19,[[3d6]]}kh1}]] --NaturalAC|[[10-5]] --Damage|[[@{level}]]d8 --Form|Bear --SR|0 }} In the above, if any of the attributes didn't exist (or were defaulted fields, say if SR had never been set before) it would skip them and provide an error message to the person executing the command.  If you wanted to cause missing attributes to be constructed, you could supply the -c: !gsa -c @{character_id} {{ --Str|27 --Dex|13 --Con|19 --NaturalAC|5 --Damage|1d8 --Form|Bear --SR|0 }} Here is the snippet of code: on('ready',function(){     "use strict";     var regex = {         stripSingleQuotes: /'([^']+(?='))'/g,         stripDoubleQuotes: /"([^"]+(?="))"/g     };     on('chat:message',function(msg){         var args,errors=[],who,cmd,characterid,createAttrs,character;         if('api' === msg.type && msg.content.match(/^!gsa\b/) ){             if(_.has(msg,'inlinerolls')){                 msg.content = _.chain(msg.inlinerolls)                     .reduce(function(m,v,k){                         var ti=_.reduce(v.results.rolls,function(m2,v2){                             if(_.has(v2,'table')){                                 m2.push(_.reduce(v2.results,function(m3,v3){                                     m3.push(v3.tableItem.name);                                     return m3;                                 },[]).join(', '));                             }                             return m2;                         },[]).join(', ');                         m['$[['+k+']]']= (ti.length && ti) || v.results.total || 0;                         return m;                     },{})                     .reduce(function(m,v,k){                         return m.replace(k,v);                     },msg.content)                     .value();             }             args = msg.content                 .replace(/<br\/>\n/g, ' ')                 .replace(/(\{\{(.*?)\}\})/g," $2 ")                 .split(/\s+--/);             cmd=args.shift().split(/\s+/);             characterid=_.last(cmd);             createAttrs=_.contains(cmd,'-c');             character=findObjs({                 type: 'character',                 id: characterid             })[0];                          if(character){                 let control = character.get('controlledby').split(/,/);                 if(playerIsGM(msg.playerid) || _.contains(control,'all') || _.contains(control,msg.playerid)) {                     _.chain(args)                         .map(o=>{                             let p=o.match(/("[^"]*"|'[^']'|.*?)[\|#]("[^"]*"|'[^']'|.*)/),                             a,v;                             if(p){                                 return {                                     attrName: p[1],                                     value: p[2].replace(regex.stripSingleQuotes,'$1').replace(regex.stripDoubleQuotes,'$1')                                 };                             } else {                                 errors.push({                                     type: 'BadArgument',                                     value: o                                 });                             }                         })                         .reject(_.isUndefined)                         .map(o=>{                             o.attr=findObjs({                                 type: 'attribute',                                 characterid: characterid,                                 name: o.attrName                             })[0];                             if(o.attr){                                 return o;                             } else if(createAttrs){                                 createObj('attribute',{                                     characterid: characterid,                                     name: o.attrName,                                     current: o.value                                 });                             } else {                                 errors.push({                                     type: 'MissingAttribute',                                     value: o.attrName                                 });                             }                         })                         .reject(_.isUndefined)                         .each(o=>{                             o.attr.set({                                 current: o.value                             });                         });                 } else {                     errors.push({                         type: 'NoCharacterAccess',                         value: 'You are not allowed to access this character.'                     });                 }             } else {                 errors.push({                     type: 'MissingCharacter',                     value: characterid                 });             }             if(errors.length){                 who=getObj('player',msg.playerid).get('_displayname');                 sendChat('GSA',`/w "${who}" <div style="padding:1px 3px;border: 1px solid #8B4513;background: #eeffee; color: #8B4513; font-size: 80%;"><div style="background-color: #ffeeee;">Errors:</div>`+                     _.chain(errors)                         .map(e=>{                             return `<div><b>${e.type}</b> ${e.value}</div>`;                         })                         .value()                         .join('') +                     `</div>`);             }         }     }); });
Because no one else has done it yet:  If only there was a robust API script that allowed us to modify Characters like we can Tokens using TokenMod.  Oh well, such a thing will never exist.  Might as well wish for a Unicorn for my next birthday.
1462407019
The Aaron
Pro
API Scripter
Hahaha. This is kind of unicorn light. Only works on a single character at a time and only the current value, not the max, but is otherwise pretty comprehensive. :)
1462427351
I think that is awesome. curious, if i replaced  !gsa @{character_id} {{ --Str|27 }} with !gsa @{character_id} {{ --Str|[[@{character_id|Str}+5]]  }} would it still function?
1462428905

Edited 1462428979
Silvyre
Roll20 Mod Team
At the moment, Character IDs are not able to be used as a keyword within Attribute calls. This is a bug that the Dev Team is aware of. You can, however, use the name of a Character as a keyword, e.g. @{Sir Bearington|Str}. Otherwise, it should work!
Thank you very much for the quick and comprehensive responses. I'll look forward to testing.
The code from The Aaron seems like a great generic solution to my problem and deserves a place in the API script library. Is the code snippet all I need? It does seem to have a dependency on playerIsGM. Does that mean it will only run for the GM? I'm hoping to use it as a player but only on a character I control.
1462451891

Edited 1462451972
The Aaron
Pro
API Scripter
I'll put something like this in the Script Library at a point in the future.   This code snippet is all you need (playerIsGM is a built in function which supersedes my original isGM function). I adjusted the above script to allow the GM access to any character, but allow players access to characters they can control or that everyone can control. To extend what Silvyre said, if you're in a context where you can use @{character_id} without a preceding selected , target , or character name , you can do the same with attributes: !gsa @{character_id} {{ --Str|[[@{Str}+5]] }} Happy Rolling!