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

Is there an API that can roll saves for groups then apply a condition to the tokens that failed?

August 25 (3 years ago)
Lee
Pro

I've been using Jakob's GroupCheck+apply damage API and am very happy with it. Fire Balling my life away. But I've found I need an API that can roll group saves and apply conditions. For something like Hypnotic Pattern. Any suggestions?

August 26 (3 years ago)
timmaugh
Pro
API Scripter

You probably won't find one that will do all of that, though there are a couple of options that let you chain others together. ScriptCards lets you build your own processing "functions" to take action only when and how necessary. Metascripts, on the other hand, play in the command lines of other scripts and let you launch them in your own time, after you have the command line in the shape you want.

SelectManager has a forselected handle that iterates over the selected tokens, issuing a command line that is customizable for each. Using this, you could have a single save check rolled for a group of tokens which you then individually compare to an attribute for the save attempt, or you could let each token have a unique roll, and check that roll against the same attribute.

APILogic gives you the ability to have If/Else logic in your command line, taking one branch given a condition (a failed save), and another branch given a different condition (a successful save).

Fetch lets you retrieve data from a token or the sheet as necessary, timed to work with SelectManager's forselected handle.

And ZeroFrame lets you control the timing even more, and gives you the capability of getting more complex, chained, or recursive actions.

Given  your situation, your workflow would probably be something like:

I need <some group of tokens> to roll saves versus <some attribute>. If they fail the check, then I need to apply <some condition>.

In practice, that might be something like:

!forselected(^) {^if [^[^1d20 + @^(selected.save-mod[0])^]^] > @^(selected.save-attr)}<...command to apply condition -- like token-mod...>{^&end}

Post back with more details about what you need to happen (and the other scripts that might be involved -- including command lines), and I can try to help you put a full macro together.


Lee said:

I've been using Jakob's GroupCheck+apply damage API and am very happy with it. Fire Balling my life away. But I've found I need an API that can roll group saves and apply conditions. For something like Hypnotic Pattern. Any suggestions?


Tokenmod accepts multiselect and I have a generic statusmarker apply hotkey.  The only catch is I have to know the exact spelling of each statusmarker, but as a forever DM in roll20, thats no problem.  I even have custom statusmarkers and know all of them too.  


!token-mod --set statusmarkers|!?{What statusmarker?}
August 26 (3 years ago)

Edited August 26 (3 years ago)
David M.
Pro
API Scripter

As a follow-up to Tim's mention of scriptcards, here's an example of a scriptcard macro for 5e Turn Undead. It finds tokens within range that are "undead", rolls their saves, and applies condition markers based on success/fail (two failure modes - turned and destroyed, with different markers for each).

Similar logic could be applied for Hypnotic Pattern, etc. It could also be modified to roll and apply damage. There are several examples of this in the thread I linked (tip: the first post has an index with links).

EDIT - I forgot I had another version of this that hides the creature names from the public output and whispers the actual names to the GM. It also uses the Spawn script with a custom image for the animated effects instead of Radar.

