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 creating a token?

Can you link a macro to drop a token onto the map? Say you are casting an aoe spell that leaves a wall, or smoke, darkness, grease etc. It would be nice to have a token that represents the effect placed on the map at the time you cast the spell. 
1469552153
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
you could create an FX with the /fx command (there is actually a spot for this in the D&D 5e roll20 sheet if you're using that). Otherwise, creating an actual token via macro would require the API, which shouldn't be a problem since you do have access. I don't know if there are any scripts around that do this exact thing though.
I use the shaped sheet- it has the fx as well. But that is just a momentary effect, and limited in its size and shape. I suppose a work around might be to use the tokens as monsters with no stats that everybody has the right to move. Keep them in a folder called spell effects. 
1469554856
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Ah, yeah, the shaped is what I was referring to. Yeah, I think the work around at the moment is to do what you mentioned. I've got a few too many irons in the fire to work on a script for this at the moment (not to mention, I'm not sure where I'd start on it).
1469556838

Edited 1469556923
The Aaron
Forum Champion
API Scripter
Gary W. said: I use the shaped sheet- it has the fx as well. But that is just a momentary effect, and limited in its size and shape. I suppose a work around might be to use the tokens as monsters with no stats that everybody has the right to move. Keep them in a folder called spell effects.  This is exactly what I do.  I add descriptions of the effects on the bio of the "monster" and put some notes in the bubbles (Save type is a letter: S/D/Cn/W/I/C), the (usual) DC, and possibly the Damage type for some spells or even the default damage.  I then keep them in a special Game called '5e Spells' and use the Character Vault to drag them into games I'm playing, or the Transmogrifier to pull them into the ones I'm running.  (One caveat, the represents breaks when you do that and has to be reconnected by a GM, but it's pretty fast to take care of.) One more thing: doing it that way means that players can then drag those templates in when they cast the spell, and delete them when they are done. 
Scott the fact that you even thought of making a script based on a casual comment- well wow.  Nice idea there Aaron. The character vault and the transmogifier are awesome. Hope Roll20 adds a folder option to them at some point. So you could drag a spell effects folder, or a standard monster folder, to a new campaign.  Actually they need to add archive a folder as well :)
1469569665
Ziechael
Forum Champion
Sheet Author
API Scripter
I use this  summoning script to place tokens as a result of summoning spells/spell templates etc. Works a treat, even with queries, like the one I set up for elemental summoning: !summon ?{size?|small|medium|large|huge|greater|elder} ?{type?|air|earth|fire|water} elemental :) I believe mine has some tweaks curtesy of The Aaron (as do 90% of my scripts) so if you have issues with it I can always post the code I use... 
1469571507
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Ziechael, that's great. I was sure there had to be something for a situation like this out there, but didn't know where it might be. I don't suppose the tweaks that Aaron made to it include allowing it to call the default token (along with bar values and bar links) as opposed to the avatar image did it?
1469602950
Ziechael
Forum Champion
Sheet Author
API Scripter
Why yes, as a matter of fact it does ;) // on('ready',function(){     var getCleanImgsrc = function (imgsrc) {   "use strict";   var parts = imgsrc.match(/(.*\/images\/.*)(thumb|med|original|max)(.*)$/);   if(parts) {  return parts[1]+'thumb'+parts[3];   }   return; }, summonToken = function(character,pageid,baseX,baseY,baseW,baseH){ "use strict"; var page=getObj('page',pageid); baseW=baseW||70; baseH=baseH||70; character.get('defaulttoken',function(defaulttoken){  var dt = JSON.parse(defaulttoken);  if(dt && getCleanImgsrc(dt.imgsrc)){ dt.imgsrc=getCleanImgsrc(dt.imgsrc); dt.left=((baseX+baseW+dt.width)>page.get('width') ? baseX-dt.width : baseX+baseW); dt.top=((baseY+baseH+dt.width)>page.get('height') ? baseY-dt.width : baseY+baseH); dt.pageid = pageid; createObj('graphic',dt);  } else { sendChat('','/w gm Cannot create token for <b>'+character.get('name')+'</b>');  } }); }; on("chat:message", function(msg) { if (msg.type == "api" && msg.content.indexOf("!summon ") === 0)  { 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 > 1) { for (var j=1;j<section.length;j++) { if (typeof parseInt(section[j]) === 'number' && Math.round(parseInt(section[j])) % 1 == 0 || section[j].length <= 4 && section[j].toLowerCase().search("d") != -1 && /\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 < section.length; m++) { if (typeof parseInt(section[m]) === 'number' && Math.round(parseInt(section[m])) % 1 == 0 || section[m].length <= 4 && section[m].toLowerCase().search("d") != -1 && /\dd/.test(section[m])) { skip = true; } else { skip = false; } } } else { skip = false; } if (skip == false) { var howMuch = j; break; } } } } if (howMuch > 0) { for (var k=1;k<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 < lowEnd)  { 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<section.length-s;k++) { if (k == section.length-1-s) { inputName = inputName + section[k]; } else { inputName = inputName + section[k] + " "; } } } if (section.length > (howMuch+1) && 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 \""  + 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 var character; if (characterImage.indexOf("marketplace") != -1) { sendChat(msg.who, "/w gm Monster of name \""  + inputName + "\" has a marketplace image.");     // Error message } else { 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: "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 } var 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 } var 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 > 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<amount;i++) { if (HP == "-1" && AC == "-1" && msg.content.toLowerCase().search("torch") != -1) { 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" && 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') ); } else { sendChat('','/w gm No character for id: <b>'+characterId+'</b>'); } } else { character = getObj('character',characterId); if(character){ summonToken( character, tok.get('pageid'), tok.get('left'), tok.get('top'), tok.get('width'), tok.get('height') ); } else { sendChat('','/w gm No character for id: <b>'+characterId+'</b>'); } } } }); if (amount > 1) { inputName = inputName + "s"; // If more than 1 summon, adds plural. } else if (amount == 1 && 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. } } } }); });
1469622809
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Awesome, gonna have to use this then :)