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

Macro Commands - Apply to multiple selected tokens

August 15 (4 years ago)

Edited September 15 (4 years ago)

Running a Palladium game and while group-init works fine, i picked up one for attacks per melee awhile back. The problem i've found with it (and i can't see the script api body anymore in the script section, it's showing as just 2 pink dots?) is it only works on one token at a time. Group-init fires fine, but i then have to select each and every token individually and apply APM.

The command itself reads: !dup-turn @{selected|APM}

I'm probably just too fatigued, but is there a way i can retool my macro to have this fire for every single token highlighted? Palladium is long enough in combat despite a lot of heavier macros i've done.

it'd be nice to have one less hoop to run those two back to back, or even together. The code for this one was posted elsewhere, taking the number of APM and basically reducing the init by -20 for each character's additional attacks. Move through board, clear turns, re-roll init/apm. Yay Palladium.

August 15 (4 years ago)
keithcurtis
Forum Champion
Marketplace Creator
API Scripter

The script would probably need to be altered to run on multiple tokens. If you post a link to the script, maybe one of our JS gurus can lend a hand.

August 15 (4 years ago)

Edited August 15 (4 years ago)

I believe this is it, from The Aaron, posted september last year.

https://app.roll20.net/forum/post/6817409/multiple-initiative-values-for-a-single-character/?pageforid=6817748#post-6817748

on('ready', () => {
    const multiplyTurn = (token_id,count) => {
        let to = JSON.parse(Campaign().get('turnorder')||'[]');
        let turn = {pr:-10000};
        to = to.filter((o) => {
            if(o.id !== token_id){
                return true;
            }
            if( o.id === token_id ) {
                if(o.pr>turn.pr) {
                    turn = o;
                }
            }
            return false;
        });
        if(turn.id){
            [...Array(parseInt(count)).keys()].forEach((n)=>{
                to.push(Object.assign({},turn,{pr:(parseInt(turn.pr)-(20*n))}));
            });
        }
        Campaign().set({
            turnorder: JSON.stringify(to.sort((a,b)=>parseFloat(b.pr)-parseFloat(a.pr)))
        });
    };

    on('chat:message', (msg) => {
        if( 'api'===msg.type && /^!dup-turn/.test(msg.content) ){
            let args = msg.content.split(/\s+/);
            let count = args[1]||2;

            msg.selected.forEach((m)=>multiplyTurn(m._id,count));
        }
    });
});
August 15 (4 years ago)

Edited August 15 (4 years ago)

For what it's worth, as it runs it'll crash the sandbox if the token lacks the attribute. Easy API restart, yeah. But i suspect it could be resolved as well. I'm afraid my expertise stops at sheet html.

Bump

bump?

August 31 (4 years ago)
The Aaron
Roll20 Production Team
API Scripter

Hmm. Somehow I missed this thread. I'll see what I can do to get you a better version. I e been pretty busy the last few weeks, so I'm a bit behind. Sorry about that. 

Bump. Please pursue if you can find the time.
September 14 (4 years ago)
The Aaron
Roll20 Production Team
API Scripter

Whoops.  Slipped through the cracks again.  I'll set a reminder and take a look tonight.  Sorry for the delay.

I doubt the request is in high demand; i appreciate you taking the time to address it.

September 15 (4 years ago)

Edited September 15 (4 years ago)
The Aaron
Roll20 Production Team
API Scripter

Ok, it actually supports selected tokens already.  That wasn't the problem.  The problem was that GroupInitiative is asynchronous, so it was only getting finished adding 1 token to the turn order before DupTurns was running.  What really needs to happen is that DupTurns waits until GroupInitiative is finished and then runs.  As it turns out, the easiest way to make that work is just to have it observe GroupInitiative and run automatically to dup turns for each token that was rolled, which is what I did:

/* global GroupInitiative */
on('ready', () => {
  const handleGroupInit = true;
  const defaultCount = 2;
  const defaultAttrName = 'apm';


    const multiplyTurn = (token_id,count) => {
        let to = JSON.parse(Campaign().get('turnorder')||'[]');
        let turn = {pr:-10000};
        to = to.filter((o) => {
            if(o.id !== token_id){
                return true;
            }
            if( o.id === token_id ) {
                if(o.pr>turn.pr) {
                    turn = o;
                }
            }
            return false;
        });
        if(turn.id){
            [...Array(parseInt(count)).keys()].forEach((n)=>{
                to.push(Object.assign({},turn,{pr:(parseInt(turn.pr)-(20*n))}));
            });
        }
        Campaign().set({
            turnorder: JSON.stringify(to.sort((a,b)=>parseFloat(b.pr)-parseFloat(a.pr)))
        });
    };


    on('chat:message', (msg) => {
        if( 'api'===msg.type && /^!dup-turn/.test(msg.content) ){
            let args = msg.content.split(/\s+/);
            let count = args[1]||defaultCount;
            let attrname = args[2]||defaultAttrName;
            if(msg.selected){
                msg.selected.forEach((m)=>{
                  let n = count;
                  let token = getObj('graphic',m._id);
                  if(token){
                      n = parseInt(getAttrByName(token.get('represents'),attrname));
                  }
                  multiplyTurn(m._id,n||count);
                });
            }
        }
    });

    const maxPRLookup = (to) => {
      let lookup = {};
      to.forEach(o=>{
        let npr = parseFloat(o.pr);
        if(!lookup[o.id] || lookup[o.id]<npr){
          lookup[o.id]=npr;
        }
      });
      return lookup;
    };

    const handleGroupInitChange = (obj,prev) => {
        let lookup = maxPRLookup(JSON.parse(prev||'[]'));
        let curlist = maxPRLookup(JSON.parse(obj||'[]'));
        let updateList = Object.keys(curlist).filter(id=>curlist[id]!==lookup[id]);

        updateList.forEach(id=>{
            let n;
            let token = getObj('graphic',id);
            if(token){
                n = parseInt(getAttrByName(token.get('represents'),defaultAttrName));
            }
            multiplyTurn(id,n||defaultCount);
        });
    };

    if(handleGroupInit && 'undefined' !== typeof GroupInitiative && GroupInitiative.ObserveTurnOrderChange){
        GroupInitiative.ObserveTurnOrderChange(handleGroupInitChange);
    }

});

You can change the variables at the top to set what to do if it doesn't have the attribute, and what the attribute's name is.  Let me know if that works for you.  I could also have it queue up a change to make, then run that change after GroupInitiative next finishes, but this seems like it should work better.

Again, sorry for the delay getting back to you!