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

[Script] ScriptCards - Thread #2

1709727529
Kurt J.
Pro
API Scripter
ScriptCards 2.7.0 on GitHub The bleeding edge release of ScriptCards 2.7.0 is now up on my GitHub Repo . This version contains a lot of cleanup, refactoring much of the code into more manageable segments. While 2.7.0 has worked with everything I've tried, it is always possible there are newly introduced bugs given the scope of the changes, so please let me know if you run into any issues. There will be a followup release when the new API features are released later r this month. Changes in 2.7.0: Added the --z command to edit z-order information for objects. --z:objecttype:objectid|operation currently supports tofront and toback for graphics. Will support additional items/options when the March 2024  API update is released. Added --#storagecharid setting to allow you to specify a character ID for future --s and --l statements (in the currently running script) Added --#gmoutputtarget setting which defaults to "gm". If you set this to "self" the output of --* lines will be whispered to the player executing the script. You can also set this to any other valid whisper target. Added --#functionbenchmarking which defaults to 0. Set to 1 to turn on some basic statistics about calls to functinos (with -->). After your script finishes running the number of calls to each called function will be reported in the API console log as well as the number of  milliseconds the script took to run. Added --~|array;fromkeys;ArrayName;HashTableName will create an array with all of the keys in a given hashtable Added --~|hashtable;fromobject;HashTableName;objecttype;objectid  Ex: --~|hashtable;fromobject;myhash;character;@{selected|character_id} this function will look up the given object and parse out all attributes (except bio, notes, and gmnotes) into a hashtable with key/value  pairs equal to the attribute names and values.   Example:   !script {{   --~|hashtable;fromobject;myhash;graphic;@{selected|token_id}   --~|array;fromkeys;keys;myhash      --%keys|foreach;keys   --+[&keys]|[:myhash("[&keys]")]   --%|   }} Added --~|hashtable;getplayerspecificpages;HashTableName Retuns a hash with the contents of the Campaign's playerspecificpages object, which will have player IDs as keys and page IDs as values for those entries. Added --~|hashtable;setplayerspecificpages;HashTableName Will set the Campaign's playerspecificpages object. The source hash table should contain player IDs as keys and page IDs as values for them. Leave off the hash table name (but include the semicolon (;)) clear the value and return  all players to the ribbon page. New Pre-Set String Variable: [&SC_VERSION_NUMERIC], which will contain the version number in the format 0X0Y0Zr, where X is the major version, Y is the minor version, Z is the update number, and r represents the occasional "a", "b", etc. versions. All components are numeric, so v2.7.0  will be represented as "207000" (2=2, 07=7, 00=0, and 0=no fix update), while a theoretical 2.7.0a would be "207001". This can be used to require a specific minimum version of ScriptCards or to change the behavior of the script depending on the version. For example, assuming this had existed for 2.6.6b, the line below  would check for that minimum version number :   --?"[&SC_VERSION_NUMERIC]" -eq "" -or "[&SC_VERSION_NUMERIC]" -lt 206062|REPORT_BAD_SC_VERSION
1709747340

Edited 1709747468
Kurt J.
Pro
API Scripter
Norman said: Thanks Kurt, I'll take a look. I'm very much a beginner with the using the API and mods and am rapidly getting out of my depth :)  anyway, I'll keep plugging away at it - best way to learn - right? This is "quick and dirty", but here is an example of doing this in ScriptCards using a chat menu and rbuttons. The player can toggle on/off the modifiers. The rest of the group will only see the result when they hit "Take the Shot" !script {{ --#reentrant|[&SendingPlayerID]_TakeAShot --&Aiming|N --&Lily-Livered|N --&PartialCover|N --&HardCover|N --&Mounted|N --&Stealthy|N --:DisplayMenu| --#whisper|self --#title|Take a Shot --#leftsub|Option Selection -->MAKE_BUTTON|Aiming;Aiming;TOGGLE_BUTTON -->MAKE_BUTTON|Lily-Livered;Lily-Livered;TOGGLE_BUTTON -->MAKE_BUTTON|PartialCover;PartialCover;TOGGLE_BUTTON -->MAKE_BUTTON|HardCover;HardCover;TOGGLE_BUTTON -->MAKE_BUTTON|Mounted;Mounted;TOGGLE_BUTTON -->MAKE_BUTTON|Stealthy;Stealthy;TOGGLE_BUTTON --+|  --+|[c][rbutton]Take the Shot::TAKE_SHOT[/rbutton][/c] --X| --:TOGGLE_BUTTON| --?"[&[&reentryval]]" -eq "N"|&[&reentryval];Y|&[&reentryval];N --^DisplayMenu| --X| --:MAKE_BUTTON|Caption;Variable;ReentryPoint --?"[&[%2%]]" -eq "N"|&BColor;#999999|&BColor;#009900 --+|[c][rbutton:#FFFFFFF:[&BColor]][%1%]::[%3%];[%1%][/rbutton][/c] --<| --:TAKE_SHOT| --#whisper| --&Mods| --?"[&Aiming]" -eq "Y"|&Mods;++1 [Aiming] --?"[&Lily-Livered]" -eq "Y"|&Mods;+-1 [Lily-Livered] --?"[&PartialCover]" -eq "Y"|&Mods;+-1 [Partial Cover] --?"[&HardCover]" -eq "Y"|&Mods;+-2 [Hard Cover] --?"[&Mounted]" -eq "Y"|&Mods;+-2 [Mounted] --?"[&Stealthy]" -eq "Y"|&Mods;+-1 [Stealthy] --=AttackRoll|1d10 [&Mods] --+Attack Roll|[$AttackRoll] --X| }}
1709750650

