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