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

Can I call Span Default Token froma script using SelectManager

1675543166

Edited 1675543743
Hello all, I'm not a very experienced scripter, but am trying to write a function that is called when a new token ("obj") that represents a character is created.  The idea is that the function will look at the represented character's attributes and will make an array of all the attributes named  "spawn_with". These attributes have a character id as current value and a comma separated list of xOffset, yOffset, xSize and ySize as the max value. The function should go through the array of attributes and use !forceselected (from SelectManager) and Spawn to spawn that token in a position relative to the original token. The command that is sent to sendChat is logged, so that I can see what actually is sent. It seems like the sendChat command does not work (nothing appears in chat). However, when I copy the logged command and paste it into chat, it does work. Is this something that can be fixed, or is it impossible to call Spawn Default Token from a script in this way?  Thanks, Thomas     spawnNearSpawn = function (obj) {         var character=findObjs({type:"character", _id: obj.get("represents")})[0];         var spawnList= findObjs({ type: 'attribute', _characterid: character.get("_id"), name: "spawn_with"});          var newCharacter, xOffset, xSize, yOffset, ySize, name;         var chatCommand;         _.each(spawnList, function(attribute){             newCharacter=getObj("character",attribute.get("current"));             xOffset=attribute.get("max").split(",")[0];             yOffset=attribute.get("max").split(",")[1];             xSize=attribute.get("max").split(",")[2];             ySize=attribute.get("max").split(",")[3];             name=newCharacter.get("name");             chatCommand="!forselected Spawn --name| "+name+" --offset| "+xOffset+","+yOffset+" --size| "+xSize+","+ySize+" {& select "+obj.get("_id")+"}";             sendChat("GM",chatCommand);             log(chatCommand);         });     };
1675694481
timmaugh
Pro
API Scripter
Hey, Thomas,  here are a couple of things to look at... Unnecessary forselected First, you're only Spawning a single token, it looks like, from a single character. For that you won't need the forselected handle... just the {&select ... } structure. You don't need to iterate over selected tokens if you only have one token selected, and that looks like what you're doing with the {&select ... } syntax. Attribute Retrieval Problem Also, it looks like you want your spawnList variable to be an array of attributes, except you're feeding a flat name value to the findObjs() function: var spawnList= findObjs({ type: 'attribute', _characterid: character.get("_id"), name: "spawn_with"});  That will only find attributes named "spawn_with", exactly. Later you want step through the array to retrieve attribute values from those attributes, but your list is likely empty. Either it's empty, or there's no way to differentiate one from another because they all have the same name (yes, you can have attributes that share a name on a character sheet, which leads to the sort of problems you can imagine). Can you describe your setup and what you're trying to do? For instance, if your setup is that on each character you have a series of attributes like: spawnwith_offsetx spawnwith_offsety spawnwith_sizex spawnwith_sizey ...and you wanted to retrieve the max value from each as the values to feed to your Spawn command, I would approach that from one direction, but with a different setup, I might approach it a different way. Command Line  - Who are you? All of that is going to lead to (what I imagine will be) a bad command line, but as for why whatever-this-generates working as a command line for you when you paste it into chat versus when it runs from the script... that's likely a matter of the difference between script- and user-generated messages. There is more data for a user-generated message (things like "who" sent the message, the playerid of the sender, and selected tokens). For a script-generated message, there is none of that. You can work around that by configuring SelectManager to give those things back: !smconfig +playerid +who SelectManager "ships" preconfigured to only give back the selected tokens, but you can turn on the other properties with the above command line. That will likely make your command line work from your script... however you'd still not need the forselected handle, and you're likely not getting the data you want to get from the attributes you're looking for. Fetch Unless what you've shown here is part of a longer process -- or if you're just trying to learn scripting -- what you've shown can be handled in the chat at game-time using fetch constructions. If your setup is that you have attributes named as above (ie, "spawnwith_xoffset"), I think it would look like: !Spawn --name|@{selected|token_name} --offset|@(@{selected|token_id}.spawnwith_offsetx),@(@{selected|token_id}.spawnwith_offsety) --size|@(@{selected|token_id}.spawnwith_sizex),@(@{selected|token_id}.spawniwth_sizey) (Air-coded caveats apply) The expectation, here, is that you'd have the token selected from whose associated character sheet you would want to draw the offset/size... but I think that's what your original code reflected, too. Note that you don't need the {&select ...} construction since you're no longer working with a script (you'd be sending this as an ability macro to chat). Instead, you just need fetch to get the attributes from the associated character sheet. Of course, if I've misunderstood what you have going (or, like I said, you want to learn coding) then post back with more information and we can get it sorted.
Thanks Tim! You did slightly misunderstand my intent, but you provided me with the information I needed and gave me some useful other tips. I think the fix was in turning on "who" and "playerid" for SelectManager. What I want to do is to have other character tokens spawn when I drag a particular character onto the map. For example, when a wizard is dragged onto the map, I want their familiar to also appear. When an orc warlord appears on the map, I want 2 regular orcs to appear. If I want the party to appear in a particular location, I can just drag the character I have designated as the “leader” onto the map. To do this, I am adding attributes to characters. Originally, these attributes were all called “spawn_with”, but I stopped doing that as you suggested, because it could lead to confusion. Now, if a character spawns with one other character, they have an attribute called “spawn_with1”. If they spawn with 2 other characters, they have an attribute called “spawn_with1” and one called “spawn_with2”, etc. For each of these spawn_with attributes, the current value is the ID of a character object (the object that will spawn). The value of “max” is a series of four number separated by commas for example “2,-2, 1,1”. In the example, this indicates that the character whose id is in the “current” value will spawn at an offset of 2, -2 from the character I drag onto the map, with a n x size of 1 and a y size of 1. I am calling this function whenever a new graphic is created. Here is my improved code: on("ready",function(){     spawnNearSpawn = function (obj) {         var character=findObjs({type:"character", _id: obj.get("represents")})[0];         var spawnList= [];         var attributeList=findObjs({ type: 'attribute', _characterid: character.get("_id")}); //creates an array of all the represented character<s attributes         _.each(attributeList, function(attribute){                //goes through the character's attributes and finds htose whose name contains "spawn_with" and adds those to the spawnList array             if(attribute.get("name").indexOf("spawn_with")>=0){                  spawnList.push(attribute);             }                      });                      var newCharacter, xOffset, xSize, yOffset, ySize, name;         var chatCommand;         /*each attribute with a name containing "spanw_with"         has a current value that contains a character id         the max value of the attribute contains xOffset,yOffset,xSize,ySize*/                  _.each(spawnList, function(attribute){             newCharacter=getObj("character",attribute.get("current"));             xOffset=attribute.get("max").split(",")[0];             yOffset=attribute.get("max").split(",")[1];             xSize=attribute.get("max").split(",")[2];             ySize=attribute.get("max").split(",")[3];             name=newCharacter.get("name");             chatCommand="!Spawn --name| "+name+" --offset| "+xOffset+","+yOffset+" --size| "+xSize+","+ySize+" {& select "+obj.get("_id")+"}";             sendChat("GM",chatCommand);             log(chatCommand);                      });     };     on("add:graphic", function(obj) {         if(obj.get("represents")!=""){             spawnNearSpawn(obj);         }        }) }) I have tested it after activating the "who" and "playerid" in SelectManager, and it seems to work. That said, if you have anything else I should think about in the script above, I would be happy to learn more. Thomas
1675737050

Edited 1675737101
timmaugh
Pro
API Scripter
OK... that makes a bit more sense, and I'm glad you got it working! The bit that had me confused was that you'd included the name in the findObjs() function, instead of first getting the array of returned attributes and then testing the names (as you do now). This method works better for attributes with similar-but-not-exactly-the-same name. =D If you're looking for suggestions about your code, you're getting the main gist of things... but I could make a couple of suggestions: 1. Use let or const instead of var. There are a lot of good reasons to use let or const, most of which boil down to keeping a better handle on your variables and knowing not only what they are, but also when they are. Rare is the time when you'll actually need the hoisting of the var declaration (at least in the Roll20 scriptosphere); in fact, this is the only case I am aware of (read the section For Scripters ). 2. Use strict equality if possible: === vs. == !== vs. != 3. You can use filter immediately on your returned attribute set and save yourself a second object, and a lot of extra code:         let spawnList =  findObjs({ type: 'attribute', _characterid: character.get("_id") })           .filter(a => /^spawn_with/i.test( a.get("name"))) ; 4. You can use destructuring assignment to cut down on more lines (and some redundant processing):         spawnList.forEach(attribute => {             let newCharacter = getObj("character", attribute.get("current"));             let [xOffset,  yOffset,  xSize,  ySize] = attribute.get("max").split(",");             let name = newCharacter.get("name");             let chatCommand = "!Spawn --name| " + name + " --offset| " + xOffset + "," + yOffset + " --size| " + xSize + "," + ySize + " {& select " + obj.get("_id") + "}";             sendChat("GM", chatCommand);             log(chatCommand);         }); All of that taken into account, it might look more like this: on("ready", function () {     const spawnNearSpawn = obj => {         let character = findObjs({ type: "character", _id: obj.get("represents") })[0];         let spawnList = findObjs({ type: 'attribute', _characterid: character.get("_id") })             .filter(a => /^spawn_with/i.test(a.get("name")));         spawnList.forEach(attribute => {             let newCharacter = getObj("character", attribute.get("current"));             let [xOffset, yOffset, xSize, ySize] = attribute.get("max").split(",");             let name = newCharacter.get("name");             let chatCommand = "!Spawn --name| " + name + " --offset| " + xOffset + "," + yOffset + " --size| " + xSize + "," + ySize + " {& select " + obj.get("_id") + "}";             sendChat("GM", chatCommand);             log(chatCommand);         });     };     on("add:graphic", function (obj) {         if (!obj.get("represents").length) {             spawnNearSpawn(obj);         }     }); });