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

Mad Knowledge AP Script Help

I am interested in creating an API script  for the following: (I posted this earlier in the general use so maybe that needs to be deleted since API script is definitely the way to go here) Player or GM will hit a button to roll knowledge check on selected monster token linked to a journal monster in my bestiary monsters have been transferred over from the compendium and I am using the Pathfinder > Pathfinder sheet The knowledge button will grab all the PC characters and use their knowledge skill roll for that monster by checking the character for its subtype The base DC of the roll is determined by 10 + the CR of the creature. Making this DC gives the name and subtypes of the creature given as a whisper to a successful player For success plus every 5 over a random piece of information is given to the player in the form of a whisper. This information could be randomly selected without duplicates from a list of options like resistances, special attacks etc. The monster character sheet can be accessed and the information relayed in the whisper to the character Example. an undead with CR 3 is encountered. The GM selects the token and rolls the knowledge check. The macro pulls all players with the skill and rolls for them (untrained max 10) and then whispers to them if they know anything or do not. If 2 party members are trained with ranks then they get a roll but others get a message saying they are not trained in that knowledge skill (or perhaps nothing at all) The macro rolls for the two eligible PCs and compares their result to the DC of the check which is 13 ( determined by 10 + the CR of the monster pulled form the monsters character sheet) In this case lets say one rolls a 12 and one rolls a 19 The macro sends successful player a whisper with the subtype and name of the monster if they hit the DC (only player 2 this time with a 19) Player 2 is sent a whisper that says the name of the creature and the subtype (and possibly relevant subtype information) For success plus every 5 over the player receives a whisper with a random (non duplicated for them) piece of information The macro rolls 2 random pieces(19 is a success plus 1) of information and randomly gets weaknesses and special attacks and this information about the creature is pulled from the sheet and whispered to the player. I have some programming experience but not with this language .. yesterday I figured out how to set up the skeleton so I could use the !command and get it to do something .. I now need some help with how to code some items.  I'm reading through the wiki but I thought some people might be able to assist me much more easily.  If you are able.  I am going to reply my questions below.
1519997061

