
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?
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?
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?}
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:
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.
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...?)
Scriptcards has two ways to get selected token ids. The first, shown below, populates a series of string variables. From the wiki:
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 Name | Parameters | Description |
---|---|---|
getselected | none | Using 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 Name | Sub Function | Parameters | Return Type | Description |
---|---|---|---|---|
array | define | arrayname;value1;value2;... | none | Creates an array called "arrayname" and adds value1, value2, etc. to the array |
array | sort | arrayname | none | Sorts the specified array in place in ascending order |
array | add | arrayname;value1;value2;... | none | Adds value1, value2, ... to the existing array "arrayname" |
array | remove | arrayname;value1;value2;... | none | Removes value1, value2, ... from the existing array "arrayname". Resets the array index to 0. |
array | replace | arrayname;currentvalue;newvalue | none | Replaces all occurrences of "currentvalue" in "arayname" with "newvalue" |
array | getindex | arrayname | stringVariable | Retrieves the current index in "arrayname" and stores it in the supplied string variable |
array | setindex | arrayname;newindex | none | Sets the current index in array "arrayname" to "newindex" |
array | getcurrent | arrayname | stringVariable | Gets 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. |
array | getnext | arrayname | stringVariable | Increments the current index in "arrayname" and returns the new current value. Will return "ArrayError" if the end of the array is reached. |
array | getprevious | arrayname | stringVariable | Decrements the current index in "arrayname" and returns the new current value. Will return "ArrayError" if the the current index was already zero. |
array | getfirst | arrayname | stringVariable | Set the current index of "arrayname" to zero and retrieve the current item. Will return "ArrayError" if there is no current item. |
array | getlast | arrayname | stringVariable | Set 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. |
array | removeat | arrayname;indexposition | none | Removes the item at index "indexposition" from "arrayname". Resets the current array index to 0. |
array | indexof | arrayname;searchvalue | stringVariable | Searches 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 |
array | getlength | arrayname | stringVariable | Returns the number of items in array "arrayname" |
array | pagetokens | arrayname;tokenid;(optional filter) | stringVariable | Creates 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.) |
array | selectedtokens | arrayname | stringVariable | Creates 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. |
array | stringify | arrayname | stringVariable | Returns all of the items in "arrayname" as a string separated by semicolons (;) |
array | fromstring | arrayname | delimiter;string | Creates an array from "string", splitting "string" on "delimiter" to separate the items |
array | statusmarkers | arrayname;tokenid | none | Creates an array (arrayname) of the active statusmarkers (sometimes called Token Markers) for the specified tokenid |
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.
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.