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 - My "Spiritual Successor" to PowerCards

1617370999

Edited 1617371061
David M.
Pro
API Scripter
Michael C. said:  I think the issue is in the two lines:   --@setattr|_charid [*T:character_id] _hp|hp^ _silent   --@setattr|_charid [*S:character_id] _hp|+[$HPRecovered] _silent Michael, a couple of things: To add a value with setattr you need to use the --mod or --modb option, otherwise it just sets the attribute to the number given. I believe !modattr and !modbattr are optional shorthand ways to do this as well. Secondly, don't you want to use [*T:character_id] instead of [*S:character_id] in your StillWounded code block? Your code was attempting to set the selected character's hp to the value that you were trying to heal the target. Try this:   --@setattr|_modb _charid [*T:character_id] _hp|+[$HPRecovered.Total] _silent Note that using modb will add the given value but will not exceed the maximum value, so if you use this you could probably remove your still wounded conditionals to simplify things. One trick when troubleshooting api calls is to change the --@ into --+@ to output the actual text that would otherwise be sent to chat. Then you can copy the printed line and test in another macro after replacing the @ with ! and the underscores with double dashes.
@David M. I actually normally use modbattr and don't know why I did it this way.  (The S instead of T was an entry error but a good catch anyway.)  I still could not get the target to update with that suggestion but modified it and the modified version worked:   --@modbattr| _charid [*T:character_id] _hp|+[$HPRecovered.Total] _silent That --+@ suggestion is a game changer - I had no idea that one could include two parameters like that. Thanks again for helping a newb.
Snow said: @Jay R. Thank you that worked. I thought I had tried that but I guess I didn't. No problem. I call macros from buttons all the time so I knew the functionality was there. :)
1617377097

Edited 1617377419
Kurt J.
Pro
API Scripter
Here is the (tweaked) fireball script I used in the GIF a few posts back. I know it is long, but I have comments in it as to what is going on at pretty much each step. It could also probably be improved by refactoring some code (like saves and outputting the damage) into subroutines. This version doesn't account for resistance or immunity either, though if refactored as mentioned that wouldn't be difficult to add. I'll probably work on that and post an updated version in the near future. The point here is to demonstrate the use of arrays. !script {{ --/Description|Deals fireball damage to all tokens that represent creations in a 20' radius around target token --/|Set up the card appearance --#title|@{selected|character_name} casts Fireball! --#bodyFontSize|12px --#titlecardbackground|#800000 --#leftsub|Save DC @{selected|spell_save_dc} --/|Get a spell slot level from the caster. --=SpellLevel|?{Spell Slot Level?|3|4|5|6|7|8|9} --#rightsub|Slot Level: [$SpellLevel] --/|Calculate damage based on spell slot. Fireball is 8d6 for 3rd level, so 5+SpellLevel d6 total. --=DamageDice|[$SpellLevel.Total] + 5 --=Damage|[$DamageDice.Total]d6 --=HalfDamage|[$Damage.Total] \ 2 --/|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] --+|  --/|Get all tokens on the page into the "alltokens" array --~|array;pagetokens;alltokens;@{target|token_id} --/|Create the "inRange" array. It will have a blank item in it to begin with, which we will remove later. --~|array;define;inRange; --/|Loop through all of the tokens in "alltokens" to check their distance --~tokenid|array;getfirst;alltokens --?[&tokenid] -eq ArrayError|endOutput --:loopCheck| --/|Skip targets that are not on the token layer or that don't represent creatures --?[*[&tokenid]:t-layer] -ne objects|continue --?"[*[&tokenid]:t-represents]" -ninc "-"|continue --/|Check the distance between the target token and the current array token. 20 feet is 4 units --~dist|distance;@{target|token_id};[&tokenid] --?[$dist] -gt 4|continue --/|If we didn't skip over this part, the token is within 20 feet, so add it to the inRange array --~|array;add;inRange;[&tokenid] --:continue| --~tokenid|array;getnext;alltokens --?[&tokenid] -ne ArrayError|loopCheck --/|Remove the dummy first item in the inRange array --~|array;removeat;inRange;0 --/|Loop through the inRange tokens and roll saves for each one and apply damage --~tokenid|array;getfirst;inRange --?[&tokenid] -eq ArrayError|endOutput --:loopDisplay| --=SaveRoll|1d20 + [*[&tokenid]:dexterity_save_bonus] --/|Compare the save roll to the save DC and either apply full or half damage --?[$SaveRoll.Total] -lt @{selected|spell_save_dc}|>ApplyDamageTokenmod;[&tokenid];3;-[$Damage.Total]|>ApplyDamageTokenmod;[&tokenid];3;-[$HalfDamage.Total] --?[$SaveRoll.Total] -ge @{selected|spell_save_dc}|madeSave --/|Output a lien for a failed saving throw --+[*[&tokenid]:character_name]:|Save [$SaveRoll] [r][$Damage] fire damage[/r] -->afterSave| --/|Output a line for a successful saving throw --:madeSave| --+[*[&tokenid]:character_name]:|Save [$SaveRoll] [r][$HalfDamage] fire damage[/r] --:afterSave| --/|Put a burn-fire visual effect on impacted tokens --vtoken|[&tokenid] burn-fire --~tokenid|array;getnext;inRange --?[&tokenid] -ne ArrayError|loopDisplay --/|Add some extra visual effects - a nova-fire at the target, and a beam-fire from source to target --vtoken|@{target|token_id} nova-fire --vbetweentokens|@{selected|token_id} @{target|token_id} beam-fire --:endOutput| --X| --/|Subroutine to apply damage with TokenMod. Could be replcaed with alterbars or chatsetattr --:ApplyDamageTokenmod|Parameters are tokenid;bar#;amount --@token-mod|_ignore-selected _ids [%1%] _set bar[%2%]_value|[%3%] --<| }}
@Kurt J.  Massive & Awesome!
1617465110

