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

1621092212
Kurt J.
Pro
API Scripter
Lionel T. said: should do the trick ? --&OpCheck|@{selected|class_1_name} @{selected|class_2_name} @{selected|class_3_name} @{selected|class_4_name} @{selected|class_5_name} --?[&OpCheck] -inc Operative|TrickAttack That would work, but you would need to put double quotes around [&OpCheck] since it will contain spaces.
Could someone explain to me what data statements, for-next loops, and conditional blocks would do that other existing Scriptcard functions couldn't? With maybe a practical example? Trying to wrap my head around this but I am not a coder. :)
1621111716

Edited 1621112814
Kurt J.
Pro
API Scripter
Jay R. said: Could someone explain to me what data statements, for-next loops, and conditional blocks would do that other existing Scriptcard functions couldn't? With maybe a practical example? Trying to wrap my head around this but I am not a coder. :) It isn't really about things that were impossible in ScriptCards prior, but more about making the language easier to use and understand. Here is an example of a script I've had hanging around for a long time that outputs a simple health display for the party: !scriptcard {{   --#title|Party Status   --#titleCardBackground|#33CC33   --#tableBGColor|#AAEEAA   -->HealthCheck|@{Quej Grastra|character_id}   -->HealthCheck|@{Brim Cheuto|character_id}   -->HealthCheck|@{Odug Ututees|character_id}   --X|End of card Execution   --:HealthCheck|   --=HealthPerc|[*[%1%]:hp] / [*[%1%]:hp^] * 100 \ 1   --=HealthColor|000000   --?[$HealthPerc.Total] -gt 50|HealthSkip   --=HealthColor|FF0000   --:HealthSkip|   --+[*[%1%]:character_name]|has [#[$HealthColor.RollText]][*[%1%]:hp] of [*[%1%]:hp^][/#] HP [R]([$HealthPerc.Total]%)[/R]   --<|End of HealthCheck }} This was one of the very early Scripts I wrote for ScriptCards, and as such doesn't use any of the language features that have been added to ScriptCards since the very early days. I just rewrote this with loops, non-branch conditionals, and data statements like this: !script {{ --#title|Party Status --#titleCardBackground|#33CC33 --#tableBGColor|#AAEEAA --%charLoop|1;20;1 --dcharId| --?"[&charId]" -eq "EndOfDataError"|%! -->DisplayCharacter|[&charId] --%| --X| --:DisplayCharacter|character_id --=HealthPerc|[*[%1%]:hp] / [*[%1%]:hp^] * 100 \ 1 --?[$HealthPerc] -gt 50|&HealthColor;000000|&HealthColor;FF0000 --+[*[%1%]:character_name]|has [#[&HealthColor]][*[%1%]:hp] of [*[%1%]:hp^][/#] HP [R]([$HealthPerc.Total]%)[/R] --<| --/|Character IDs for each PC --d!|"-LRXezam7DzGpyujp-jN";"-LRNuvj1Z8PeU-UcNVFx";"-LRNwKdKpiIR8TtYL1rr" }} It is a little shorter if you discount the added blank lines for readability, and that is with only three PCs. More importantly (to me) it is much more readable. There are several things changed here, but here are the major ones: By using a For...Next loop, I don't have to have a separate call to HealthCheck for each character, an by reading them from the data statement at the end of the script, if I add players, or a character dies, etc. I only have to update the data statement. It was possible to create loops manually with branches before, but this makes it a bit more like a traditional programming language. In a real game, I would the data statement(s) into a handout and include it as a template whenever I wanted to use the character IDs in my scripts - that way if I have 500 ScriptCards and I add/replace a character I just need to update it in the handout and it will work everywhere. I picked 20 iterations for the For...Next loop, but that was just a number larger than the number of characters I plan on having. It could be 1000. The %! in the conditional will break out of the loop if we hit EndOfDataError. Calculation of the Health Percentage remains the same, but non-branching conditionals allow me to set the HealthColor variable directly on the conditional line instead of having to create a new label skip over if I don't want to change the color. The original card was also before the inclusion of String variables, which make more sense here since then we don't have to add .RollText to everything. The script above doesn't use statement blocks, but here is an example of one that does: !script {{ --&attackDice|1d20 --?"@{selected|rtype}" -inc "{advantage"|&attackDice;2d20kh1 --?"@{selected|rtype}" -inc "{disadvantage"|&attackDice;2d20kl1 --=AttackRoll|[&attackDice] [BASE] + @{selected|strength_mod} [STR] + @{selected|pb} [PROF] --+Attack Roll|[$AttackRoll] vs AC @{target|npc_ac} --?[$AttackRoll] -ge @{target|npc_ac} -and [$AttackRoll.Base] -ne 20 -and [$AttackRoll.Base] -ne 1|[ --=Damage|1d8 + @{selected|strength_mod} [STR] --+Hit!|@{selected|character_name} hit @{target|character_name} for [$Damage] HP --]| --?[$AttackRoll.Base] -eq 20|[ --=Damage|2d8 + @{selected|strength_mod} [STR] --+Critical Hit!|@{selected|character_name} hit @{target|character_name} for [$Damage] HP --]| --?[$AttackRoll] -lt @{target|npc_ac} -and [$AttackRoll.Base] -ne 20 -and [$AttackRoll.Base] -ne 1|[ --+Miss!|@{selected|character_name} attacks @{target|character_name}, but misses --]| --?[$AttackRoll.Base] -eq 1|[ --=Fumble|[T#fumble-table] --+Critical Fumble!|@{selected|character_name} attempted to attack @{target|character_name} but something went horribly wrong! --+|[$Fumble.tableEntryText] --]| }} Without statement blocks, each of the different sections would need to have a label to branch to, and then have to worry about resuming somewhere after the damage report (assuming you wanted to do something else. With statement blocks, each conditional can have any number of statements that get executed if the conditional is matched without branching away and back, so the code is easier to follow (especially with indenting showing where the blocked begin and end). EDIT: Updated attack roll example to include handling advantage/disadvantage/normal and character attributes to make it a generally more useful example.
1621112741

Edited 1621112763
David M.
Pro
API Scripter
Lionel T. said: should do the trick ? --&OpCheck|@{selected|class_1_name} @{selected|class_2_name} @{selected|class_3_name} @{selected|class_4_name} @{selected|class_5_name} --?[&OpCheck] -inc Operative|TrickAttack @Lionel, That's the way I started to approach this, as well. However, the OP still needs to isolate the relevant class level for display purposes (see the last two lines of the TrickAttack block). That's why I thought a single loop might be the better solution, with the incremental loop index pulling double duty for the class_n_name and class_n_level sheet attributes.
Ok, Let me say in advance that I'm sorry for the space that this post is going to take up, but I need to provide examples of something I'm having a problem with using Scriptcards. First, let me state the problem. When I use Kurt's Magic Missile script on one creature, it subtracts damage from all creatures of the same top on the tabletop. So I figured I would test it out to see if it was something I was doing wrong. I set up 4 Bandits on the tabletop and then used  Kurt's MM script (listed below). When it did damage to one Bandit, it subtracted damage from all Bandits.  !scriptcard {{ --#title|Magic Missile --#sourceToken|@{selected|token_id} -->GetAndCheckSlotInformation| --=MissileCount|[$SlotLevel] + 2 -->BuildAndAskTargets|[$MissileCount.Total] --=DisplayCount|1 --=MissileDamage|0 --#leftsub|Slot level [$SlotLevel] --#rightsub|Ranged Attack --#emoteText|@{selected|character_name} uses a level [$SlotLevel.Total] spell slot to fire [$MissileCount.Total] missiles of magical force! --:MissileLoop| -->FireMissile|[$DisplayCount.Total] --=DisplayCount|[$DisplayCount] + 1 --?[$DisplayCount] -le [$MissileCount]|MissileLoop --+Total|Total damage is [$MissileDamage] -->DeductSpellSlot| --#rightsub|Level [$SlotLevel] Left: [$SlotsRemaining] --X| --:FireMissile| --&ThisTarget|[&target[%1%]] --=ThisMissile|1d4 + 1 --=MissileDamage|[$MissileDamage] + [$ThisMissile] --+Missile|[$DisplayCount.Total] Hits [*[&ThisTarget]:character_name] for [$ThisMissile] [b]force[/b] damage -->PlayEffects|@{selected|token_id};[&ThisTarget];none;burst-smoke;beam-magic;spell_01 -->ApplyDamageTokenmod|[*[&ThisTarget]:character_id];3;-[$ThisMissile.Total] --<| --:GetAndCheckSlotInformation| --=SlotLevel|?{Spell Slot Level?|1|2|3|4|5|6|7|8|9} --=SlotsTotal|[*S:lvl[$SlotLevel]_slots_total] --=SlotsExpended|[*S:lvl[$SlotLevel]_slots_expended] --?[$SlotsExpended.Total] -ge [$SlotsTotal.Total]|NoSlotsLeft --<| --:NoSlotsLeft| --+|[*S:character_name] has no level [$SlotLevel.Total] spell slots available. --X| --:DeductSpellSlot| --=SlotsExpended|[$SlotsExpended] + 1 --@setattr|_charid [*S:character_id] _lvl[$SlotLevel]_slots_expended|[$SlotsExpended] _silent --=SlotsRemaining|[$SlotsTotal] - [$SlotsExpended] --<| --:PlayEffects|Parameters are : sourcetoken; targettoken; source effect; target effect; line effect; sound effect --vtoken|[%1%] [%3%] --vtoken|[%2%] [%4%] --vbetweentokens|[%1%] [%2%] [%5%] --@roll20AM|_audio,play,nomenu|[%6%] --<| --:ApplyDamageTokenmod|Parameters are tokenid;bar#;amount --@token-mod|_ignore-selected _ids [%1%] _set bar[%2%]_value|[%3%] --<| --:ApplyDamageAlterbars| --@alter|_target|[%1%] _bar|[%2%] _amount|[%3%] --<| --:BuildAndAskTargets| --&TargetString| --=targetCount|1 --:TargetLoop| --&TargetString|+t;target[$targetCount.Total];Missile [$targetCount.Total] Target --=targetCount|[$targetCount.Total] + 1 --?[$targetCount.Total] -le [%1%]|>AddSeparator --?[$targetCount.Total] -le [%1%]|TargetLoop --iPlease click the button below to select magic missile targets. The same target can be selected multiple times;Select [%1%] Targets|[&TargetString] --/| --<| --:AddSeparator| --&TargetString|+|| --<| }} So I decided to do a test with a script for Thunderwave to see if I was doing something wrong. Once again, I set up 4 Bandits and used the below Thunderwave Script on one of them. It worked perfectly, and only subtracted damage from the Bandit I attacked. Looking at both scripts, I just don't understand why the Magic Missile script takes damage from all the Bandits when I just attack one, but the Thunderwave script doesn't. They both use the same Tokenmod command.  !scriptcard {{ --#title|🌀Thunderwave 🌀 --#sourceToken|@{selected|token_id} --#leftsub|1st Level --#rightsub|V S : Range 15' Cube --#titleFontColor|#FFFFFF --#titleCardBackground|#000066 --#evenRowBackground|#0000FF --#evenRowFontColor|#BDC3C7 --#oddRowBackground|#000066 --#oddRowFontColor|#BDC3C7 --/|Get a spell slot level from the caster. --=SpellLevel|?{Spell Slot Level?|1|2|3|4|5|6|7|8|9} --+|[c][b]Save DC @{selected|spell_save_dc} * Slot Level: [/b][$SpellLevel][/c] -->GetAndCheckSlotInformation| --/|Calculate damage based on spell slot. Thunderwave is 2d8 for 1st level, so 1+ SpellLevel d8 total. --=DamageDice|[$SpellLevel.Total] + 1 --=Damage|[$DamageDice.Total]d8 --=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;@{selected|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 casting token and the current array token. 15 feet is 3 units --~dist|distance;@{selected|token_id};[&tokenid] --?[$dist] -eq 0 -or [$dist] -gt 3|continue --/|If we didn't skip over this part, the token is within 10 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];constitution;@{selected|spell_save_dc};[$Damage.Total];thunder;thisTokenDamage;saveResult;con --+[*[&tokenid]:character_name]:|Save [$savingThrow] [r][$thisTokenDamage] thunder damage.[/r] -->ApplyDamageTokenmod|[&tokenid];3;[$thisTokenDamage] --/|Put a burst-magic visual effect on impacted tokens --vtoken|[&tokenid] burst-water --/|Get the next token and continue the loop until we run out. --~tokenid|array;getnext;inRange --?[&tokenid] -ne ArrayError|loopDisplay --:endOutput| -->DeductSpellSlot| --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 replaced with alterbars or chatsetattr --:ApplyDamageTokenmod|Parameters are tokenid;bar#;amount --@token-mod|_ignore-selected _ids [%1%] _set bar[%2%]_value|-[%3%] --<| --:GetAndCheckSlotInformation| --=SlotLevel|[$SpellLevel] --=SlotsTotal|[*S:lvl[$SlotLevel]_slots_total] --=SlotsExpended|[*S:lvl[$SlotLevel]_slots_expended] --?[$SlotsExpended.Total] -eq 0|NoSlotsLeft --?[$SlotsExpended.Total] -gt [$SlotsTotal.Total]|NoSlotsLeft --@roll20AM|_audio,play,nomenu|Thunder-Crash --<| --:DeductSpellSlot| --=SlotsExpended|[$SlotsExpended] -1 --@setattr|_charid [*S:character_id] _lvl[$SlotLevel]_slots_expended|[$SlotsExpended] _silent --=SlotsRemaining|[$SlotsTotal] - 1 --+|Level [$SpellLevel.Total] Spell Slots Left: [$SlotsExpended] --X| --:NoSlotsLeft| --+|[b][*S:character_name] has no level [$SlotLevel.Total] spell slots available.[/b] --X|NoSlotsLeftStop }}
Created a test game, and that code worked fine for me (once I figured out how to create a character). I know it is a strange question, but I noticed that class just gets typed in by the player. Are you sure the spelling on the character sheet (including capitalization) is correct? Is there a space before the class name on the character sheet, etc? Sorry, Kurt, I didn't see this response.  It was completely operator error.  I was trying to use it on a NPC, which doesn't have the same attributes.  Once I clicked the right person, it did work.
1621119789

Edited 1621119982
I'm nearing completion of my suite of DM and NPC Tools version 2.  I hope to post the code and associated API scripts here soon.  Here's a bit of a tease showing the various dialogs and reports. The first set (Blue) are designed to be player character centric and make it easier to manage Player Characters (from a DM/GM point of view).  In the center of this image is the top level form called "DM Tools", and from it, you navigate to all the other DM Tools.   The second set (Green) are designed to manage your monsters, NPCs and encounters. In the center of this image is the top level form called "NPC Tools", and from it, you navigate to all the other NPC Tools.   The third set is a menu system with for all my utility macros. My favorite features: clicking on a character or NPC, will move the map and ping the character, there are quick options to move tokens between the GM and Objects layers, and finally, the ability to quickly review and set UDL settings for the page and player characters.   I just need to figure out how to post my Macro Mule Character Sheet where all my macro code is saved so others can easily use this. I'll post future updates to the Scriptcard API Working and Sharing forum. DM Tools  NPC Tools Utility Macros
@DM Goss - this may be a stupid-answer, I-know-that-already, no-brainer but depending upon how your bring a token on to the page may cause this problem if they are multiples of the same NPC sheet.  I have had this problem several times that had nothing to do with the scripts I was using but that the game linked all of them together so that what happened to one happened to all. 
A weird one using conditionals... Here is a sample of a lib function...    --~WeaponDmgStat|string;replace;@{;;[&WeaponDamageStat]    --~WeaponDmgStat|string;replace;};;[&WeaponDmgStat]    --~WeaponDmgStat|string;replace;dex;Dexterity;[&WeaponDmgStat]    --~WeaponDmgStat|string;replace;str;Strengh;[&WeaponDmgStat] --+Damage stat for weapon:|***[&WeaponDmgStat]***    --=DmgStat|0    --? "[&WeaponDmgStat]" -inc "Strength" |[       --+debug:| str alone       --=DmgStat|[*S:str-mod]    --]|    --? "[&WeaponDmgStat]" -inc "Strength" -and "[&WeaponDmgStat]" -inc "floor" |[       --+debug:| half str       --=DmgStat|[*S:str-mod] \ 2    --]|    --? "[&WeaponDmgStat]" -inc "Strength" -and "[&WeaponDmgStat]" -inc "+floor" |[       --+debug:| treehalves str       --=DmgStat| [*S:str-mod] \ 2 + [*S:str-mod]    --]|    --? [&WeaponDmgStat] -inc "Dexterity " -and "1" -eq "1" |[       --+debug:| dex alone       --=DmgStat|[*S:dex-mod]    --]|      --+DmgStat|[$DmgStat]    --=Damages| [&WeaponDamage] + [$DmgStat] [STAT] + [$WeaponEnh] [ENH] + [$WeaponSpecialize] [WSF]    --+Damages:| [$Damages] After displaying in the console with the log function; in the evaluateConditional the full conditional, left operand, right operand and the operator, i'm running the block where the &WeaponDmgStat value is "Dexterity-mod". "conditional :  \"Dexterity-mod \" -inc \"Strength\" <span style=\"line-height: normal ; color: #000000\">" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"Strength\"***" "ScriptCards conditional error: Condition contains an invalid clause joiner. Only -and and -or are supported. Assume results are incorrect.  \"Dexterity-mod \" -inc \"Strength\" <span style=\"line-height: normal ; color: #000000\">" "conditional :  \"Dexterity-mod \" -inc \"Strength\" -and \"Dexterity-mod \" -inc \"floor\"" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"Strength\"***" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"floor\"***" "conditional :  \"Dexterity-mod \" -inc \"Strength\" -and \"Dexterity-mod \" -inc \"+floor\"" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"Strength\"***" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"+floor\"***" "conditional :  Dexterity-mod  -inc \"Dexterity<span style=\"line-height: normal ; color: #000000\">\" -and \"1\" -eq \"1\"" "Left ***Dexterity-mod***" "operator ***-inc***" "Right ***\"Dexterity<span style=\"line-height:***" "ScriptCards conditional error: Condition contains an invalid clause joiner. Only -and and -or are supported. Assume results are incorrect.  Dexterity-mod  -inc \"Dexterity<span style=\"line-height: normal ; color: #000000\">\" -and \"1\" -eq \"1\"" "Left ***;***" "operator ***color:***" "Right ***#000000\">\"***" "Left ***\"1\"***" "operator ***-eq***" "Right ***\"1\"***"    --? "[&WeaponDmgStat]" -inc "Strength" |[ <- do not work    --? "[&WeaponDmgStat]" -inc "Strength" -and "[&WeaponDmgStat]" -inc "floor" |[ <- okay    --? "[&WeaponDmgStat]" -inc "Strength" -and "[&WeaponDmgStat]" -inc "+floor" |[ <- okay    --? "[&WeaponDmgStat]" -inc "Dexterity " -and "1" -eq "1" |[ <- do not work. Am i fuckin' dumb ? Why do i have the span included in the right operand ? And not in the okay statements ? I'm going crazy about it  :oF
1621124025
Kurt J.
Pro
API Scripter
Lionel T. said: A weird one using conditionals... Here is a sample of a lib function...    --~WeaponDmgStat|string;replace;@{;;[&WeaponDamageStat]    --~WeaponDmgStat|string;replace;};;[&WeaponDmgStat]    --~WeaponDmgStat|string;replace;dex;Dexterity;[&WeaponDmgStat]    --~WeaponDmgStat|string;replace;str;Strengh;[&WeaponDmgStat] --+Damage stat for weapon:|***[&WeaponDmgStat]***    --=DmgStat|0    --? "[&WeaponDmgStat]" -inc "Strength" |[       --+debug:| str alone       --=DmgStat|[*S:str-mod]    --]|    --? "[&WeaponDmgStat]" -inc "Strength" -and "[&WeaponDmgStat]" -inc "floor" |[       --+debug:| half str       --=DmgStat|[*S:str-mod] \ 2    --]|    --? "[&WeaponDmgStat]" -inc "Strength" -and "[&WeaponDmgStat]" -inc "+floor" |[       --+debug:| treehalves str       --=DmgStat| [*S:str-mod] \ 2 + [*S:str-mod]    --]|    --? [&WeaponDmgStat] -inc "Dexterity " -and "1" -eq "1" |[       --+debug:| dex alone       --=DmgStat|[*S:dex-mod]    --]|      --+DmgStat|[$DmgStat]    --=Damages| [&WeaponDamage] + [$DmgStat] [STAT] + [$WeaponEnh] [ENH] + [$WeaponSpecialize] [WSF]    --+Damages:| [$Damages] After displaying in the console with the log function; in the evaluateConditional the full conditional, left operand, right operand and the operator, i'm running the block where the &WeaponDmgStat value is "Dexterity-mod". "conditional :  \"Dexterity-mod \" -inc \"Strength\" <span style=\"line-height: normal ; color: #000000\">" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"Strength\"***" "ScriptCards conditional error: Condition contains an invalid clause joiner. Only -and and -or are supported. Assume results are incorrect.  \"Dexterity-mod \" -inc \"Strength\" <span style=\"line-height: normal ; color: #000000\">" "conditional :  \"Dexterity-mod \" -inc \"Strength\" -and \"Dexterity-mod \" -inc \"floor\"" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"Strength\"***" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"floor\"***" "conditional :  \"Dexterity-mod \" -inc \"Strength\" -and \"Dexterity-mod \" -inc \"+floor\"" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"Strength\"***" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"+floor\"***" "conditional :  Dexterity-mod  -inc \"Dexterity<span style=\"line-height: normal ; color: #000000\">\" -and \"1\" -eq \"1\"" "Left ***Dexterity-mod***" "operator ***-inc***" "Right ***\"Dexterity<span style=\"line-height:***" "ScriptCards conditional error: Condition contains an invalid clause joiner. Only -and and -or are supported. Assume results are incorrect.  Dexterity-mod  -inc \"Dexterity<span style=\"line-height: normal ; color: #000000\">\" -and \"1\" -eq \"1\"" "Left ***;***" "operator ***color:***" "Right ***#000000\">\"***" "Left ***\"1\"***" "operator ***-eq***" "Right ***\"1\"***" How is WeaponDmgStat getting its initial value before the string replacements are happening?
1621124083
Kurt J.
Pro
API Scripter
Will M. said: I'm nearing completion of my suite of DM and NPC Tools version 2.  I hope to post the code and associated API scripts here soon.  Here's a bit of a tease showing the various dialogs and reports. The first set (Blue) are designed to be player character centric and make it easier to manage Player Characters (from a DM/GM point of view).  In the center of this image is the top level form called "DM Tools", and from it, you navigate to all the other DM Tools.   The second set (Green) are designed to manage your monsters, NPCs and encounters. In the center of this image is the top level form called "NPC Tools", and from it, you navigate to all the other NPC Tools.   The third set is a menu system with for all my utility macros. My favorite features: clicking on a character or NPC, will move the map and ping the character, there are quick options to move tokens between the GM and Objects layers, and finally, the ability to quickly review and set UDL settings for the page and player characters.   I just need to figure out how to post my Macro Mule Character Sheet where all my macro code is saved so others can easily use this. I'll post future updates to the Scriptcard API Working and Sharing forum. DM Tools  NPC Tools Utility Macros Will, that is pretty amazing :) Can't wait to see it.
Will, I am drooling over all your scriptcards, especially the NPC management. Can't wait to see the new version!
&WeaponDamageStat is set in another sub (which was surely call) : --/|---------------------------------------------------------------------------- --/| GetWeaponStats - param : primary_hand | off_hand --/|  * if primary hand, using weapon9 attributes in character sheet --/|  * if off hand, using weapon10 attributes in character sheet --/|---------------------------------------------------------------------------- --:Lib35E_Combat_GetWeaponStats|    --? [%1%] -eq primary_hand|GetWeaponStatsPrimaryHand    --? [%1%] -eq off_hand|GetWeaponStatsOffHand    --:GetWeaponStatsPrimaryHand|       --&WeaponDamage|[*S:weapon9damage]       --&WeaponName|[*S:weapon9name]       --&WeaponType|[*S:weapon9type]       --=WeaponRange|[*S:weapon9range]       --=WeaponCritMin|[*S:weapon9critmin]       --=WeaponEnh|[*S:weapon9enh]       --=WeaponFocus|[*S:weapon9focus]       --=WeaponSpecialize|[*S:weapon9specialize]       --&WeaponDamageStat|[*S:weapon9damagestat]       --&WeaponSpecialProperties|[*S:weapon9specialproperties]       --^EndGetWeaponStats| Kurt J. said: Lionel T. said: A weird one using conditionals... Here is a sample of a lib function...    --~WeaponDmgStat|string;replace;@{;;[&WeaponDamageStat]    --~WeaponDmgStat|string;replace;};;[&WeaponDmgStat]    --~WeaponDmgStat|string;replace;dex;Dexterity;[&WeaponDmgStat]    --~WeaponDmgStat|string;replace;str;Strengh;[&WeaponDmgStat] --+Damage stat for weapon:|***[&WeaponDmgStat]***    --=DmgStat|0    --? "[&WeaponDmgStat]" -inc "Strength" |[       --+debug:| str alone       --=DmgStat|[*S:str-mod]    --]|    --? "[&WeaponDmgStat]" -inc "Strength" -and "[&WeaponDmgStat]" -inc "floor" |[       --+debug:| half str       --=DmgStat|[*S:str-mod] \ 2    --]|    --? "[&WeaponDmgStat]" -inc "Strength" -and "[&WeaponDmgStat]" -inc "+floor" |[       --+debug:| treehalves str       --=DmgStat| [*S:str-mod] \ 2 + [*S:str-mod]    --]|    --? [&WeaponDmgStat] -inc "Dexterity " -and "1" -eq "1" |[       --+debug:| dex alone       --=DmgStat|[*S:dex-mod]    --]|      --+DmgStat|[$DmgStat]    --=Damages| [&WeaponDamage] + [$DmgStat] [STAT] + [$WeaponEnh] [ENH] + [$WeaponSpecialize] [WSF]    --+Damages:| [$Damages] After displaying in the console with the log function; in the evaluateConditional the full conditional, left operand, right operand and the operator, i'm running the block where the &WeaponDmgStat value is "Dexterity-mod". "conditional :  \"Dexterity-mod \" -inc \"Strength\" <span style=\"line-height: normal ; color: #000000\">" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"Strength\"***" "ScriptCards conditional error: Condition contains an invalid clause joiner. Only -and and -or are supported. Assume results are incorrect.  \"Dexterity-mod \" -inc \"Strength\" <span style=\"line-height: normal ; color: #000000\">" "conditional :  \"Dexterity-mod \" -inc \"Strength\" -and \"Dexterity-mod \" -inc \"floor\"" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"Strength\"***" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"floor\"***" "conditional :  \"Dexterity-mod \" -inc \"Strength\" -and \"Dexterity-mod \" -inc \"+floor\"" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"Strength\"***" "Left ***\"Dexterity-mod \"***" "operator ***-inc***" "Right ***\"+floor\"***" "conditional :  Dexterity-mod  -inc \"Dexterity<span style=\"line-height: normal ; color: #000000\">\" -and \"1\" -eq \"1\"" "Left ***Dexterity-mod***" "operator ***-inc***" "Right ***\"Dexterity<span style=\"line-height:***" "ScriptCards conditional error: Condition contains an invalid clause joiner. Only -and and -or are supported. Assume results are incorrect.  Dexterity-mod  -inc \"Dexterity<span style=\"line-height: normal ; color: #000000\">\" -and \"1\" -eq \"1\"" "Left ***;***" "operator ***color:***" "Right ***#000000\">\"***" "Left ***\"1\"***" "operator ***-eq***" "Right ***\"1\"***" How is WeaponDmgStat getting its initial value before the string replacements are happening?
1621157124

Edited 1621157186
Kurt J.
Pro
API Scripter
Lionel T. said: &WeaponDamageStat is set in another sub (which was surely call) : After some experimentation, I think it all boils down to this line: --~WeaponDmgStat|string;replace;@{;;[&WeaponDamageStat] The chat server thinks the @{ is beginning an attribute reference, which is completed on the following line and doesn't have any idea what to look for. If I break that line up into two separate lines: --~WeaponDmgStat|string;replace;@;;[&WeaponDamageStat] --~WeaponDmgStat|string;replace;{;;[&WeaponDmgStat] It seems to behave for men - though there is some junk thrown in the console because it is attempting to dereference the @{str-mod} that is contained in the field. It does seem to work, though.
1621169145

Edited 1621176789
I thought first you had the good guess. It seems there was a "non visible" character in the conditional. After i dropped the line and write it again, it worked like a charm. gngngngm. Kurt J. said: After some experimentation, I think it all boils down to this line: --~WeaponDmgStat|string;replace;@{;;[&WeaponDamageStat] The chat server thinks the @{ is beginning an attribute reference, which is completed on the following line and doesn't have any idea what to look for. If I break that line up into two separate lines: --~WeaponDmgStat|string;replace;@;;[&WeaponDamageStat] --~WeaponDmgStat|string;replace;{;;[&WeaponDmgStat] It seems to behave for men - though there is some junk thrown in the console because it is attempting to dereference the @{str-mod} that is contained in the field. It does seem to work, though.
Would it be ... possible to add in the function list  - "string;replaceAll;foo;bar" (replace multiple occurences of foo by bar) - "string;trim;x"                    (trim character x, of blank by default) ?
Can any of you Scriptcard Wizards develop an Initiative scriptcard that respects the "toggle advantage on Initiative" option? Not sure how to get the init tracker to pop up.
1621190669

Edited 1621190737
Kurt J.
Pro
API Scripter
Jay R. said: Can any of you Scriptcard Wizards develop an Initiative scriptcard that respects the "toggle advantage on Initiative" option? Not sure how to get the init tracker to pop up. Here is a ScriptCard with comments that should work for both PCs and NPCs that will take the initiative style setting on the character sheet settings page into account. At the moment, ScriptCards doesn't have a mechanism to pop open the turn tracker window, so the DM would need to do this manually. !script {{ --/|Roll Initiative taking initiative_style into account --#title|Initiative --#sourcetoken|@{selected|token_id} --/|Assume it will be a Normal roll --&rollType|1d20 --#leftsub|Normal --/|If "kh1" is in the initiative_style attribute, rolling with advantage --?"[*S:initiative_style]" -inc "kh1"|[ --&rollType|2d20kh1 --#leftsub|Advantage --]| --/|If "kl1" is in the initiative_style attribute, rolling with disadvantage --?"[*S:initiative_style]" -inc "kl1"|[ --&rollType|2d20kl1 --#leftsub|Disadvantage --]| --#rightsub|[*S:initiative_bonus] Modifier --/|Roll the initiative roll and add the characters initiative modifier --=InitRoll|[&rollType] + [*S:initiative_bonus] --/|Add the roll to the turn order (replace for this token if already there) --~|turnorder;replacetoken;@{selected|token_id};[$InitRoll.Total] --/|Report out the results --+|@{selected|token_name} rolls [$InitRoll] for initiative }}
1621191834
Kurt J.
Pro
API Scripter
Lionel T. said: Would it be ... possible to add in the function list  - "string;replaceAll;foo;bar" (replace multiple occurences of foo by bar) - "string;trim;x"                    (trim character x, of blank by default) ? These are both pretty easy. I'll add them for the next release.
Kurt J. said: Jay R. said: Can any of you Scriptcard Wizards develop an Initiative scriptcard that respects the "toggle advantage on Initiative" option? Not sure how to get the init tracker to pop up. Here is a ScriptCard with comments that should work for both PCs and NPCs that will take the initiative style setting on the character sheet settings page into account. At the moment, ScriptCards doesn't have a mechanism to pop open the turn tracker window, so the DM would need to do this manually. !script {{ --/|Roll Initiative taking initiative_style into account --#title|Initiative --#sourcetoken|@{selected|token_id} --/|Assume it will be a Normal roll --&rollType|1d20 --#leftsub|Normal --/|If "kh1" is in the initiative_style attribute, rolling with advantage --?"[*S:initiative_style]" -inc "kh1"|[ --&rollType|2d20kh1 --#leftsub|Advantage --]| --/|If "kl1" is in the initiative_style attribute, rolling with disadvantage --?"[*S:initiative_style]" -inc "kl1"|[ --&rollType|2d20kl1 --#leftsub|Disadvantage --]| --#rightsub|[*S:initiative_bonus] Modifier --/|Roll the initiative roll and add the characters initiative modifier --=InitRoll|[&rollType] + [*S:initiative_bonus] --/|Add the roll to the turn order (replace for this token if already there) --~|turnorder;replacetoken;@{selected|token_id};[$InitRoll.Total] --/|Report out the results --+|@{selected|token_name} rolls [$InitRoll] for initiative }} Thanks so much for this, Kurt! I've made a couple of aesthetic changes and it works great. I realize that I don't need the turn tracker to pop up since I have to run Combat Master anyway for at least one monster's initiative, and CM pops up the tracker on its own.
A thought experiment for any who are willing.  Kurt J.s fireball spell works wonders for an AOE spell that extends equally out from the center point.  However, what about a directional spell such as lightning bolt?  Would there be a way to restrict an attack to targets on a line, cone or other geometric shape?  (As opposed to just manually selecting targets such as in Kurt's Magic Missile spell.)
Michael C. said: A thought experiment for any who are willing.  Kurt J.s fireball spell works wonders for an AOE spell that extends equally out from the center point.  However, what about a directional spell such as lightning bolt?  Would there be a way to restrict an attack to targets on a line, cone or other geometric shape?  (As opposed to just manually selecting targets such as in Kurt's Magic Missile spell.) I think it would be possible if you had 4 pieces of information; Origin point (@selected), Direction, Shape(line, cone, sphere/circle), Size.  Of the 4, direction is the most problematic.  Assuming the origin point is the Selected token, then you could develop a test for each shape (Line or Cone) of various sizes assuming you had a direction.  I don't think there is a way right now to capture a  specific point clicked on the map, unless it is the target of a token, which may limit you to two possible ways to get a direction (that I can think of): Query the caster for a 360 degree direction, maybe a dropdown list with 0,45,90,....,315 as options. Spawn a spell target utility token, place it on the map to indicate direction and location of the point then do the basic line calcs to determine who is in the area of effect and who isn't.  You may have to write this as two scripts, a Spawn Target script and a Cast Spell.   Then all you need is to do the simple cone and line math to determine if a token would be in the area of effect. 
@Will M.  Good suggestions.  Hadn't thought of the drop down query for degree of direction but I had thought of the idea of using the source-to-target utility token as a directional control - I do use utility tokens for targeting AOE spells - but the problem remains of how to test for any targets along the line or between the delineations of the geometric shape since the only reference points are the source and target.  (Of course, the easiest expedient is to just have the player select all targets within the line or shape manually.)  
1621204754
David M.
Pro
API Scripter
Having just implemented cones in the latest version of my Radar script, I'm going to go out on a limb and say this would be pretty tedious to accomplish totally within a scriptcard, especially if it is going to handle cones, lines, and bursts. Bursts are easy (already done with fireball scriptcard). For cones, you need to calculate the critical angles using direction and cone "width", then check to see if the token is within range and that the angle to the target token falls within the start &amp; end critical angles (possibly converting one or the other for cases where endAngle &gt; startAngle, like a 90deg cone pointing straight up at direction 0degrees - start angle is 315 and endAngle is 45. example: 350deg needs to be "between" these two numbers). Should be able to handle negative angle input as well, for ease of use.&nbsp; For line effects, sounds like you would need to define a rectangle and use a "pointInPolygon" function. Just checking x&amp;y against an orthogonal bounding box is insufficient if you allow non-orthogonal direction angles. If you're really interested, here is the js approach I used in the Radar script for that, using some matrix math. Point "P" is an object defined by&nbsp; const pt = function(x,y) { this.x = x, this.y = y }; Polygon "polygon" is just an array of point objects using the above definition. Here's the function: const isPointInPolygon = function(P, polygon) { const between = (p, a, b) =&gt; p &gt;= a &amp;&amp; p &lt;= b || p &lt;= a &amp;&amp; p &gt;= b; let inside = false; for (let i = polygon.length-1, j = 0; j &lt; polygon.length; i = j, j++) { const A = polygon[i]; const B = polygon[j]; // corner cases if (P.x == A.x &amp;&amp; P.y == A.y || P.x == B.x &amp;&amp; P.y == B.y) return true; if (A.y == B.y &amp;&amp; P.y == A.y &amp;&amp; between(P.x, A.x, B.x)) return true; if (between(P.y, A.y, B.y)) { // if P inside the vertical range // filter out "ray pass vertex" problem by treating the line a little lower if (P.y == A.y &amp;&amp; B.y &gt;= A.y || P.y == B.y &amp;&amp; A.y &gt;= B.y) continue // calc cross product `PA X PB`, P lays on left side of AB if c &gt; 0 const c = (A.x - P.x) * (B.y - P.y) - (B.x - P.x) * (A.y - P.y) if (c == 0) return true; if ((A.y &lt; B.y) == (c &gt; 0)) inside = !inside } } return inside? true: false; } This was adapted from the following link:&nbsp; <a href="https://stackoverflow.com/posts/63436180/revisions" rel="nofollow">https://stackoverflow.com/posts/63436180/revisions</a>
1621206267
Kurt J.
Pro
API Scripter
David M. said: Having just implemented cones in the latest version of my Radar script, I'm going to go out on a limb and say this would be pretty tedious to accomplish totally within a scriptcard, especially if it is going to handle cones, lines, and bursts. Bursts are easy (already done with fireball scriptcard). For cones, you need to calculate the critical angles using direction and cone "width", then check to see if the token is within range and that the angle to the target token falls within the start &amp; end critical angles (possibly converting one or the other for cases where endAngle &gt; startAngle, like a 90deg cone pointing straight up at direction 0degrees - start angle is 315 and endAngle is 45. example: 350deg needs to be "between" these two numbers). Should be able to handle negative angle input as well, for ease of use.&nbsp; For line effects, sounds like you would need to define a rectangle and use a "pointInPolygon" function. Just checking x&amp;y against an orthogonal bounding box is insufficient if you allow non-orthogonal direction angles. If you're really interested, here is the js approach I used in the Radar script for that, using some matrix math. Point "P" is an object defined by&nbsp; const pt = function(x,y) { this.x = x, this.y = y }; Polygon "polygon" is just an array of point objects using the above definition. Here's the function: const isPointInPolygon = function(P, polygon) { const between = (p, a, b) =&gt; p &gt;= a &amp;&amp; p &lt;= b || p &lt;= a &amp;&amp; p &gt;= b; let inside = false; for (let i = polygon.length-1, j = 0; j &lt; polygon.length; i = j, j++) { const A = polygon[i]; const B = polygon[j]; // corner cases if (P.x == A.x &amp;&amp; P.y == A.y || P.x == B.x &amp;&amp; P.y == B.y) return true; if (A.y == B.y &amp;&amp; P.y == A.y &amp;&amp; between(P.x, A.x, B.x)) return true; if (between(P.y, A.y, B.y)) { // if P inside the vertical range // filter out "ray pass vertex" problem by treating the line a little lower if (P.y == A.y &amp;&amp; B.y &gt;= A.y || P.y == B.y &amp;&amp; A.y &gt;= B.y) continue // calc cross product `PA X PB`, P lays on left side of AB if c &gt; 0 const c = (A.x - P.x) * (B.y - P.y) - (B.x - P.x) * (A.y - P.y) if (c == 0) return true; if ((A.y &lt; B.y) == (c &gt; 0)) inside = !inside } } return inside? true: false; } This was adapted from the following link:&nbsp; <a href="https://stackoverflow.com/posts/63436180/revisions" rel="nofollow">https://stackoverflow.com/posts/63436180/revisions</a> I started implementing the Bresenham line drawing algorithm in ScriptCards. It not too bad, though I need to track down a bug in my logic :) If/when I get it working I'll post it.
Trying to get the character summary function in the Tools library to work after copying the library code into a handout from the git. Still not working and I get the following error in the API window: "ScriptCards Error: Label Lib5E_Character_Summary is not defined on line 4" Can anyone advise? !script {{ +++5E Tools+++ --#leftsub|@{selected|token_name} --#sourceToken|@{selected|token_id} --#emoteText|*@{selected|token_name} checks character sheet.* --&gt;Lib5E_Character_Summary|character_id --X| }}
1621208728
Kurt J.
Pro
API Scripter
Jay R. said: Trying to get the character summary function in the Tools library to work after copying the library code into a handout from the git. Still not working and I get the following error in the API window: "ScriptCards Error: Label Lib5E_Character_Summary is not defined on line 4" Can anyone advise? !script {{ +++5E Tools+++ --#leftsub|@{selected|token_name} --#sourceToken|@{selected|token_id} --#emoteText|*@{selected|token_name} checks character sheet.* --&gt;Lib5E_Character_Summary|character_id --X| }} Is you handout named " ScriptCards Library 5E Tools" (case sensitive)?
Kurt J. said: Jay R. said: Trying to get the character summary function in the Tools library to work after copying the library code into a handout from the git. Still not working and I get the following error in the API window: "ScriptCards Error: Label Lib5E_Character_Summary is not defined on line 4" Can anyone advise? !script {{ +++5E Tools+++ --#leftsub|@{selected|token_name} --#sourceToken|@{selected|token_id} --#emoteText|*@{selected|token_name} checks character sheet.* --&gt;Lib5E_Character_Summary|character_id --X| }} Is you handout named " ScriptCards Library 5E Tools" (case sensitive)? Lousy uppercase 'C' messed me up. :) Thanks, Kurt. Yes, the scriptcard runs now, but doesn't output anything useful. Looks like it thinks that the character is an npc when it's in fact a pc. ScriptCards Nnedi Emezi Str &nbsp;[ character_id:strength] ([ character_id:strength_mod]) Dex &nbsp;[ character_id:dexterity] ([ character_id:dexterity_mod]) Con &nbsp;[ character_id:constitution] ([ character_id:constitution_mod]) Int &nbsp;[ character_id:intelligence] ([ character_id:intelligence_mod]) Wis &nbsp;[ character_id:wisdom] ([ character_id:wisdom_mod]) Cha &nbsp;[ character_id:charisma] ([ character_id:charisma_mod]) AC &nbsp;[ character_id:npc_ac] HP [ character_id:hp]/[ character_id:hp^] CR &nbsp;[ character_id:npc_challenge] Type &nbsp;[ character_id:npc_type] Vulnerable &nbsp;[character_id:npc_vulnerabilities] Resistant &nbsp;[ character_id:npc_resistances] Immune &nbsp;[character_id:npc_immunities] Cond. Immune &nbsp;[ character_id:npc_condition_immunities] Cond. Immune &nbsp;[character_id:npc_senses]
1621209650
Kurt J.
Pro
API Scripter
Got my line algorithm to work :) Here is a sample of what it does right now, but a couple of things to note: I realized I need to add a couple of math functions to the function library (like ABS) :) The code I have here is doing it by string replacing the negative sign with nothing, but that isn't clean. Multiplying by negative 1 wasn't working, so I'll have to investigate that as well. For this example, I added a new visual effects option to allow me to show something on the impacted squares. This isn't in the public code for ScriptCards anywhere yet, so while you can run the script below you won't see the acid explosions. All that is left to make this work for line spells now is to build the list of tokens on the page (just like the Fireball script) and use each point on the line to look for matching tokens. That should be pretty easy, especially with the loops in place now ;) I capped it at 50 squares total, but it could easily be increased by changing that number. Here is the GIF: And here is the current code: !script {{ --#sourcetoken|@{selected|token_id} --#targettoken|@{target|token_id} --=X1|[*S:t-left] \ 70 --=Y1|[*S:t-top] \ 70 --=X2|[*T:t-left] \ 70 --=Y2|[*T:t-top] \ 70 --?[$X1] -lt [$X2]|=SX;1|=SX;-1 --?[$Y1] -lt [$Y2]|=SY;1|=SY;-1 --=DX|[$X2] - [$X1] --~dx|string;replace;-;;[$DX.Total] --=DX|[&amp;dx] --=DY|[$Y2] - [$Y1] --~dy|string;replace;-;;[$DY.Total] --=DY|[&amp;dy] --=ERR|[$DX] - [$DY] --=Y|[$Y1.Total] --=X|[$X1.Total] --vbetweentokens|@{selected|token_id} @{target|token_id} beam-magic --%L|1;50 --+Square|[$X],[$Y] --=VX|[$X] * 70 + 35 --=VY|[$Y] * 70 + 35 --vpoint|[$VX] [$VY] burn-acid --?[$X.Total] -eq [$X2.Total] -and [$Y.Total] -eq [$Y2.Total]|%! --=E2|[$ERR.Total] * 2 --?[$E2] -gt [$DY]|[ --=ERR|[$ERR] - [$DY] --=X|[$X] + [$SX] --]| --?[$E2] -lt [$DX]|[ --=ERR|[$ERR] + [$DX] --=Y|[$Y] + [$SY] --]| --%| }} Cones are going to be more tricky, but it might be possible to adapt this by running a line to the terminus of the cone, then doing a line 90 degrees in either direction for the end width of the cone, then tracking those lines back to the origin. Filling in the gaps might be a little challenging, but probably not too hard.
1621209761
Kurt J.
Pro
API Scripter
Jay R. said: Kurt J. said: Jay R. said: Trying to get the character summary function in the Tools library to work after copying the library code into a handout from the git. Still not working and I get the following error in the API window: "ScriptCards Error: Label Lib5E_Character_Summary is not defined on line 4" Can anyone advise? !script {{ +++5E Tools+++ --#leftsub|@{selected|token_name} --#sourceToken|@{selected|token_id} --#emoteText|*@{selected|token_name} checks character sheet.* --&gt;Lib5E_Character_Summary|character_id --X| }} Is you handout named " ScriptCards Library 5E Tools" (case sensitive)? Lousy uppercase 'C' messed me up. :) Thanks, Kurt. Yes, the scriptcard runs now, but doesn't output anything useful. Looks like it thinks that the character is an npc when it's in fact a pc. ScriptCards Nnedi Emezi Str &nbsp;[ character_id:strength] ([ character_id:strength_mod]) Dex &nbsp;[ character_id:dexterity] ([ character_id:dexterity_mod]) Con &nbsp;[ character_id:constitution] ([ character_id:constitution_mod]) Int &nbsp;[ character_id:intelligence] ([ character_id:intelligence_mod]) Wis &nbsp;[ character_id:wisdom] ([ character_id:wisdom_mod]) Cha &nbsp;[ character_id:charisma] ([ character_id:charisma_mod]) AC &nbsp;[ character_id:npc_ac] HP [ character_id:hp]/[ character_id:hp^] CR &nbsp;[ character_id:npc_challenge] Type &nbsp;[ character_id:npc_type] Vulnerable &nbsp;[character_id:npc_vulnerabilities] Resistant &nbsp;[ character_id:npc_resistances] Immune &nbsp;[character_id:npc_immunities] Cond. Immune &nbsp;[ character_id:npc_condition_immunities] Cond. Immune &nbsp;[character_id:npc_senses] The call to the library needs to pass a character ID :) Doing it this way: --&gt;Lib5E_Character_Summary|character_id is literally passing the text "character_id" :) Try this: --&gt;Lib5E_Character_Summary|@{selected|character_id}
Kurt J. said: Jay R. said: Kurt J. said: Jay R. said: Trying to get the character summary function in the Tools library to work after copying the library code into a handout from the git. Still not working and I get the following error in the API window: "ScriptCards Error: Label Lib5E_Character_Summary is not defined on line 4" Can anyone advise? !script {{ +++5E Tools+++ --#leftsub|@{selected|token_name} --#sourceToken|@{selected|token_id} --#emoteText|*@{selected|token_name} checks character sheet.* --&gt;Lib5E_Character_Summary|character_id --X| }} Is you handout named " ScriptCards Library 5E Tools" (case sensitive)? Lousy uppercase 'C' messed me up. :) Thanks, Kurt. Yes, the scriptcard runs now, but doesn't output anything useful. Looks like it thinks that the character is an npc when it's in fact a pc. ScriptCards Nnedi Emezi Str &nbsp;[ character_id:strength] ([ character_id:strength_mod]) Dex &nbsp;[ character_id:dexterity] ([ character_id:dexterity_mod]) Con &nbsp;[ character_id:constitution] ([ character_id:constitution_mod]) Int &nbsp;[ character_id:intelligence] ([ character_id:intelligence_mod]) Wis &nbsp;[ character_id:wisdom] ([ character_id:wisdom_mod]) Cha &nbsp;[ character_id:charisma] ([ character_id:charisma_mod]) AC &nbsp;[ character_id:npc_ac] HP [ character_id:hp]/[ character_id:hp^] CR &nbsp;[ character_id:npc_challenge] Type &nbsp;[ character_id:npc_type] Vulnerable &nbsp;[character_id:npc_vulnerabilities] Resistant &nbsp;[ character_id:npc_resistances] Immune &nbsp;[character_id:npc_immunities] Cond. Immune &nbsp;[ character_id:npc_condition_immunities] Cond. Immune &nbsp;[character_id:npc_senses] The call to the library needs to pass a character ID :) Doing it this way: --&gt;Lib5E_Character_Summary|character_id is literally passing the text "character_id" :) Try this: --&gt;Lib5E_Character_Summary|@{selected|character_id} *smacks head* Thanks, Kurt! :) Off to try that now.
1621220157
David M.
Pro
API Scripter
Hey Kurt, thanks for sharing that slick Bresenham algorithm. I'd never heard of it before - pretty efficient. For some reason(**) I was thinking most of the&nbsp; line spells were 10ft wide (hence my polygon comments), but double checked and I was clearly mistaken. Very nice! **The only line spell my 5e players have is Gust of Wind, which is 10ft wide. Must have been where I got it :)
@Kurt/Dave/Jay - think I started something.&nbsp; :-)&nbsp; Kurt - I checked it out and the script did as foretold. It's a masterpiece and I dub thee "Braniac."&nbsp; I have been reading through it and I must admit it out of my pay grade.&nbsp; There were a couple areas where I didn't follow the logic: 1.&nbsp;&nbsp; --?[$E2] -gt [$DY]|[ --=ERR|[$ERR] - [$DY] --=X|[$X] + [$SX] --]| seems to be a conditional branch that is calling a gosub " [ --=ERR|[$ERR] - [$DY] --=X|[$X] + [$SX] --] " which doesn't make sense to me. 2.&nbsp; --%&nbsp; ?? I checked the wiki and it isn't there.&nbsp; Loop? 3.&nbsp; I assume that t-left and t-top are the X/Y coordinates&nbsp;of the tokens but why divide by 70?&nbsp; The size of each page is adjustable, one of the issues I had been trying to think through. 4.&nbsp; I can see that&nbsp; --+Square|[$X],[$Y]&nbsp; checks off every "square" along the line but how would one then test for the presence of a token at that point? Just one more idiotic thought: I hadn't considered this until Kurt's script but since the --vbetweentokens function literally does what the thought experiment intended (at least for lines ), is it possible to use the internal logic of the function for this purpose rather than reinvent the wheel ?&nbsp;&nbsp; My head hurts.&nbsp; I'm going to bed.&nbsp; ;-)&nbsp; Thanks, all, for working on this.
1621222044
David M.
Pro
API Scripter
Michael, check out the&nbsp; forum post &nbsp;that first introduced the new for...loop and conditional block syntax with v1.2.7. If the conditions are met, then the variables --=Err and --=X are set directly. This is relatively new, but I don't recall which version started supporting direct variable assignment. The [&nbsp; &nbsp;--]| syntax denotes a conditional code block. "If true, then "do all the stuff" within those delimiters.&nbsp; The&nbsp; --%&nbsp; signals the end of the for loop that started with&nbsp; --%L|1;50.&nbsp; The divide by 70 assumes a default cell width of 1 square = 70x70 pixels. The top and left token properties are given in pixels, so you divide by 70 to get "squares". If you had a different grid snapping increment, you'd have to account for this by adjusting the formula. It's late, not touching this tonight ;)
1621246516

Edited 1621247004
Kurt J.
Pro
API Scripter
David M. said: Michael, check out the&nbsp; forum post &nbsp;that first introduced the new for...loop and conditional block syntax with v1.2.7. If the conditions are met, then the variables --=Err and --=X are set directly. This is relatively new, but I don't recall which version started supporting direct variable assignment. The [&nbsp; &nbsp;--]| syntax denotes a conditional code block. "If true, then "do all the stuff" within those delimiters.&nbsp; The&nbsp; --%&nbsp; signals the end of the for loop that started with&nbsp; --%L|1;50.&nbsp; The divide by 70 assumes a default cell width of 1 square = 70x70 pixels. The top and left token properties are given in pixels, so you divide by 70 to get "squares". If you had a different grid snapping increment, you'd have to account for this by adjusting the formula. It's late, not touching this tonight ;) With the new [*P:] notation it should be easy enough to replace to 70s. I never modify the grid size, so I just used 70 to get things rolling here. Here is the next iteration. This does the same thing as above, but this time it calls the "checkTokens" subroutine and builds an array that contains all of the tokens that are impacted by the line. &nbsp;This isn't perfect &nbsp;yet, because tokens larger than 1x1 aren't taken into account (it will only register a "hit" if the line passes through the top left corner of the large token). I'll have to come up with a method for addressing that. Right now, of course, all it does is return the token IDs that were hit as an array. Actually doing something with them would be dependent on the spell being cast, so there isn't anything implemented there. I also drop the caster's token from the hit list. !script {{ --#sourcetoken|@{selected|token_id} --#targettoken|@{target|token_id} --~|array;pagetokens;alltokens;@{target|token_id} --~|array;define;tokensHit; --=X1|[*S:t-left] \ 70 --=Y1|[*S:t-top] \ 70 --=X2|[*T:t-left] \ 70 --=Y2|[*T:t-top] \ 70 --?[$X1] -lt [$X2]|=SX;1|=SX;-1 --?[$Y1] -lt [$Y2]|=SY;1|=SY;-1 --=DX|[$X2] - [$X1] --~dx|string;replace;-;;[$DX.Total] --=DX|[&amp;dx] --=DY|[$Y2] - [$Y1] --~dy|string;replace;-;;[$DY.Total] --=DY|[&amp;dy] --=ERR|[$DX] - [$DY] --=Y|[$Y1.Total] --=X|[$X1.Total] --vbetweentokens|@{selected|token_id} @{target|token_id} beam-magic --%L|1;50 --+Square|[$X],[$Y] --&gt;checkTokens|[$X];[$Y] --=VX|[$X] * 70 + 35 --=VY|[$Y] * 70 + 35 --vpoint|[$VX] [$VY] burn-acid --?[$X.Total] -eq [$X2.Total] -and [$Y.Total] -eq [$Y2.Total]|%! --=E2|[$ERR.Total] * 2 --?[$E2] -gt [$DY]|[ --=ERR|[$ERR] - [$DY] --=X|[$X] + [$SX] --]| --?[$E2] -lt [$DX]|[ --=ERR|[$ERR] + [$DX] --=Y|[$Y] + [$SY] --]| --%| --~|array;remove;tokensHit;@{selected|token_id} --~hits|array;stringify;tokensHit --+Tokens Hit:|[&amp;hits] --X| --:checkTokens|x;y --~tokenid|array;getfirst;alltokens --:tokenLoop| --?[&amp;tokenid] -eq ArrayError|endCheckLoop --~exists|array;indexof;tokensHit;[&amp;tokenid] --?[&amp;exists] -ne ArrayError|continue --=TX|[*[&amp;tokenid]:t-left] \ 70 --=TY|[*[&amp;tokenid]:t-top] \ 70 --?[$TX] -eq [%1%] -and [$TY] -eq [%2%]|[ --~|array;add;tokensHit;[&amp;tokenid] --]| --:continue| --~tokenid|array;getnext;alltokens --^tokenLoop| --:endCheckLoop| --&lt;| }} There is lots of room for improvement here... Right now I'm checking every token on the page's position for each step of the line to see if the token is there. I could probably come up with something better by building a position list first and then checking that, but the brute force method works for now.
1621251017
David M.
Pro
API Scripter
Nifty, Kurt! Yeah, I was wondering about performance on heavily populated maps, but this looks like a great proof of concept. I think your idea on pre-populating a data structure is a good one. I suppose it could either be three arrays [tokID1, tokID2..], [x1, x2..], [y1, y2] with matching indices, or a single one with defined order [tokID1, x1, y1, tokID2, x2..etc].&nbsp; Looking at the array documentation: is there a way to directly return the value of an array at a given index (i.e. a Lookup, or "ValueAt" function equivalent)? I might be missing something, but it seems like you'd currently have to do a setindex followed by a getcurrent to accomplish this?&nbsp;
@Kurt/David - Wow.&nbsp; Y'all don't sleep, do ya?&nbsp; ;-)&nbsp; Kurt - quite extraordinary, perfect or not.&nbsp;&nbsp; David - 1.&nbsp; Thought I had seen it but had not understood the structure until now.&nbsp; So&nbsp; the conditional test, if true, looks at the [ ... --] structure as a whole and the structure can not only contain a variable assignment but can contain multiple variable assignments - in this case&nbsp; --=ERR|[$ERR] - [$DY] and&nbsp; --=X|[$X] + [$SX]&nbsp; separated by --=. &nbsp;Got it/. 2. I had thought I remembered a new function like that recently created by Kurt.&nbsp; I get lost sometimes referring back to the wiki and couldn't find it in the posts. 3.&nbsp; Ah! For some reason I had 75 in my mind and didn't relate it to the size of the token.&nbsp;&nbsp; 4.&nbsp; Okay, sure - testing for all tokens on the page may be brute force but I am a great believer in "whatever works."&nbsp; :-)&nbsp; Hmmm...since many tokens occupy 140 and 210 spaces that would be a problem.&nbsp; But doesn't&nbsp; --~|array;pagetokens;alltokens;@{target|token_id}&nbsp; pull in all tokens regardless of size?&nbsp; Couldn't a different function be created to use the internal logic of that function for different size tokens in this case?&nbsp; And the logic behind vbetweentokens seems to draw a straight(ish) line between tokens.&nbsp; Could that logic be used? David - since I have dubbed Kurt "Brainiac" I will have to resort to dubbing you "Brainiac Jr."&nbsp; With any luck neither Kurt nor you will mind.&nbsp; :-P
1621256075
David M.
Pro
API Scripter
Yes, all of the tokens get populated regardless of size. I think Kurt's point was that the current version only checks a single point(**) on each token vs the center of the affected square. If the line passes through the corner square of a larger token, it will get missed. Also just an fyi, it looks like you would need to be sure to have the tokens snapped to the grid. If you alt-move any tokens by even one pixel then if I'm reading it correctly the coordinates won't match and they won't get picked up. That is probably a pretty rare thing, but figured I'd mention it just in case that's a "thing" at your table. (**) I thought that the top and left graphic object properties contained the&nbsp;coordinates of the&nbsp; center &nbsp;of the token relative to the top/left of the map, though Kurt mentioned it was the top and left corner of the token in this scriptcard - Kurt can you confirm?&nbsp; Reference
1621256659
Kurt J.
Pro
API Scripter
David M. said: Yes, all of the tokens get populated regardless of size. I think Kurt's point was that the current version only checks a single point(**) on each token vs the center of the affected square. If the line passes through the corner square of a larger token, it will get missed. Also just an fyi, it looks like you would need to be sure to have the tokens snapped to the grid. If you alt-move any tokens by even one pixel then if I'm reading it correctly the coordinates won't match and they won't get picked up. That is probably a pretty rare thing, but figured I'd mention it just in case that's a "thing" at your table. (**) I thought that the top and left graphic object properties contained the&nbsp;coordinates of the&nbsp; center &nbsp;of the token relative to the top/left of the map, though Kurt mentioned it was the top and left corner of the token in this scriptcard - Kurt can you confirm?&nbsp; Reference You are right... it is to the center. I'll have to adjust. I just assumed that "top" and "left" would be the "top" and "left" :) The Roll20 API is so... wacky... sometimes :)
1621257341
David M.
Pro
API Scripter
Haha, yeah for sure! I did the same thing when I was first working on the Radar script - spent hours troubleshooting why nothing was working quite right before I realized the center-of-token convention. I'd never been so relieved and annoyed at the same time!
1621282011

Edited 1621282586
Kurt J.
Pro
API Scripter
Here is what I have so far (again, the GIF is running at half speed :() This script pre-caches the positions of all of the page tokens for easy retrieval later, meaning the list of tokens only has to be run over once at the beginning. I have ~120 tokens on my test page, it runs without any real type of delay. Tthe GIF above is running at half speed, and I had left a lot of logging in ScriptCards when I recorded it, so that isn't representative of the actual speed. I also fixed a bug in the Bresenham implementation that was causing issues with some lines. Here is the code for this version. It doesn't take resistance/immunity/vulnerability into account yet, and you won't get the visual effect until I release the updated ScriptCards that has the "--Vpoint|" command, but I also tried to make the script generic enough that it could be easily adapted to any line-based spell by changing a few parameters at the top of the script. !script {{ --/|Lightning Bolt script. Can be changed to other line-based spells by updating these parameters: --&amp;spellName|Lightning Bolt --/|Note: because the spell will be cast at a min level of 3, we use 5 here to get the total dice (5+3=8) --&amp;spellL0DamageDice|5 --&amp;spellDamageDieType|6 --&amp;damageType|lightning --&amp;saveType|dexterity --#sourcetoken|@{selected|token_id} --#targettoken|@{target|token_id} --~|array;pagetokens;alltokens;@{target|token_id} --=SpellLevel|?{Spell Slot Level?|3|4|5|6|7|8|9} --#leftsub|Save DC @{selected|spell_save_dc} --#rightsub|Slot Level: [$SpellLevel] --#title|Lightning Bolt --/|Calculate damage based on spell slot. --=DamageDice|[$SpellLevel.Total] + [&amp;spellL0DamageDice] --=Damage|[$DamageDice.Total]d[&amp;spellDamageDieType] --=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] --+|&amp;nbsp; --~|array;define;tokensHit; --&gt;checkForTokenHits|burn-frost --~|array;removeat;tokensHit;0 --~hits|array;stringify;tokensHit --/+Tokens Hit:|[&amp;hits] --/|Loop through the inRange tokens and roll saves for each one and apply damage --~tokenid|array;getfirst;tokensHit --?[&amp;tokenid] -eq ArrayError|endOutput --:loopDisplay| --=SaveRoll|1d20 + [*[&amp;tokenid]:[&amp;saveType_save_bonus] --/|Compare the save roll to the save DC and either apply full or half damage --?[$SaveRoll.Total] -lt @{selected|spell_save_dc}|&gt;ApplyDamageAlterbars;[&amp;tokenid];3;-[$Damage.Total]|&gt;ApplyDamageAlterbars;[&amp;tokenid];3;-[$HalfDamage.Total] --?[$SaveRoll.Total] -ge @{selected|spell_save_dc}|madeSave --/|Output a lien for a failed saving throw --+[*[&amp;tokenid]:t-name]:|Save [$SaveRoll] [r][$Damage] [&amp;damageType][/r] --&gt;afterSave| --/|Output a line for a successful saving throw --:madeSave| --+[*[&amp;tokenid]:t-name]:|Save [$SaveRoll] [r][$HalfDamage] [&amp;damageType][/r] --:afterSave| --~tokenid|array;getnext;tokensHit --?[&amp;tokenid] -ne ArrayError|loopDisplay --:endOutput| --X| --:ApplyDamageTokenmod|Parameters are tokenid;bar#;amount --@token-mod|_ignore-selected _ids [%1%] _set bar[%2%]_value|[%3%] --&lt;| --:ApplyDamageAlterbars|Parameters are tokenid;bar#;amount --@alter|_target|[%1%] _bar|[%2%] _amount|-[%3%] _show|none --&lt;| --:checkForTokenHits|Y as %1% for VFX --&gt;createTokenLookup| --=X1|[*S:t-left] \ 70 --=Y1|[*S:t-top] \ 70 --=X2|[*T:t-left] \ 70 --=Y2|[*T:t-top] \ 70 --?[$X1] -lt [$X2]|=SX;1|=SX;-1 --?[$Y1] -lt [$Y2]|=SY;1|=SY;-1 --=DX|[$X2] - [$X1] --~dx|string;replace;-;;[$DX.Total] --=DX|[&amp;dx] --=DY|[$Y2] - [$Y1] --~dy|string;replace;-;;[$DY.Total] --=DY|[&amp;dy] --=ERR|[$DX] - [$DY] --=Y|[$Y1.Total] --=X|[$X1.Total] --/+Vals|X1:[$X1] Y1:[$Y1] X2:[$X2] Y2:[$Y2] DX:[$DX] DY:[$DY] ERR:[$ERR] --%L|1;50 --/+Square|[$X],[$Y] --&gt;checkTokens|[$X];[$Y] --?"X[%1%]X" -ne "XX" |[ --=VX|[$X] * 70 + 35 --=VY|[$Y] * 70 + 35 --vpoint|[$VX] [$VY] [%1%] --]| --?[$X.Total] -eq [$X2.Total] -and [$Y.Total] -eq [$Y2.Total]|%! --=E2|[$ERR.Total] * 2 --?[$DY] -ge 0|=NDY;0 - [$DY.Total]|[ --~tempNDY|string;replace;-;;[$DY.Total] --=NDY|[&amp;tempNDY] --]| --=NDY|[$DY] {NEGATE} --?[$E2] -gt [$NDY]|[ --=ERR|[$ERR] - [$DY] --=X|[$X] + [$SX] --]| --?[$E2] -lt [$DX]|[ --=ERR|[$ERR] + [$DX] --=Y|[$Y] + [$SY] --]| --%| --~|array;remove;tokensHit;@{selected|token_id} --&lt;| --:checkTokens|x;y --?"X[&amp;tok[%1%]-[%2%]]X" -ne "XX"|[ --~split|string;split;!;[&amp;tok[%1%]-[%2%]] --%P|1;[$splitCount.Total];1 --=var|split[&amp;P] --?"X[&amp;[$var.RollText]]X" -eq "XX"|% --~exists|array;indexof;tokensHit;[&amp;[$var.RollText]] --?[&amp;exists] -ne ArrayError|skipAdd --?[&amp;[$var.RollText]] -eq ArrayError|skipAdd --~|array;add;tokensHit;[&amp;[$var.RollText]] --:skipAdd| --%| --&lt;| --:createTokenLookup| --~tokenid|array;getfirst;alltokens --:tokenSetupLoop| --?[&amp;tokenid] -eq ArrayError|endSetupLoop --=tLeft|[*[&amp;tokenid]:t-left] - 1 \ 70 --=tTop|[*[&amp;tokenid]:t-top] - 1 \ 70 --=tWidth|[*[&amp;tokenid]:t-width] --?[$tWidth] -eq 70|[ --&amp;tok[$tLeft]-[$tTop]|+[&amp;tokenid]! --/+[*[&amp;tokenid]:character_name]|At [$tLeft],[$tTop] : [&amp;tok[$tLeft.Total]-[$tTop.Total]] --]| --?[$tWidth] -eq 140|[ --=NX|[$tLeft] + 1 --=NY|[$tTop] + 1 --&amp;tok[$tLeft]-[$tTop]|+[&amp;tokenid]! --&amp;tok[$tLeft]-[$NY]|+[&amp;tokenid]! --&amp;tok[$NX]-[$tTop]|+[&amp;tokenid]! --&amp;tok[$NX]-[$NY]|+[&amp;tokenid]! --]| --?[$tWidth] -eq 210|[ --=PX|[$tLeft] - 1 --=PY|[$tTop] - 1 --=NX|[$tLeft] + 1 --=NY|[$tTop] + 1 --&amp;tok[$tLeft]-[$tTop]|+[&amp;tokenid]! --&amp;tok[$PX]-[$PY]|+[&amp;tokenid]! --&amp;tok[$tLeft]-[$PY]|+[&amp;tokenid]! --&amp;tok[$NX]-[$PY]|+[&amp;tokenid]! --&amp;tok[$PX]-[$tTop]|+[&amp;tokenid]! --&amp;tok[$NX]-[$tTop]|+[&amp;tokenid]! --&amp;tok[$PX]-[$NY]|+[&amp;tokenid]! --&amp;tok[$tLeft]-[$NY]|+[&amp;tokenid]! --&amp;tok[$NX]-[$NY]|+[&amp;tokenid]! --]| --~tokenid|array;getnext;alltokens --^tokenSetupLoop| --:endSetupLoop| --&lt;| }} More improvements to come... And 1.2.8 of ScriptCards later tonight.
1621288903

Edited 1621290639
David M.
Pro
API Scripter
Pretty sweet, Kurt! I have a question about the expected result for affected squares using the Bresenham algorithm with an example to make sure I'm understanding it.&nbsp; EDIT - Some of this information changed after updating to v1.2.8. Eleven squares are now being included in the line effect, and they are ones that make sense (minimal area crossed by the line). Y'all can just disregard this post :)&nbsp; For the following grid, a line extending from the two "X" squares passes through all of the highlighted squares. Is the algorithm supposed to include all of those squares? &nbsp; If I add tokens to the above map and run the lightning bolt macro, it only "hits" 14 of the 17 valid target squares if my assumption above is true. Obviously, the origin square is omitted, and was removed from my count of valid squares. I made sure that the imp tokens were snapped to the grid before running the macro.&nbsp; Does this sound right to you? Are some of my highlighted squares not supposed to be included based on the algorithm? Through trial and error, I found the three squares that aren't being included (red arrows drawn in image below). I thought it might be due to having to be within some margin of error (passing through "enough" of the square to be counted), but the square denoted by the blue arrow below seems to have less square area covered by the line than the one immediately to the left of the character token. Thoughts?&nbsp;
1621288962

Edited 1621290262
Kurt J.
Pro
API Scripter
ScriptCards v 1.2.8 Now Available It's up on the GIST , and here are the changes: New String Functions Two new string functions : replaceall and trim. replaceall works just like replace except it will replace all occurrences of the search string. Trim will remove spaces from the beginning and end of the string. New VFX Option You can now spawn visual effects and arbitrary points with the --Vpoint| command. The parameters (separated by spaces) are X Y shape-color, and are measured in pixels. If #activepage is set, the VFX will play on that page. Otherwise it will default to the player ribbon page. For example: --Vpoint|150 150 burst-fire New Roll Parser Functions As you may know, the ScriptCards roll parser works from left to right, always maintaining a current value and applying whatever comes next to the total. This has allowed me to add some in-line functions to the roll parser. These are: {ABS} - Absolute value {CEIL} - Ceiling value (round up to the next whole number) {FLOOR} - Floor value (round down to the next whole number) {ROUND} - Round value (round up if the decimal portion is greater than 0.5, otherwise round down) {NEGATE} - Multiply the value by negative one. These can simply be added to rolls (separated by spaces, of course): --#DieRoll|1d20 + 5 * 1.5 {FLOOR} As some background, I found that there is a regex bug that does not allow for negative numbers to be used as multipliers in a roll. They get interpreted as changing the operator to "-" and then subtracted. {NEGATE} allows me to work around this while I try to resolve the regex issue.
1621289658
Kurt J.
Pro
API Scripter
David M. said: Pretty sweet, Kurt! I have a question about the expected result for affected squares using the Bresenham algorithm with an example to make sure I'm understanding it.&nbsp; For the following grid, a line extending from the two "X" squares passes through all of the highlighted squares. Is the algorithm supposed to include all of those squares? &nbsp; If I add tokens to the above map and run the lightning bolt macro, it only "hits" 14 of the 17 valid target squares if my assumption above is true. Obviously, the origin square is omitted, and was removed from my count of valid squares. I made sure that the imp tokens were snapped to the grid before running the macro.&nbsp; Does this sound right to you? Are some of my highlighted squares not supposed to be included based on the algorithm? Through trial and error, I found two squares that aren't being included (red arrows drawn in image below). I thought it might be due to having to be within some margin of error (passing through "enough" of the square to be counted), but the square denoted by the blue arrow below seems to have less square area covered by the line than the one immediately to the left of the character token. Thoughts?&nbsp; Bresenham is a line plotting algorithm for pixel-based displays, so what you observe above is to be expected. Pretty much it is going to come down to DM preference... I know in my Foundry game, the measurement tool and shape overlays use this type of mechanism to determine impacted squares on the grid. I can see it both ways... I know in the past at the physical table I've considered how much of the square is covered to determine if a creature was impacted by a spell. Players will always want even the slightest traversal of a square to count - which I can completely understand :) There might be better line drawing implementations that would yield "more accurate" results, but the inability to ask roll20 "whats in THIS square" means some compromises to be able to find objects fast enough to make the process reasonable.
1621289822
David M.
Pro
API Scripter
I updated to 1.2.8 and re-ran the lightning bolt macro, but not getting vfx. I was hoping to use it to verify my observations above. I searched the code for "vpoint" but it only returned one instance (a comment in the&nbsp;Inline Update Notes). Also, looks like a tokenid is repeatedly being written to the console log :) EDIT - Also, when I run the macro with 1.2.8, only 11 imps are now making saves in my example above, whereas with 1.2.7 14 of them were included.
1621290039

Edited 1621290149
David M.
Pro
API Scripter
OK, I was just curious. It just seemed odd that squares with more space covered were getting omitted while some with less space covered were being included. Then there is still the case of three fewer creatures being included (with the same setup) in the latest version vs 1.2.7.&nbsp;
1621290215
Kurt J.
Pro
API Scripter
David M. said: I updated to 1.2.8 and re-ran the lightning bolt macro, but not getting vfx. I was hoping to use it to verify my observations above. I searched the code for "vpoint" but it only returned one instance (a comment in the&nbsp;Inline Update Notes). Also, looks like a tokenid is repeatedly being written to the console log :) EDIT - Also, when I run the macro with 1.2.8, only 11 imps are now making saves in my example above, whereas with 1.2.7 14 of them were included. Unless you set an #activepage, the VFX from POINT will play on the player ribbon page. I should have included that above:)
1621290521
David M.
Pro
API Scripter
Grr, I should have guessed that! Working for me now. Btw, just want to be clear I wasn't complaining about anything. I think this is awesome! Also, the 3 creatures that are no longer included in my above example with the current version are ones that make sense to me (minimal coverage area). Great work!
1621291129

Edited 1621291192
Kurt J.
Pro
API Scripter
I'm in the process of updating the Wiki, but as a side track I've started updating the first post of this thread with permalinks to the various version update notes. ( First Post ) (I also ninja-updated 1.2.8 to remove the log of the page id for --Vpoint effects :))
Kurt J. said: I'm in the process of updating the Wiki, but as a side track I've started updating the first post of this thread with permalinks to the various version update notes. ( First Post ) (I also ninja-updated 1.2.8 to remove the log of the page id for --Vpoint effects :)) A couple suggestions: Add the attribute name list for Tokens (t-*) Correct the syntax description for totitle string function, I believe its currently documented totile &nbsp; Correct the syntax description for statusmarkers array function should be --~cnt|array;statusmarkers;ArrayName;TokenId I believe.