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

Passing object IDs between scripts

Apologies for the noob question - I've only just started with API scripting and am learning-by-copying! (I do have a basic programming background, btw). I am using, and have modified, Nick Olivo's script for summoning a spiritual weapon by creating a graphic object (<a href="https://gist.github.com/nolivo/42d04f35dce85f647b9479380b7a894a" rel="nofollow">https://gist.github.com/nolivo/42d04f35dce85f647b9479380b7a894a</a>). All works fine. I would now like to create a script that will remove the graphic when it is un-summoned (yep, I know the player can select the token and just hit delete, but where's the fun in that!) After searching and finding a lot of posts that said you cannot delete objects in the API (they were old, I guess it has changed since then?) I found the .remove() function. If I set a variable to contain the ID of the created object in my script, I can then (at the end of my script, for testing) call the .remove() function on that variable and it does get deleted ok. So now I just need to move that remove call to a different script. So my question is how do I (or is it possible to?) return the ID of the created object from the first script, to be able to pass it into a second one which can delete it?
1612972690
timmaugh
Pro
API Scripter
The state variable could do it, but it's like storing a global variable and generally not good practice. If you have your script declared as an IIFE, you can write it so the closure stays resident in memory, holding it's data. Track the ID(s) of the items you've created in an array (or object), and then write an access function to retrieve the proper bit of data in the return statement of your script closure, expose that access function to another script to call. I'll try to post an example when I get back to my computer.
1612972971
David M.
Pro
API Scripter
timmaugh, if done in this way, would the ID get wiped if you had to refresh the browser or close between game sessions? I thought only the state object persisted between sessions?&nbsp;
Thanks for the quick replies - I look forward to seeing an example - @timmaugh. I understand what you're saying but don't know the syntax for doing this in the Roll20 api. In the meantime, I am trying an alternative method. In the "unsummon" function I am trying to search the current page for all graphics and (since I know the name that I gave the graphic) am trying to find the object that way, then I can .remove() it. Currently not working atm, not sure why, but if I get it working I'll post that method as an alternative way of doing it.
1612974082
The Aaron
Roll20 Production Team
API Scripter
I would probably store something on the graphic object that you can look for later. You could use the name if you aren't showing it, or one of the bars, or the gmnotes field. Then you just find the objects with that value and remove them.&nbsp;
1612974732
timmaugh
Pro
API Scripter
@David... you make a good point that the IIFE closure doesn't persist between sessions. If you need that, state is one option, an attribute on an associated character (for instance, if the thing you created was associated with a character, you could drop a custom attribute on the sheet to retrieve later), or a mule character/handout if there isn't a single associated character. To make the IIFE work, see Aaron's latest revealing module pattern template in this post .&nbsp;Variables declared in the outer scope will survive between calls to the script (except for between sessions or browser refreshes, as David mentions), but they won't be exposed to other scripts. To do that, you'd expose the accessing/retrieval function in the&nbsp; return&nbsp; &nbsp;statement. In the below example, I store an object for each character that has keys of characterid (string) and graphics (array). { &nbsp; &nbsp; characterID: -M1234567890abcdefg, &nbsp; &nbsp; graphids: [-Mg12345678901abcdef, -Mfg1234567890abcde] } So, using that in the RVP template... const TheBestScriptEver = (() =&gt; { &nbsp;&nbsp;&nbsp;&nbsp;let graphicArray = [];&nbsp; &nbsp; // this will exist between calls &nbsp;&nbsp;&nbsp;&nbsp;// ...stuff to create an graphic for a character, tracking the characterid and and graphicid &nbsp;&nbsp;&nbsp;&nbsp;if (!graphicArray.filter(g =&gt; g.characterid === characterid).length) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;graphicArray.push({ characterid: characterid, graphics: [graphicid] } &nbsp;&nbsp;&nbsp;&nbsp;} else { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; graphicArray.filter(g =&gt; g.characterid === characterid)[0].graphics.push(graphicid); &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;// accessing function &nbsp;&nbsp;&nbsp;&nbsp;const getCharGraphics = (charid) =&gt; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; return (graphicArray.filter(g =&gt; g.characterid === charid)[0] || { graphics: [] }).graphics; &nbsp;&nbsp;&nbsp;&nbsp;}; &nbsp;&nbsp;&nbsp;&nbsp;// ... other stuff in the script until the return statement &nbsp;&nbsp;&nbsp;&nbsp;return { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; GetCharGraphics: getCharGraphics &nbsp;&nbsp;&nbsp;&nbsp;}; })(); Then other scripts could access the data at: TheBestScriptEver.GetCharGraphics()
Thanks all for your replies. The info regarding "global" variables will definitely be useful as I get more into the scripting. For now, the method of searching for the name seems to work well. Good idea putting something unique and 'hidden' in, e.g. the GM field is a good idea, but for the spiritual weapon I think searching for the name is fine. I'm also happy searching across all pages, as I have no problem deleting all of the character's summoned weapons! I've put the macro calls into attributes on the cleric's character sheet, so they pop up when he selects his token (which means it will always be the 'correct' character selected. The check to see if a token is selected is just in case the script is called directly in chat). Only "issue" I have now is that it would be nice if the unsummon could be called with the weapon token selected OR the cleric's token. But as the weapons doesn't have a char sheet, I can't see a way of doing that with macro buttons for now. For anyone else interested, here are my scripts for summon and unsummon. Credit to Nick Olivo for the summon script (see my original post) - I made only minor tweaks to it. Summon: on("ready",function() { &nbsp; &nbsp; on("chat:message",function(msg){ &nbsp; &nbsp; &nbsp; &nbsp; if(msg.type=="api" &amp;&amp; msg.content.indexOf("!spiritualWeapon")==0) &nbsp; &nbsp; &nbsp; &nbsp; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var selected = msg.selected; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (selected===undefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat("API","Please select a character."); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var tok = getObj("graphic",selected[0]._id); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var character = getObj("character",tok.get("represents")); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var playerlist = character.get("controlledby") &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var weapon = createObj("graphic",{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; left:tok.get("left")+tok.get("width"), &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; top:tok.get("top"), &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; height:tok.get("height")/2, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; width:tok.get("width")/2, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pageid:tok.get("pageid"), &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; layer:"objects", &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; imgsrc:"<a href="https://s3.amazonaws.com/files.d20.io/images/178799339/pYoFMeFHsDozHrG6wc8b3Q/thumb.png?16056264685" rel="nofollow">https://s3.amazonaws.com/files.d20.io/images/178799339/pYoFMeFHsDozHrG6wc8b3Q/thumb.png?16056264685</a>", &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: tok.get("name") + "'s Spiritual Weapon", &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; controlledby:playerlist, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; aura1_radius:3, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; aura1_color:"#ffff99", &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; showplayers_aura1:true, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; isdrawing:true &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat(tok.get("name"),"I summoned my trusty spiritual weapon."); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; spawnFx(tok.get("left")+tok.get("width"),tok.get("top"),"burst-holy",tok.get("pageid")); &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); }); Unsummon: on("ready",function() { &nbsp; &nbsp; on("chat:message",function(msg){ &nbsp; &nbsp; &nbsp; &nbsp; if(msg.type=="api" &amp;&amp; msg.content.indexOf("!removeSpiritualWeapon")==0) &nbsp; &nbsp; &nbsp; &nbsp; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var selected = msg.selected; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (selected===undefined) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat("API","Please select a character."); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var tok = getObj("graphic",selected[0]._id); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var spiritualWeapons = findObjs({&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _type: "graphic", &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; name: tok.get("name") + "'s Spiritual Weapon", &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _.each(spiritualWeapons, function(obj) {&nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; spawnFx(obj.get("left"),obj.get("top"),"burst-holy",obj.get("pageid")); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; obj.remove() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); });
1612978169

Edited 1612978552
David M.
Pro
API Scripter
Since we're on the topic of summoning tokens, you may want to check out my SpawnDefaultToken script. Can be used to summon all kinds of spells, creatures, etc. without having to have dedicated scripts for each. Nick O put out a nice YouTube video showing some of the features (though his character summoning example is a tad outdated with the current version - see thread for details).&nbsp; Summoning a spell effect begins at around the 8:07 mark of the video. EDIT - More of an FYI than anything. I would not recommend using any of my code as an example of how to do things "the right way", haha!
1612978280
The Aaron
Roll20 Production Team
API Scripter
You could check to see if the selected token's name matches: if(/ 's Spiritual Weapon$/.test(tok.get('name'))) {
@The Aaron &nbsp;- Yeah, it was more being able to have a clickable "unsummon" button pop up when the weapon's token is selected (as well as the cleric's). I'm assuming that I'd need to create a character sheet for the weapon and have an ability (sorry, I meant ability when I talked about attribute above) that calls the unsummon script - as per the cleric. Otherwise it will need to be a macro bar button (or token button, but that would then appear on every token select). tbh, my original idea was to have a character sheet for the weapon, and the summon would just spawn the token belonging to that character sheet, but, being a noob to this, I didn't know how to do that - hence just creating a graphic instead @David M Thanks for that - I will definitely look at your script. I was just dipping my toe in the water and trying to get my head around the basics with a simple script at first. Now I want to be able to give my rogue the ability to summon and unsummon his familiar (in whatever form he decides) I think I will need to look at more complex methods! All really useful (and very quick!) replies - thank you.&nbsp;
1612979637
David M.
Pro
API Scripter
If you don't want to require token selection, you could always pass the controlling character's name as an argument to your unsummon script. !removeSpiritualWeapon ?{Whose Weapon?|Bob|Jeff|Susan|Beelzebub Destroyer of Worlds|Jimmy} Then parse the msg in your script into an array with something like let args = msg.content.split(/\s+/); let playerName = args[1]
1612980002
David M.
Pro
API Scripter
Chris A. said: Thanks for that - I will definitely look at your script. I was just dipping my toe in the water and trying to get my head around the basics with a simple script at first. Now I want to be able to give my rogue the ability to summon and unsummon his familiar (in whatever form he decides) I think I will need to look at more complex methods! Yep, there's no replacement for "learning by doing" (after "learning by copying"). For your rogue's multi-form familiar, Nick's video shows how to use the script with rollable table tokens which sounds like just what you would want.