Edited 1520025694
For this part I am trying to get the system to grab the knowledge skill modifier for the roll, roll it and send the result (baby steps, I know) gatherData = function (){ 'use strict'; var karollBraxe = &&What do I put here to call the roll from the character sheet for Knowledge Arcana?&&; }, handleInput = function(msg,karollBraxe) { var karollBraxe = karollBraxe; &&How do I call the value from the gatherData function?&& if(msg.type == "api" && msg.content.indexOf("!MadKnowledge") !== -1) { sendChat ("MadKnowledge", 'Acquired Knowledge'+karollBraxe+''); } }, So this compiles if i assign a value to var KarolBraxe such as a 7 I tried putting in 1d20 + @{Braxe Tinderwig,"Knowledge-Arcana" in the gatherData function but that doesn't seem to be right I tried putting in a value , such as 7 and then trying to report it in handleInput function but it puts "unknown" in the chat when I run it
1520026321
The Aaron
Pro
API Scripter
It's much easier to help with scripts when you can see the whole thing.  I'm guessing you're passing handleInput like this on('chat:message',handleInput) somewhere so that it can be called when chat:message events occur.  It will only ever be invoked with 1 parameter, the message object.  Depending on what karollBraxe is (guessing a character?) you'd get it a few different ways.  The most likely would be by looking at the objects that are selected (msg.selected) and grabbing the graphic (token) objects associated with the _id properties in it, then using the token's .get('represents') id to find the character object, or possibly go straight to the attribute object representing the knowledge skill modifier.  It will depend on your character sheet as to what that is named.  You could also pass the id of a character on the command line with @{Karol Braxe|character_id} and then parse it out of msg.content.  Or find all the characters controlled by the executing player, etc. If you're using the  Revealing Module Pattern (which I'm guessing you are based on the name you chose and how it's written), you can just invoke gatherData() directly from handleInput(), just like you're calling sendChat().
Ok .. so my next step is to have the GM select a token and then type a ! command to grab information off that token where: The token is the monster I want to grab data from this token such as CR and the name of the monster and its subtype Assuming they were coded to the character sheet as CR, subtype and name .. how do I grab these pieces of data? If it was set up to check for !LoadMonsterData what would be the chunk of code to grab the data off the selected token and grabbed the data? I'm trying things like getObj() and getAttrByName() and such but I am not even sure these are the right things to be using.  This seems very basic but I cannot seem to wrap my head around what this code should look like.
1520778016
GiGs
Pro
Sheet Author
API Scripter
It's best to post the code you've tried so people helping you can just tweak it to make it work. It's easier than trying to guess what you already know or write a script from scratch. (I just noticed Aaron said basically the same above.) You can get the information in more than one way, using getObj, getAttrByName, or the getAttrs function. 
I'm just looking for an actual example of the line of code or lines of code that would grab an attribute from a selected token by its name.  Basically all I have figured out is how to get it to look for a ! command I specify and do a thing .. but none of the things I try to do work .. I'm basically trying to get it to do anything from the sheet.  and where would I post the code .. isn't that a bit long to post here?  Is there a proper way to do that?
The above code is what I have so far .. I'm basically throwing things in the var section and trying to spit them out in the sendChat just to figure out the proper syntax and code to grab items off the sheet .. once i learn how to do that I can set up the grab for all the stuff I need off the monster token selected .. in theory.
A little about what I know. Last time I coded was with C and Basic A and Pascal like 30 years ago I went through and took the class on javascript and seemed pretty straight forward on the syntax of basic stuff I read the documentation on the API wiki but a lot of that seems greek I'm pretty intuitive and can usually look at existing code and apply it to a new situation to do mine. It took me quite a while to tear apart some written scripts to figure out how to get the !command to work (seemed like a lot more code than it should have been but it works now I know the logic behind what I'm trying to do but not the syntax, functions to use that already exist in the API etc. Basically I'm a teachable noob.  Thanks to anyone taking time to help me out!  Really appreciated.
Testing To See If The Gist Link Works <a href="https://gist.github.com/ClemCon/f0dfd0b6d50eb1bb11be38b164a89d00" rel="nofollow">https://gist.github.com/ClemCon/f0dfd0b6d50eb1bb11be38b164a89d00</a>
1520782560
GiGs
Pro
Sheet Author
API Scripter
The Gist link works fine. For future reference, you can format code in this window. See the toolbar above the edit box when writign a post? click the far left button and in the drop down click Code.&nbsp; I'll post some comments below. Your code looks like this: // MadKnowledge Knowledge Roll Handler // ClemCon var MadKnowledge = MadKnowledge || (function() { 'use strict'; var version = '1.0.2', lastupdate = '1.3.18', char = ?????(pull the selected token somehow), HP = getAttrByName(char.id,'HP'), karollBraxe = 7, checkInstall = function() { log('-=&gt; MadKnowledge v'+version+' &lt;=- ['+lastupdate+']'); }, handleInput = function(msg) { if(msg.type == "api" && msg.content.indexOf("!MadKnowledge") !== -1) { sendChat ("MadKnowledge", 'Acquired Knowledge'+karollBraxe+''), sendChat ("MadKnowledge", 'Character Grab' +char+' with HP' +HP+ ''); } }, registerEventHandlers = function() { on('chat:message', handleInput); }; return { CheckInstall: checkInstall, RegisterEventHandlers: registerEventHandlers }; }()); on("ready", function(){ 'use strict'; MadKnowledge.CheckInstall(); MadKnowledge.RegisterEventHandlers(); }); A couple of variables at the top of your code (char and HP) are declared too early. You want them inside or after the handleInput function. The structure of this code is a bit complex when you're not used it it (it confused me for ages too). If you follow the code from where it starts (the on(ready) function, through the register event handler function, you can see it sets up an event listener, to watch chat messages. When something is typed in chat, it launches the handleInput function. I go through this to establish that any functions you want to add should start in the handleInput function.&nbsp; So, variables that depend on the chat message contents should be moved there. Now in handleInput, you can see it has a parameter 'msg'. This is data object with a huge amount information - it includes the chat message, which if you have used things like @(selected} or @{target} in the message, will include the ids of the tokens clicked.&nbsp; So, you need to figure out what information you'll be sending to the function. Say, you decide on something like: !MadKnowledge ?{Target|tokenid} This would pass to the function&nbsp; the token id. If you have a token selected, or several, it also automatically includes that information. So lets look at how you'd need to set up handleInput. Before you do this remove these two lines char = ?????(pull the selected token somehow),&nbsp; HP = getAttrByName(char.id,'HP'), from earlier in the script. handleInput = function(msg) { if(msg.type == "api" && msg.content.indexOf("!MadKnowledge") !== -1) { // we can set up variables here, now that the script is initialised. let args = msg.content.split(" "); // msg.content is the text written in the chat. By choosing a separator, we create a handy array. // in this case args[0] is "!MadKnowldedge" and args[1] is the token id. // for clarity, lets set args[1] to a variable since we might need to use it a lot. const monster_id = args[1]; // there are at least two ways you can go here. For your purposes, probably the best way is to use getAttrByName let HP = getAttrByName(monster_id, "HP", "current"); sendChat ("MadKnowledge", 'Acquired Knowledge'+karollBraxe+''), sendChat ("MadKnowledge", 'Character Grab' +char+' with HP' +HP+ ''); } }, The above should get you the value of the HP stat. You also want to know how to identify the characters. msg is your friend. If you have tokens selected when you run this, that information is passed to the script in a variable called msg.selected. This is an array of graphic objects, you can use _foreach or a for loop to loop through them and get character details. Someone else will have to help you with that since it's bedtime for me.&nbsp; Hope this post helps set you on the right track.
Ok .. so I set this up like you said .. but i'm unclear on a couple things. I want to select a monster token that is linked with a journal entry that has a character sheet. I want to run the !MadKnowledge and have it grab multiple variables from that character sheet 1. Do I need to type something in front of !MadKnowledge to get it to pass the token information if it is selected? Like do I have to type !MadKnowledge @Selected If it automatically passes the selected token information with just selecting it to the function I should be able to access any part of the character sheet right?&nbsp; So that !MadKnowledge input while selecting a token lets me grab information off that token? or do I need to send more information? 2.&nbsp; I feel like the way it is currently written when I type !MadKnowledge it is trying to look for something after !MadKnowledge for args[1].&nbsp; If I am using a selected token and passing information that way do I need to do something different than args[1] to get the info from the selected token? If I run this now it only seems to do Character Grab undefined with HP undefined.&nbsp; It isn't grabbing any relevant information.&nbsp;
ok .. so I get this to work with typing in&nbsp; !MadKnowledge @{Selected|character_id}&nbsp; Is there a way to code it in the API so it reads off the selected token ...&nbsp; is that by using msg.selected(character_id)?
1520861268

Edited 1520861309
Mad Knowledge This Script Will Now Work By Selecting A Pathfinder NPC Dragged From Bestiary To A Journal Once Created The Command: !MadKnowledge @{Selected|character_id} @{target|character_id} @{target|character_name} Will Use The selected Token and then target a PC token to report random data back to the PC in a whisper and also CC the GM This is still under development but this version works to give some basic stuff.&nbsp; Thanks for any feedback. <a href="https://gist.github.com/ClemCon/f0dfd0b6d50eb1bb11be38b164a89d00" rel="nofollow">https://gist.github.com/ClemCon/f0dfd0b6d50eb1bb11be38b164a89d00</a>
1520864624
GiGs
Pro
Sheet Author
API Scripter
I've just had a quick skim before bed. A minor point: You use parseInt a lot in places you don't need to.&nbsp; parseInt is used when you think the data being parsed might be a string, or null. If you are creating new variables from scratch, you can just set them, like this: var rarity = 0; var knowTarget = 10 + CR + rarity A more useful point: const currentplayer = args[3] + " " + args[4]; const currentplayerID = args[2]; If the player here is the person using the macro, you can get these using from msg. const currentPlayer = msg.who; // this is the player name, if you want character name, you can search for characters controlled by that player const currentPlayerID = msg.playerid; // actually while writing this, I remembered the player id is not the same as the character id, but since I'm going to bed, I'll leave this as an exercise. Or you can use @{selected|character_id} (or Target you can have multiple target selections in the same line) to get the character id, and then use that to get the other details.
1520954190

Edited 1520954363
Ok .. fixed the parseInt () Issues .. I was having trouble with it adding things together instead of actually adding math .. I realize now if even 1 part is a string it does this so i only need to parseInt() that part.&nbsp; I had done all the parts just to ensure it was using actual numbers.&nbsp; I think this has been fixed. Updates: The !MadKnowledge script now reports whether you have ranks in the knowledge required and gives subtype and related information on first success and then gives lore (stored in the notes 1 on the npc sheet) and then starts giving random information every 3 over the DC.&nbsp; (Changed to 3 instead of 5 as normal to give more successful results) Bugs: Tried using msg.who but it returns the actual player and not the character they are using.&nbsp; Since players have multiple characters sometimes it might be difficult to use this to figure out which one they want to use the data for.&nbsp; Not sure if there is an easy way? I am getting an error when it tries to grab the repeating section row at index X for repeating_weapon if it doesn't exist on the character sheet. This may be a pathfinder sheet question but in general is there a way to check and see if it even exists or to grab the length of this index before trying to grab it in the first place to avoid this error?&nbsp; Basically when it tries to grab this piece of information off the character sheet it may or may not exist if the character has a weapon or not.&nbsp; We want to display this info as one of the success options.&nbsp; A little help here would be appreciated. Current Version <a href="https://gist.github.com/ClemCon/f0dfd0b6d50eb1bb11" rel="nofollow">https://gist.github.com/ClemCon/f0dfd0b6d50eb1bb11</a>... Formatting: I would like some help with what would be a good way to format the presentation of this and maybe an example to look at on how to do a nice polished return of information to the player in the whisper.
1520962704
The Aaron
Pro
API Scripter
Sorry for my absence, was on vacation. Tokens represent a character via their 'represents' property, which will contain the character_id of the character they represent.&nbsp; There is no way to tell the difference between a an ID for a token and an ID for a character (or a drawing or anything else) other than to make the assumption that it is one of those and try to get an object of that type.&nbsp; You could create a function for this: const resolveCharacter = (id) =&gt; { // Assume it's a character: let theChar = getObj('character',id); if(!theChar){ let theToken = getObj('token',id); if(theToken){ theChar = getObj('character',theToken.get('represents')); } } return theChar; }; resolveCharacter() will either return the character that the id represents, or null; If you cared, you could also find what the id represented with: const getIdType = (id) =&gt; { let obj = findObjs({id})[0]; if(obj){ return obj.get('type'); } return 'none'; }; That probably won't be as fast as just checking for a few specific types. Minor correction to the above, msg.who is the name of the speaker, which may be the player or may be the character selected as "speaking as".&nbsp; I use this to get the display name of players so I can whisper to them: &nbsp; &nbsp; &nbsp; &nbsp; let who=(getObj('player',msg.playerid)||{get:()=&gt;'API'}).get('_displayname'); This will set who to either the display name of the player or 'API' in the case that the api command came from another API script via the chat, and there is no player (That used to not work, but it changed so I had to update all my scripts to not assume a player sent the message!). You can then use that with whispers by quoting it: sendChat('Some API',`/w "${who}" A whispered message.`); Back ticks (`) are the used for template literals in Javascript as of ES2015 and let you embed expressions with the ${ } syntax.&nbsp; very handy for avoiding a whole bunch of "+" all over the place. When argument parsing, I like to split on whitespace like this: let cmds = msg.content.split(/\s+/); That makes it so double taping space or using tabs (maybe from copy pasting) doesn't cause an issue. Hope that helps.
1520962816

Edited 1520962900
GiGs
Pro
Sheet Author
API Scripter
You're right about msg.who being an issue if they have multiple characters. It's better in that case to use Selected or Target to get the ids. About the repeating issue, yes you can test if the value returned is valid. When you grab a value, if it is undefined , something went wrong. The code snippet below should (crosses fingers) deal with that. The nesting of if (att !== undefined) inside the other if statements is deliberate, btw.&nbsp; let Att1 = getAttrByName(monster_id, "repeating_weapon_$0_name", "current"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let Att2 = getAttrByName(monster_id, "repeating_weapon_$1_name", "current"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let Att3 = getAttrByName(monster_id, "repeating_weapon_$2_name", "current"); let Attnotes = ""; if (Att1 !== undefined) { Attnotes = "Attacks: " + Att1 ; if (if (Att2 !== undefined) { Attnotes = Attnotes + " || " + Att2 ; if (Att3 !== undefined) { Attnotes = Attnotes + " || " + Att3 ; } } Attnotes = Attnotes + " || " + Note2 } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; let Note1 = getAttrByName(monster_id, "customn1", "current"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let Note2 = getAttrByName(monster_id, "customn2", "current"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var info = [ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "AC: "+ AC + " Touch: " + Touch + " Flat-Footed: " + FF + ", CMD: " + CMD, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "DR: "+DR+ " SR: " + SR, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Defenses: " + DA + " Immune: " + Imm + " Resist: " + Resist, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Weaknesses: " + Vuln + ", Typical Alignment: " + Align +", Heal Conditions: " + Heal, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Senses: "+vision, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Fort: " + Fort + ", Con: "+ Con + ", HD: " + HD, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Reflex: " + Ref + ", Dex: " + Dex + ", Touch AC: " + Touch, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Will: " + Will + ", Int: " + Int + ", Wis: " + Wis + ", Cha: " + Cha, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Reach " + Reach + ", BAB: " + BAB + ", Str: " + Str + ", CMB: " + CMB +' '+CMBnotes, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Speed " + Speed + ", Burrow: " + Burrow + ", Climb " + Climb + ", Fly: " + Fly + " "+ FlyM + ", Swim: " + Swim + " " + SpeedNotes, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Attacks: " + Att1 +" || "+ Att2 + " || " + Att3 + " || " + Note2, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ]; if(Attnotes !== "") { info.push(Attnotes); } I altered the info array - removing the attacks section, and building that outside the array with whatever attacks actually exist, then adding it onto the end.
1520963174
GiGs
Pro
Sheet Author
API Scripter
Regarding presentation of output: You can use html & css to format your output. Build a string including in the script that includes all the divs, styles, etc., and print it with sendChat.
So the issue I'm having is that unless the character sheet actually "added" an attack it doesn't exist to pull from in the first place.&nbsp;&nbsp; So even though this formatting is super nicer, it still has the same issue I think. let Att2 = getAttrByName(monster_id, "repeating_weapon_$1_name", "current");&nbsp; for example will give an error if that attribute doesn't actually even exist because that monster doesn't have a second attack in the first place. Attacks are added to the character sheet by hitting an add button which creates the objects when you add them.&nbsp;&nbsp; Does that clarify the issue?
1520966472
The Aaron
Pro
API Scripter
I was thinking getAttrByName() would always return a value (though might spam "error"s to the API Console).&nbsp; Probably you could check that Attr2.length &gt; 0 instead of testing for undefined. If that doesn't work, you can do the resolving of $# indexes yourself, which is a terrible pain but I have a function for it...
1520967155
The Aaron
Pro
API Scripter
Here's an example script for resolving a repeating attribute: on('ready',function(){ &nbsp; &nbsp; "use strict"; &nbsp; &nbsp; var attrLookup = function(character,name,caseSensitive){ &nbsp; &nbsp; &nbsp; &nbsp; let match=name.match(/^(repeating_.*)_\$(\d+)_.*$/); &nbsp; &nbsp; &nbsp; &nbsp; if(match){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let index=match[2], &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; attrMatcher=new RegExp(`^${name.replace(/_\$\d+_/,'_([-\\da-zA-Z]+)_')}$`,(caseSensitive?'i':'')), &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; createOrderKeys=[], &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; attrs=_.chain(findObjs({type:'attribute', characterid:character.id})) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .map((a)=&gt;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return {attr:a,match:a.get('name').match(attrMatcher)}; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .filter((o)=&gt;o.match) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .each((o)=&gt;createOrderKeys.push(o.match[1])) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reduce((m,o)=&gt;{ m[o.match[1]]=o.attr; return m;},{}) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .value(), &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sortOrderKeys = _.chain( ((findObjs({ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; type:'attribute', &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; characterid:character.id, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: `_reporder_${match[1]}` &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })[0]||{get:_.noop}).get('current') || '' ).split(/\s*,\s*/)) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .intersection(createOrderKeys) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .union(createOrderKeys) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .value(); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(index&lt;sortOrderKeys.length && _.has(attrs,sortOrderKeys[index])){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return attrs[sortOrderKeys[index]]; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return findObjs({ type:'attribute', characterid:character.id, name: name})[0]; &nbsp; &nbsp; }; &nbsp; &nbsp; on('chat:message',function(msg){ &nbsp; &nbsp; &nbsp; &nbsp; if('api' === msg.type && msg.content.match(/^!resolve-repeating\b/) ){ let args=_.rest(msg.content.split(/\s+/)), who = getObj('player',msg.playerid).get('displayname'); if(args.length){ _.chain(msg.selected) .map((o)=&gt;getObj('graphic',o._id)) .reject(_.isUndefined) .filter((t)=&gt;t.get('represents').length) .map(t=&gt;{ return {token:t,character:getObj('character',t.get('represents'))};}) .reject(o=&gt;_.isUndefined(o.character)) .map(o=&gt;{ o.attrs=_.reduce(args,(m,a)=&gt;{ let attr=attrLookup(o.character,a,false); m[a]=(attr ? attr.get('name') : '[MISSING]'); return m; },{}); return o; }) .map(o=&gt; `&lt;div&gt;&lt;h3&gt;${o.character.get('name')}&lt;/h3&gt;&lt;ul&gt;${_.map(o.attrs,(a,n)=&gt;`&lt;li&gt;&lt;b&gt;${n}&lt;/b&gt;-&lt;code&gt;${a}&lt;/code&gt;&lt;/li&gt;`).join('')}&lt;/ul&gt;&lt;/div&gt;`) .tap(out=&gt;{ sendChat('',`/w ${who} ${out.join('')}`); }); } else { sendChat('',`/w ${who} &lt;b&gt;No tokens selected&lt;/b&gt;`); } } &nbsp; &nbsp; }); }); This displays the value of the named attribute for all selected tokens' characters, handling the $# syntax for repeating rows correctly. !resolve-repeating repeating_inventory_$1_inventory_weight dexterity someOtherAttr
Current Version Now gives a nice report of information.&nbsp;&nbsp; This was generated by selecting the monster token created using the compendium&nbsp; (Owlbear in this case) and then initiating the MadKnowledge Token Macro: !MadKnowledge @{Selected|character_id} @{target|character_id}&nbsp; And then clicking on the PC to roll the check (currently Braxe Tinderwig in this example) and then a whisper is sent to that player telling them what they know. Let me know what you think! Bugs:&nbsp; I'm still not sure how to find out the number of attacks that have been added to a character created using the pathfinder sheet.&nbsp; tried several variations of .length.
1520973687
The Aaron
Pro
API Scripter
This should work: let attrs = filterObjs((a) =&gt; a.get('type') === 'attribute' && a.get('characterid')===monster_id && /^repeating_weapon_[^_]*_name$/.test(a.get('name')) ); let numAttrs = attrs.length;
That Worked Great!&nbsp; Thanks Guys! My goal was to have this much working by Saturday so ahead of schedule thanks to the help. Please feel free to use this anyone who plays pathfinder using the pathfinder compendium compatible sheet and let me know if it works for you. I'm hoping this encourages way more role playing of character information at my table instead of me telling them random facts from the bestiary Updates: Fixed the error and able to now list all attacks as a bit of information you might find on the creature Additional Use Of Sheet Notes: Notes 1 on character sheet is for GM to input Lore they want the player to know Notes 2&nbsp; on character sheet is for GM to input special attacks they want the player to have info on GM needs to input the Subtype if it doesn't parse over using a Capital letter (Undead for example) Next Add a take 10 option for bards? Any suggestions? <a href="https://gist.github.com/ClemCon/f0dfd0b6d50eb1bb11" rel="nofollow">https://gist.github.com/ClemCon/f0dfd0b6d50eb1bb11</a>...
1521042495
vÍnce
Pro
Sheet Author
This looks cool.&nbsp; You offering this up to the public?&nbsp; Any other special instructions/setup?&nbsp; Is it tied to a specific sheet's attributes?&nbsp; Definitely need to check this out.&nbsp; TIA
So this uses the pathfinder sheet but could be modified for other sheets if you just replace the API script pulls to the other sheet To set it up you add the API and have pathfinder sheets linked to your campaign Then when you create a journal entry for a monster you can drag from the compendium or input manually the information&nbsp; In game players can click the monster they want to roll knowledge on and then select their pc token or the token that is rolling the knowledge and it will whisper back what they know. I have it set up with a macro that creates a button when the token is selected and looks like&nbsp; !MadKnowledge @{Selected|character_id} @{target|character_id} or the player could type that into the box with a monster selected I would like to make this available to the public but the code still lacks a lot of polish .. I'm not sure what level it needs to be before submitting for approval.&nbsp; But it is available to the public now on the GITHUB <a href="https://gist.github.com/ClemCon/f0dfd0b6d50eb1bb11" rel="nofollow">https://gist.github.com/ClemCon/f0dfd0b6d50eb1bb11</a>... Additional Use Of Sheet Notes: Notes 1 on character sheet is for GM to input Lore they want the player to know *this is more like description and knowledge stuff typically found at the bottom of the bestiary entry Notes 2 on character sheet is for GM to input special attacks they want the player to have info on *this will eventually be fixed to pull from special attacks GM needs to input the Subtype on the character sheet if it doesn't parse over using a Capital letter (Undead for example) *I am working to add the next subtype so it will return demon information for outsider (demon) and not just outsider