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

Change existing Token Bar 1, 2, and 3 Value/Max/Link with API

My game's npc token bars are set up differently than the modules have them set (Bar 1 Value: npc_ac. Bar 3 Value: hp_max. Bar 3 Max: hp_max.  I usually keep Bar 2 blank for resources).

I'm annoyed that every new game I create, I have to take HOURS to individually change every token value, max, and link setting and save that token to its assigned sheet.  I'm aware that the Apply Default Settings changes future tokens dragged from the Compendium.  It also seems to change existing tokens on the VTT but only certain settings like Bar Location and Bar Style but for some reason, it will not change Value, Max, or Link as I have it set to default for existing tokens.  Is this a glitch or is it designed this way?  I recently upgraded to Pro. Is there a way to use the API to change all existing tokens to default settings and also automatically save them as the default token on their assigned character sheets?  I'm assuming its not possible to do ALL that with one API but is there a way to streamline it a bit?

Thank you for your attention!

July 18 (2 years ago)
timmaugh
Forum Champion
API Scripter

TokenMod can help. It can manage the bar links and/or values. It sounds like you want the values, only, so to do that over many different tokens you're going to want to install SelectManager and Fetch, too.

(TokenMod, SelectManager, and Fetch are available in the one-click installer.)

I am assuming you're talking about 5E, and that you're using the 5E by Roll20 sheet. If not, post back with the game system and sheet you're using.

The actual command line is near the end of this message, but if you want to see *who* would be affected before you actually run it, then install the above scripts AND ZeroFrame (another script in the one-click), and run this command:

!forselected(^) {^&if @^(selected.npc[1]) = 1 && `@^(selected.npc_name[missing])` != missing}I would have acted on @^(selected.npc_name). {^&end}{^&simple}{&select *}

It will output a simple statement for each of the tokens it would have taken action on, just so you can be sure the logical traps are working correctly.

Both the above command and the one below will only select tokens on your current page, so you'll have to run it once on every page in your game.

Once you're happy with the tokens it will be touching, run this command:

!forselected(^) {^&if @^(selected.npc[1]) = 1 && `@^(selected.npc_name[missing])` != missing}token-mod --set bar1|@^(selected.npc_ac) bar2| bar3|@^(selected|hp_max)|@^(selected|hp_max){^&end}{&select *}

That one uses the same selection mechanism to get all of the tokens on the current page, as well as the same logical test to make sure what we are working with is an npc token, then it substitutes the values in for the bars.

July 18 (2 years ago)
Gauss
Forum Champion

Hi Philip, 

Underneath the settings for Bar 1/2/3 Value, Bar 1/2/3 Max, and Bar 1/2/3 Link is the following statement: 
"On Default Tokens created by doing a Compendium drop onto the virtual table top, set the Bar 1's value to this attribute."

Based on that statement, they do not affect existing tokens. They only affect Compendium entries being dragged into Roll20. 

The statement is in small print and easily missed. 

July 18 (2 years ago)
The Aaron
Roll20 Production Team
API Scripter

I have a script I wrote for someone to fix this at one point (below).  This sets the ac as bar1 and the hp as bar3.  It also sets the passive wisdom as bar2, you can disable that by putting // at the beginning of lines 23, 27, and 31.  Additionally, it updates the character's default token to be correct.

You can use it on selected tokens with:

!fix-bars

Or do all tokens in the game with:

!fix-bars --all

If you want to use this, I highly recommend copying your game and trying it on that before using it on your primary game.


Script:

