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 for finding things

H All, I want to create a macro that will do something along the following lines:- Player clicks macro Macro searches for token closest to player (Will prob need to be API Script I think) Gets value from character sheet for closest token and rolls perception check against that value. If Successful check call token_mod to perform an action on the closest token and display chat message If unsuccessful display chart message. I'm using D&D 5e. I don't want the specific code, just ideas of the best way to do this please and pointers to where I can find documentation to assist. I'm still new to Roll20 Macros and APIs, but am a fairly good programmer, so once I know where to look I should be able to do this. Thanks Andy
1643533642
Andrew R.
Pro
Sheet Author
You probably want to ask in the API forum.  I'd like to point out that monster tokens are usually MOOKS and don't have a Character Sheet linked to them. 
Thanks Andrew,  The tokens I'm using have NPC character Sheets linked to them I'll post this in the API Forum later today  if noone else can think of way to do this in Macro
1643570356
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Andrew R. said: You probably want to ask in the API forum.  I'd like to point out that monster tokens are usually MOOKS and don't have a Character Sheet linked to them.  Just a quick clarification for anyone reading along. "Mooks" is not an official term, but as used in the popular token linking wiki and help center pages means tokens that are linked to a character sheet, but in a many-to-one relationship. I.e. many tokens sharing a common sheet.
1643574250
The Aaron
Roll20 Production Team
API Scripter
Generally, NPC tokens represent a character, but their bars are not linked to attributes in that character. 
1643586630
The Aaron
Roll20 Production Team
API Scripter
Here's a function for finding all the roll20 map objects near a given roll20 map object, or location: static Roll20Type(o) { const validTypes = [ 'ability', 'attribute', 'campaign', 'card', 'character', 'deck', 'graphic', 'hand', 'handout', 'jukeboxtrack', 'macro', 'message', 'page', 'path', 'player', 'rollabletable', 'tableitem', 'text', 'token' ]; if('function' === typeof o.get){ let t = o.get('type'); if(validTypes.includes(t)){ return t; } } } static Roll20MapObjectType(o) { const validTypes = ['path','text','graphic']; let t = Util.Roll20Type(o); if(validTypes.includes(t)){ return t; } } } const findNearObjs = (location, predicate=()=>true) => { let x=0; let y=0; let pageid = Campaign().get('playerpageid'); let pred = predicate; if(Util.Roll20MapObjectType(location)){ pred = (o,d)=>location.id !== o.id && predicate(o,d); x = location.get('left'); y = location.get('top'); pageid= location.get('pageid'); } else { x = location.x || x; y = location.y || y; pageid = location.pageid || pageid; } const distSq = (obj) => Math.pow((obj.get('left')-x),2)+Math.pow((obj.get('top')-y),2); const compareDistSq = (a,b) => a.distSq-b.distSq; return findObjs({ pageid }) .map(o=>({distSq:distSq(o),object:o})) .filter((o)=>pred(o.object,o.distSq)) .sort(compareDistSq); }; The function is findNearObjs(), and takes two arguments: A location object.  This can either be a roll20 object with a location (path, text, or graphic), or a simple Javascript object with an x, y, and pageid property.  It will default to 0, 0, and the current ribbon page. A predicate function which will be applied to each found object.  It will be supplied the object as the first parameter and the squared distance from the test location/object as the second parameter. The function returns a sorted array of objects based on their distance from the test location/object.  Each entry in the array has two properties: object - This is the roll20 object that was found. distSq - This is the squared distance from the test location/object. Note: Squared distances are used for efficiency as square roots are expensive to perform and the relationship between distances and squared distances are preserved. Here's an example script using it, which you can call by selecting a token: !find-near or by providing a location: !find-near 700 700 The script demonstrates different predicate functions as well as dealing with the resulting structure. Code: on('ready',()=>{ class Util { static Roll20Type(o) { const validTypes = [ 'ability', 'attribute', 'campaign', 'card', 'character', 'deck', 'graphic', 'hand', 'handout', 'jukeboxtrack', 'macro', 'message', 'page', 'path', 'player', 'rollabletable', 'tableitem', 'text', 'token' ]; if('function' === typeof o.get){ let t = o.get('type'); if(validTypes.includes(t)){ return t; } } } static Roll20MapObjectType(o) { const validTypes = ['path','text','graphic']; let t = Util.Roll20Type(o); if(validTypes.includes(t)){ return t; } } } const findNearObjs = (location, predicate=()=>true) => { let x=0; let y=0; let pageid = Campaign().get('playerpageid'); let pred = predicate; if(Util.Roll20MapObjectType(location)){ pred = (o,d)=>location.id !== o.id && predicate(o,d); x = location.get('left'); y = location.get('top'); pageid= location.get('pageid'); } else { x = location.x || x; y = location.y || y; pageid = location.pageid || pageid; } const distSq = (obj) => Math.pow((obj.get('left')-x),2)+Math.pow((obj.get('top')-y),2); const compareDistSq = (a,b) => a.distSq-b.distSq; return findObjs({ pageid }) .map(o=>({distSq:distSq(o),object:o})) .filter((o)=>pred(o.object,o.distSq)) .sort(compareDistSq); }; const getPageForPlayer = (playerid) => { let player = getObj('player',playerid); if(playerIsGM(playerid)){ return player.get('lastpage') || Campaign().get('playerpageid'); } let psp = Campaign().get('playerspecificpages'); if(psp[playerid]){ return psp[playerid]; } return Campaign().get('playerpageid'); }; on('chat:message',msg=>{ if('api'===msg.type && /^!find-near(\b\s|$)/i.test(msg.content) && playerIsGM(msg.playerid)){ let who = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname'); let token = (msg.selected || []) .map(o=>getObj('graphic',o._id)) .filter(g=>undefined !== g) [0] ; let minDist = Math.pow(1.5 * 70,2); let maxDist = Math.pow(5.5 * 70,2); if(token){ //let near = findNearObjs(token,(t,dsq)=>['objects'].includes(t.get('layer')) && 'graphic'===t.get('type') && dsq>minDist && dsq < maxDist); let near = findNearObjs(token,(t,dsq)=> ['gmlayer','objects'].includes(t.get('layer')) && 'graphic'===t.get('type') && dsq > minDist && dsq < maxDist ); sendChat('',`/w "${who}" <div></div>Near token ${token.get('name')} (min: ${Math.sqrt(minDist).toFixed(2)}, max: ${Math.sqrt(maxDist).toFixed(2)}):</div><ol>${near.map(o=>`<li><b>${Math.sqrt(o.distSq).toFixed(2)}</b> ${o.object.get('name')}</li>`).join('')}</ol></div>`); } else { let pageid = getPageForPlayer(msg.playerid); let args = msg.content.split(/\s+/).slice(1); let loc = { x:parseInt(args[0])||0, y:parseInt(args[1])||0, pageid }; let near = findNearObjs(loc,(t)=>['gmlayer','objects'].includes(t.get('layer')) && 'graphic'===t.get('type')); sendChat('',`/w "${who}" <div></div>Near location (${loc.x},${loc.y}):</div><ol>${near.map(o=>`<li><b>${Math.sqrt(o.distSq).toFixed(2)}</b> ${o.object.get('name')}</li>`).join('')}</ol></div>`); } } }); });
Thanks Aaron.  Been using your script and modifying it to get what I want.  I'm almost there.  Just trying to get the perception bonus for a character, however the call is returning a promise object rather than the value.  How can I get the value I want into my script for further processing? let perception = CharSheetUtils.getSheetAttr(character, 'perception_bonus'); log('Perception Bonus:' + perception); The above returns "Perception Bonus:[object Promise]"