Edited 1617500958
David M.
Pro
API Scripter
This is awesome, Kurt! Sharing a scriptcard below for 5e Turn Undead, using Kurt's Fireball card as a starting point. What it does: Finds all tokens on objects and gmlayer (for invisible undead) Filters for npc_type to affect only those with "undead" keywords Makes saves for affected tokens within range (note: Euclidean distance used - so Pythagorean theorem) and displays in chat with color-coded text for pass/fail Checks the cleric level (note: assumes single class!) to determine if failure will turn or destroy based on CR of the undead Assigns a token condition marker on failure: "dead" marker for destroyed undead, and a custom "fear" marker to indicate turned. These can be changed based on the markers you have available (look for the conditional call to the AddConditionMarker procedure found within the FailedSave procedure) Creates a 30ft animated wavefront representing the burst of divine energy, using the Radar script (via SelectManager). Silent option to prevent radar chat results, and pingLife|0 to not produce visible "pings" Example gif with a 5th level cleric (destroys CR 1/2 or lower) surrounded by skeletons (CR 1/4), Specters (CR 1), and an Ogre Zombie (CR 2).    Here's the code: (EDIT- updated to take the greater of the attributes "wisdom_save_bonus" and "npc_wis_save_base". If the latter is blank we do some substitution to ensure the former is used). Updates in bold. !script {{ --#title|@{selected|character_name} Turns Undead! --#leftsub|Save DC @{selected|spell_save_dc} --:(0) CREATE AN ANIMATED WAVEFRONT WITH RADAR SCRIPT| uses SelectManager to retain selected token --@forselected|radar_range|30ft _pinglife|0 _wavedelay|20 _wavespacing|10 _silent|true --:(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|euclideandistance;@{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 --:SaveLoop| -->GetSaveBonus|[&tokenid];wisdom;wis --=SaveRoll|1d20 + [$saveBonus] [BONUS] --?[$SaveRoll.Total] -ge @{selected|spell_save_dc}|>MadeSave|>FailedSave --~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"| --=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] --<| --:FailedSave| add either a dead or fear condition marker to the token, depending on CR --+[*[&tokenid]:character_name]:|[#990000][b]Failed Save[/b][/#] [$SaveRoll] --=CR|[*[&tokenid]:npc_challenge] --?[$CR] -le [$CRdestroy]|>AddConditionMarker;[&tokenid];dead|>AddConditionMarker;[&tokenid];Fear::1510130 --<| --:AddConditionMarker| accepts tokenID and condition marker as parameter --@token-mod|_ignore-selected _ids [%1%] _set statusmarkers|[%2%] --<| }}
I'm grabbing the Undead script for sure.  Great work! Now for a simple question, why doesn't this work?  Wanting to output the token_size based on a supplied string variable but it works if I hard code it.  I would like to get the same result for Size1 and Size2. !scriptcard {{ --&name|Riding Horse --+Size1|@{Riding Horse|token_size} --+Size2|@{[&name]|token_size} }} Result:
1617469692
Kurt J.
Pro
API Scripter
David M. said: This is awesome, Kurt! Sharing a scriptcard below for 5e Turn Undead, using Kurt's Fireball card as a starting point. What it does: Finds all tokens on objects and gmlayer (for invisible undead) Filters for npc_type to affect only those with "undead" keywords Makes saves for affected tokens within range (note: Euclidean distance used - so Pythagorean theorem) and displays in chat with color-coded text for pass/fail Checks the cleric level (note: assumes single class!) to determine if failure will turn or destroy based on CR of the undead Assigns a token condition marker on failure: "dead" marker for destroyed undead, and a custom "fear" marker to indicate turned. These can be changed based on the markers you have available (look for the conditional call to the AddConditionMarker procedure found within the FailedSave procedure) Creates a 30ft animated wavefront representing the burst of divine energy, using the Radar script (via SelectManager). Silent option to prevent radar chat results, and pingLife|0 to not produce visible "pings" @David M. - Very cool :) Gonna be stealing that one. @Will M. - The issue is that @{} notation happens on the chat server before ScriptCards gets to do any processing, so what ends up getting passed into the script is literally "Riding Horse|token_size" since the chat server doesn't know what [&name] is. You can pass in a token ID that you store to use, with the [*x:x] notation in ScriptCards. Something like this (untested): !scriptcard {{ --&name|Riding Horse --&tokenid|@{Riding Horse|token_id} --+Size2|[*[&tokenid]:t-token_size] }}
Awesome David.  I am going to "borrow" both scripts and hopefully it's gonna take me only a month to understand what Kurt devised and you modified.
1617493646

Edited 1617493719
Kurt J.
Pro
API Scripter
Here is an updated version of the Fireball script. This has the following improvements: Consolidates the saving throw logic into a procedure that can be called for each token. Saving throws take into account Resistance, Vulnerability, and Immunity appropriately If a creature has an enhanced saving throw, it will be added to the roll (some creatures have a base save bonus which may be different from their attribute-based modifier) !script {{ --/Description|Deals fireball damage to all tokens that represent creations in a 20' radius around target token --/|Set up the card appearance --#title|@{selected|character_name} casts Fireball! --#bodyFontSize|12px --#titlecardbackground|#800000 --#leftsub|Save DC @{selected|spell_save_dc} --/|Get a spell slot level from the caster. --=SpellLevel|?{Spell Slot Level?|3|4|5|6|7|8|9} --#rightsub|Slot Level: [$SpellLevel] --/|Calculate damage based on spell slot. Fireball is 8d6 for 3rd level, so 5+SpellLevel d6 total. --=DamageDice|[$SpellLevel.Total] + 5 --=Damage|[$DamageDice.Total]d6 --=HalfDamage|[$Damage.Total] \ 2 --/|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] --+|  --/|Get all tokens on the page into the "alltokens" array --~|array;pagetokens;alltokens;@{target|token_id} --/|Create the "inRange" array. It will have a blank item in it to begin with, which we will remove later. --~|array;define;inRange; --/|Loop through all of the tokens in "alltokens" to check their distance --~tokenid|array;getfirst;alltokens --?[&tokenid] -eq ArrayError|endOutput --:loopCheck| --/|Skip targets that are not on the token layer or that don't represent creatures --?[*[&tokenid]:t-layer] -ne objects|continue --?"[*[&tokenid]:t-represents]" -ninc "-"|continue --/|Check the distance between the target token and the current array token. 20 feet is 4 units --~dist|distance;@{target|token_id};[&tokenid] --?[$dist] -gt 4|continue --/|If we didn't skip over this part, the token is within 20 feet, so add it to the inRange array --~|array;add;inRange;[&tokenid] --:continue| --~tokenid|array;getnext;alltokens --?[&tokenid] -ne ArrayError|loopCheck --/|Remove the dummy first item in the inRange array --~|array;removeat;inRange;0 --/|Loop through the inRange tokens and roll saves for each one and apply damage --~tokenid|array;getfirst;inRange --?[&tokenid] -eq ArrayError|endOutput --:loopDisplay| -->MakeSavingThrow|[&tokenid];dexterity;@{selected|spell_save_dc};[$Damage.Total];fire;thisTokenDamage;saveResult;dex --+[*[&tokenid]:character_name]:|Save [$savingThrow] [r][$thisTokenDamage] fire damage[/r] --/|Put a burn-fire visual effect on impacted tokens --vtoken|[&tokenid] burn-fire --/|Get the next token and continue the loop until we run out. --~tokenid|array;getnext;inRange --?[&tokenid] -ne ArrayError|loopDisplay --/|Add some extra visual effects - a nova-fire at the target, and a beam-fire from source to target --vtoken|@{target|token_id} nova-fire --vbetweentokens|@{selected|token_id} @{target|token_id} beam-fire --:endOutput| --X| --/|Subroutine to make saving throws and calculate damage amounts --:MakeSavingThrow|tokenid;savetype;dc;damage;damagetype;damagevariable;saveresultvariable;shortabilityname --?"X[*[%1%]:npc_[%8%]_save_base]" -eq "X"|>set_string;baseSaveBonus;|>set_string;baseSaveBonus; + + [*[%1%]:npc_[%8%]_save_base] --=savingThrow|1d20 + [*[%1%]:[%2%]_save_bonus] [&baseSaveBonus] --&dmgmult| --?[$savingThrow] -ge [%3%]|>set_string;dmgmult; \ 2 --?[$savingThrow] -lt [%3%]|>set_string;[%7%];fail --?[$savingThrow] -ge [%3%]|>set_string;[%7%];success --?"[*[%1%]:npc_resistances]" -inc "[%5%]" -and [$savingThrow] -lt [%3%]|>set_string;dmgmult; \ 2 --?"[*[%1%]:npc_resistances]" -inc "[%5%]" -and [$savingThrow] -ge [%3%]|>set_string;dmgmult; * 0 --?"[*[%1%]:npc_vulnerabilities]" -inc "[%5%]" -and [$savingThrow] -ge [%3%]|>set_string;dmgmult; --?"[*[%1%]:npc_vulnerabilities]" -inc "[%5%]" -and [$savingThrow] -lt [%3%]|>set_string;dmgmult; * 2 --?"[*[%1%]:npc_immunities]" -inc "[%5%]"|>set_string;dmgmult; * 0 --=[%6%]|[%4%] [&dmgmult] --<| --:set_string|mod_variable;value --&[%1%]|[%2%] --<| --/|Subroutine to apply damage with TokenMod. Could be replcaed with alterbars or chatsetattr --:ApplyDamageTokenmod|Parameters are tokenid;bar#;amount --@token-mod|_ignore-selected _ids [%1%] _set bar[%2%]_value|[%3%] --<| }}
1617500898
David M.
Pro
API Scripter
Kurt, good catch with the "npc_attr_save_base" check for those creatures with higher bonuses than their attribute would give. I updated the Turn Undead card above to take the greater of these two attributes. Used your --?"X[*....]" -eq "X" trick to catch the blank values. Clever! 
1617528478
Duncan R.
Pro
Sheet Author
I might modify the fireball for grenades, don’t think that would be much work at all as Kurt has done all the hard work already.
1617539061

Edited 1617541200
Gets better with every revision.  Would you want to add your previous slot check subs?  Something like:  -->GetAndCheckSlotInformation|  -->DeductSpellSlot|   --X|  --:GetAndCheckSlotInformation|   --=SlotLevel|?{Spell Slot Level Used?|1|2|3|4|5|6|7|8|9}   --=SlotsTotal| @{selected|lvl[$SpellLevel]_slots _total]   --=SlotsExpended| @{selected|lvl[$SpellLevel] _slots _slots_expended]    --?[$SlotsExpended.Total] -ge [$SlotsTotal.Total]|NoSlotsLeft   --<|   --:DeductSpellSlot|   --=SlotsExpended|[$SlotsExpended] + 1   --@setattr|_charid @{selected|character_id} _lvl[$SpellLevel]_slots_expended|[$SlotsExpended] _silent   --=SlotsRemaining|[$SlotsTotal] - [$SlotsExpended]   --+|Level [$SpellLevel] Slots Left: [$SlotsRemaining]    --X|   --:NoSlotsLeft|   --+|[b]@{selected|character_id} has no level [$SlotLevel.Total] spell slots available.[/b]   --X|NoSlotsLeftStop
I have a clarification question.  I have this script for NPCs who can cast Fireball that works great.  !scriptcard {{    --#emoteState|Hidden   --Lsettings|gmspells   --~Victim|getSelected   --IWho is the; caster?|t;Caster;Caster   --#sourceToken|[&Caster]   --#title|[*S:t-name] - Fireball   --#leftsub|2 actions   --#rightsub|Verbal, Somatic   --#noMinMaxHighlight|1   --=LevelCast|?{Level|3|4|5|6|7|8|9}   --=DiceAmount|[$LevelCast] * 2   --=Damage|[$DiceAmount]d6   --=HalfDamage|[$Damage] \ 2   --=CritDamage|[$Damage] * 2   --=CritSave|10 + [*S:spell_dc]   --+Casting|[*S:t-name]  deals [b][$Damage] fire damage[/b] in a 20' burst. [br][br]Basic Reflex save at [b]DC [*S:spell_dc] [/b].[br]   --=i|0      --:ApplyDamage|     --=i|[$i] + 1 --#targetToken|[&Victim[$i.Total]] --=ReflexSave|[*T:saving_throws_reflex] + 1d20 --?[$ReflexSave.Base] -eq "20" -or [$ReflexSave] -ge [$CritSave]|CritSave --?[$ReflexSave.Base] -eq "1"|CritFail --?[$ReflexSave] -ge [*S:spell_dc]|>HalfDamage|>FullDamage   --:CritSave| --+Critical Save - |[i][*T:t-name] critically saves and takes [b]no damage[/b].[/i] --?[$i] -eq [$VictimCount]|>Done|>ApplyDamage     --X|   --:CritFail| --?"[*T:name]" -inc Haggle|FullDamage --+Critical Failure - |[i][*T:t-name] critically fails and takes [b][$CritDamage] damage[/b].[/i] --@alter|_target|[*T:t-id] _bar|1 _amount|-[$CritDamage] _show|none --?[$i] -eq [$VictimCount]|>Done|>ApplyDamage     --X|   --:HalfDamage| --?"[*T:name]" -inc Haggle|CritSave --+Saved - |[b][i][*T:t-name][/i] saves[/b] and takes [$HalfDamage] damage. .     --@alter|_target|[*T:t-id] _bar|1 _amount|-[$HalfDamage] _show|none     --?[$i] -eq [$VictimCount]|>Done|>ApplyDamage     --X|   --:FullDamage| --?"[*T:name]" -inc Haggle|HalfDamage --+Failed - |[b][i][*T:t-name][/i] fails[/b] and takes [b][$Damage][/b] damage. --@alter|_target|[*T:t-id]  _bar|1 _amount|-[$Damage] _show|none     --?[$i] -eq [$VictimCount]|>Done|>ApplyDamage   --:Done|   --X| }} However, if I remove the information request and change the source to --#sourceToken|@{target|Caster|token_id}, the API crashes with a possible infinite loop error. I thought the sourceToken tag just passed in the ID of the token involved. So why does the first work and not the second?
David M. said: This is awesome, Kurt! Sharing a scriptcard below for 5e Turn Undead, using Kurt's Fireball card as a starting point. What it does: Finds all tokens on objects and gmlayer (for invisible undead) Filters for npc_type to affect only those with "undead" keywords Makes saves for affected tokens within range (note: Euclidean distance used - so Pythagorean theorem) and displays in chat with color-coded text for pass/fail Checks the cleric level (note: assumes single class!) to determine if failure will turn or destroy based on CR of the undead Assigns a token condition marker on failure: "dead" marker for destroyed undead, and a custom "fear" marker to indicate turned. These can be changed based on the markers you have available (look for the conditional call to the AddConditionMarker procedure found within the FailedSave procedure) Creates a 30ft animated wavefront representing the burst of divine energy, using the Radar script (via SelectManager). Silent option to prevent radar chat results, and pingLife|0 to not produce visible "pings" Example gif with a 5th level cleric (destroys CR 1/2 or lower) surrounded by skeletons (CR 1/4), Specters (CR 1), and an Ogre Zombie (CR 2).    Very Nice David ! Thx, good job !
1617614018

Edited 1617614099
Hello, Love the Scriptcards! Brilliant stuff but I am stuck. I get inconsistent results when I run my scriptcard. A little bit of background. We are playing an old school sci-fi rpg called Laserburn - its a percentile based system where you need to roll under a defined score. First you roll to hit, then you roll location, then you roll to see if it penetrates the armour and finally you roll damage. I also have to say I do not have any programming background so the code below may horrify you:)   I have managed to get this working but as mentioned it doesn't give consistent results - the issue is always at the damage part - sometimes it rolls it other times not and I am sure it has something to do with my call procedures...any help would be greatly appreciated. The screenshots show the results of the script card executed one after the other but with the first not calling damage whereas the second one does.... !scriptcard {{ --#title|Badabadabadabaaam --#sourceToken|@{selected|token_id} --#emoteText|@{selected|token_name} is shooting their @{selected|selectedweapontext1} --=Shots|?{How Many Shots?|1|2|3} --+[#008]Shots[/#]|[$Shots] --?[$Shots.Total] -eq 1|>RollAndOutput1 --?[$Shots.Total] -eq 2|>RollAndOutput2 --?[$Shots.Total] -eq 3|>RollAndOutput3 --?[$Shots.Total] -gt 3|>TooManyShots --X| --:TooManyShots| --+Results|Too Many shots! --<| --:RollAndOutput1| --=R1|1d100 + 6 --+[#C40]1st Shot[/#]|@{selected|token_name} rolls [$R1] --? [$R1] -le @{selected|bar1}|>Hit --? [$R1] -gt @{selected|bar1}|>Miss --<| --:RollAndOutput2| --=R1|1d100 + 6 --+[#C40]1st Shot[/#]|@{selected|token_name} rolls [$R1] --? [$R1] -le @{selected|bar1}|>Hit --? [$R1] -gt @{selected|bar1}|>Miss --=R2|1d100+12 --+[#490]2nd Shot[/#]|@{selected|token_name} rolls [$R2] --? [$R2] -le @{selected|bar1}|>Hit --? [$R2] -gt @{selected|bar1}|>Miss --<| --:RollAndOutput3| --=R1|1d100+6 --+[#C40]1st Shot[/#]|@{selected|token_name} rolls [$R1] --? [$R1] -le @{selected|bar1}|>Hit --? [$R1] -gt @{selected|bar1}|>Miss --=R2|1d100+12 --+[#490]2nd Shot[/#]|@{selected|token_name} rolls [$R2] --? [$R2] -le @{selected|bar1}|>Hit --? [$R2] -gt @{selected|bar1}|>Miss --=R3|1d100+18 --+[#C8E]3rd Shot[/#]|@{selected|token_name} rolls [$R3] --? [$R3] -le @{selected|bar1}|>Hit --? [$R3] -gt @{selected|bar1}|>Miss --<| --:Hit| --=Hit|1d100 --?[$Hit.Base] -le 10 |>HeadShot --?[$Hit.Base] -ge 11 -and [$Hit.Base] -le 20|>RightArm --?[$Hit.Base] -ge 21 -and [$Hit.Base] -le 30|>LeftArm --?[$Hit.Base] -ge 31 -and [$Hit.Base] -le 50|>UpperBody --?[$Hit.Base] -ge 51 -and [$Hit.Base] -le 70|>LowerBody --?[$Hit.Base] -ge 71 -and [$Hit.Base] -le 85|>RightLeg --?[$Hit.Base] -ge 86 |>LeftLeg --<| --:HeadShot| --=HeadShot|1 --+HeadShot|[$Hit] -->Penetrate| --<| --:RightArm| --=LimbShot|1 --+RightArm|[$Hit] -->Penetrate| --<| --:LeftArm| --=LimbShot|1 --+LeftArm|[$Hit] -->Penetrate| --<| --:UpperBody| --=BodyShot|1 --+UpperBody|[$Hit] -->Penetrate| --<| --:LowerBody| --=BodyShot|1 --+LowerBody|[$Hit] -->Penetrate| --<| --:RightLeg| --=LimbShot|1 --+RightLeg|[$Hit] -->Penetrate| --<| --:LeftLeg| --=LimbShot|1 --+LeftLeg|[$Hit] -->Penetrate| --<| --:Penetrate| --=Penetrate|1d100 --?[$Penetrate.Base] -le @{selected|bar3}|>Shot Penetrates --?[$Penetrate.Base] -gt @{selected|bar3}|>Shot Doesnt Penetrate --<| --:Shot Penetrates| --=Penetration|1 --+Shot Penetrates|[$Penetrate] --? [$Penetration] -eq 1|>Damage --<| --:Damage| --?[$HeadShot] -eq 1|>hdmg --?[$BodyShot] -eq 1|>bdmg --?[$LimbShot] -eq 1|>ldmg --<| --:hdmg| --=hdmg|1d10 --?[$hdmg.Base] -le 5 |>HDeath --?[$hdmg.Base] -eq 6 |>HBlinded --?[$hdmg.Base] -ge 7 -and [$hdmg.base] -le 8|>HKOed --?[$hdmg.Base] -ge 9|>HSerious --<| --:bdmg| --=bdmg|1d10 --?[$bdmg.Base] -le 4 |>BDeath --?[$bdmg.Base] -ge 5 -and [$bdmg.base] -le 6|>BKOed --?[$bdmg.Base] -ge 7 -and [$bdmg.base] -le 8|>BSerious --?[$bdmg.Base] -ge 9|>BLight --<| --:ldmg| --=ldmg|1d10 --?[$ldmg.Base] -eq 1 |>LDeath --?[$ldmg.Base] -ge 2 -and [$hdmg.base] -le 6|>LSerious --?[$ldmg.Base] -ge 7|>LLight --<| --:HDeath| --+Death|[$hdmg] --<| --:HBlinded| --+Blinded|[$hdmg] --<| --:HKOed| --+KOed|[$hdmg] --<| --:HSerious| --+Serious|[$hdmg] --<| --:BDeath| --+Death|[$bdmg] --<| --:BKOed| --+KOed|[$bdmg] --<| --:BSerious| --+Serious|[$bdmg] --<| --:BLight| --+Light|[$bdmg] --<| --:LDeath| --+Death|[$ldmg] --<| --:LSerious| --+Serious|[$ldmg] --<| --:LLight| --+Light|[$ldmg] --<| --:Shot Doesnt Penetrate| --+Shot Doesnt Penetrate|[$Penetrate] --<| --:Miss| --+[#C00]Miss[/#]|You Miss --<| }}
1617622614
David M.
Pro
API Scripter
Sten, it looks like you have a conditional within your ldmg procedure that does not always resolve likely due to a cut/paste error?  --?[$ ldmg .Base] -ge 2 -and [$ hdmg .base] -le 6|>LSerious I believe the second roll reference in that conditional should be another [$ldmg] instead of [$hdmg].
Thanks David - well spotted! I fixed that and ran again but am still getting inconsistent results - attached is a picture of when I run it for 3 shots First shot works as it should but he 3rd shot doesn't.....Other times when I run it it works fine and can't figure out why....
1617625445

Edited 1617625464
David M.
Pro
API Scripter
You have several places where [$SomeRoll].Base is written as [$SomeRoll].base. I believe roll variable properties are case-sensitive. Might be the issue you are seeing?
Awesome that seems to have fixed the problem - thanks so much. It has however now revealed another issue:)  It now rolls damage every time but it sometimes rolls it twice or three times for the same location
Actually think I have resolved the issue.... changed the damage section from: --:Damage| --?[$HeadShot] -eq 1|>hdmg --?[$BodyShot] -eq 1|>bdmg --?[$LimbShot] -eq 1|>ldmg --<| to: --:Damage| --?[$Hit.Base] -le 10 |>hdmg --?[$Hit.Base] -ge 11 -and [$Hit.Base] -le 20|>ldmg --?[$Hit.Base] -ge 21 -and [$Hit.Base] -le 30|>ldmg --?[$Hit.Base] -ge 31 -and [$Hit.Base] -le 50|>bdmg --?[$Hit.Base] -ge 51 -and [$Hit.Base] -le 70|>bdmg --?[$Hit.Base] -ge 71 -and [$Hit.Base] -le 85|>ldmg --?[$Hit.Base] -ge 86 |>ldmg --<| @David - thanks so much for your help. Really appreciate it!
1617628602

Edited 1617628988
David M.
Pro
API Scripter
EDIT - Cross-posted It's probably because of this code block: --:Damage| --?[$HeadShot] -eq 1|>hdmg --?[$BodyShot] -eq 1|>bdmg --?[$LimbShot] -eq 1|>ldmg --<| Since you never reset the HeadShot, BodyShot, and LimbShot flags, the card "remembers" the flag values from previous shots and potentially executes multiple calls from your Damage procedure. Maybe in your hdmg procedure, reset using --=HeadShot|0 at the end. Similar with the bdmg & ldmg procedures.  
1617656878
Kurt J.
Pro
API Scripter
ScriptCards 1.1.13 is Now Available I found a couple of bugs (like setindex didn't work at all) so I've updated the development GIST with 1.1.13. I also added a new array command: --~myString|array;stringify;arrayname will return the whole array as a string variable, separated by semicolons.
1617826636
David M.
Pro
API Scripter
Looks like all lower case titlefontcolor works :)
Thanks!
Apologies again.  You all are way above my level of understanding but I am trying to get there.  I was trying to figure out passing parameters to a subroutine but didn't have much luck. The two sections used are:  (Disregard the attribute variable.)    --:Damage|   --=WeaponStats|?{Weapon?|Dagger,0|Shortsword,1|Longbow,2}   --?[&WeaponStats] -eq 0|^Hit;Dagger;1d4;slashing   --?[&WeaponStats] -eq 1|^Hit;Shortsword;1d6;slashing   --?[&WeaponStats] -eq 2|^Hit;Longbow;1d8;piercing   --:Hit|   --=Damage|[%2%]    --+Hit!|@{selected|token_name} hits @{target|token_name} with a [%1%] for [$Damage] [%3%] damage. Is the issue that I am trying to pass strings and numeric variables at the same time? Construction of the pass?   TIA
Kurt J. said: Next, we get all of the tokens on the page for the selected token and display their names: !script {{  --~dummy|array;pagetokens;myarray;@{selected|token_id}  --~count|array;getcount;myarray  --+Total:|There are [&count] graphical objects on the page  --~testvar|array;getfirst;myarray  --:loop|  --+Value|[*[&testvar]:character_name]  --~testvar|array;getnext;myarray  --?[&testvar] -ne ArrayError|loop  --+Loop1|Is Done! }} Hi Kurt, How would you adapt your scrit above to only feed your array with selected tokens and not all the tokens present on a page ? Thx in advance (i'm lazy, I could have tried to figure out but i had a few beers at lunch and my brain is screwed-up)! Regards,
1617889680
David M.
Pro
API Scripter
Michael, to call a procedure, you need to use the > operator instead of ^. Try this: --:Damage| --=WeaponStats|?{Weapon?|Dagger,0|Shortsword,1|Longbow,2} --?[&WeaponStats] -eq 0|>Hit;Dagger;1d4;slashing --?[&WeaponStats] -eq 1|>Hit;Shortsword;1d6;slashing --?[&WeaponStats] -eq 2|>Hit;Longbow;1d8;piercing --X| --:Hit| --=Damage|[%2%] --+Hit!|@{selected|token_name} hits @{target|token_name} with a [%1%] for [$Damage] [%3%] damage. --<| Note the --X| line at the end of the macro (before any procedures). Otherwise, the card will keep going down the line and try to run the procedures again, which is not what you want. Also note the --<| which signals the end of the procedure and tells the card to return to the line from which the procedure was called. The card will resume reading line-by-line from that point. 
1617899975

Edited 1617900798
I love this script.  I have been doing some pretty amazing things with it. I have a problem that really isn't a problem but it is showing as an error in the Output Console. This line, and every other one like it --?"[&currentAmmoType]" -inc Regular|>HeavyRegular Gives this error in the Output Console "ScriptCards conditional error: Condition contains an invalid clause joiner. Only -and and -or are supported. Assume results are incorrect." The line works correctly, every time, but it still feels like I am missing something.  This whole type of conditional is a little hazy for me, because the location of the quotes seems to a bit arcane. For the record, the value stored in currentAmmoType in this case would be: Regular P.S. ScriptCards makes Shadowrun so much easier to play online.  It, with ChatSetAttr and TokenMod, automates so much of the crunch.  It has streamlined play sooo much i am not sure I could ever go back to playing it at a table.  Thank you for all your hard work.
1617917037
Kurt J.
Pro
API Scripter
David M. said: Michael, to call a procedure, you need to use the > operator instead of ^. Try this: --:Damage| --=WeaponStats|?{Weapon?|Dagger,0|Shortsword,1|Longbow,2} --?[&WeaponStats] -eq 0|>Hit;Dagger;1d4;slashing --?[&WeaponStats] -eq 1|>Hit;Shortsword;1d6;slashing --?[&WeaponStats] -eq 2|>Hit;Longbow;1d8;piercing --X| --:Hit| --=Damage|[%2%] --+Hit!|@{selected|token_name} hits @{target|token_name} with a [%1%] for [$Damage] [%3%] damage. --<| Note the --X| line at the end of the macro (before any procedures). Otherwise, the card will keep going down the line and try to run the procedures again, which is not what you want. Also note the --<| which signals the end of the procedure and tells the card to return to the line from which the procedure was called. The card will resume reading line-by-line from that point.  Yep, David is spot on. There are two different mechanisms for branching in ScriptCards : Direct Branches (--^ and the default for conditionals) and subroutine branches (--> and prefacing a conditional branch label with the ">" character). Direct Branches don't store anything about where they came from and can't pass parameters, while subroutine branches (you'll catch me calling them "gosub" branches sometimes - a throwback to my old Basic days) keep track of where to go back to and a parameter list.
1617918343
Kurt J.
Pro
API Scripter
Woombak said: Kurt J. said: Next, we get all of the tokens on the page for the selected token and display their names: !script {{  --~dummy|array;pagetokens;myarray;@{selected|token_id}  --~count|array;getcount;myarray  --+Total:|There are [&count] graphical objects on the page  --~testvar|array;getfirst;myarray  --:loop|  --+Value|[*[&testvar]:character_name]  --~testvar|array;getnext;myarray  --?[&testvar] -ne ArrayError|loop  --+Loop1|Is Done! }} Hi Kurt, How would you adapt your scrit above to only feed your array with selected tokens and not all the tokens present on a page ? Thx in advance (i'm lazy, I could have tried to figure out but i had a few beers at lunch and my brain is screwed-up)! Regards, As it stands, it wouldn't be straightforward... you would need to use the getselected string function and then loop through them and add them to the array. So...... ScriptCards v1.1.14 Now Available Up on the Development GIST is a small update that makes the following changes: New array function : selectedtokens:  --~count|array;selectedtokens;myarrayname will return the number of selected tokens as the string variable "count", and create an array called "myarrayname" with the token IDs in it. Updated the behavior of the "pagetokens" array function to also return the number of tokens it found to the assigned string variable. Potentially important change: I found a bug in that arraysand array index values were not being properly cleared between macro executions, meaning that arrays created in one script could potentially be showing up in later scripts. Which could cause issues (like if you were expecting an array to be empty and looking at the length and finding items in it).
1617918855
Kurt J.
Pro
API Scripter
Steve H. said: I love this script.  I have been doing some pretty amazing things with it. I have a problem that really isn't a problem but it is showing as an error in the Output Console. This line, and every other one like it --?"[&currentAmmoType]" -inc Regular|>HeavyRegular Gives this error in the Output Console "ScriptCards conditional error: Condition contains an invalid clause joiner. Only -and and -or are supported. Assume results are incorrect." The line works correctly, every time, but it still feels like I am missing something.  This whole type of conditional is a little hazy for me, because the location of the quotes seems to a bit arcane. For the record, the value stored in currentAmmoType in this case would be: Regular P.S. ScriptCards makes Shadowrun so much easier to play online.  It, with ChatSetAttr and TokenMod, automates so much of the crunch.  It has streamlined play sooo much i am not sure I could ever go back to playing it at a table.  Thank you for all your hard work. Steve,   Can you copy/paste your script here inside a code block? I'm guessing there are some hidden characters somewhere we can't see, because I can only get that behavior if I remove the quotes around the variable and make sure there is a space in the value. If you put your script together in something like Microsoft Word, it is also possible that it is inserting "fancy" quote marks, which would have the same impact. If that is the case, removing the quotes and re-typing them inside the macro editor (I create all of my scripts in something like Notepad++ or Visual Studio Code) would eliminate the problem. - Kurt
Nothing is better than one of those "Aha!!" moments when you put together things you learned once and forgot and then it suddenly makes your current problem go away. The quotes are needed for strings with spaces in them.  I knew this.  I learned this many many moons ago.  But I did not retain this.  The minute you mentioned a space in the value it clicked.  I had one conditional, that I have never even tested, that has a space in the value and I wasn't using quotes on the value.  Suddenly all my other issues cleared up.    Sometimes all it takes is someone saying something off hand to make you see the obvious.   Thanks!
David/Kurt - Ah, that was the key - one can't pass parameters with a goto instruction only gosub.  I was trying to keep from returning from the call but still pass on parameters.  No can do.  Okay, but there is no issue with passing different types of variables then? Very cool.  Every time I get further into this the more versatile it is.  Good on ya, Kurt.  (Thanks for the help too David.)  
1617968165
David M.
Pro
API Scripter
This one is for Kurt and/or Tim: Erik M. posted on the Spawn thread about an issue he was having calling Spawn via SelectManager from a scriptcard. It was working in one card, but not another, with nearly identical api call lines. The error he was getting was this:  (From API):  No selected tokens to use for that command. Please select some tokens then try again. I tracked it down to the presence of an --#targetToken line in the card that caused the error. When I ran a stripped down card with just this line and the --@forselected call, I was able to replicate Erik's error msg. When I remove the line below, it works.  --#targetToken|@{target|token_id} So this results in the error: !scriptcard {{  --#targetToken|@{target|token_id}  --@forselected|Spawn _name|GenericSpellAoE _targets|1 _side|1 _order|toFront }}  ...while this runs fine: !scriptcard {{  --@forselected|Spawn _name|GenericSpellAoE _targets|1 _side|1 _order|toFront }}  For some reason, the -#targetToken line appears to interfere with whatever voodoo SelectManager & Scriptcards uses to communicate with each other regarding the selected token. Any thoughts?
1617983980

Edited 1617984045
Ack!  Still having issues with the card for a weapon attack passing parameters to a subroutine.  I'm sure it's something stupid I am missing...The program raises no errors but fails to pass to the Damage subroutine.  Where I indicated --+FullStop in bold is where the program ends without the hit or crit displaying or the target being altered.  Both the fumble and the miss are displayed when their subroutine is called. !scriptcard  {{   --#title|Weapon Attack   --#sourceToken|@{selected|token_id}   --#targetToken|@{target|token_id}   --#emoteText|[*S:character_name] attacks [*T:character_name]   --#titleCardFont|#ffff00   --#titleCardBackground|#90BB2B   --#evenRowBackground|#3D4E06   --#evenRowFontColor|#ffff00   --#oddRowBackground|#C2DB20   --#oddRowFontColor|#267150   --:AbilityAssign|   --&Attribute|?{Attribute|Strength|Dexterity|Intelligence|Wisdom|Charisma}   --=Strength|@{selected|strength_mod}   --=Dexterity|@{selected|dexterity_mod}   --=Intelligence|@{selected|intelligence_mod}   --=Wisdom|@{selected|wisdom_mod}   --=Charisma|@{selected|charisma_mod}   --:Attack|   --=TargetAC|@{target|npc_ac}   --?[$TargetAC.Total] -gt 0|DoneWithAC   --=TargetAC|@{target|ac}   --:DoneWithAC|   --&RollType|?{Advantage or Disadvantage?|Normal,1d20|Advantage,2d20kh1|Disadvantage,2d20kl1}   --=AttackRoll|[&RollType] [BASE] + [$[&Attribute]] [MOD] + @{selected|pb} [PROF] + @{selected|global_attack_mod} [GLOBE]   --+Attack|@{selected|character_name} rolls [$AttackRoll] vs AC [$TargetAC].   --?[$AttackRoll.Base] -eq 1|Fumble   --?[$AttackRoll.Total] -ge [$TargetAC.Total]|>Damage   --+Miss|The attack missed.   --X|   --:Fumble|   --+Fumble!|The attack went horribly wrong.   --X|   --:Damage|   --=WeaponStats|?{Weapon?|Dagger,0|Shortsword,1|Longbow,2}   --?[$WeaponStats] -eq 0|>Hit;Dagger;1d4;slashing   --?[$WeaponStats]-eq 1|>Hit;Shortsword;1d6;slashing   --?[$WeaponStats]-eq 2|>Hit;Longbow;1d8;piercing   --+Full Stop|   --X|     --:Hit|   --?[$AttackRoll.Base] -eq 20|Crit   --&DamageType|[%2%]   --=Damage| [$[&DamageType]] + [$[&Attribute]] [MOD] + @{selected|global_damage_mod_roll} [GLOBE]   --+Hit!|[*S:token_name] hits [*T:token_name] with a [%1%] for [$Damage] [%3%] damage.   --@alter|_target|@{target|token_id} _bar|1 _amount|-[$Damage]   --X|   --:Crit|   --&DamageType|[%2%]   --=Damage| [$[&DamageType]] * 2 + [$[&Attribute]] [MOD] + @{selected|global_damage_mod_roll} * 2 [GLOBE]    --+Critical Hit!|[*S:token_name] hits [*T:token_name] with a [%1%] for [$Damage] [%3%] damage.   --@alter|_target|@{target|token_id} _bar|1 _amount|-[$Damage]   --X| }} TIA
1617984550
David M.
Pro
API Scripter
Michael, the last line of each of your procedures should be --<|, not --X|. The --X| tells the script to stop parsing. You'll want to have a --X| before your group of procedures, but each procedure should end with --<|, which tells the parser to return to the line from which the procedure was called in the primary section of your scriptcard. 
1617985484

Edited 1617986013
Michael, I think there might be a couple problems —:Hit| should have parameters after it, to reference the ones you sent  And these two lines   --?[$WeaponStats]-eq 1|>Hit;Shortsword;1d6;slashing   --?[$WeaponStats]-eq 2|>Hit;Longbow;1d8;piercing There needs to be a space between [$WeaponStats] and -eq. Like David said your procedures are called and ended inconsistently, but I think you do want them to end with X|.  You just need to not call them with >.  I am not sure it causes any issues with the way a you have it set up, but it could later with other procedures that you want to behave differently. 
1617985900

Edited 1617987388
David M.
Pro
API Scripter
EDIT - Edited the card below. Combined the hit and crit into a single procedure. This was a little strange in the way it was set up initially (usually you keep your hit/miss/fumble/crit conditionals all together, but I think I retained your approach below. Good catch with the -eq lines, Steve! Also, a few rolls were not actually rolls since --& was used instead of --=. Try this, fully proceduralized: !scriptcard {{ --#title|Weapon Attack --#sourceToken|@{selected|token_id} --#targetToken|@{target|token_id} --#emoteText|[*S:character_name] attacks [*T:character_name] --#titleCardFont|#ffff00 --#titleCardBackground|#90BB2B --#evenRowBackground|#3D4E06 --#evenRowFontColor|#ffff00 --#oddRowBackground|#C2DB20 --#oddRowFontColor|#267150 --:AbilityAssign| --&Attribute|?{Attribute|Strength|Dexterity|Intelligence|Wisdom|Charisma} --=Strength|@{selected|strength_mod} --=Dexterity|@{selected|dexterity_mod} --=Intelligence|@{selected|intelligence_mod} --=Wisdom|@{selected|wisdom_mod} --=Charisma|@{selected|charisma_mod} --:Get target Stats| --=TargetAC|@{target|npc_ac} --?[$TargetAC.Total] -gt 0|DoneWithAC --=TargetAC|@{target|ac} --:DoneWithAC| --:Roll Attack| --&RollType|?{Advantage or Disadvantage?|Normal,1d20|Advantage,2d20kh1|Disadvantage,2d20kl1} --=AttackRoll|[&RollType] [BASE] + [$[&Attribute]] [MOD] + @{selected|pb} [PROF] + @{selected|global_attack_mod} [GLOBE] --+Attack|@{selected|character_name} rolls [$AttackRoll] vs AC [$TargetAC]. --:Determine results| --?[$AttackRoll.Base] -eq 1|>Fumble --?[$AttackRoll.Total] -ge [$TargetAC.Total]|>Damage --?[$AttackRoll.Total] -lt [$TargetAC.Total]|>Miss --X|End macro --:PROCEDURES:| --:Damage| --=WeaponStats|?{Weapon?|Dagger,0|Shortsword,1|Longbow,2} --?[$WeaponStats] -eq 0|>HitOrCrit;Dagger;1d4;slashing --?[$WeaponStats] -eq 1|>HitOrCrit;Shortsword;1d6;slashing --?[$WeaponStats] -eq 2|>HitOrCrit;Longbow;1d8;piercing --<| --:Fumble| --+Fumble!|The attack went horribly wrong. --<| --:Miss| --+Miss|The attack missed. --<| --:HitOrCrit| --?[$AttackRoll.Base] -eq 20|Crit --Hit:| --=BaseDamage|[%2%] --=Damage| [$BaseDamage] + [$[&Attribute]] [MOD] + @{selected|global_damage_mod_roll} [GLOBE] --+Hit!|[*S:character_name] hits [*T:character_name] with a [%1%] for [$Damage] [%3%] damage. --@alter|_target|@{target|token_id} _bar|1 _amount|-[$Damage] --X| --:Crit| --=BaseDamage|[%2%] --=Damage| [$BaseDamage] * 2 + [$[&Attribute]] [MOD] +@{selected|global_damage_mod_roll} * 2 [GLOBE] --+Critical Hit!|[*S:character_name] hits [*T:character_name] with a [%1%] for [$Damage] [%3%] damage --@alter|_target|@{target|token_id} _bar|1 _amount|-[$Damage] --<| }}
1617986673
Kurt J.
Pro
API Scripter
David M. said: This one is for Kurt and/or Tim: Erik M. posted on the Spawn thread about an issue he was having calling Spawn via SelectManager from a scriptcard. It was working in one card, but not another, with nearly identical api call lines. The error he was getting was this:  (From API):  No selected tokens to use for that command. Please select some tokens then try again. I tracked it down to the presence of an --#targetToken line in the card that caused the error. When I ran a stripped down card with just this line and the --@forselected call, I was able to replicate Erik's error msg. When I remove the line below, it works.  --#targetToken|@{target|token_id} So this results in the error: !scriptcard {{  --#targetToken|@{target|token_id}  --@forselected|Spawn _name|GenericSpellAoE _targets|1 _side|1 _order|toFront }}  ...while this runs fine: !scriptcard {{  --@forselected|Spawn _name|GenericSpellAoE _targets|1 _side|1 _order|toFront }}  For some reason, the -#targetToken line appears to interfere with whatever voodoo SelectManager & Scriptcards uses to communicate with each other regarding the selected token. Any thoughts? To make things weirder, I was playing around with a script I was testing for a "bag of tricks" that would check for unoccupied spaces in which to spawn the new tokens. Sometimes it works fine, and then sometimes I get the error above (which I tracked down to coming from SelectManager). I'm not using targeting at all, and about 40% of the time (just arrowing up and hitting enter again) I get the "no tokens selected" error, with it firing normally the other 60% of the time. I don't know how SeletManager does its magic, but there seems to be instances where it doesn't always pick up the selected tokens to pass along. One solution would be to add a way to specify the source token on the command line for --Spawn, but (while I'd like to see that!) it would only solve the issue for this particular case (calling Spawn from ScriptCards).
1617987708
David M.
Pro
API Scripter
Thanks Kurt. Weird, indeed. I PM'd Tim about it - he may have some insight.
1617989013
David M.
Pro
API Scripter
Michael, here's another approach to your card that keeps all the hit/miss/fumble/crit logic together. Uses a SetWeapon procedure to assign string variables used in later procedures. I also noticed that the crit damage was calculating incorrectly above due to the left-to-right mathematical order of operations that scriptcards uses (rather than PEDMAS), so I re-ordered the modifiers to use a single x2 multiplier.  !scriptcard {{ --#title|Weapon Attack --#sourceToken|@{selected|token_id} --#targetToken|@{target|token_id} --#emoteText|[*S:character_name] attacks [*T:character_name] --#titleCardFont|#ffff00 --#titleCardBackground|#90BB2B --#evenRowBackground|#3D4E06 --#evenRowFontColor|#ffff00 --#oddRowBackground|#C2DB20 --#oddRowFontColor|#267150 --:AbilityAssign| --&Attribute|?{Attribute|Strength|Dexterity|Intelligence|Wisdom|Charisma} --=Strength|@{selected|strength_mod} --=Dexterity|@{selected|dexterity_mod} --=Intelligence|@{selected|intelligence_mod} --=Wisdom|@{selected|wisdom_mod} --=Charisma|@{selected|charisma_mod} --:Attack| --=TargetAC|@{target|npc_ac} --?[$TargetAC.Total] -gt 0|DoneWithAC --=TargetAC|@{target|ac} --:DoneWithAC| --:Roll Attack| --&RollType|?{Advantage or Disadvantage?|Normal,1d20|Advantage,2d20kh1|Disadvantage,2d20kl1} --=AttackRoll|[&RollType] [BASE] + [$[&Attribute]] [MOD] + @{selected|pb} [PROF] + @{selected|global_attack_mod} [GLOBE] --+Attack|@{selected|character_name} rolls [$AttackRoll] vs AC [$TargetAC]. --:Set Weapon Info| --=WeaponStats|?{Weapon?|Dagger,0|Shortsword,1|Longbow,2} --?[$WeaponStats] -eq 0|>SetWeapon;Dagger;1d4;slashing --?[$WeaponStats] -eq 1|>SetWeapon;Shortsword;1d6;slashing --?[$WeaponStats] -eq 2|>SetWeapon;Longbow;1d8;piercing --:Determine results| --?[$AttackRoll.Base] -eq 1|>Fumble --?[$AttackRoll.Total] -lt [$TargetAC.Total]|>Miss --?[$AttackRoll.Total] -ge [$TargetAC.Total]|>Hit;[&WeaponDmg] --?[$AttackRoll.Base] -eq 20|>Crit;[&WeaponDmg] --X|End macro --:PROCEDURES:| --:SetWeapon| pass weapon, dmg, dmg_type --&WeaponName|[%1%] --&WeaponDmg|[%2%] --&DamageType|[%3%] --<| --:Fumble| --+Fumble!|The attack went horribly wrong. --<| --:Miss| --+Miss|The attack missed. --<| --:Hit| pass WeaponDmg string --=Damage| [%1%] [BASE] + [$[&Attribute]] [MOD] + @{selected|global_damage_mod_roll} [GLOBE] --+Hit!|[*S:character_name] hits [*T:character_name] with a [&WeaponName] for [$Damage] [&DamageType] damage. --@alter|_target|@{target|token_id} _bar|1 _amount|-[$Damage] --<| --:Crit| pass WeaponDmg string --=Damage| [%1%] [BASE] + @{selected|global_damage_mod_roll} [GLOBE] * 2 + [$[&Attribute]] [MOD] --+Critical Hit!|[*S:character_name] hits [*T:character_name] with a [&WeaponName] for [$Damage] [&DamageType] damage. --@alter|_target|@{target|token_id} _bar|1 _amount|-[$Damage] --<| }}
1617991568
timmaugh
Pro
API Scripter
Kurt J. said: David M. said: This one is for Kurt and/or Tim: Erik M. posted on the Spawn thread about an issue he was having calling Spawn via SelectManager from a scriptcard. It was working in one card, but not another, with nearly identical api call lines. The error he was getting was this:  (From API):  No selected tokens to use for that command. Please select some tokens then try again. I tracked it down to the presence of an --#targetToken line in the card that caused the error. When I ran a stripped down card with just this line and the --@forselected call, I was able to replicate Erik's error msg. When I remove the line below, it works.  --#targetToken|@{target|token_id} *--SNIP--* I don't know how SeletManager does its magic, but there seems to be instances where it doesn't always pick up the selected tokens to pass along. One solution would be to add a way to specify the source token on the command line for --Spawn, but (while I'd like to see that!) it would only solve the issue for this particular case (calling Spawn from ScriptCards). So, that message is definitely coming from SelectManager. What's happening is that SM is always tracks the *last* user message to grab the selected property (as well as who and playerid). In this case, it's bumping up against the problem where a message with an @{target|item} call doesn't have a selected property, so it thinks there aren't any tokens over which to iterate. I have a workaround for this I'm about to release as part of Meta Toolbox... basically SelectManager is getting an {&inject... }  formation where you can backfill even for a message where you've used a targeting statement (or where you just don't want to take the time to select all of the tokens). You can manually type the tokens you want, or you can write them to another meta variable (not a ScriptCards variable, since this is all preprocessing before SC gets the message). In this way, the GM could have a variable for the party, and instead of selecting 8 different tokens, she would have a variable referring to those tokens. Her command line would include the inject using the variable, and auto-magically populate the selected array. She could have a variable for the elves of the party, or those with mental telepathy... or the commoners of a village... and then pick that from a drop down query at the time of executing the command. I won't go into too much detail about the feature so that the thread can still stay topical to ScriptCards, but I wanted to explain enough to let people know that a workaround is coming from the direction of SelectManager... ...I just have to bolt-on a reporting interface. =D
Thanks to David & Steve!  (David - "A little strange" is a kind way to typify my programming.)  :-)  Steve's (good!) catch, the "--X|", "--&" and other issues were caused by my attempting to get the macro to work in different ways, track which procedure was causing the issue and not cleaning up the code before posting it.  Turned into a lot of errors. :-(  Sorry, my bad.  However, David's macro does work fine. On the other hand, the SetWeapon procedure has me confused.  I see that SetWeapon receives the three parameters and assigns all three to a different string variable then returns to the calling instruction.  It then passes &WeaponDmg roll to the Hit or Crit subroutine. Got it.  Some questions of construction though: 1.  Is the word "pass" a required instruction or a tag to help the programmer? (e.g. would  "--:SetWeapon| weapon, dmg, dmg_type"  work just as well?)  Does it matter what names are used at the receiving SetWeapon subroutine? (*e.g. " pass Ralph, Edward, George"  rather than " pass weapon, dmg, dmg_type ".)  In other words, are they just "holding" the passed parameters to be assigned to the string variables? 2.  The Hot and Crit subroutines accepted parameters as "   --:Hit|  pass WeaponDmg string".  Is  "string"  also a parameter instruction defining  WeaponDmg  or could it just have said " --:Hit| pass [&Weapondmg] " ? (or --:Hit| [&Weapondmg]? ) 3.   &WeaponName and  &DamageType   were not passed as parameters to the Hit or Crit subroutines but still used by them.  Was  Weapondmg  a required parameter to be passed?  Could one just substitute [&Weapondmg]  for the  [%1%] ? (Or would that be  [$[&Weapondmg]] ?) 4.  The dmg tag is not a numeric variable but a string. So, "1d4" etc. rolls are handled as strings rather than numbers? 5.  No parentheses or brackets are needed (or effective?) to calculate math formulas as the left-to-right calculations effectively sum everything to the left of the next math instruction?  (The wiki said that but I guess it didn't sink in until now.)  :-) Thanks!
Michael C. said: 1.  Is the word "pass" a required instruction or a tag to help the programmer? (e.g. would  "--:SetWeapon| weapon, dmg, dmg_type"  work just as well?)  Does it matter what names are used at the receiving SetWeapon subroutine? (*e.g. " pass Ralph, Edward, George"  rather than " pass weapon, dmg, dmg_type ".)  They can be anything.  I am pretty sure your initial subroutines would work with —:Hit|weapon, dmg, type (with the other previously mentioned fixes) You would also need that on —:Crit|
1618001293
David M.
Pro
API Scripter
Re: 1&2 - Anything to the right of the | in a line header is ignored by the parser, so I only added that text as a comment to make it clear what was being passed into those particular procedures. You can literally type anything in there and it will be ignored. 3 - Yes, it would probably be clearer and more consistent to do as you said and not pass anything to the hit and crit procedures, just using the [&WeaponDmg} variable, like this. You would also edit the conditional calls to the hit & crit procs to no longer pass the variable.   --:Hit| --=Damage| [&WeaponDmg] [BASE] + [$[&Attribute]] [MOD] + @{selected|global_damage_mod_roll} [GLOBE] --+Hit!|[*S:character_name] hits [*T:character_name] with a [&WeaponName] for [$Damage] [&DamageType] damage. --@alter|_target|@{target|token_id} _bar|1 _amount|-[$Damage] --<| 4 - If you were to hardcode a roll, it would look like --=VarName|1d4. We only need to use a string variable for &WeaponDmg  because we want the parser to use the text "1d4" when defining the Roll variable. If we made WeaponDmg a roll variable instead of a string variable, we would have had to reference [$WeaponDmg.Text], because we don't want to pass a value of a roll, but the text.  5 - Haven't messed with much where the order of operations mattered, but seems so :) Like Steve was implying, there is more than one way to skin a cat. I was just trying to demonstrate using procedures because you asked. If you kept all the --X's at the end of your initial "procedures", they wouldn't really be a need for them to be procedures. You could have turned them into branches (goto's) instead. Also perfectly valid! 
Okay!  The light is beginning to glow in the nether regions of my mind.  Thank you both for the input.  :-)
1618008553
Kurt J.
Pro
API Scripter
Michael C. said: 5.  No parentheses or brackets are needed (or effective?) to calculate math formulas as the left-to-right calculations effectively sum everything to the left of the next math instruction?  (The wiki said that but I guess it didn't sink in until now.)  :-) Thanks! Correct. The roll parser maintains a running value that is updated after each portion of an operation takes place. Behind the scenes, I store a "current operator" value which defaults to addition. Order of operations is not taken into account, just moving left to right across the roll evaluation. So while you might expect: --=Test|2 + 2 + 10 * 2 to be 24 (10 * 2 first, then +2 and +2) you will actually get 28 (2 + 2 = 4 + 10 = 14 * 2). One way to think of it is that you are using a calculator and hitting the equals button after every part of the calculation. Another side effect of the tracking of a current operator is that it can lead to some things that might look a bit odd like: --=Test|2 4 5 6 10 Will actually result in a value of 30 (because the addition is implied). Encountering an operator changes the current operator value, so: --=Test|10 8 - 6 4 2 will be 6, because it will add 10 and 8, and then subtract 6, 4, and 2 One quirk (that I should probably fix at some point) is that a negative number will also update the current operator, so: --=Test|2 4 6 -8 10 will be -6 (add 2, 4, and 6, then subtract 8 and 10. Because of this, I don't recommend leaving out operators, but you could technically do so any time you weren't going to have negative numbers.