Ok, give this a shot...  Commands    !apply-change --<argument>[param]|[value] ...  ::= Applies a change to the selected or specified tokens.    --bar  ::= This specified which bar to apply the change to. ex:  --bar|1    --amount  ::= This is an amount to change by.   --amount|-10 .  You can supply tags to an amount like by appending a comma separated list in brackets   --amount[fire]|-10    --amount[force,piercing]|-3      --damage   ::= This is just like amount, but multiplied by -1.   --amount|-10  is the same as  --damage|10    --mod  ::= This is a modifier rule.  It takes the form  --mod[<rule>[,<rule>,...]]|<operation><number>    <rule>  can be is a  <type>:<value>  where  <type>  is one of the following strings.  It can optionally be prefaced by a  !  to negate the meaning.   status  ::= the  <value>  will be one of the status marker names (same ones used by TokenMod)   tag  ::= the  <value>  will be one of the tags you supplied to a  --amount  or  --damage  argument      <operation>  must be one of the following characters.  It can optionally be followed by a number in brackets to denote the number of decimal places to retain (defaults to floor of the value).   *  -- multiple the amount by the  <number>    -  -- subtract the  <number>  from the a amount   +  -- add the  <number>  to the amount   /  -- divide the amount by the  <number>    =  -- replace the amount with the  <number>    %  -- replace the amount with the modules by  <number>        --ids  ::= a list of ids like TokenMod takes.   --ids <id1> <id2> <id3>      Ok.  That probably sounds complicated.  I'll give some examples that should make this easier:  Simplest use, applying damage to all selected tokens:  !apply-change --bar|3 --damage|[[3d6]] Half damage if they saved, denoted by a red status marker:  !apply-change {{
  --bar|3
  --damage|[[6d6]]
  --mod[status:red]|*.5
}} Using a tag, if they creatures with brown mark are resistant to fire:  !apply-change {{
  --bar|3
  --damage[fire]|[[6d6]]
  --mod[tag:fire,status:brown]|*.5
}} Resistant and saved:  !apply-change {{
  --bar|3
  --damage[fire]|[[6d6]]
  --mod[tag:brown,status:brown]|*.5
  --mod[status:red]|*.5
}} How about more than one damage type:  !apply-change {{
  --bar|3
  --damage[acid]|[[2d4]]
  --damage[fire]|[[6d6]]
  --mod[tag:brown,status:brown]|*.5
  --mod[status:red]|*.5
}} And maybe a green status demarks immunity to acid:  !apply-change {{
  --bar|3
  --damage[acid]|[[2d4]]
  --damage[fire]|[[6d6]]
  --mod[tag:brown,status:brown]|*.5
  --mod[status:red]|*.5
  --mod[status:green]|=0
}} And possibly the half damage save doesn't apply to acid:  !apply-change {{
  --bar|3
  --damage[acid]|[[2d4]]
  --damage[fire]|[[6d6]]
  --mod[tag:brown,status:brown]|*.5
  --mod[!tag:acid,status:red]|*.5
  --mod[status:green]|=0
}} For completeness, maybe you count HP to 2 decimal places:  !apply-change {{
  --bar|3
  --damage[acid]|[[2d4]]
  --damage[fire]|[[6d6]]
  --mod[tag:brown,status:brown]|*[2].5
  --mod[!tag:acid,status:red]|*[2].5
  --mod[status:green]|=0
}} Or possibly on a save, the acid damage heals:  !apply-change {{
  --bar|3
  --damage[acid]|[[2d4]]
  --damage[fire]|[[6d6]]
  --mod[tag:brown,status:brown]|*[2].5
  --mod[!tag:acid,status:red]|*[2].5
  --mod[status:green,!status:red]|=0
  --mod[status:red,status:red]|*-1
}} Rules are evaluated in order, and all rules are evaluated, so you might find yourself using ! and the parts from prior rules to get an or sort of case.  Here's the code.  Once you (and anyone else) has played with it a bit, I'll see about adding some other things.  It probably needs a way to bound to the bar max and zero, round instead of floor, specify more complicated rules, maybe a stop rule syntax or some and/or syntax for rules.   //ApplyChange
on('ready',function(){
    'use strict';
    var modifiers = {
        places: function(places){ return (number)=>parseFloat((parseInt(number,10)||0).toFixed(places)); },
        '*': function(amount){ return (number)=>number*amount;},
        '+': function(amount){ return (number)=>number+amount;},
        '/': function(amount){ return (number)=>number/amount;},
        '-': function(amount){ return (number)=>number/amount;},
        '=': function(amount){ return (number)=>amount;},
        '%': function(amount){ return (number)=>number%amount;}
    },
    matchers = {
        'status': function(status){
            return (token,amount,context)=>{
                let sm=_.reduce(
                    token.get('statusmarkers').split(/,/),
                    (m,o)=>{let p=o.split(/@/); m[p[0]]=(p[1]||0); return m; },
                    {}
                );
                return _.has(sm,status);
            };
        },
        'negate': function(fn){ return (token,amount,context)=>!fn(context,token); },
        'tag': function(tag){ return (token,amount,context)=>_.contains(amount.tags,tag); }
    },
    buildMatcher = function(params){
        return (function(matchers){
            return function(token,amount,context){
                return !_.find(matchers,(m)=>!m(token,amount,context));
            };
        }(_.chain(params)
            .reduce((m,o)=>{
                let p=_.rest(o.match(/^(!)?([^:]*):(.*)$/));
                p[0]=!!p[0];
                m.push({
                    negate: p[0],
                    type: p[1],
                    value: p[2]
                });
                return m;
            }, [])
            .map((o)=>{
                if('negate'===o.type || !_.has(matchers,o.type)){
                    return _.constant(true);
                }
                return (o.negate ?
                    matchers.negate(matchers[o.type](o.value)) :
                    matchers[o.type](o.value) );
            })
            .value()));
    },
    buildModifier = function(op,amount,places){
        return (_.isUndefined(places) ?
            modifiers[op](amount) :
            _.compose(modifiers.places(amount) , modifiers[op](amount)) );
    };
    on('chat:message',function(msg){
        if('api' === msg.type && msg.content.match(/^!apply-change/) && playerIsGM(msg.playerid) ){
            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();
            }
            let args = _.rest(msg.content
                    .replace(/<br\/>\n/g, ' ')
                    .replace(/(\{\{(.*?)\}\})/g," $2 ")
                    .split(/\s+--/)),
                ids = _.pluck(msg.selected,'_id'),
                context = {
                    bar: 1,
                    amount: [],
                    mods: []
                };
                // !apply-change --bar|1 --amount|-[[xdy]] --mod[status:red,!status:green,tag:fire]|*.5 --ids @{target|token_id}
                // !apply-change --bar|1 --damage|[[xdy]] --mod[status:green]|*.5 --ids @{target|token_id}
            _.map(args,function(arg){
                let cmds=((arg.match(/([^\s]+[\|#]'[^']+'|[^\s]+[\|#]"[^"]+"|[^\s]+)/)||[])[0]||'').split(/[\|#]/),
                    mult=1,
                    cmdparts=cmds.shift().match(/^([^\[]*)(?:\[([^\]]*)\])?$/),
                    cmd=cmdparts[1],
                    params=((cmdparts[2]||'').length ? cmdparts[2].trim.split(/\s*,\s*/) : []);
                switch(cmd){
                    case 'ids':
                        ids=_.union(_.rest(arg.split(/\s+/)),ids);
                        break;
                    case 'bar':
                        context.bar = (parseInt(cmds[0])||1);
                        break;
                    case 'damage':
                        mult=-1;
                        /* falls through */
                    case 'amount':
                        context.amount.push({
                            amount: mult*parseInt(cmds[0]||0),
                            tags: params
                        });
                        mult=1;
                        break;
                    case 'mod':
                        let parts=cmds[0].match(/^([\*\+\-\/=%](?:\[\d\])?)?([\d\.]*)/),
                            amount=parseFloat(parts[2]),
                            opParts=parts[1].match(/^(.)(?:\[(\d*)\])?$/),
                            op=opParts[1],
                            places=opParts[2]||0;
                        context.mods.push({
                            matcher: buildMatcher(params),
                            modifier: buildModifier(op,amount,places)
                        });
                        break;
                }
            });
            // get tokens
            if(ids.length){
                _.chain(ids)
                    .uniq()
                    .map(function(t){
                        return getObj('graphic',t);
                    })
                    .reject(_.isUndefined)
                    .each(function(token) {
                        let bar=`bar${context.bar}_value`,
                            change=0;
                        _.map(context.amount, (amount)=>{
                            let value=amount.amount;
                            
                            if(context.mods.length){
                                _.each(context.mods,(mod)=>{
                                    if(mod.matcher(token,amount,context)){
                                        value=mod.modifier(value);
                                    }
                                });
                            }
                            change+=value;
                        });
                        token.set(bar,parseFloat(token.get(bar))+change);
                    });
            }
        }
    });
});
  Cheers!