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

Nested with a rolltable

1700441431

Edited 1700441475
I am looking to reference a Rolltable in a macro. nothing too complicated. !summon RollTable-Result H I have a rolltable that returns one of six names. I need the macro to randomly select a name from the list then run the summon command.  I can get a result from the table with this macro: !rt [[ 1t[ComicHit] ]]  I just can't seem to get the summon macro to use the result of the rolltable macro. Can someone give me a hand with the syntax?
1700460899
timmaugh
Pro
API Scripter
I don't think you should have to go so far as using Recursive Tables for this... you are not continuing to roll from the table, or executing a command that you find in your rollable table. I'm not sure which "summon" script you are using, but typically a script author would unpack the inline roll to get the value out of it. If that is happening, then a command something like this should work: !summon [[1t[ComicHit]]] (not sure where the "H" came from in your initial example, or if it is necessary... but you can add it back if it is.) If the above does NOT work, then I would think your summon script isn't unpacking the roll to get the result. In that case, you can get the value from the roll yourself by using the MetaScriptToolbox (available in the 1-click): !summon [[1t[ComisHit]]].value
so the normal syntax would be: !summon Pow H this initiates the summon API, finds a character named Pow, and loads it as size huge (hence the H) beside the selected token on the screen.  what I am trying to do is check the rolltable to randomly choose from the options of Bang, Boom, Pow, Kapow, Wham, & Smash.  (each of these is a token that will appear if I reference them directly, but when trying to randomize via the rolltable to pick a random name to summon, it gives me grief. I tried the scripts you suggested. they gave me an error:  Monster of name "$[[0]]" does not exist.  
1700507322
The Aaron
Roll20 Production Team
API Scripter
That summon script doesn't support inline rolls as part of the argument. Can you link to it, I can fix it. Alternatively, you can use Tim's MetaScriptToolbox to handle this.  You'd just need to find and install MetaScriptToolbox from the 1-click, then try the above.  If it still doesn't work, you'll want to remove the summon script and add it again (it will need to be after the MetaScriptToolbox if it's doing a direct register of the chat handler).
Wakko, so the modified summon script and the MetaScriptToolbox are probably simpler options but if you want an alternative, particularly if you want do some conditional choices on say the size passed into the summon script depending on the table entry. You could use  ScriptCards for this . Something like: !scriptcard {{ --&summonTableName|CosmicHit --#title|Summon Coolness --=SummonRoll|[T#[&summonTableName]] --@summon|[$SummonRoll.tableEntryText] H }} Could work for you and if you want, ScriptCards could do conditionals to set the size to another value than H if that was desired. For example: --&summonSize|H --?"[$SummonRoll.tableEntryText]" -eq "Bang"|&summonSize;M --@summon|[$SummonRoll.tableEntryText] [&summonSize] Just another option for you if there are other things you want to add to the macro.
/* This is an improved Summoning API that was built upon. Original author: Brandon W. Original here: <a href="https://app.roll20.net/forum/post/466778/scipt-auto-create-a-token#post-466778" rel="nofollow">https://app.roll20.net/forum/post/466778/scipt-auto-create-a-token#post-466778</a> Special thanks to Brian for helping me with some of the script. I hope this serves others well, do not hesistate to contact me regarding bugs or improvements. -GM King Usage: 1. Select token you want the monster to appear next to. 2. In chat, type "!summon (name of monster) (number to summon)" without brackets or quotation marks. 3. By default the monster will take up a 5x5 ft square (medium or small creature). To summon larger creatures add a letter to the end of the message: &nbsp; &nbsp; F= Fine, D = Diminutive, T= Tiny, L = large, H = Huge, G = Gargantuan, C = Colossal, cust = customised size (this tag must be followed by two numbers, see examples). 4. You can also summon a torch that will give 40 ft of vision with 20 ft of dim light (torch must be in the name). NB. The name of the monster must be in your character sheet list and the image must be the token image you wish to use (marketplace images do not work). Examples: !summon torch !summon eagle !summon eagle 2 !summon eagle 1d3 !summon eagle 1d3+1 !summon cave troll 1d3+1 H !summon flame sphere cust 140 140 NB. When making a custom size every 70 = 1 square. */ on("chat:message", function(msg) { &nbsp; &nbsp; if (msg.type == "api" &amp;&amp; msg.content.indexOf("!summon ") === 0)&nbsp; { var GM = ""; var size1 = 0; var size2 = size1; var s = 0; var inputName = ""; var howMuch = 0; var amount = 0; // Amount of summoned monsters. var mod = 0; // Modifier to amount. var skip = false; var action = "Summons "; var section = msg.content.split(" "); // Breaks API call into sections. if (section.length &gt; 1) { for (var j=1;j&lt;section.length;j++) { if (typeof parseInt(section[j]) === 'number' &amp;&amp; Math.round(parseInt(section[j])) % 1 == 0 || section[j].length &lt;= 4 &amp;&amp; section[j].toLowerCase().search("d") != -1 &amp;&amp; /\dd/.test(section[j]) || section[j].toLowerCase() == 'cust') { if (section[j].toLowerCase() == 'cust') { var howMuch = j; break; } else { if (j != section.length-1) { for (var m = j+1; m &lt; section.length; m++) { if (typeof parseInt(section[m]) === 'number' &amp;&amp; Math.round(parseInt(section[m])) % 1 == 0 || section[m].length &lt;= 4 &amp;&amp; section[m].toLowerCase().search("d") != -1 &amp;&amp; /\dd/.test(section[m])) { skip = true; } else { skip = false; } } } else { skip = false; } if (skip == false) { var howMuch = j; break; } } } } if (howMuch &gt; 0) { for (var k=1;k&lt;howMuch;k++) { if (k == howMuch-1) { inputName = inputName + section[k]; } else { inputName = inputName + section[k] + " "; } } if (section[howMuch].toLowerCase().search("d") != -1) // If user chose to roll. { if (section[howMuch].indexOf("+") != -1) { mod = section[howMuch].split("+"); // If user specified a modfier. section[howMuch] = mod[0]; mod = parseInt(mod[1],10); } var count = 0; var diceRoll = section[howMuch].split("d"); diceRoll[0] = diceRoll[0].replace(/\D/g,''); diceRoll[1] = diceRoll[1].replace(/\D/g,''); var lowEnd = parseInt(diceRoll[0]); var highEnd = parseInt(diceRoll[1]); while (count &lt; lowEnd)&nbsp; { amount = amount + randomInteger(highEnd); // Rolls dice. count++; log("Summoner: rolls a " + amount) } } else if (section[howMuch].toLowerCase() != 'cust') { var amount = parseInt(section[howMuch],10); // Creates variable for number of summoned monsters. } else { amount == 1; } amount = amount + mod; } else if (howMuch == 0) { if (section[section.length-1].length == 1) { s = 1; howMuch = section.length - 2; } amount = 1; for (var k=1;k&lt;section.length-s;k++) { if (k == section.length-1-s) { inputName = inputName + section[k]; } else { inputName = inputName + section[k] + " "; } } } if (section.length &gt; (howMuch+1) &amp;&amp; section[howMuch].toLowerCase() != "cust") { if (section[howMuch+1].toLowerCase() == "f") { size1 = -63; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "d") { size1 = -56; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "t") { size1 = -35; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "l") { size1 = 70; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "h") { size1 = 140; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "g") { size1 = 210; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "c") { size1 = 280; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "cust") { if (typeof section[howMuch+2] != 'undefined') { size1 = parseInt(section[howMuch+2]); } else { sendChat(msg.who, "/w gm Please ensure the \"cust\" tag is followed by two values (every 70 = 1 square)."); // If monster is not in character list. } if (typeof section[howMuch+3] != 'undefined') { size2 = parseInt(section[howMuch+3]); } else { sendChat(msg.who, "/w gm Please ensure the \"cust\" tag is followed by two values (every 70 = 1 square)."); // If monster is not in character list. } } } else if (section[howMuch].toLowerCase() == "cust") { if (typeof section[howMuch+1] != 'undefined') { size1 = parseInt(section[howMuch+1]); } else { sendChat(msg.who, "/w gm Please ensure the \"cust\" tag is followed by two values (every 70 = 1 square)."); // If monster is not in character list. } if (typeof section[howMuch+2] != 'undefined') { size2 = parseInt(section[howMuch+2]); } else { sendChat(msg.who, "/w gm Please ensure the \"cust\" tag is followed by two values (every 70 = 1 square)."); // If monster is not in character list. } } } else { sendChat(msg.who, "/w gm No monsters specified to summon"); // If user writes too many things. } var check = findObjs({ _type: "character", name: inputName },{caseInsensitive: true})[0]; if (typeof check == 'undefined') { sendChat(msg.who, "/w gm Monster of name \""&nbsp; + inputName + "\" does not exist."); // If monster is not in character list. } else { var list = findObjs({ _type: "character", name: inputName},{caseInsensitive: true}); var characterId = list[0].id; // Extract character ID from character sheet var characterName = list[0].get('name'); // Extract character name from character sheet var characterImage = list[0].get('avatar'); // Extract character image URL from character sheet &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (characterImage.indexOf("marketplace") != -1) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat(msg.who, "/w gm Monster of name \""&nbsp; + inputName + "\" has a marketplace image.");&nbsp; &nbsp; // Error message &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { characterImage = characterImage.replace("med","thumb"); // Change character image to thumb if med characterImage = characterImage.replace("max","thumb"); // Change character image to thumb if max var selected = msg.selected; _.each(selected, function(obj) { var tok = getObj("graphic", obj._id); // Create variable for selected token (for summoned monster positioning) var HP = findObjs({ _type: "attribute", name: "HP", _characterid: characterId },{caseInsensitive: true})[0];&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //finds the health from charactersheet if (typeof HP == 'undefined') { HP = "-1"; } else { HP = (HP) ? HP.get("current") : 0;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Changes HP to useable string } var AC = findObjs({ _type: "attribute", name: "AC", _characterid: characterId },{caseInsensitive: true})[0];&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // finds the ac from charactersheet if (typeof AC == 'undefined') { AC = "-1"; } else { AC = (AC) ? AC.get("current") : 0;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Changes AC to useable string } if (amount == 0) { amount = 1; } else if (amount &gt; 20) { var tooMany = amount; amount = 20; log("Summoner: User specified " + tooMany + " monsters. Scaling to 20.") // If user spams the field. } for (var i=0;i&lt;amount;i++) { if (HP == "-1" &amp;&amp; AC == "-1" &amp;&amp; msg.content.toLowerCase().search("torch") != -1) { action = "Lights" createObj("graphic",&nbsp; { represents: characterId,&nbsp; // Links new token to charactersheet left: tok.get("left")+70, top: tok.get("top"), width: 70+size1, height: 70+size2, name: characterName, pageid: tok.get("pageid"), imgsrc: characterImage, layer: "objects", light_radius: 40, light_dimradius: 20, light_otherplayers: true }); } else if (HP == "-1" &amp;&amp; AC == "-1") { // sendChat(msg.who, "/w gm HP or AC value not set (if this is a torch please include \"torch\" in name.");&nbsp; &nbsp; // Error message createObj("graphic",&nbsp; { represents: characterId,&nbsp; // Links new token to charactersheet left: tok.get("left")+70, top: tok.get("top")-35, width: 70+size1, height: 70+size2, name: characterName, showname: false, showplayers_name: false, showplayers_bar1: false, pageid: tok.get("pageid"), imgsrc: characterImage, layer: "objects" }); } else { createObj("graphic",&nbsp; { represents: characterId,&nbsp; // Links new token to charactersheet left: tok.get("left")+70, top: tok.get("top"), width: 70+size1, height: 70+size2, name: characterName, bar2_value: AC, bar3_value: HP, bar3_max: HP, showname: true, showplayers_name: true, showplayers_bar1: true, pageid: tok.get("pageid"), imgsrc: characterImage, layer: "objects" }); } } }); if (amount &gt; 1) { inputName = inputName + "s"; // If more than 1 summon, adds plural. } else if (amount == 1 &amp;&amp; action == "Lights") { amount = " a"; inputName = "torch"; } if (typeof amount === 'number') { amount = amount.toString(); } // sendChat(msg.who, GM + action + amount + " " + inputName + "!"); // Announces how many and what Summoner has summoned. } } } });
in the end I am using it in a Marvel game to pop up old school Batman FX (Bang! Pow!) graphics behind targets.&nbsp; Summon works to do it. I was just trying to step it up a bit. As is, I have several different macros (one for each graphic) and I have to choose. I thought making it randomize could be cool.
another option: I use summon to bring in a character with a multisided token, then I use Token Mod to randomize the side.&nbsp; Might be a simpler solution. The Aaron, can you help me with that macro? Naming the multisided character "Hits", the summon part is&nbsp; !Summon Hits H&nbsp; Thanks, Wakko
1700633846

Edited 1700687498
The Aaron
Roll20 Production Team
API Scripter
Wow! King's Summon script, that takes me back.&nbsp; That will be pretty easy to fix to work with rollable tables. I'll do the mod tomorrow. &nbsp;
Thank you so much.
1700687483

Edited 1701057660
The Aaron
Roll20 Production Team
API Scripter
Ok, this version should do what you want.&nbsp; Let me know if there are any issues.&nbsp; I used a slightly updated base version from the version you were using. /* This is an improved Summoning API that was built upon. Original author: Brandon W. Original here: <a href="https://app.roll20.net/forum/post/466778/scipt-auto-create-a-token#post-466778" rel="nofollow">https://app.roll20.net/forum/post/466778/scipt-auto-create-a-token#post-466778</a> Special thanks to Brian for helping me with some of the script. I hope this serves others well, do not hesistate to contact me regarding bugs or improvements. -GM King Usage: 1. Select token you want the monster to appear next to. 2. In chat, type "!summon (name of monster) (number to summon)" without brackets or quotation marks. 3. By default the monster will take up a 5x5 ft square (medium or small creature). To summon larger creatures add a letter to the end of the message: F= Fine, D = Diminutive, T= Tiny, L = large, H = Huge, G = Gargantuan, C = Colossal, cust = customised size (this tag must be followed by two numbers, see examples). 4. You can also summon a torch that will give 40 ft of vision with 20 ft of dim light (torch must be in the name). 5. !summon-r will cause a summonned token with muliple sides to have a random side selected. NB. The name of the monster must be in your character sheet list and the image must be the token image you wish to use (marketplace images do not work). Examples: !summon torch !summon eagle !summon eagle 2 !summon eagle 1d3 !summon eagle 1d3+1 !summon cave troll 1d3+1 H !summon flame sphere cust 140 140 !summon-r onomatopoeia H NB. When making a custom size every 70 = 1 square. */ /* global TokenNameNumber */ on('ready',function(){ const getCleanImgsrc = (imgsrc) =&gt; { let parts = (imgsrc||'').match(/(.*\/images\/.*)(thumb|med|original|max)([^?]*)(\?[^?]+)?$/); if(parts) { let leader = parts[1].replace(/^https:\/\/s3.amazonaws.com\/files.d20.io\//,'<a href="https://files.d20.io/" rel="nofollow">https://files.d20.io/</a>'); return `${leader}thumb${parts[3]}${parts[4] ? parts[4] : `?${Math.round(Math.random()*9999999)}`}`; } }; const processCreated = (( 'undefined' !== typeof TokenNameNumber &amp;&amp; TokenNameNumber.NotifyOfCreatedToken ) ? TokenNameNumber.NotifyOfCreatedToken : _.noop ) ; const summonToken = (character,pageid,baseX,baseY,baseW,baseH,randomSide) =&gt; { baseW=baseW||70; baseH=baseH||70; character.get('defaulttoken',function(defaulttoken){ const dt = JSON.parse(defaulttoken); if(randomSide &amp;&amp; dt.sides){ let sides = dt.sides.split(/\|/).map(decodeURIComponent); let side = randomInteger(sides.length)-1; let imgsrc = getCleanImgsrc(sides[side]); if(imgsrc) { dt.imgsrc = imgsrc; dt.currentSide = side; } else { sendChat('',`/w gm Cannot switch to side ${side+1} for &lt;b&gt;${character.get('name')}&lt;/b&gt;: &lt;code&gt;Marketplace image&lt;/code&gt;`); } } if(dt &amp;&amp; getCleanImgsrc(dt.imgsrc)){ dt.imgsrc=getCleanImgsrc(dt.imgsrc); dt.left=baseX; dt.top=(baseY-(baseH/2)-(dt.height/2)); if(dt.top-(dt.height/2)&lt;0){ dt.top=(baseY+(baseH/2)+(dt.height/2)); } dt.pageid = pageid; dt.layer="objects"; processCreated(createObj('graphic',dt)); } else { let isMarkplace = false; if(dt) { isMarkplace = /\/marketplace\//i.test(dt.imgsrc); } sendChat('',`/w gm Cannot create token for &lt;b&gt;${character.get('name')}&lt;/b&gt; ${isMarkplace ? "&lt;code&gt;Marketplace image&lt;/code&gt;" : ''}`); } }); }; const processInlinerolls = (msg) =&gt; { if(msg.hasOwnProperty('inlinerolls')){ return msg.inlinerolls .reduce((m,v,k) =&gt; { let ti=v.results.rolls.reduce((m2,v2) =&gt; { if(v2.hasOwnProperty('table')){ m2.push(v2.results.reduce((m3,v3) =&gt; [...m3,(v3.tableItem||{}).name],[]).join(", ")); } return m2; },[]).join(', '); return [...m,{k:`$[[${k}]]`, v:(ti.length &amp;&amp; ti) || v.results.total || 0}]; },[]) .reduce((m,o) =&gt; m.replace(o.k,o.v), msg.content); } else { return msg.content; } }; on("chat:message", (msg) =&gt; { if (msg.type == "api" &amp;&amp; /^!summon(-r?)?(\b|$)/i.test(msg.content)) { let size1 = 0; let size2 = size1; let s = 0; let inputName = ""; let howMuch = 0; let amount = 0; // Amount of summoned monsters. let mod = 0; // Modifier to amount. let skip = false; let action = "Summons "; let section = processInlinerolls(msg).split(/\s+/); // Breaks API call into sections. let [,randomSide] = msg.content.match(/^!summon(?:-(r)?)?/i); if (section.length &gt; 1) { for (let j=1;j&lt;section.length;j++) { if (typeof parseInt(section[j]) === 'number' &amp;&amp; Math.round(parseInt(section[j])) % 1 == 0 || section[j].length &lt;= 4 &amp;&amp; section[j].toLowerCase().search("d") != -1 &amp;&amp; /\dd/.test(section[j]) || section[j].toLowerCase() == 'cust') { if (section[j].toLowerCase() == 'cust') { howMuch = j; break; } else { if (j != section.length-1) { for (let m = j+1; m &lt; section.length; m++) { if (typeof parseInt(section[m]) === 'number' &amp;&amp; Math.round(parseInt(section[m])) % 1 == 0 || section[m].length &lt;= 4 &amp;&amp; section[m].toLowerCase().search("d") != -1 &amp;&amp; /\dd/.test(section[m])) { skip = true; } else { skip = false; } } } else { skip = false; } if (skip == false) { howMuch = j; break; } } } } if (howMuch &gt; 0) { for (let k=1;k&lt;howMuch;k++) { if (k == howMuch-1) { inputName = inputName + section[k]; } else { inputName = inputName + section[k] + " "; } } if (section[howMuch].toLowerCase().search("d") != -1) // If user chose to roll. { if (section[howMuch].indexOf("+") != -1) { mod = section[howMuch].split("+"); // If user specified a modfier. section[howMuch] = mod[0]; mod = parseInt(mod[1],10); } let count = 0; let diceRoll = section[howMuch].split("d"); diceRoll[0] = diceRoll[0].replace(/\D/g,''); diceRoll[1] = diceRoll[1].replace(/\D/g,''); let lowEnd = parseInt(diceRoll[0]); let highEnd = parseInt(diceRoll[1]); while (count &lt; lowEnd) { amount = amount + randomInteger(highEnd); // Rolls dice. count++; log("Summoner: rolls a " + amount); } } else if (section[howMuch].toLowerCase() != 'cust') { amount = parseInt(section[howMuch],10); // Creates variable for number of summoned monsters. } else { amount == 1; } amount = amount + mod; } else if (howMuch == 0) { if (section[section.length-1].length == 1) { s = 1; howMuch = section.length - 2; } amount = 1; for (let k=1;k&lt;section.length-s;k++) { if (k == section.length-1-s) { inputName = inputName + section[k]; } else { inputName = inputName + section[k] + " "; } } } if (section.length &gt; (howMuch+1) &amp;&amp; section[howMuch].toLowerCase() != "cust") { if (section[howMuch+1].toLowerCase() == "f") { size1 = -63; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "d") { size1 = -56; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "t") { size1 = -35; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "l") { size1 = 70; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "h") { size1 = 140; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "g") { size1 = 210; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "c") { size1 = 280; size2 = size1; } else if (section[howMuch+1].toLowerCase() == "cust") { if (typeof section[howMuch+2] != 'undefined') { size1 = parseInt(section[howMuch+2]); } else { sendChat(msg.who, "/w gm Please ensure the \"cust\" tag is followed by two values (every 70 = 1 square)."); // If monster is not in character list. } if (typeof section[howMuch+3] != 'undefined') { size2 = parseInt(section[howMuch+3]); } else { sendChat(msg.who, "/w gm Please ensure the \"cust\" tag is followed by two values (every 70 = 1 square)."); // If monster is not in character list. } } } else if (section[howMuch].toLowerCase() == "cust") { if (typeof section[howMuch+1] != 'undefined') { size1 = parseInt(section[howMuch+1]); } else { sendChat(msg.who, "/w gm Please ensure the \"cust\" tag is followed by two values (every 70 = 1 square)."); // If monster is not in character list. } if (typeof section[howMuch+2] != 'undefined') { size2 = parseInt(section[howMuch+2]); } else { sendChat(msg.who, "/w gm Please ensure the \"cust\" tag is followed by two values (every 70 = 1 square)."); // If monster is not in character list. } } } else { sendChat(msg.who, "/w gm No monsters specified to summon"); // If user writes too many things. } let check = findObjs({ _type: "character", name: inputName },{caseInsensitive: true})[0]; if (typeof check == 'undefined') { sendChat(msg.who, "/w gm Monster of name \"" + inputName + "\" does not exist."); // If monster is not in character list. } else { let list = findObjs({ _type: "character", name: inputName},{caseInsensitive: true}); let characterId = list[0].id; // Extract character ID from character sheet let characterName = list[0].get('name'); // Extract character name from character sheet let character; let selected = msg.selected||[]; _.each(selected, function(obj) { let tok = getObj("graphic", obj._id); // Create variable for selected token (for summoned monster positioning) let HP = findObjs({ _type: "attribute", name: "hitpoints", _characterid: characterId },{caseInsensitive: true})[0]; //finds the health from charactersheet if (typeof HP == 'undefined') { HP = "-1"; } else { HP = (HP) ? HP.get("current") : 0; // Changes HP to useable string } let SP = findObjs({ _type: "attribute", name: "speed", _characterid: characterId },{caseInsensitive: true})[0]; //finds the speed from charactersheet if (typeof SP == 'undefined') { SP = "-1"; } else { SP = (SP) ? SP.get("current") : 0; // Changes SP to useable string } let AC = findObjs({ _type: "attribute", name: "armorclass", _characterid: characterId },{caseInsensitive: true})[0]; // finds the ac from charactersheet if (typeof AC == 'undefined') { AC = "-1"; } else { AC = (AC) ? AC.get("current") : 0; // Changes AC to useable string } if (amount == 0) { amount = 1; } else if (amount &gt; 20) { let tooMany = amount; amount = 20; log("Summoner: User specified " + tooMany + " monsters. Scaling to 20."); // If user spams the field. } for (let i=0;i&lt;amount;i++) { if (HP == "-1" &amp;&amp; AC == "-1" &amp;&amp; msg.content.toLowerCase().search("torch") != -1) { let characterImage = getCleanImgsrc(list[0].get('avatar')); // Extract character image URL from character sheet if (!characterImage) { sendChat(msg.who, "/w gm Monster of name \"" + inputName + "\" has a marketplace image or is missing. &lt;code&gt;"+list[0].get('avatar')+"&lt;/code&gt;"); // Error message } else { action = "Lights"; createObj("graphic", { represents: characterId, // Links new token to charactersheet left: tok.get("left")+70, top: tok.get("top"), width: 70+size1, height: 70+size2, name: characterName, pageid: tok.get("pageid"), imgsrc: characterImage, layer: "objects", light_radius: 40, light_dimradius: 20, light_otherplayers: true }); } } else if (HP == "-1" &amp;&amp; AC == "-1") { character = getObj('character',characterId); if(character){ summonToken( character, tok.get('pageid'), tok.get('left'), tok.get('top'), tok.get('width'), tok.get('height'), !!randomSide ); } else { sendChat('','/w gm No character for id: &lt;b&gt;'+characterId+'&lt;/b&gt;'); } } else { character = getObj('character',characterId); if(character){ summonToken( character, tok.get('pageid'), tok.get('left'), tok.get('top'), tok.get('width'), tok.get('height'), !!randomSide ); } else { sendChat('','/w gm No character for id: &lt;b&gt;'+characterId+'&lt;/b&gt;'); } } } }); if (amount &gt; 1) { inputName = inputName + "s"; // If more than 1 summon, adds plural. } else if (amount == 1 &amp;&amp; action == "Lights") { amount = " a"; inputName = "torch"; } if (typeof amount === 'number') { amount = amount.toString(); } // sendChat(msg.who, GM + action + amount + " " + inputName + "!"); // Announces how many and what Summoner has summoned. } } }); });
so I have two issues. first, the token that appears is three squares up and three squares to the left of the originally selected token. Second, it doesn't seem to work on a character with multiple sides (on that note, once it does, how do I get it to random which side appears?)&nbsp; I'm not sure if I am doing anything wrong. The summoned token appears (except the multi-sided one) but in the wrong place.
more info: Here are results. The first (Wham) is a single sided token. It worked. The second (Kapow) is multi-sided. it failed. "{\"content\":\"!summon Wham H\",\"playerid\":\"-NjYN0IRY9znXuLDZp63\",\"selected\":[{\"_id\":\"-Njt3Wn2P7kA8rzYwv_T\",\"_type\":\"graphic\"}],\"type\":\"api\",\"who\":\"Wakko (GM)\",\"variables\":{\"all\":{},\"alltables\":{},\"mules\":{}},\"mules\":[],\"lastIndex\":0,\"unlock\":{\"zeroframe\":\"-NjtC2AbWgaoSdy5dcQh\"},\"apitrigger\":\"ZeroFrame-NjtC2AbWgaoSdy5dcQi\",\"origcontent\":\"!summon Wham H\",\"parsedinline\":[],\"globals\":{}} -- at apiscript.js:24878:21" "{\"content\":\"!summon Kapow H\",\"playerid\":\"-NjYN0IRY9znXuLDZp63\",\"selected\":[{\"_id\":\"-Njt3Wn2P7kA8rzYwv_T\",\"_type\":\"graphic\"}],\"type\":\"api\",\"who\":\"Wakko (GM)\",\"variables\":{\"all\":{},\"alltables\":{},\"mules\":{}},\"mules\":[],\"lastIndex\":0,\"unlock\":{\"zeroframe\":\"-NjtC3gmn-mPaIGH8jbh\"},\"apitrigger\":\"ZeroFrame-NjtC3gmn-mPaIGH8jbi\",\"origcontent\":\"!summon Kapow H\",\"parsedinline\":[],\"globals\":{}} -- at apiscript.js:24878:21" "ERROR: You must specify a valid layer to create this object type."
1700693754
The Aaron
Roll20 Production Team
API Scripter
Interesting.&nbsp; Would you mind PM'ing me an invite to your game and GM'ing me so I can investigate in situ?
sent. I'll give you DM rights when you log in
1701057807
The Aaron
Roll20 Production Team
API Scripter
Ok. I've adjusted the script to be slightly more predictable regarding where it creates a token.&nbsp; This might not be the appropriate change for everyone, but I added the update above anyway. New behavior is to place the summoned token directly above the selected token (bottom edge adjacent to top edge), unless that would put it off the page, in which case it places it below the selected token (top edge to bottom edge). Additionally, I added the ability for it to randomly select a side of the summoned token, if you use it with the command: !summon-r Kapow H
You sir are a scripting hero!