Edited 1709750796
Hi all I am trying to jazz up the Death Saves with a Scriptcard.  Initially I had this !script {{ --/| set up the template, supernotes required to override default style --#whisper|self,gm --#overridetemplate|scroll --#title|Some flavour text --=Roll|%{selected|death_save} }} which I like because it does it and alongside the companion script, updates the character sheet. However, the player concerned is left in the dark unless they have their character sheet open, in which case they can see the radio buttons update, but I would rather the player also got the result of the roll.  Also that script results in a "visible to all" message that just gives the character name and }}.  Taking the last 2 curly braces out of the script fixes that, but that just seems wrong, so I started putting a new script together like so: !script {{ --/| set up the template, supernotes required to override default style --#whisper|self --#overridetemplate|scroll --=Roll| 1d20 + @{selected|global_save_mod} [Bless] + @{selected|death_save_bonus} [Death Save Bonus] --+| [F:Gothic:24][#570702]You roll [$Roll][/F][/#] --#title|some flavour text --*|@{selected|token_name} rolled a [$Roll] for death save }} which gives a nice scroll with flavour text to the player and the roll result to the GM, but I can't figure out a way to modify deathsave_succ or deathsave_fail using Modsetattr since it reports non numeric values.  I can also see this one getting messy as I try and account for natural ones, natural twenties. Can anyone see an easy way to adapt the first one to also give the player the outcome of the roll without the need to have their sheet open (we use 5e token action menu because it is awesome and takes up so much less screen space even when using 2 monitors), or a better way to do option 2?  I feel someone out there must have been down this ginnel before me ; ))
Hey Simon, Here is one possible way to handle the second scriptcard you have. It will check the roll and then use  ScriptCards Object Modification  to change the attributes. This does make the assumption that you are playing D&D 5e and are using the D&D 5e sheet by Roll20. It will handle 2 failures on a natural 1 and restoring 1 HP on a nat 20. It clears out the death saves and failures when stable or dead. !script {{ --/| set up the template, supernotes required to override default style --#whisper|self --#overridetemplate|scroll --#sourceToken|@{selected|token_id} --=Roll| 1d20 + [*S:global_save_mod] [Bless] + [*S:death_save_bonus] [Death Save Bonus] --+| [F:Gothic:24][#570702]You roll [$Roll][/F][/#] --#title|some flavour text --*|[*S:t-name] rolled a [$Roll] for death save --?[$Roll.Base] -eq 1|CriticalFailure --?[$Roll.Base] -eq 20|CriticalSuccess --?[$Roll] -ge 10|Success|Failure --:Done| --X| --:Success| --?"[*S:deathsave_succ2]" -eq "on"|[ --+|You are now stable at 0 HP! -->ClearDeathSaves| --]|[ --?"[*S:deathsave_succ1]" -eq "on"|&succNum;2|&succNum;1 --!a:[*S:character_id]|deathsave_succ[&succNum]:on --]| --^Done| --:Failure| --?"[*S:deathsave_fail2]" -eq "on"|[ --+|You have now died. RIP [*S:t-name] -->ClearDeathSaves| --]|[ --?"[*S:deathsave_fail1]" -eq "on"|&failNum;2|&failNum;1 --!a:[*S:character_id]|deathsave_fail[&failNum]:on --]| --^Done| --:CriticalSuccess| --+|You recover miraclously and have 1 HP. --!a:[*S:character_id]|hp:1 -->ClearDeathSaves| --^Done| --:CriticalFailure| --+|You have suffered a tragic setback. Critical failure --?"[*S:deathsave_fail1]" -eq "on"|[ --+|You have now died. RIP [*S:t-name] -->ClearDeathSaves| --]|[ --!a:[*S:character_id]|deathsave_fail1:on|deathsave_fail2:on --]| --^Done| --:ClearDeathSaves| --!a:[*S:character_id]|deathsave_succ1:|deathsave_succ2:|deathsave_succ3:|deathsave_fail1:|deathsave_fail2:|deathsave_fail3: --<| }} Let me know if you have any questions about this.
Hi Joshua Thank you so much!  Yes, we play 5e and use the 5e OGL sheet, sorry, should have been clear about that.   I would never have got it to that stage in a month of Sundays.  With the first option I had just figured out that the 5e Companion needed the player switch to be set and then it worked for just player and GM, but oddly in testing I noticed a total with any bonuses that equalled a 9 was being classed as a success...weird.  I far prefer your script, thanks once again
Kurt J. said: Norman said: Thanks Kurt, I'll take a look. I'm very much a beginner with the using the API and mods and am rapidly getting out of my depth :)  anyway, I'll keep plugging away at it - best way to learn - right? This is "quick and dirty", but here is an example of doing this in ScriptCards using a chat menu and rbuttons. The player can toggle on/off the modifiers. The rest of the group will only see the result when they hit "Take the Shot" !script {{ --#reentrant|[&SendingPlayerID]_TakeAShot --&Aiming|N --&Lily-Livered|N --&PartialCover|N --&HardCover|N --&Mounted|N --&Stealthy|N --:DisplayMenu| --#whisper|self --#title|Take a Shot --#leftsub|Option Selection -->MAKE_BUTTON|Aiming;Aiming;TOGGLE_BUTTON -->MAKE_BUTTON|Lily-Livered;Lily-Livered;TOGGLE_BUTTON -->MAKE_BUTTON|PartialCover;PartialCover;TOGGLE_BUTTON -->MAKE_BUTTON|HardCover;HardCover;TOGGLE_BUTTON -->MAKE_BUTTON|Mounted;Mounted;TOGGLE_BUTTON -->MAKE_BUTTON|Stealthy;Stealthy;TOGGLE_BUTTON --+|  --+|[c][rbutton]Take the Shot::TAKE_SHOT[/rbutton][/c] --X| --:TOGGLE_BUTTON| --?"[&[&reentryval]]" -eq "N"|&[&reentryval];Y|&[&reentryval];N --^DisplayMenu| --X| --:MAKE_BUTTON|Caption;Variable;ReentryPoint --?"[&[%2%]]" -eq "N"|&BColor;#999999|&BColor;#009900 --+|[c][rbutton:#FFFFFFF:[&BColor]][%1%]::[%3%];[%1%][/rbutton][/c] --<| --:TAKE_SHOT| --#whisper| --&Mods| --?"[&Aiming]" -eq "Y"|&Mods;++1 [Aiming] --?"[&Lily-Livered]" -eq "Y"|&Mods;+-1 [Lily-Livered] --?"[&PartialCover]" -eq "Y"|&Mods;+-1 [Partial Cover] --?"[&HardCover]" -eq "Y"|&Mods;+-2 [Hard Cover] --?"[&Mounted]" -eq "Y"|&Mods;+-2 [Mounted] --?"[&Stealthy]" -eq "Y"|&Mods;+-1 [Stealthy] --=AttackRoll|1d10 [&Mods] --+Attack Roll|[$AttackRoll] --X| }} ooo, I really like this.  so many applications.
Hey Kurt, I'm trying to use the new specifying a default value with version 2.6.6b in the One-Click and it seems to be adding an extra colon to the value in front. Am I doing something wrong? Thanks! !scriptcard {{ --#sourceToken|@{selected|token_id} --#emoteState|Hidden --#whisper|self --+Output|[*S:enhanced_defense:::Nope] }}
1709945324
Kurt J.
Pro
API Scripter
Erik M. said: Hey Kurt, I'm trying to use the new specifying a default value with version 2.6.6b in the One-Click and it seems to be adding an extra colon to the value in front. Am I doing something wrong? Thanks! !scriptcard {{ --#sourceToken|@{selected|token_id} --#emoteState|Hidden --#whisper|self --+Output|[*S:enhanced_defense:::Nope] }} Sorry about that. This is a bug in the code, and will be fixed in 2.7.2, which will be hitting the GitHub shortly.
1709945925
Kurt J.
Pro
API Scripter
ScriptCards 2.7.2 is now on GitHub Fixes for a few bugs, including some introduced by the 2.7.0 refactor. If you installed 2.7.0 from the GitHub, I highly recommend upgrading to 2.7.2. When using --~turnorder|addcustom, you can now specify and additional parameter to determine where in the turn order the entry gets inserted. You can leave the custom counter and formula empty (;;;) if you don't want to specify them, but the semicolons must be there. The available placement options are "top", "bottom", "before:tokenid", "after:tokenid" Ex: --~|turnorder;addcustom;My Fancy Custom Turn;99;-1;after:@{selected|token_id} Added --~|hashtable;getjukeboxtracks;HashTableName. This will create a hash table with the names of every jukebox track as keys and their object ID. It will also create keys with TrackName-playing, TrackName-loop, and TrackName-Volume. Updated function benchmarking output to include the number of script lines executed.
Hi, Does anyone have a sample script card they don't mind sharing for selecting a number to return a result. Something like choose a number between 1 and 10 and return the corresponding text for that number from a list of 10 items. Thanks in advance
Hey FFR, So if I am understanding your ask correctly here are a couple of options for ways to ask for input and choose from a list. In both of the below there is an array with 10 items.  ScriptCards Arrays  are 0 based so in both there is a conversion for people to choose from 1 to 10 and convert that to 0 to 9 for the array lookup. The first uses a Roll20 Roll Query  which may not be desirable depending. Roll20 processes all roll queries ?{} first before ScriptCards can act, so that will always prompt the user and ScriptCards has no control over that. !script {{ --~|array;define;ChoiceArr;One;Two;Three;Four;Five;Six;Seven;Eight;Nine;Ten --#title|Choose a number --&ChosenNumber|?{Choose a number|1,0|2,1|3,2|4,3|5,4|6,5|7,6|8,7|9,8|10,9} --+|You have chosen [b][@ChoiceArr([&ChosenNumber])][/b] }} The second uses ScriptCards Re-Entry buttons  to whisper a chat menu to the user and then display the corresponding array item. !script {{ --~|array;define;ChoiceArr;One;Two;Three;Four;Five;Six;Seven;Eight;Nine;Ten --#title|Choose a number --#reentrant|NumberChooser[$SendingPlayerID] --#whisper|self --&msg| --%i|0;[@ChoiceArr(maxindex)];1 --&msg|+[rbutton][= [&i] + 1]::DisplayChoice;[&i][/rbutton] --%| --+|[c][b]Choose a number between 1 and 10[/b][/c] --+|[c][&msg][/c] --:Done| --X| --:DisplayChoice|ChosenNumber --#whisper|0 --+|You have chosen [b][@ChoiceArr([&reentryval])][/b] --^Done| }}
1710106855

Edited 1710106934
Pak
Pro
I prefer the "--i" information request way over the roll query way. I don't care for the fact that roll queries ask their questions before the SC can run. You can also use "--i" to choose targets using SC. Just swap the "q" tag with the "t" tag. The tradeoff is another button you have to click in the chat: !scriptcard  {{    --iChoose a number from 1 to 10. ;CLICK HERE|q;number;Choose|1|2|3|4|5|6|7|8|9|10   --+|You chose [&number]!   --X| }}
Joshua N. Thanks for your response. Both options work for me so I've gone for the second options as I like selecting the choice in chat
I love Scriptcards in the sense that it's got a lot more functionality than Powercards... however, my biggest hangup is converting all my macros into using Scriptcards :( Do we have a good translation from Powercards to Scriptcards? Or if someone in the community is willing to take one of my old PowerCards and convert it over to Script - I might be able to rebuild my game system. Thoughts?
Hey David. I don't know of any conversion tool for powercards to scriptcards. I am not familiiar enough with powercards to write one but if you open a thread, post your powercard here, or go to the ScriptCards discord, I'm sure people could help you out. The discord is linked on the  ScriptCards wiki . I think posting the powercard and a screenshot of the output would probably be enough for folks to help you convert to ScriptCards.
Thanks, Joshua. I'll try out that discord too. Didn't even think about that resource.
1712099585

Edited 1712099653
I have a feature request that may possibly be only useful for me: With the --I command, would it be possible to send the query to a specific person, i.e. the GM? Often in a macro, there are attributes that can change (Like a withstand rating or if an NPC is using an ability) that players shouldn't know, but in order to get it into the script, I need to do a query. Since the player is the one attacking, they are the one that sees the query.  I've tried whispering a reentrant button to the GM, but it is a little clunky.  Thanks for looking!
So --i uses the button to get Roll20 target or roll query functionality. Roll20 specifies the sender there. Can you describe what was clunky about the re-entry button approach? I use reentrant scriptcards often and they have completely replaced --i usage for me so perhaps if you post your ScriptCard and what was clunky about it, folks could help possibly alleviate that.
1712160569

Edited 1712160676
Sure! So I have a subroutine in a library called ApplyDefense. It has an --i query as to if the target wants to use their defense attribute against an attack. It would be preferable if the players didn't know how many times the NPC has used that attribute in a turn. Here is the relevant code in my attack macro: --?[*T:IsNPC:::No] -eq Yes|[ --?[&CombatSkill] -ne Firearms -and [*T:dodge_this_turn:::No] -eq No|*;[rbutton]Use Defense?::ChooseDefense[/rbutton] --?[&CombatSkill] -eq Firearms -and [*T:firearm_dodge] -eq Yes -and [*T:dodge_this_turn:::No] -eq No|*;[rbutton]Use Defense?::ChooseDefense[/rbutton] --X| --]|[ --?[&CombatSkill] -ne Firearms -and [*T:dodge_this_turn:::No] -eq No|>ApplyDefense;[*T:character_id] --?[&CombatSkill] -eq Firearms -and [*T:firearm_dodge] -eq Yes -and [*T:dodge_this_turn:::No] -eq No|>ApplyDefense;[*T:character_id] --]| --:DefenseChosen| --:ChooseDefense| --IIs [*T:t-name] using ; Defense?|q;Def;Is the target using Defense?|Yes|No --?[&Def] -eq Yes|>ApplyDefense;[*T:character_id] --^DefenseChosen| --X| The library code is: --:ApplyDefense|Target ID --?[*T:enhanced_defense:::No] -eq Yes|=DefenseValue;[*T:defense_base] + [*T:enhanced_defense]|=DefenseValue;[*T:defense_base] --?[$DefenseValue] -le [*[%1%]:defense_used:::0]|SkipDefense --?[&Def] -eq Yes|SkipAskDefense --IIs [*[%1%]:t-name] using ; Defense?|q;Def;Is the target using Defense?|Yes|No --:SkipAskDefense| --?[&Def] -eq Yes|[ --=TotalDice|[$TotalDice] - [$DefenseValue] + [*[%1%]:defense_used] --!a:[%1%]|!defense_used:+=1 --]| --?[&Def] -eq No|SkipDefense -->SpendWillpower|-2;[%2%] --:SkipDefense| --<| The issue is that that once the script hits the reentrant button, it properly sends the button to me as the GM, but when the script starts again, any further action is taken by the GM and not the player who started the attack. 
So one possible way to accomplish this would be to replace all the --i prompts with rbuttons for the choice. I wouldn't use an rbutton to then go into a --i prompt. That seems redundant to me. Here's a barebones sample: !script {{ --#title|Re-Entry Example --#reentrant|ReentrantExample[&SendingPlayerID] --#whisper|self --&OriginalSender|[&SendingPlayerName] --#sourceToken|@{selected|token_id} --#targetToken|@{target|token_id} --/|YOUR STUFF --*Use Defense?|[rbutton]Yes::ChooseDefense;1[/rbutton] [rbutton]No::ChooseDefense;0[/rbutton] --:Done| --X| --:ChooseDefense| --&Def|[&reentryval] --?[&Def] -eq 1|>ApplyDefense;[*T:character_id] --^DefenseChosen| --:DefenseChosen| --#whisper|[&OriginalSender] --+|Resume to original sender --^Done| --:ApplyDefense|TargetCharacterID --/|YOUR LIBRARY CODE --<| }} So the couple things, the example starts out with --#whisper|self so the sending player is prompted with whatever choices they need to make. Their name is then stored into the string variable --&OriginalSender| which we'll use. Then the Use Defense? whispers the Yes No buttons to the GM like you had but those buttons will pass in 1 or 0 to the --:ChooseDefense label and procede like you have. Then in the --:DefenseChosen| label that you go-to in your example, you could use the &OriginalSender string variable to whisper to the original player to resume them making any other further choices to be made. When you are setting a variable, --i to do a roll query isn't the only way. Rbuttons already pass in a string variable to the function named [&reentryval] so you can use rbuttons without also needing a roll query. In using --i, you are delaying Roll20 chat processing for @{target} or roll query ?{} prompts. But by using @{target} or ?{} you are limited by how Roll20 implements those and ScriptCards doesn't have control of that implementation. Let me know if you have any questions about that example or how to apply it to your specific use case.
Thanks! This was good in that it works perfectly. And bad, cause now I have to go through my entire campaign to change all my info requests to buttons. LOL
Hello, I'm wondering if there is a command to execute character ability macro direct to chat. I currently use a sheet button command to create buttons in the chat box that can be clicked to execute a ability macro so trying to skip the button creation step to automatically execute the macro Hopeful someone can help
1714301398

Edited 1714301794
Andrew R.
Pro
Sheet Author
I'm testing the new Experimental Mod setting with a copy of my 13th Age Glorantha Library game, and I wanted to replace the  Original Method of Saving and Loading and try the  Enhanced Methods of Saving and Loading. I'm getting an error message.  Here's the script I'm running: !scriptcard {{     --#title|Style_Runes     --/| Rune-Air (Orange)     --#titlecardbackground|#FFA500     --S#Rune|Air }} and here's the error message I'm getting (including the Mods Experimental line):  "SANDBOX: Ready fired after 4.39s, 1138 objects." "WARNING: Refusing to set \"current\" to undefined. [Roll20 attribute -NwZEHmFt-tTY2RhdogE]" The Attribute  SCT_Rune-air has been created in my  ScriptCards_Storage character as expected, and it looks empty to me.  I suspect I have used the --S# incorrectly, but there isn't an example to follow, so I'm slightly in the dark here.  Edit: My suspicion is correct! Here's the corrected script: !scriptcard {{     --#title|Style_Runes     --/| RuneAir-titlecardbackground (Orange)     --#titlecardbackground|#FFA500     --S#RuneAir|titlecardbackground }} and the Attribute  SCT_RuneAir-titlecardbackground has the correct value in it. Phew! 
1714516743
Kurt J.
Pro
API Scripter
ScriptCards 2.7.8 Now on OneClick The latest version of ScriptCards (2.7.8) has been pushed to OneClick. It is a big one, so here is a summary of the changes since 2.6.6: - Added array fromrollvar which will convert the array items in a roll variable into a standalone array. Syntax: --~|array;fromrollvar;ArrayName;RollVariableName;type where type is "rolled", "kept", or "dropped". NOTE that the roll variable name is just the name of the variable and not a [$rollvar] reference. Added built-in array variable "args" which will be set to the parameters (arguments) passed to a GOSUB command. This means that you can use [@args(0)] in place of [%1%], etc. Note that arrays are zero-based. [@args(length)] and [@args(maxindex)] can be used to retrieve the number of parameters passed. The older [%x%] references still work, but you can use this to call a function with a variable number of parameters, or use a foreach loop for them, etc. Fix sandbox crash error in destroy:graphic trigger and added error handling to destroy:page and destroy:door code as well. If you have a ScriptCards_Triggers character defined when your sandbox starts, ScriptCards will register to observe changes from Token-Mod and run the change:graphic trigger if it exists when tokenmod announces a change. The listen to tokenmod behavior defaults to off to prevent issues with existing code. You can set an attribute on the ScriptCards_Triggers character called 'listen_to_tokenmod' and make its current value 1 to enable this behavior Added --~|array;attributes;ArrayName;charid;(optional 'name' starts with) . Returns an array of IDs for attributes on a character. If the last parameter is specified, only attributes with names starting with that value will be returned. Storing empty hash tables will now remove the attribute from the storage character Added function: --~|array;fromplayerlist;ArrayName . Returns an array of all of the IDs of all of the players (that aren't GMs) in the game Added function: --~|array;fromgmplayerlist;ArrayName . Returns an array of all of the IDs of all of the GM players in the game Added function: --~StringVar|system;playerisgm;playerpageid . Will set the StringVar string variable to 1 if the passed playerid is a GM, otherwise will set it to 0 Added function: --~|array;fromcontrolledcharacters;ArrayName;playerid . Will return an array of all of the characters controlled by the passed player Updated [*O:...:character:...] to accept "isgm" as a psuedo property, which will return 1 if the player object is a GM and 0 if not. Ex: --+GM?|[*O:[&SendingPlayerID]:player:isgm] Added a new function: --~|system;runaction;character_id;abilityname;param1;param2;param3;param4;... This function will look for an ability called "abilityname" (case sensitive) on character "charcter_id" and read the Action property of the ability. It will replace the text [REPL1], [REPL2]... and so forth with the parameters passed in param1, param2, etc. The sequence -_-_ will be replaced in the final macro, allowing you to potentially add lines to anything that uses -- to separate parameters, etc. Note that the calls are issued by the API, so there is no current player, so interactive scripts will not do anything (ie, targets, roll queries, etc.) but if the action you are using takes ids, you can pass them as [REPL] items. Example: Given the SetMacro ability on the "Macro_Mule": !token-mod --ids [REPL1] --set statusmarkers|[REPL2][REPL3] T his will turn on the blue marker: --~|system;runaction;@{Macro_Mule|character_id};SetMarker;@{selected|token_id};+;blue And this will turn it off: --~|system;runaction;@{Macro_Mule|character_id};SetMarker;@{selected|token_id};-;blue In these examples, [REPL1] gets replaced with the selected token id, [REPL2] gets replaced with either + or -, and [REPL3] gets replaced with blue. Obviously these can be variables or other ScriptCards constructs. Note that "runability" is an alias for "runaction" Removed some extraneous logging that might cause slowdowns in some scripts as junk was output to the API Console. Updated parsing code to completely ignore --/ lines by skipping the processing block if the tag stats with / Added optional parameter for Rbysectionid . If you add a ;1 to the end of the call, it will perform a case-insensitive match. Added " !sc-purgestachedscripts " as an alternatively recognized command. This will clear from memory all script that have been "stashed", either by reentrant code or --i commands the only real effect here is to free up memory, as this happens automatically on a sandbox restart. Added " --rsearch ", which has the same syntax as --rfind, except that it will do a case-insentitive partial match. This is converted to a regex, so if you need to find special regex characters (like +), prefix them with a "\". Ex: --rsearch|@{selected|character_id};sword;repeating_attack;atkname Will search for the first repeating_attack with an atkname containing "sword" and set the repeating index to that row. Ex2: --rsearch|@{selected|character_id};\+3;repeating_attack;atkname Will search for "+3" and return the first item containing it. Corrected bug where specifying a default value when setting an attribute resulted in an extra preceeding ":" Added --~|hashtable;getjukeboxtracks;HashTableName . This will create a hash table with the names of every jukebox track as keys and their object ID. It will also create keys with TrackName-playing, TrackName-loop, and TrackName-Volume. Here is a simple jukebox/soundboar !script {{   --#reentrant|jukebox   --:SHOW_JUKEBOX|   --#hidecard|0   --~|hashtable;getjukeboxtracks;myhash   --~|array;fromkeys;keys;myhash   --%keys|foreach;keys   --?"[&keys]" -ninc "-"|[     --+[&keys]|[r][rbutton]Play::PLAY_AUDIO;[&keys][/rbutton][/r]   --]|   --%|  --+|   --+|[c][rbutton]Refresh Jukebox::SHOWJUKEBOX[/rbutton][/c]  --X|  --:PLAY_AUDIO|    --#hidecard|1    --a|[&reentryval]  -X|  }} Rewrote distance functions to consolidate code, use better practices When using --~|turnorder;addcustom , you can now specify and additional parameter to determine where in the turn order the entry gets inserted. You can leave the custom counter and formula empty (;;;) if you don't want to specify them, but the semicolons must be there. The available placement options are "top", "bottom", "before:tokenid", "after:tokenid" Ex: --~|turnorder;addcustom;My Fancy Custom Turn;99;-1;after:@{selected|token_id} Major refactoring of the ScriptCards code to improve maintainability and be more friendly to the sandbox. Added the --z command to edit z-order information for objects. --z:objecttype:objectid|operation currently supports tofront and toback for graphics. Will support additional items/options when the March 2024 API update is released. Added --#storagecharid setting to allow you to specify a character ID for future --s and --l statements (in the currently running script) Added --#gmoutputtarget setting which defaults to "gm". If you set this to "self" the output of --* lines will be whispered to the player  executing the script. You can also set this to any other valid whisper target.  Added --#functionbenchmarking which defaults to 0. Set to 1 to turn on some basic statistics about calls to functions (with -->). After your script finishes running the number of calls to each called function will be reported in the API console log as well as the number of milliseconds the script took to run. Added --~|array;fromkeys;ArrayName;HashTableName which   will create an array with all of the keys in a given hashtable Added --~|hashtable;fromobject;HashTableName;objecttype;objectid Ex: --~|hashtable;fromobject;myhash;character;@{selected|character_id}| this function will look up the given object and parse out all attributes (except bio, notes, and gmnotes) into a hashtable with key/value pairs equal to the attribute names and values. Example:  !script {{    --~|hashtable;fromobject;myhash;graphic;@{selected|token_id}    --~|array;fromkeys;keys;myhash      --%keys|foreach;keys    --+[&keys]|[:myhash("[&keys]")]    --%|  }} Added --~|hashtable;getplayerspecificpages;HashTableName  which retuns a hash with the contents of the Campaign's playerspecificpages object, which will have player IDs as keys and page IDs as values for those entries. Added --~|hashtable;setplayerspecificpages;HashTableName  which will set the Campaign's playerspecificpages object. The source hash table should contain player IDs as keys and page IDs as values for them. Leave off the hash table name (but include the semicolon (;)) clear the value and return all players to the ribbon page. New Pre-Set String Variable: [&SC_VERSION_NUMERIC] , which will contain the version number in the format 0X0Y0Zr, where X is the major version, Y is the minor version, Z is the update number, and r represents the occasional "a", "b", etc. versions. All components are numeric, so v2.7.0 will be represented as "207000" (2=2, 07=7, 00=0, and 0=no fix update), while a theoretical 2.7.0a would be "207001". This can be used to require a specific minimum version of ScriptCards or to change the behavior of the script depending on the version. For example, the following line:  --?"[&SC_VERSION_NUMERIC]" -eq "" -or "[&SC_VERSION_NUMERIC]" -lt 207000|REPORT_BAD_SC_VERSION  would check for that minimum version number.
David M. You once posted the following scriptcard in another thread which is now closed  which adds a condition marker and tooltip text to a token How easy is it to adjust this to 1 condition eg Prone. I prefer chat generated sheet buttons to drop down menus so trying to get add Prone and Remove prone as stand alone scripts but lack the skill set to adapt what you've done. Previous posted scriptcard as follows:- Here's a scriptcard that: Queries if you want to add or remove a condition Queries for which condition to add or remove. Performs a lookup for the statusmarker tied to that condition Adds or removes the statusmarker to the token corresponding to the condition specified Updates the tooltip with the condition(s). Any statusmarkers that aren't found during the lookup are just ignored by the tooltip Note 1: these conditions could be names (e.g. prone, stunned) or full descriptions (e.g. "disadvantage on attacks", etc.). I only added a few conditions below. You would need to update the ?{Which Condition...} query as well as the two lookup functions with the conditions you want to use. These lines are bolded in the code sample below. Note 2: If you select "Remove all conditions", you will still be prompted for "Which Condition?". You'll just have to ignore that and click through. !script {{ --#hidecard|1 --:USER QUERY FOR CONDITIONS TO ADD/REMOVE| Note: Remove all will still query for a condition, just ignore & click thru --&addRemove|?{Adding or removing condition?|Add Condition,Add|Remove Condition,Remove|Remove All Conditions,RemoveAll} --&condition|?{Which Condition?|Concentrating|Charmed|Prone|Restrained} --:LOOKUP STATUSMARKER FOR GIVEN CONDITION DESCRIPTION| -->LookupMarkerFromCondition|[&condition] --:GET CURRENT STATUS MARKERS ON TOKEN| insert into array called markerArr --~|array;statusmarkers;markersArr;@{selected|token_id} --:ADD OR REMOVE THE MARKER FROM ARRAY| --?[&addRemove] -eq "Add"|[ --~|array;add;markersArr;[&marker] --]| --?[&addRemove] -eq "Remove"|[ --~|array;remove;markersArr;[&marker] --]| --?[&addRemove] -eq "RemoveAll"|[ -->RemoveAllConditions| --]| --:LOOP THRU MODIFIED ARRAY OF MARKERS & BUILD TOOLTIP LIST OF CONDITIONS| --~thisMarker|array;getfirst;markersArr --?[&thisMarker] -eq ArrayError|>RemoveAllConditions --:Loop| -->LookupAndAppendTooltip|[&thisMarker] -->AppendStatusMarkerString|[&thisMarker] --~thisMarker|array;getnext;markersArr --?[&thisMarker] -ne ArrayError|Loop --:UPDATE THE TOKEN WITH THE TOOLTIP AND MODIFY THE STATUSMARKERS API TOKEN PROPERTY --!t:@{selected|token_id}|tooltip:[&tooltip]|show_tooltip:true|statusmarkers:[&statusmarkers] --:End| --X| End Macro --:FUNCTIONS| --:LookupAndAppendTooltip| accepts statusmarker as parameter, appends corresponding condition to tooltip string --?[%1%] -eq "cobweb"|&desc;Restrained --?[%1%] -eq "back-pain"|&desc;Prone --?[%1%] -eq "chained-heart"|&desc;Charmed --?[%1%] -eq "overdrive"|&desc;Concentrating --?"X[&tooltip]" -eq "X"|[ --&tooltip|+[&desc] --]|[ --&tooltip|+, [&desc] --]| --<| --:LookupMarkerFromCondition| accepts condition text as parameter, finds corresponding statusmarker --?[%1%] -eq "Restrained"|▮cobweb --?[%1%] -eq "Prone"|▮back-pain --?[%1%] -eq "Charmed"|▮chained-heart --?[%1%] -eq "Concentrating"|▮overdrive --<| --:AppendStatusMarkerString| accepts statusmarker string, appends as comma-delimited list --?"X[&statusmarkers]" -eq "X"|[ --&statusmarkers|+[%1%] --]|[ --&statusmarkers|+,[%1%] --]| --<| --:RemoveAllConditions| --!t:@{selected|token_id}|tooltip:|show_tooltip:false|statusmarkers: --X| --<| }}
So I'm not sure if I need ScriptCards help, SmartAOE help, Fetch help, or just general mental help here.  So what I am trying to do is to use SmartAOE to spawn the AoEControlToken, then Fetch to get the token id of that token, then (eventually) ScriptCards to do some distance calculations between tokens on the page to see if they are in the AOE.  !scriptcard {{ --#emoteState|Hidden --#whisper|GM --#sourceToken|@{selected|token_id} --@smartAOE|_radius|10 ft _aoetype|square, float _forceintersection|true _PlayerID|[&SendingPlayerID] _selectedID|[*S:t-id]  --+AOE|@(AoEControlToken.token_id) --+Aiziazaar Muph|@(Aiziazaar Muph.token_id) }} Unfortunately, this is the output I get: The AoEControlToken spawns and works, Fetch grabs my character token id just fine, but not the AoEControlToken id. Can someone tell me where my idiocy lies? Thanks! 
1715400167

Edited 1715400597
timmaugh
Pro
API Scripter
What is happening is that the metascripts run before ScriptCards (and certainly before ScriptCards dispatching SmartAoE). That means your Fetch construction (for the AoE token) is discovered long before the token is ever spawned, so there is nothing for it to grab when it runs. You can pop the SmartAoEcommand out of the ScriptCard to have it work. Then, if you use ZeroFrame batching, you can both avoid the dropped line bug and delay the ScriptCard long enough to make sure SmartAoE has finished...  With SelectManager installed and configured to give back all properties, you should be able to simplify the SAoE command, too. So make sure you've run this command one time in your game: !smconfig +playerid +who And then this form should work (though I am air-coding it): !{{(^)   !smartAOE --radius|10 ft --aoetype|square, float --forceintersection|true   !scriptcard ({)     --#emoteState|Hidden     --#whisper|GM     --#sourceToken|@{selected|token_id}     --+AOE|@^(AoEControlToken.token_id)     --+Aiziazaar Muph|@(Aiziazaar Muph.token_id)   (}){^&delay .3) }} Note the deferral character breaking up the Fetch return until the line is dispatched (the caret: ^), and note that the ScriptCard command is delayed .3 seconds (it also uses the deferral character because we want that delay tag to be discovered when the ScriptCard line is running, not when the overall, parent message runs... ie, the first message). If .3 seconds turns out to be not enough, you can adjust. Post back if that doesn't work, and I'll take a closer look at it.
1715433957
David M.
Pro
API Scripter
Interesting. I'm curious, what id would be returned if there were multiple instances of the AoEControlToken on the map (e.g., there's already an AoE persistent effect when this macro runs)? Does Fetch return the first id it finds on the page? If so, Erik might want to use a custom control token for this macro that has a unique name to avoid incorrect distance measurements. @Erik, s ince you are presumably moving the control token into position before triggering (and possibly wanting to use a custom control token anyway for the above reason), can't you just use a custom control token for SmartAoE whose sheet has a token action that runs your Scriptcard logic? In this case the custom control token would be the --#sourcetoken. Technically, this would work with the default control token, too, but I would avoid since that token action would show up for every instance of it. This being said, I could be misunderstanding the reasoning behind your original approach, so Tim's solution might be more appropriate for you. Either way you do it, note that there might be edge cases where the shaded AoE displayed by SmartAoE might not agree with the SC distance calculation. For example, the center of larger tokens may be outside the shaded AoE region. SC only uses token center for distance measurements, and SmartAoE uses a comparison of the percentage of the grid area covered by the AOE outline to define the AoE region. This is obviously much more likely to occur for circular or conical AoEs where the outline cuts through a square. There may also be some discrepancies depending on if the control token is snapping to center or to intersection. YMMV, just wanted you to be aware :)
1715435254
timmaugh
Pro
API Scripter
That's a good point, David... And, to be clear, in a situation where there are multiple tokens of the same name, Fetch will retrieve only the first it finds. It was setup to mimic the Roll20 behavior for @{selected} and @{Character Name} references, which also return only the first encountered token.
Ok, a lot of this makes sense. That code is only a very small chunk of about a 500 line Scriptcard macro that does improvised spellcasting for a Mage the Awakening 2e game. There are a lot of twists and turns there.  The macro doesn't always spawn an AOE if their spell is targeted so having the SmartAOE command outside the macro is a bit superfluous and needs other data from choices the players make.  I really just wanted to use control token so that the players get a fairly accurate image of where their AOE was going to go. I'm actually not triggering SmartAOE at all except to delete the token after use. I'm ok with a little waffling between it and Scriptcards distance calculations since there are few large tokens in the game. I think that my ultimate solution may be just to use an --I command ( --iSelect the ; AOE Control Token|t;AOEID; Select the control token)  to have them target the AoEControlToken and get the token id from there.  Thanks for all your thoughts and help. I always learn something new!
1715450624
David M.
Pro
API Scripter
Ah, got it. A "one macro to rule them all" situation. Cool, improvised spellcasting sounds interesting, good luck!
1715451539
timmaugh
Pro
API Scripter
Just FYI, that second step wouldn't necessarily have to require them to select the token. By that point you know the token has been created, so you could use fetch to get the ID, you could use a select statement from select manager to pick the token as the selected token, or you could even just use a roll 20 formation to get the ID, since the token is already sitting on the board. Just one more way you can reduce the level of user interaction and automate it if you wanted to.
Hi, Have an idea but no clue how to make it work in a scriptcard or if it's even doable in a scriptcard Is there a way to have an ability on a token 1 that will message the GM when token 2 starts or ends it move within a set range of token 1. For example I'm looking for a prompt to remind me that a token may be affected by a Troglodytes stench among other things
timmaugh said: Just FYI, that second step wouldn't necessarily have to require them to select the token. By that point you know the token has been created, so you could use fetch to get the ID, you could use a select statement from select manager to pick the token as the selected token, or you could even just use a roll 20 formation to get the ID, since the token is already sitting on the board. Just one more way you can reduce the level of user interaction and automate it if you wanted to. Really? How would Select Manager manage (ahem) to do that?
1715482356
timmaugh
Pro
API Scripter
Something like... {& select AOEControlToken} Or, using a wildcard: {& select AOE*} With the Metascript Toolbox installed, just add that to your existing command line, and the message will see the referenced token(s) as selected, even though they won't be selected on the VTT. In fact, you might have *other* tokens selected on the board, but they won't be included unless they satisfy what you have designated in the {&select} tag. The particulars of this are discussed in this thread , and in this thread  , with a minor update in this post .
I'm having trouble with a hashtable. I have successfully used them in other places and copied their use in a different context and apparently messed something up. Here is a test version of the context. !script {{   --#title|Day Changes   --#leftsub|Kine gain 1 blood   --#rightsub|Kindred lose 1 blood   --l:|Kine   --&loop|0   --&one|[:Kine("[&loop]")]   --&six|6   --h:[Kine]("[&loop]")|[&six]   --&two|[:Kine("[&loop]")]   --*| loop=[&loop] one=[&one] six=[&six] two=[&two] }} The Kine hashtable is simply a list of numbers 0 to 50 with values from 0 to 10. Kine("0") has value 10 to start, I would expect the output to be "loop=0 one=10 six=6 two=6" However what I get is "loop=0 one=10 six=6 two=10" Clearly Kine("0") is still 10. Why hasn't it changed to 6?
Hey James B, To me this line looks off: --h:[Kine]("[&loop]")|[&six] I don't think the brackets should be around Kine there. I think the line should look like: --h:Kine("[&loop]")|[&six] In this test, which replaces loading a saved hash named Kine with setting a new one it seems to update fine: !script {{ --#title|Day Changes --#leftsub|Kine gain 1 blood --#rightsub|Kindred lose 1 blood --~|hash;set;Kine;0==10 --&loop|0 --&one|[:Kine("[&loop]")] --&six|6 --h:Kine("[&loop]")|[&six] --&two|[:Kine("[&loop]")] --*| loop=[&loop] one=[&one] six=[&six] two=[&two] }} Hopefully that works for you as well.
Exactly right! Thank you so much--I don't know where I got it. I thought I had copied from an area Ihad used it elsewhere which had worked, but obviously not.