on('ready',()=>{

  const attr = (c,n) => (findObjs({ type: 'attribute', characterid: c.id, name: n })[0]||{get:()=>0});
  let characters = {};

  const fixToken = (t) => {
    let c = getObj('character',t.get('represents'));
    if(c){
      let npc = (attr(c,'npc').get('current')>0);
      let ac = attr(c, (npc ? 'npc_ac' : 'ac'));
      let wis = attr(c,'wisdom_mod');
      let per_skill = attr(c,'perception_prof');
      let per_base = attr(c,'npc_perception_base');
      let senses = (`${attr(c,'npc_senses').get('current')}`.match(/passive\s+perception\s+(\d+)/i)||[0,0])[1];
      let hp = attr(c,'hp');
      let pb = attr(c,'pb');
      let pw = attr(c,'passive_wisdom');

      let passivePerception = 10 + (npc ? parseInt(per_base.get('current')) : (parseInt(wis.get('current')) + parseInt(((`${per_skill.get('current')}`.length>0) ? pb.get('current') : 0))));

      t.set({
        bar1_link: ( ac ? ac.id : (npc ? 'sheetattr_npc_ac' : 'sheetattr_ac' )),
        bar2_link: (npc ? '' : (pw ? pw.id : 'sheetattr_passive_wisdom')),
        bar3_link: ( npc ? '' : hp.id),

        bar1_value: ac.get('current'),
        bar2_value: (parseInt(senses) || passivePerception),
        bar3_value: (npc ? hp.get('max') : hp.get('current')),

        bar1_max: '',
        bar2_max: '',
        bar3_max: hp.get('max')
      });

      let p = getObj('page',t.get('pageid'));
      let scale = parseFloat(p.get('snapping_increment'));

      if( !characters.hasOwnProperty(c.id) && c.get('name') === t.get('name')) {

        let w = parseInt(t.get('width'));
        let h = parseInt(t.get('height'));
        if(scale<1){
          let mod = 1/scale;
          t.set({
            height: Math.floor(h*mod),
            width: Math.floor(w*mod)
          });
        }
        setDefaultTokenForCharacter(c,t);
        if(scale<1){
          t.set({
            height: h,
            width: w
          });
        }
        characters[c.id] = true;
      }
    }
  };


  on('chat:message', (msg) => {

    if('api'===msg.type && /^!fix-bars\b/i.test(msg.content) && playerIsGM(msg.playerid)) {
      let args = msg.content.split(/\s+/).map(s => s.toLowerCase());
      let who = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname');

      let workQueue = {};
      if(args.includes('--all')){
        workQueue = findObjs({type:'graphic'});
      } else if(msg.selected){
        workQueue = msg.selected
          .map(o=>getObj('graphic',o._id))
          .filter(g=>undefined !== g)
          ;
      } else {
        sendChat('Fix Bars', `/w "${who}" <div>Select one or more tokens to fix bar mappings on, or use <code>!fix-bars --all</code> to fix bar mappings on all tokens in the game.</div>`);
        return;
      }

      let count = 0;
      let timeMark = Date.now();
      const drainQueue = ()=>{
        let t = workQueue.shift();
        if(t){
          fixToken(t);
          ++count;
          if((Date.now()-timeMark)>5000){
            timeMark = Date.now();
            sendChat('Fixing Tokens',`/w gm ...${count}`);
          }
          setTimeout(drainQueue,0);
        } else {
          characters = {};
          sendChat('Fixing Tokens',`/w gm Finished Fixing Tokens`);
        }
      };

      sendChat('Fixing Tokens',`/w gm Fixing ${workQueue.length} Tokens`);
      drainQueue();
    }
  });
});

The Aaron Forgive me, I'm extremely new to API. I went to the Mods (API Scripts) section in my game settings. New Script. Typed in your code and saved it as an API script. That should be it? Is there something I need to do in the game itself or should that take care of everything?  It hadn't changed anything in my game at all so I must be missing a step. I do want to change all the tokens so does that mean I have to add

!fix-bars --all

to the beginning of your code?

July 19 (2 years ago)

After loading the script into the Mods (API Scripts) section, enter the game and type the '!fix-bars --all' in the chat window to run the code in the game.

July 19 (2 years ago)
The Aaron
Roll20 Production Team
API Scripter

As Jaren said. API commands get entered in the chat (or in macros or character abilities). They begin with ! And it must be for first character on a given line. Be sure you're testing this out on a copy of your game. 

The Aaron Oh wow that worked really well! Thank you so much!
So I didn't want any of my Monster tokens to have any of their bars linked to any attribute (except unique NPCs that are sticking around) so I just wanted to fiddle with their Value or Max. You said that // negates a line of code completely... but how does one turn off something linked to an attribute already?
By default, roll20 modules have Bar 2 linked to npc_ac and I'd like to turn that off to just be blank.  It looks like maybe putting ' ', makes it blank? Like in line 30 and 31? 

July 19 (2 years ago)

Edited July 19 (2 years ago)
The Aaron
Roll20 Production Team
API Scripter

If you want npc's bar1 to not be linked and be blank, you can use this block for lines 21–33:

      t.set({
        bar1_link: ( npc ? '' : (ac ? ac.id : 'sheetattr_ac' )),
        bar2_link: (npc ? '' : (pw ? pw.id : 'sheetattr_passive_wisdom')),
        bar3_link: ( npc ? '' : hp.id),

        bar1_value: ( npc ? '' : ac.get('current')),
        bar2_value: (parseInt(senses) || passivePerception),
        bar3_value: (npc ? hp.get('max') : hp.get('current')),

        bar1_max: '',
        bar2_max: '',
        bar3_max: hp.get('max')
      });