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

Help with initiative on the turn order

1491684293

Edited 1491684382
OK I know there are other group initiative tools out there, but I want to be able to select a group of tokens, and only execute the script against certain tokens as identified by an attribute, in this case characterType.  I want to be able to select the group quickly by just tracing a selection box around them and not have to shift-click each of the monsters/NPCs, but it will ignore the PC's. This script set I was trying to add a new group of tokens to the initiative turn order.  However, no matter what I have tried, the JSON is read and shows the tokens that I have set through the Add Turn option; however, those that I push to the JSON do not show up on the Turn Order list.  If I run the script again, I see those items added to the JSON value.  What am I missing?  Or is it not possible to do in this method? Some other things I am trying to find - If the below is possible and I am just missing something, how can I find the pageid value from within the script? Why, when I read the attributes do they not come out as numbers, but rather as text?  What is the better way to convert them to a numeric other than the way I am now by multiplying by 1?  (Currently if I don't  do this then if the random roll for initRoll comes to 8, and the character has a dexterityMod of 2, it concatenates them to 82.) I also want to be able to select the tokens and roll for initiative, replacing the current values, is there a quicker way to do this other than reading in the JSON and removing then re-adding the selected tokens? I am open to going a completely different route if needed, this is a learning journey for me. Note the functions for getCharacterObj and getAttrByName are scripts from other developers, and will be of great utility value! EDIT:  I forgot to mention that the sorting of the Turn Order with the below code works fine. function addMonsterInit(selectedToken) {     Campaign().set('initiativepage',false)     var characterObj = getCharacterObj(selectedToken)     var characterRace = getAttrByName(characterObj.id,'Race','current')     var dexterityMod = 0     dexterityMod = getAttrByName(characterObj.id,'DexterityMod','current')*1     var turnOrder     var initRoll = randomInteger(10)*1     if (Campaign().get('turnorder') == '') {         turnOrder =[]     } else {         turnOrder = JSON.parse(Campaign().get('turnorder'))     }    if (characterType == 'Monster' || characterRace == 'GM-NPC'){         log("name - "+characterObj.get('name')+ ' - ID'+characterObj.id+' race - '+characterRace+' - Dex Mod '+dexterityMod+" - init "+initRoll + ' W/Dex ' + (initRoll+dexterityMod))         turnOrder.push({         id: characterObj.id,         pr: (initRoll+dexterityMod),         custom: '',         pageid: "7F73B956-71C3-4C5B-8C72-576EE4F15EA4"     }) }     turnOrder.sort(function(a,b) {         first = a.pr          second = b.pr         return second - first;     })    log(turnOrder)     Campaign().set('turnorder', JSON.stringify(turnOrder))     Campaign().set('initiativepage',true) } on('ready', function() {     on("chat:message", function(msg) {        if (msg.type === "api" && msg.content == "!monsterInit") {             var selectedTokens = msg.selected;             _.each(selectedTokens,rollMonsterInit)         }     }) })
1491705536