!script {{
  --#title|@{selected|character_name} Turns Undead!
  --#leftsub|Save DC @{selected|spell_save_dc}

  --:(0) CREATE AN ANIMATED EFFECT WITH SPAWN SCRIPT| uses SelectManager to retain selected token
  --@forselected|Spawn _name|HolyBurst _expand|50,30, true _size|13,13 _order|tofront
  --:(1) DETERMINE CR OF UNDEAD THAT CAN BE DESTROYED|
  --=charLevel|@{selected|level}
  --?[$charLevel] -lt 5|>SetCRdestroy;0
  --?[$charLevel] -ge 5 -and [$charLevel] -lt 8|>SetCRdestroy;0.5
  --?[$charLevel] -ge 8 -and [$charLevel] -lt 11|>SetCRdestroy;1
  --?[$charLevel] -ge 11 -and [$charLevel] -lt 14|>SetCRdestroy;2
  --?[$charLevel] -ge 14 -and [$charLevel] -lt 17|>SetCRdestroy;3
  --?[$charLevel] -ge 17|>SetCRdestroy;4

  --:(2) GET ALL TOKENS INTO THE "allTokens" ARRAY| will have blank 1st element to be removed later
  --~|array;pagetokens;allTokens;@{selected|token_id}

  --:(3) CREATE THE "inRange" ARRAY TO HOLD TOKENS IN RANGE|
  --~|array;define;inRange;

  --:(4) PREP ARRAY FOR LOOP| if no array elements then end macro
  --~tokenid|array;getfirst;allTokens
  --?[&tokenid] -eq ArrayError|endOutput

  --:(5) FIND ALL TOKENS IN RANGE|
  --:RangeLoop|
          --:TOKEN MUST BE ON OBJECTS OR GMLAYER AND TYPE MUST INCLUDE UNDEAD|
          --?[*[&tokenid]:t-layer] -ne objects -and [*[&tokenid]:t-layer] -ne gmlayer|NextToken
          --?"[*[&tokenid]:npc_type]" -ninc "undead"|NextToken
          
          --:CHECK DISTANCE IN UNITS. 30ft is 5UNITS|
          --~dist|distance;@{selected|token_id};[&tokenid]
          --?[$dist] -gt 5|NextToken
          
          --:ADD TO THE "inRange" ARRAY|
          --~|array;add;inRange;[&tokenid]

          --:NextToken|
          --~tokenid|array;getnext;allTokens
          --?[&tokenid] -ne ArrayError|RangeLoop

  --:(6) REMOVE DUMMY FIRST ITEM IN inRange ARRAY|
  --~|array;removeat;inRange;0

  --:(7) ROLL SAVES FOR EACH TOKEN IN RANGE| if fail, set a token condition marker to denote turned
  --~tokenid|array;getfirst;inRange
  --?[&tokenid] -eq ArrayError|End
  --=i|0
  --:SaveLoop|
         --=i|[$i]+1
          --&FailureText|
          -->GetSaveBonus|[&tokenid];wisdom;wis
          --=SaveRoll|1d20 + [$saveBonus] [BONUS]        
          --?[$SaveRoll.Total] -ge @{selected|spell_save_dc}|>MadeSave;[$i]|>FailedSave;[$i]
          --~tokenid|array;getnext;inRange
          --?[&tokenid] -ne ArrayError|SaveLoop

  --:End|
  --X|

--:PROCEDURES|
  --:SetCRdestroy| accepts CR as parameter
      --=CRdestroy|[%1%]
  --<|

  --:GetSaveBonus| accepts tokenid, full attribute name, short attribute name as parameters
      --:TAKE THE GREATER OF "attribute_save_bonus" OR "npc_attr_save_base"|
      --=bonus1|[*[%1%]:[%2%]_save_bonus]
      --&bonus2|[*[%1%]:npc_[%3%]_save_base]
      --:SOMETIMES "npc_attr_save_base" IS BLANK, SO SET TO -99. OTHERWISE USE ATTR VALUE|
          --?X[&bonus2] -eq "X"|>Set_npc_attr_save_bonus;-99|>Set_npc_attr_save_bonus;[&bonus2]
      --:FINALLY SET THE SAVE BONUS|
      --?[$bonus2] -gt [$bonus1]|>SetSaveBonus;[$bonus2]|>SetSaveBonus;[$bonus1]
  --<|

  --:Set_npc_attr_save_bonus| blank value is set to -99, otherwise use value stored in attribute
      --=bonus2|[%1%]
  --<|

  --:SetSaveBonus|
      --=saveBonus|[%1%]
  --<|

  --:MadeSave| 
        --*[*[&tokenid]:character_name]:|[#009900][b]Made Save[/b][/#] [$SaveRoll]
        --+Creature [%1%]:|[#009900][b]Made Save[/b][/#] [$SaveRoll]
  --<|

  --:FailedSave| add either a dead or fear condition marker to the token, depending on CR
      --=CR|[*[&tokenid]:npc_challenge]
      --?[$CR] -le [$CRdestroy]|>AddConditionMarker;[&tokenid];dead;-Dest.|>AddConditionMarker;[&tokenid];Fear::1510130;-Turn
       --*[*[&tokenid]:character_name]:|[#990000][b]Failed Save[/b][/#] [$SaveRoll] [b][&FailureText][/b]
       --+Creature [%1%]:|[#990000][b]Failed Save[/b][/#] [$SaveRoll] [b][&FailureText][/b]
  --<|

  --:AddConditionMarker| accepts tokenID, condition marker, and descriptive text as parameters
      --@token-mod|_ignore-selected _ids [%1%] _set statusmarkers|[%2%]
      --&FailureText|[%3%]
  --<|
}}

Click for animated gif:


August 26 (3 years ago)
Lee
Pro

That kicks ass, I will be using that. But it would be handy if there was something with the flexibility to be used for hypnotic pattern, confusion, stinking cloud, entangling roots ext... I'll check out the link. 

August 26 (3 years ago)
David M.
Pro
API Scripter

The scriptcard above could be modified for different spells, or even multiple spells if you really wanted to build in the logic. The thread I linked is just for completed, working scriptcards. Here is the wiki documentation for the script, and here's where you would want to post if you need some help with a scriptcard problem. Btw, the one above uses a bunch of different techniques (arrays, loops, attribute syntax, gm-only whispers, farming out other scripts, etc.) - not all scriptcards need to be that complicated.

Btw 2, the animated effect above was created using the SpawnDefaultToken script, which in this case was called from within the scriptcard macro (which is made possible by the SelectManager script, whew!). It is just added flair and not required for the primary functionality.

Wow i love this effect !!! please share !!! hehe

On my side, i'd love to get something that groupschecks+applydamage (groupcheck on selected tokens, and apply damage on a separate click, not directly in the group check), but also that takes into consideration the immunities or vulnerabilities/resistances of the monsters !!


I know that there are scriptcards like fireball from kurt, that does take into consideration the immunities and so on of the monsters, and automatically apllies damages, but it works automatically on an area, and i'd prefer just the funcitionalities of the group check on selected tokens....


Group check really needs the immunities thing improvement and i'm sure that scriptcards magic can have the answer.


I wish i could combine a multi token selection, the getsave routine, and a button at the bottom of the saves card so it can then apply damages. (of course that would imply to know what kind of damages it makes, and the rest of the group check routine (what save ? DC ? What if made or fail save...?)





August 27 (3 years ago)
David M.
Pro
API Scripter

Scriptcards has two ways to get selected token ids. The first, shown below, populates a series of string variables. From the wiki:


Assign Variable to Built-In Function (--~)

ScriptCards has the ability to run a function built into the API script and return the results to a variable. Some functions return roll variables, while others return strings. See the function table below for details. The format is:

--~VariableName|function;param1;param2;param3;...

Function NameParametersDescription
getselectednoneUsing the variable name in the tag, a series of string variables will be created: VariableNameCount will contain the number of selected tokens. VariableName1 will contain the token ID of the first selected token, VariableName2 the second, etc.

Here's an example of how you can loop through the selected tokens. You could use this method instead of going through the tokens on page array and filtering on distance to convert Kurt's Fireball example or to generalize it.

!script {{
  --#title|Loop through Selected Toks Example
  --~ids|getselected

  --=i|0
  --:Loop|
      --=i|[$i] +1
      --+Token [$i]|[&ids[$i.Raw]]
      --?[$i] -lt [$idsCount]|Loop
}}

The second way is to use one of the newer array functions (getselected is bolded below). This is also described in the wiki.

Function NameSub FunctionParametersReturn TypeDescription
arraydefinearrayname;value1;value2;...noneCreates an array called "arrayname" and adds value1, value2, etc. to the array
arraysortarraynamenoneSorts the specified array in place in ascending order
arrayaddarrayname;value1;value2;...noneAdds value1, value2, ... to the existing array "arrayname"
arrayremovearrayname;value1;value2;...noneRemoves value1, value2, ... from the existing array "arrayname". Resets the array index to 0.
arrayreplacearrayname;currentvalue;newvaluenoneReplaces all occurrences of "currentvalue" in "arayname" with "newvalue"
arraygetindexarraynamestringVariableRetrieves the current index in "arrayname" and stores it in the supplied string variable
arraysetindexarrayname;newindexnoneSets the current index in array "arrayname" to "newindex"
arraygetcurrentarraynamestringVariableGets the item at the current array index in "arrayname" and returns it as a string variable. Will return "ArrayError" if there is no current item.
arraygetnextarraynamestringVariableIncrements the current index in "arrayname" and returns the new current value. Will return "ArrayError" if the end of the array is reached.
arraygetpreviousarraynamestringVariableDecrements the current index in "arrayname" and returns the new current value. Will return "ArrayError" if the the current index was already zero.
arraygetfirstarraynamestringVariableSet the current index of "arrayname" to zero and retrieve the current item. Will return "ArrayError" if there is no current item.
arraygetlastarraynamestringVariableSet the current index of "arrayname" to the last item in the array and retrieve the current item. Will return "ArrayError" if there is no current item.
arrayremoveatarrayname;indexpositionnoneRemoves the item at index "indexposition" from "arrayname". Resets the current array index to 0.
arrayindexofarrayname;searchvaluestringVariableSearches the array "arrayname" for and item with the value of "searchvalue" and, if found, returns the index of the first matching value or "ArrayError" if not found
arraygetlengtharraynamestringVariableReturns the number of items in array "arrayname"
arraypagetokensarrayname;tokenid;(optional filter)stringVariableCreates an array (arrayname) of all of the token ids (technically Graphics objects) on the same page as the specified token id. Returns the number of tokens found to the stringVariable. Available optional filters are : all, char, graphic, pc, npc. The filters will return only those tokens that match that criteria (char means tokens have a "represents", graphic doesn't. pc indicates a represents token has a controlledby, npc doesn't.)
arrayselectedtokensarraynamestringVariableCreates an array (arrayname) of all of the token ids (technically Graphics objects) selected when the script was executed. Returns the number of tokens found to the stringVariable.
arraystringifyarraynamestringVariableReturns all of the items in "arrayname" as a string separated by semicolons (;)
arrayfromstringarraynamedelimiter;stringCreates an array from "string", splitting "string" on "delimiter" to separate the items
arraystatusmarkersarrayname;tokenidnoneCreates an array (arrayname) of the active statusmarkers (sometimes called Token Markers) for the specified tokenid
August 29 (3 years ago)

tagging this so that i can play with it later.

August 31 (3 years ago)

Edited August 31 (3 years ago)
Victor B.
Pro
Sheet Author
API Scripter

No, the reality is you won't find this for what you want.  You can create a specific macro for every spell, but that will get tedious based on number of spells.  Also you need to have strong programming skills to create a turn undead macro and convert to every other spell in 5e which is insane.  

There's no way around rolling.  Group check is close, great API but doesn't tell you the actual token that succeeded and failed.  Give a simple list.  Apply damage, which is an unpublished (to my knowledge) add on is a great way to auto apply damage using Group Check.  The combo is awesome. Limitations.  If you roll advantage, has to be done outside of the API, etc., but still great.  You need an ability to select tokens and apply a condition that has a duration AFTER you've rolled the saves.  Track that condition and duration through the combat.  Remove that condition if after the duration.  Show to the GM that a concentration check is needed if damage is taken.  That's combat master.  Honestly nothing else comes close.  

September 02 (3 years ago)

Hi, 

With Michael's help, I've been able to correct some mistakes I made. 

I actually corrected all the $ and & mistakes and it seems to work pretty well now. 

!script {{



--/|Demande quel JP, quel DD, quels degats, quel effet sauvegarde, quel type dégats
--&saveType|?{Quel JP?|Dexterité,dex|Constitution,con|Sagesse,wis}
--=DC|?{DD ?|15}
--=Damage|?{Dégats}
--&DCeffect|?{Effet Sauvegarde ?|Annule,annule|Demi dégats,demi}
--&damageType|?{Quel type de Dégats ?|Acide,acid|Contondant,bludgeoning|Feu,fire|Force,force|Foudre,lightning|Froid,cold|Nécrotique,necrotic|Perforant,piercing|Poison,poison|Psychique,psychic|Radiant,radiant|Tonnerre,thunder|Tranchant,slashing}




--/|Get all of the tokens on the page so we can cache their positions
--~|array;pagetokens;alltokens;@{selected|token_id}
--#leftsub|DD [$DC]
--#rightsub|Dégats: [$Damage]
--#title|JP de Groupe
--#titleCardBackground|#03038a
--#oddRowBackground|#d8d8e6
--#evenRowBackground|#FFFFFF
--#whisper|gm

--/|Calculate damage
--=HalfDamage|[$Damage.Total] \ 2
--=DoubleDamage|[$Damage.Total] * 2
--=QuarterDamage|[$Damage.Total] \ 4

--/|Since we want to be able to hover over a roll and see the dice details, output the rolled damage at the
--/|top of the card. If all critters make their save, the half damage roll won't contain the details.
--+|[c][b]Damage Roll: [/b][$Damage][/c]
--+|

--/|Create an array with the selected token
--~tokenid|array;selectedtokens;Selected


--/|The first item in the array will be a blank dummy item, so remove it.
--~|array;removeat;selected;0

--/|Loop through the tokensHit tokens and roll saves for each one and apply damage
--~tokenid|array;getfirst;Selected
--?[&tokenid] -eq ArrayError|endOutput
--:loopDisplay|
--=SaveRoll|1d20 + [*[&tokenid]:npc_[&saveType]_save]

--/|Compare the save roll to the save DC and either apply full or half damage
--?"[*[&tokenid]:npc_immunities]" -inc "[&damageType]"|Immune
--?"[*[&tokenid]:npc_resistances]" -inc "[&damageType]"|Resistant
--?"[*[&tokenid]:npc_vulnerabilities]" -inc "[&damageType]"|Vulnerable
--?[$SaveRoll.Total] -lt [$DC]|>ApplyDamageTokenmod;[&tokenid];1;-[$Damage.Total]|>ApplyDamageTokenmod;[&tokenid];1;-[$HalfDamage.Total]
--?[$SaveRoll.Total] -ge [$DC]|madeSave

--/|Here are various damage applications if the creature is immune, resistant, or vulnerable. In some cases, we will reuse output lines,
--/|for example, a resistant creature that fails its save will jump to "madeSave", since that is the correct damage amount. (half), while
--/|a vulnerable creature that makes its save will jump to "FailedSave" since that will be normal damage.

--/|Output a line for a failed saving throw (we will also jump here for a vulnerable creature that MAKES its save)
--:FailedSave|
--+[*[&tokenid]:t-name]:|Save [$SaveRoll] [r][$Damage] [&damageType][/r]
--^afterSave|

--:Immune|
--+[*[&tokenid]:t-name]:|n'est pas affecté par le sort !
--^afterSave|

--:Resistant|
--?[$SaveRoll.Total] -lt [$DC]|>ApplyDamageTokenmod;[&tokenid];1;-[$HalfDamage.Total]|>ApplyDamageTokenmod;[&tokenid];1;-[$QuarterDamage.Total]
--?[$SaveRoll.Total] -lt [$DC]|madeSave
--+[*[&tokenid]:t-name]:|Save [$SaveRoll] [r][$QuarterDamage] [&damageType][/r]
--^afterSave|

--:Vulnerable|
--?[$SaveRoll.Total] -lt [$DC]|>ApplyDamageTokenmod;[&tokenid];1;-[$DoubleDamage.Total]|>ApplyDamageTokenmod;[&tokenid];1;-[$Damage.Total]
--?[$SaveRoll.Total] -ge [$DC]|FailedSave
--+[*[&tokenid]:t-name]:|Save [$SaveRoll] [r][$DoubleDamage] [&damageType][/r]
--^afterSave|

--/|Output a line for a successful saving throw
--:madeSave|
--+[*[&tokenid]:t-name]:|Save [$SaveRoll] [r][$HalfDamage] [&damageType][/r]
--:afterSave|

--~tokenid|array;getnext;Selected
--?[&tokenid] -ne ArrayError|loopDisplay

--:endOutput|

--X|

--:ApplyDamageTokenmod|Parameters are tokenid;bar#;amount
--@token-mod|_ignore-selected _ids [%1%] _set bar[%2%]_value|[%3%]
--<|

}}


Now i have 2-3 last issues in what i try to achieve.


First : For DCeffect, when i choose "annule" i'd like to have the script jump to the immune save test part. For example, some cantrips can affect several creatures, but there are no effect if they make the save.

I should put a conditional : maybe something like

--?"[*[&DCeffect]] -inc "annule" -and [$SaveRoll.Total] -ge [$DC]|Immune

Would that be correct ? And should i put this in the list of rolls comparisons ? or somewhere else ?


Second : I'd like to put a button so the damages are rolled only if i press this button, and not automatically as it is for the moment.

How to write this button command ?and do i have to put it instead of every >applydamagetokenmod commands ? or only once just before the tokenmod command ?


Third (but that's just a little cherry on the big cake) : I have a little tokenmod macro that puts the deadmarker and switch the token to the maplayer when one of my monsters dies (that is helpful when i have a lot of monsters, because it puts them "toback" and prevents me to select them by mistake.

How easy would it be to say that : --?"[*[&tokenid]:bar1] -lt 1|>applytokenmarkerandmoveitonmaplayer

Would that work ? There's no "or else" in that line...don't know if it needs one or not, and i think i should probably put some parameters in that subroutine call

Where would that subroutine be placed in the script ? Would the script remember whose token we're talking about ?

It seems pretty easy but maybe it's not that easy at all....


Thanks for your help.