Edited 1491744183
The Aaron
Pro
API Scripter
You are putting the Character ID in the turnorder, but it should be the Token ID.  Only tokens can have turns. Scott W. said: Some other things I am trying to find - If the below is possible and I am just missing something, how can I find the pageid value from within the script? Why, when I read the attributes do they not come out as numbers, but rather as text?  What is the better way to convert them to a numeric other than the way I am now by multiplying by 1?  (Currently if I don't  do this then if the random roll for initRoll comes to 8, and the character has a dexterityMod of 2, it concatenates them to 82.) I also want to be able to select the tokens and roll for initiative, replacing the current values, is there a quicker way to do this other than reading in the JSON and removing then re-adding the selected tokens? 1.  It depends on what you mean.   To find the page a Token is on, you can get it from token.get('pageid'). To find the page that you are looking at (as GM), you can get it from your player object with player.get('lastpage').  It's called last page because it's the last page you loaded as a GM.  If you have two instances open and you are on an instance that isn't the last one you loaded a page in, it would be wrong.  Also, if you are rejoined as a player, you'd need to look at the next section, for players. To find the page one of your players is on, you can look for their playerid in Campaign().get('playerspecificpages').  If you don't find it there, then they are on the page returned by Campaign.get('playerpageid'). 2. Attribute fields are text fields.  No assumption is made as to their type.  The idiomatic way of turning text to numbers in Javascript is with parseInt(): var number = parseInt(attr.get('current'),10); if the attribute holds a number or a string that contains a number, it will be converted (base 10) and stored in the variable number.  If not, the variable number will contain the special value NaN.  (not a number).  NaN doesn't equal anything, so if you need to check for NaN, you can use _.isNaN(number).  Generally though, you can just use the falsyness of the number to help you initialize it: var number = parseInt(attr.get('current'),10) || 0; In this case, number will either be the value stored in the attribute, or it will be 0. 3.  Um... Probably?  If I were writing this, I'd parse the current initiative first, then build a new object to represent it, then set it last.  I'd clear the tokens that are left after filtering somewhere in the middle.  Something like this completely untested   lightly tested code: on('ready', function(){     "use strict";     on('chat:message', function(msg){         if('api' === msg.type && msg.content.match(/^!monsterInit/i)){             let to=JSON.parse(Campaign().get('turnorder'))||[],                 pageid;             _.chain(msg.selected)                 .map((o)=>getObj('graphic',o._id))                 .reject(_.isUndefined)                 .map((t)=>({                     token: t,                     characterRace: getAttrByName(t.get('represents'),'Race'),                     characterType: getAttrByName(t.get('represents'),'Type')                 }))                 .filter((o)=> 'Monster' === o.characterType || 'GM-NPC' === o.characterRace)                 .tap((a)=>{                     let ids=_.map(a,(o)=>o.token.id);                     to=_.reject(to,(o)=>_.contains(ids,o.id));                     pageid = _.has(a,0) && a[0].token.get('pageid');                 })                 .map((o)=>({                     id: o.token.id,                     pr: randomInteger(10) + (parseInt(getAttrByName(o.token.get('represents'),'DexterityMod'),10)||0),                     custom: '',                     pageid: pageid                 }))                 .tap((a)=>{                     Campaign().set({                         turnorder: JSON.stringify(                             _.sortBy(_.union(a,to),(o)=>-o.pr)                         ),                         initiativepage: pageid                     });                 });         }     }); }); I made the assumption that characterType would be in the Type attribute (you don't actually set it in your code).  If you want the lowest number first, just use o.pr instead of -o.pr in the _.sortBy() call.
Thanks Aaron, I think I am following how the script snip above is working, and will continue to learn more about that style of coding.  It doesn't look to complex, but just a different way of looking at the code.  In the meantime I took your other suggestions and have tweaked my script a bit, well more than just a bit.  I have decided to drop rolling only for the monsters, and instead will go for all of the tokens selected, eliminating those that are incapacitated, or dead.  That worked well in my last session, and sped up the game quite a bit actually.  I have further tweaked it to first pull all of the non-token entries off of the the turn order, well as long as they are always entered with a negative number for their id, clearing the list, and then cycling through the selected tokens and adding them to the array.  Finally after adding all of the tokens, all of the non-token entries are returned to the list, the list is sorted, and we are done. If I may ask, this is not really pertinent to just this script, but over all.  Which is the preferred method for a group of related scripts that are being called from macro's/chat -  Is it better to have one on('ready',function(){}) and then checking to insure the call was for the api and then setting up a case/break to see what the requested functionality is, or to have multiple on('ready',function(){}) blocks, one for each individual function.  I know the later makes it easier to cannibalize your code, but it just seems to me like it would add a lot of overhead as each of those blocks would be evaluated to see if they meet the criteria.  Or does it stop looking once it finds one that matches?  I want to make sure I am being a courteous coder, and not putting undo strain on hosts for the API sandboxes.
1491909460
The Aaron
Pro
API Scripter
Wrap your whole script in a single on('ready',...). Try not to duplicate event registration, so have a single on('chat:message',...) and check for your various commands that pertain to this script in it (but don't feel like you have to merge disparate scripts into a single file or anything). Also, don't modify the msg object padded to on('chat:message',...), its shared between calls and another script might be looking at the same message. You can _.clone() it if you want a modifiable copy.  If you start using the state object, be sure to namespace all your data in a property.  Happy Rolling!