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

Help with Ice Knife Macro

Hi all, I'm having a little trouble with the Ice Knife spell. It's a complicated one as it has two steps, the first is that it targets a single creature with an attack spell roll with 1d10 damage, second is that it does an AoE saving throw damage of 1d6 to the creature and every creature in a 5 ft range. I've used ScriptCards and SmartAoE to create the AoE damage, but I need some ideas about the initial damage to the single target. Here's the macro so far: !script {{ --#title|Ice Knife --#sourcetoken|@{selected|token_id} --#emoteText|[*S:character_name] Casts Ice Knife. --#bodyfontface|Helvetica --#oddrowfontcolor|#290000 --#evenrowfontcolor|#290000 --#bodyfontsize|12px --#titlecardbackgroundcolor|#930c10 --#titlecardbackgroundcolor|#672223 --#titlefontcolor|#fffebd --#titlefontsize|1.5em --#titlefontshadow|0 --#oddrowbackground|#f7ce65 --#evenrowbackground|#f7ce65 --#oddrowbackground|#b3ab96 --#evenrowbackground|#b3ab96 --#subtitlefontcolor|#fffebd --#tablebgcolor|#fffebd --#lineheight|10pt --#buttonfontsize|10px --#buttonbackground|#930c10 --#buttonbackground|#672223 --#buttonbackground|rgba(103,34,35,1); display:inline-block; --#buttonfontface|Tahoma --#buttonpadding|8px --#bodyfontface|Helvetica --#bodyfontsize|14px -->WhatSpellSlot| --X| --:IceKnife| --@smartaoe| _title|Ice Knife _leftsub|Slot level [$SlotLevel] _rightsub|DC @{selected|spell_save_dc} WIS _titlecardbackground|linear-gradient(#672223, #930c10) _titlefontcolor|#fffebd _titlefontface|Tahoma _titlefontsize|1.5em _titlefontlineheight|10pt _bodyfontface|Helvetica _bodyfontsize|12px _oddrowfontcolor|#290000 _evenrowfontcolor|#290000 _tablebgcolor|#290000 _evenrowbackground|#f7ce65 _oddrowbackground|#f7ce65 _evenrowbackground|#b3ab96 _oddrowbackground|#b3ab96 _subtitlefontcolor|#fffebd _chatAvatarsEnabled|1 _aoeType|circle, float _radius|5ft _aoeColor|#80ff0000 _aoeOutlineColor|#80ff0000 _controlTokName|Ice Knife _controlTokSize|3 _gridColor|#99000000 _minGridArea|0.25 _minTokArea|0.25 _fx|burn-magic _dc|@{selected|spell_save_dc} _saveFormula|5eDEX _damageFormula1|[$damageFormula1] _damageType1|Ice _ignore|SmartAoE_Ignore,1 _bar|1 _autoApply|1 _instant|1 _forceIntersection|0 _desc|You create a shard of ice and fling it at one creature within range. Make a ranged spell attack against the target. On a hit, the target takes 1d10 piercing damage. Hit or miss, the shard then explodes. The target and each creature within 5 feet of the point where the ice exploded must succeed on a Dexterity saving throw or take 2d6 cold damage.%br%At Higher Levels. When you cast this spell using a spell slot of 2nd level or higher, the cold damage increases by 1d6 for each slot level above 1st. --X| --:WhatSpellSlot| --+At what level is the spell being cast?| --+|[rbutton]1::WhatLVL;1[/rbutton][rbutton]2::WhatLVL;2[/rbutton][rbutton]3::WhatLVL;3[/rbutton][rbutton]4::WhatLVL;4[/rbutton][rbutton]5::WhatLVL;5[/rbutton][rbutton]6::WhatLVL;6[/rbutton][rbutton]7::WhatLVL;7[/rbutton][rbutton]8::WhatLVL;8[/rbutton][rbutton]9::WhatLVL;9[/rbutton] --X| --:WhatLVL| --=SlotLevel|[&reentryval] --=SlotsTotal|0 --=SlotsExpended|[*S:lvl[$SlotLevel]_slots_expended] --?[$SlotsExpended.Raw] -eq [$SlotsTotal.Raw]|[ -->NoSlotsLeft| --]|[ --?[$SlotLevel] -eq 0|NoSlot --=SlotsLeft|[$SlotsExpended] - 1 --!a:[*S:character_id]|lvl[$SlotLevel]_slots_expended:[$SlotsLeft] --=damageFormula1|[= [$SlotLevel]+1]d6 --+|Level [$SlotLevel] Slots Left: [$SlotsLeft] -->IceKnife| --X| --:NoSlot| --X| --:NoSlotsLeft| --+|[b][*S:character_name] has no level [$SlotLevel.Total] spell slots available.[/b] --X|NoSlotsLeftStop --X| }}
I have an ice knife scriptcard. I'll dig it out tomorrow and post it if you are still looking around for ideas. Maybe something in there will be helpful to you.
Ok so here is what I did for Ice Knife. Feel free to use it or any part of it if you can. I have moved functions into the script that I normally keep in libraries so that this script is self-contained. I don't use SmartAoE so maybe a lot of this won't apply to you but maybe some parts will be useful. !scriptcard {{ --/|Ice Knife has an attack and area of effect damage --/|Script can be set to auto-roll NPC token saves for AoE damage --/|Script can be set to auto-apply damage to NPC tokens --/|NOTE: PC tokens are always prompted for the player to roll the save. Never autorolls for a player --/|NPC saves also roll a second roll for advantage/disadvantage --/|Auto-applying damage will not use adv/disadvantage and only consider the initial saving throw --/|Requires: ScriptCards 2.3.8+ --/|TODO: allow upcasting --/|VARIABLES TO SET --&FeatureName|Ice Knife --&MinimumSpellLevel|1 --&TargetACAttribute|npc_ac --&AutoRollNPCSaves|false --&AutoApplyDamage|false --&HPBar|1 --&emoteMessage|conjures a sharp cold blade! --&verboseOutput|true --#sourceToken|@{selected|token_id} --#targetToken|@{target|token_id} --#title|[&FeatureName] --#emoteText|[*S:character_name] [&emoteMessage] --#reentryval|[&FeatureName][*S:character_id] --Rfind|[*S:character_id];[&FeatureName];repeating_spell-[&MinimumSpellLevel];spellname --?"[*R:spellname]" -eq "NoRepeatingAttributeLoaded"|>NotFound;[&FeatureName] --/|Check spell slot remaining --=RemainingSlots|[*S:lvl[&MinimumSpellLevel]_slots_expended] --?[$RemainingSlots] -eq 0|InsufficientResources --:DeductResources| --!a:[*S:character_id]|lvl[&MinimumSpellLevel]_slots_expended:-=1 --:AttackRoll| --=AtkRoll|1d20 + [*S:spell_attack_bonus] [SPELLATTACK] --?[$AtkRoll] -lt [*T:[&TargetACAttribute]]|Miss --?[$AtkRoll.Base] -eq 20|Crit|Hit --:SplashDamage| --=SplashDamageRoll|[*R:spelldamage2] --+Splash Damage|Each creature in area needs to make a [*R:spellsave] vs DC [*R:roll_output_dc] or take [$SplashDamageRoll] [*R:spelldamagetype2] damage. [*R:spellsavesuccess][br] --:DetermineTokensInArea| --/|Check for friendly fire first --~pcTokenCount|array;pagetokens;PCTokenArr;[*C:playerpageid];pc --%pctid|foreach;PCTokenArr -->CheckAdjacency|[*T:t-id];[&pctid];PCAdjacent --?[&PCAdjacent] -eq 1|[ --+Friendly Fire|[b][*[&pctid]:t-name][/b] needs to make a [sheetbutton][*R:spellsave]::[*[&pctid]:character_name]::Saves[/sheetbutton] --]| --%| --~|array;define;AdjancentNPCTokenArr;[*T:t-id] --~npcTokenCount|array;pagetokens;NPCTokenArr;[*C:playerpageid];npc --%npctid|foreach;NPCTokenArr --?"[&npctid]" -eq "[*T:t-id]"|% -->CheckAdjacency|[*T:t-id];[&npctid];NPCAdjacent --?[&NPCAdjacent] -eq 1|[ --~|array;add;AdjancentNPCTokenArr;[&npctid] --]| --%| --%adjacentTID|foreach;AdjancentNPCTokenArr --&damageType|[*R:spelldamagetype2] --?"[*[&adjacentTID]:npc_immunities]" -inc "[&damageType(tolowercase)]"|[ --*IMMUNE|[*[&adjacentTID]:t-name] is adjacent but immune to [&damageType(tolowercase)] --%| --]| --&buttonParams|[&adjacentTID]%[*R:spellsave]%[*R:roll_output_dc] --?"[&AutoRollNPCSaves]" -ne "true"|[ --*[*[&adjacentTID]:t-name]|[rbutton][*R:spellsave] Save::NPCSavingThrow;[&buttonParams][/rbutton] --]|[ -->NPCSavingThrow|[&adjacentTID];[*R:spellsave];[*R:roll_output_dc] --]| --%| --:Done| --X| --:Miss| --=AtkDamage|0 --+[&FeatureName]|Attack Roll:[$AtkRoll] misses![br] --^SplashDamage| --:Hit| --=AtkDamage|[*R:spelldamage] --+[&FeatureName]|Attack Roll:[$AtkRoll] hits for [$AtkDamage] [*R:spelldamagetype] damage[br] --^SplashDamage| --:Crit| --+[&FeatureName]|Attack Roll:[$AtkRoll] critical hit! --=DamageRoll|[*R:spelldamage] --=CritRoll|[*R:spelldamage] --=AtkDamage|[$DamageRoll] + [$CritRoll] [CRIT] --+Total Damage|[$DamageRoll] + [$CritRoll] critical damage for [$AtkDamage] total [*R:spelldamagetype] damage[br] --^SplashDamage| --:CheckAdjacency|TargetTokenID;AdjacentTokenID;StringVariableToSet --/|If the tokens overlap then they are also considered adjacent for the purposes of splash damage -->DoTokensOverlap|[%1%];[%2%];0;[%3%] --<| --/|Check to see if two tokens overlap --/|The y coordinates are negative because roll20 0,0 is the top left corner of the page --/|that makes the y coordinates for tokens negative on a traditional grid --/|The PixelPadding is to allow checking if tokens overlap without sharing a border --/|Set PixelPadding to 1 to remove adjacent tokens that share a border --/|Set to 0 to also get adjacent tokens --:DoTokensOverlap|TokenID1;TokenID2;PixelPadding;StringVariableToSet --=T1HalfHeight|[*[%1%]:t-height] / 2 --=T1HalfWidth|[*[%1%]:t-width] / 2 --/|L1x is realLeft --=L1x|[*[%1%]:t-left] - [$T1HalfWidth] + [%3$] --/|L1y is negative realTop --=L1y|-[*[%1%]:t-top] + [$T1HalfHeight] - [%3%] --/|R1x is right --=R1x|[*[%1%]:t-left] + [$T1HalfWidth] - [%3%] --/|R1y is negative bottom --=R1y|-[*[%1%]:t-top] - [$T1HalfHeight] + [%3%] --=T2HalfHeight|[*[%2%]:t-height] / 2 --=T2HalfWidth|[*[%2%]:t-width] / 2 --/|L2x is realLeft --=L2x|[*[%2%]:t-left] - [$T2HalfWidth] + [%3%] --/|L2y is negative realTop --=L2y|-[*[%2%]:t-top] + [$T2HalfHeight] - [%3$] --/|R2x is right --=R2x|[*[%2%]:t-left] + [$T2HalfWidth] - [%3%] --/|R2y is negative bottom --=R2y|-[*[%2%]:t-top] - [$T2HalfHeight] + [%3%] --&[%4%]|1 --?[$L1x] -gt [$R2x] -or [$L2x] -gt [$R1x]|&[%4%];0 --?[$R1y] -gt [$L2y] -or [$R2y] -gt [$L1y]|&[%4%];0 --<| --X| --/|AutoRoll will use params and non-Auto will use re-entry buttons --:NPCSavingThrow|NPCTokenID;SaveType;DC --?"X[%1%]" -eq "X"|[ --&params|[&reentryval] --#hidecard|1 --]|[ --&params|[%1%]%[%2%]%[%3%] --]| --~npcSTInfo|string;split;%;[&params] --?"[&npcSTInfo2]" -eq "Strength"|&npcSTShortname;str --?"[&npcSTInfo2]" -eq "Dexterity"|&npcSTShortname;dex --?"[&npcSTInfo2]" -eq "Constitution"|&npcSTShortname;con --?"[&npcSTInfo2]" -eq "Intelligence"|&npcSTShortname;int --?"[&npcSTInfo2]" -eq "Wisdom"|&npcSTShortname;wis --?"[&npcSTInfo2]" -eq "Charisma"|&npcSTShortname;cha --~npcSTMod|math;max;[*[&npcSTInfo1]:npc_[&npcSTShortname]_save];[*[&npcSTInfo1]:[&npcSTInfo2(tolowercase)]_save_bonus] --=saveRoll|1d20 + [$npcSTMod] [MOD] --=advRoll|1d20 + [$npcSTMod] [MOD] --?[$saveRoll] -ge [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3]|&result;success --?[$saveRoll] -lt [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3]|&result;failure --?[$saveRoll] -ge [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3]|&result;successmixed --?[$saveRoll] -lt [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3]|&result;failuremixed --&tookDamage|0 --?"[*T:t-id]" -eq "[&npcSTInfo1]" -and [$AtkDamage] -gt 0|&initialDamage;1|&initialDamage;0 --?"[&result]" -inc "failure" -or [&initialDamage] -eq 1|&tookDamage;1 --&msg|Sv:[$saveRoll] A/D:[$advRoll] [b][&result][/b] --?"[&verboseOutput]" -eq "true"|[ --*[*[&npcSTInfo1]:t-name]|[&msg] --]| --?"[&AutoApplyDamage]" -eq "true" -and "[&tookDamage]" -eq 1|>ApplyDamage;[&npcSTInfo1];[&result] --?"[&AutoApplyDamage]" -ne "true" -and "[&result]" -ne "success"|[ --*[*[&npcSTInfo1]:t-name]|[rbutton]Apply Damage::ApplyDamage;[&npcSTInfo1]%[&result][/rbutton] --]| --<| --/|Add a stop here so re-entry doesn't fallthrough when not set to auto --X| --:ApplyDamage|NPCTokenID;Result --?"X[%1%]" -eq "X"|[ --&adParams|[&reentryval] --#hidecard|1 --]|[ --&adParams|[%1%]%[%2%] --]| --&adTID|[&adParams(split,%,0)] --/|Check damage modifiers. Already checked immunity previously -->Lib5E_CheckDamageModifiers|ResistType;[*R:spelldamagetype2] --=TotalDamage|[$SplashDamageRoll] [&ResistType] --/|Check if initial target to add any piercing damage and check result so that successful saving throws don't get splash damage --?"[&adTID]" -eq "[*T:t-id]"|[ --?"[&AutoApplyDamage]" -eq "true" -and "[&adParams(split,%,1)]" -inc "success"|=TotalDamage;0 --=TotalDamage|[$TotalDamage] [SPLASH] + [$AtkDamage] [ATTACK] --]| --?"[&verboseOutput]" -eq "true"|[ --*Applying Damage|[$TotalDamage] damage to bar [&HPBar] of [b][*[&adTID]:t-name][/b] --]| --!t:[&adTID]|bar[&HPBar]_value:-=[$TotalDamage] --<| --/|Add a stop here so re-entry doesn't fallthrough when not set to auto --X| --/|From Kurt J's dnd5elib library but porting over here to make scriptcard self contained --:Lib5E_CheckDamageModifiers|damageVariableName;damageType --&[%1%]| --?"[*T:npc_vulnerabilities]" -inc "[%2%]"|>_Lib5E_IsVulnerable;[%1%] --?"[*T:npc_resistances]" -inc "[%2%]"|>_Lib5E_IsResistant;[%1%] --?"[*T:npc_immunities]" -inc "[%2%]"|>_Lib5E_IsImmune;[%1%] --<| --:_Lib5E_IsVulnerable| --&[%1%]| * 2 [Vulnerable] --<| --:_Lib5E_IsResistant| --&[%1%]| \ 2 [Resistant] --<| --:_Lib5E_IsImmune| --&[%1%]| * 0 [Immune] --<| --:NotFound|FeatureName --#emoteText|Not Found --+ERROR|[%1%] not found. --^Done| --:InsufficientResources| --#emoteText|Out of spell slots --+Out of Uses|No more remaining spells for [&FeatureName] --^Done| }} The logic is the caster chooses a target, the script makes an attack roll against that target and determines if it hits the target ac or is a crit, if it hits or crits the attack damage is rolled. The script will then go through all the PC and NPC tokens to check if they are adjacent to the target. If any PCs are adjacent, the script will prompt them to make a save. NPC saving throws and NPC damage can be set to auto-roll and auto-apply by setting: --&AutoRollNPCSaves|false --&AutoApplyDamage|false to true. Depending on those settings, the scriptcard will either auto-roll or whisper a button to the GM to roll the save or apply the damage. Maybe some parts of that you can use for your own. Let me know if you have any questions or can't understand something about it. Perhaps you can just skip the splash damage and adjacency parts entirely for your script and just use the attack roll.
Thanks Joshua, I knew this would be complicated and seeing your macro I see I would probably not have been able to do this myself. I'm getting an error, though. I have debug output, but the forum won't let me post it for some reason. I'll see if I can pick stuff out of it. I'm getting this when clicking the dexterity npc roll button.  No ability was found for %{-MNyt2VudhN8Cx1U4J_g|Saves}
"ContentIn: [&reentryval] Match: [&reentryval], vName: reentryval, replacement -MOiVzzmOLb4m-jVqsfe%Dexterity%" "Line Counter: 150, Tag:&params, Content:-MOiVzzmOLb4m-jVqsfe%Dexterity%" "Line Counter: 151, Tag:#hidecard, Content:1" "Setting parameter hidecard to value 1 - 1" "Line Counter: 152, Tag:], Content:[" "ContentIn: string;split;%;[&params] Match: [&params], vName: params, replacement -MOiVzzmOLb4m-jVqsfe%Dexterity%" "Line Counter: 155, Tag:~npcSTInfo, Content:string;split;%;-MOiVzzmOLb4m-jVqsfe%Dexterity%" "ContentIn: /?\"[&npcSTInfo2]\" -eq \"Strength\" Match: [&npcSTInfo2], vName: npcSTInfo2, replacement Dexterity" "Line Counter: 156, Tag:/?\"Dexterity\" -eq \"Strength\", Content:&npcSTShortname;str" "ContentIn: /?\"[&npcSTInfo2]\" -eq \"Dexterity\" Match: [&npcSTInfo2], vName: npcSTInfo2, replacement Dexterity" "Line Counter: 157, Tag:/?\"Dexterity\" -eq \"Dexterity\", Content:&npcSTShortname;dex" "ContentIn: /?\"[&npcSTInfo2]\" -eq \"Constitution\" Match: [&npcSTInfo2], vName: npcSTInfo2, replacement Dexterity" "Line Counter: 158, Tag:/?\"Dexterity\" -eq \"Constitution\", Content:&npcSTShortname;con" "ContentIn: /?\"[&npcSTInfo2]\" -eq \"Intelligence\" Match: [&npcSTInfo2], vName: npcSTInfo2, replacement Dexterity" "Line Counter: 159, Tag:/?\"Dexterity\" -eq \"Intelligence\", Content:&npcSTShortname;int" "ContentIn: /?\"[&npcSTInfo2]\" -eq \"Wisdom\" Match: [&npcSTInfo2], vName: npcSTInfo2, replacement Dexterity" "Line Counter: 160, Tag:/?\"Dexterity\" -eq \"Wisdom\", Content:&npcSTShortname;wis" "ContentIn: /?\"[&npcSTInfo2]\" -eq \"Charisma\" Match: [&npcSTInfo2], vName: npcSTInfo2, replacement Dexterity" "Line Counter: 161, Tag:/?\"Dexterity\" -eq \"Charisma\", Content:&npcSTShortname;cha" "Line Counter: 162, Tag:&npcSTSortname, Content:Dexterity" "ContentIn: math;max;[*[&npcSTInfo1]:npc_[&npcSTShortname]_save];[*[&npcSTInfo1]:[&npcSTInfo2(tolowercase)]_save_bonus] Match: [&npcSTInfo1], vName: npcSTInfo1, replacement -MOiVzzmOLb4m-jVqsfe" "ContentIn: math;max;[*-MOiVzzmOLb4m-jVqsfe:npc_[&npcSTShortname]_save];[*[&npcSTInfo1]:[&npcSTInfo2(tolowercase)]_save_bonus] Match: [&npcSTShortname], vName: npcSTShortname, replacement " "Error: No attribute or sheet field found for character_id -MNyt2VudhN8Cx1U4J_g named npc__save" "ContentIn: math;max;undefined;[*[&npcSTInfo1]:[&npcSTInfo2(tolowercase)]_save_bonus] Match: [&npcSTInfo1], vName: npcSTInfo1, replacement -MOiVzzmOLb4m-jVqsfe" "Line Counter: 163, Tag:~npcSTMod, Content:math;max;undefined;2" "ContentIn: 1d20 + [$npcSTMod] [MOD] Match: [$npcSTMod], vName: npcSTMod, vSuffix: Total, replacement 2" "Line Counter: 164, Tag:=saveRoll, Content:1d20 + 2 [MOD]" "ContentIn: 1d20 + [$npcSTMod] [MOD] Match: [$npcSTMod], vName: npcSTMod, vSuffix: Total, replacement 2" "Line Counter: 165, Tag:=advRoll, Content:1d20 + 2 [MOD]" "ContentIn: ?[$saveRoll] -ge [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3] Match: [$saveRoll], vName: saveRoll, vSuffix: Total, replacement 14" "ContentIn: ?14 -ge [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3] Match: [&npcSTInfo3], vName: npcSTInfo3, replacement " "ContentIn: ?14 -ge -and [$advRoll] -ge [&npcSTInfo3] Match: [$advRoll], vName: advRoll, vSuffix: Total, replacement 3" "ContentIn: ?14 -ge -and 3 -ge [&npcSTInfo3] Match: [&npcSTInfo3], vName: npcSTInfo3, replacement " "Line Counter: 166, Tag:?14 -ge -and 3 -ge , Content:&result;success" "ScriptCards conditional error: Condition contains an invalid clause joiner on line. Only -and and -or are supported. Assume results are incorrect. 14 -ge -and 3 -ge " "Condition 14 -ge -and 3 -ge evaluation result: false" "ContentIn: ?[$saveRoll] -lt [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3] Match: [$saveRoll], vName: saveRoll, vSuffix: Total, replacement 14" "ContentIn: ?14 -lt [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3] Match: [&npcSTInfo3], vName: npcSTInfo3, replacement " "ContentIn: ?14 -lt -and [$advRoll] -lt [&npcSTInfo3] Match: [$advRoll], vName: advRoll, vSuffix: Total, replacement 3" "ContentIn: ?14 -lt -and 3 -lt [&npcSTInfo3] Match: [&npcSTInfo3], vName: npcSTInfo3, replacement " "Line Counter: 167, Tag:?14 -lt -and 3 -lt , Content:&result;failure" "ScriptCards conditional error: Condition contains an invalid clause joiner on line. Only -and and -or are supported. Assume results are incorrect. 14 -lt -and 3 -lt " "Condition 14 -lt -and 3 -lt evaluation result: false" "ContentIn: ?[$saveRoll] -ge [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3] Match: [$saveRoll], vName: saveRoll, vSuffix: Total, replacement 14" "ContentIn: ?14 -ge [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3] Match: [&npcSTInfo3], vName: npcSTInfo3, replacement " "ContentIn: ?14 -ge -and [$advRoll] -lt [&npcSTInfo3] Match: [$advRoll], vName: advRoll, vSuffix: Total, replacement 3" "ContentIn: ?14 -ge -and 3 -lt [&npcSTInfo3] Match: [&npcSTInfo3], vName: npcSTInfo3, replacement " "Line Counter: 168, Tag:?14 -ge -and 3 -lt , Content:&result;successmixed" "ScriptCards conditional error: Condition contains an invalid clause joiner on line. Only -and and -or are supported. Assume results are incorrect. 14 -ge -and 3 -lt " "Condition 14 -ge -and 3 -lt evaluation result: false" "ContentIn: ?[$saveRoll] -lt [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3] Match: [$saveRoll], vName: saveRoll, vSuffix: Total, replacement 14" "ContentIn: ?14 -lt [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3] Match: [&npcSTInfo3], vName: npcSTInfo3, replacement " "ContentIn: ?14 -lt -and [$advRoll] -ge [&npcSTInfo3] Match: [$advRoll], vName: advRoll, vSuffix: Total, replacement 3" "ContentIn: ?14 -lt -and 3 -ge [&npcSTInfo3] Match: [&npcSTInfo3], vName: npcSTInfo3, replacement " "Line Counter: 169, Tag:?14 -lt -and 3 -ge , Content:&result;failuremixed" "ScriptCards conditional error: Condition contains an invalid clause joiner on line. Only -and and -or are supported. Assume results are incorrect. 14 -lt -and 3 -ge " "Condition 14 -lt -and 3 -ge evaluation result: false" "Line Counter: 170, Tag:&tookDamage, Content:0" "ContentIn: ?\"-MOiVzzmOLb4m-jVqsfe\" -eq \"[&npcSTInfo1]\" -and [$AtkDamage] -gt 0 Match: [&npcSTInfo1], vName: npcSTInfo1, replacement -MOiVzzmOLb4m-jVqsfe" "ContentIn: ?\"-MOiVzzmOLb4m-jVqsfe\" -eq \"-MOiVzzmOLb4m-jVqsfe\" -and [$AtkDamage] -gt 0 Match: [$AtkDamage], vName: AtkDamage, vSuffix: Total, replacement 7" "Line Counter: 171, Tag:?\"-MOiVzzmOLb4m-jVqsfe\" -eq \"-MOiVzzmOLb4m-jVqsfe\" -and 7 -gt 0, Content:&initialDamage;1|&initialDamage;0" "Condition \"-MOiVzzmOLb4m-jVqsfe\" -eq \"-MOiVzzmOLb4m-jVqsfe\" -and 7 -gt 0 evaluation result: true" "ContentIn: ?\"[&result]\" -inc \"failure\" -or [&initialDamage] -eq 1 Match: [&result], vName: result, replacement " "ContentIn: ?\"\" -inc \"failure\" -or [&initialDamage] -eq 1 Match: [&initialDamage], vName: initialDamage, replacement 1" "Line Counter: 172, Tag:?\"\" -inc \"failure\" -or 1 -eq 1, Content:&tookDamage;1"
I think the debug output was too long. Looking at this section of the debug output it looks like something on the NPC character sheet isn't defined. 
1704228060

Edited 1704228199
So I think you are clicking a button for a PC save. PC save buttons are sheetbuttons that prompt the player to roll an Ability that all my players have on their character sheets named Saves. That is what looks like is happening with that error. I am sorry I didn't call attention to that but yeah the PC saves are setup to roll Abilities on their own character sheet. The NPC save buttons should be re-entry buttons that come back in at the  NPCSavingThrow branch. So NPC buttons shouldn't prompt for an ability like that. NPC sheets don't need anything. If you don't want to check for friendly fire you could delete this whole section: --:DetermineTokensInArea| --/|Check for friendly fire first --~pcTokenCount|array;pagetokens;PCTokenArr;[*C:playerpageid];pc --%pctid|foreach;PCTokenArr -->CheckAdjacency|[*T:t-id];[&pctid];PCAdjacent --?[&PCAdjacent] -eq 1|[ --+Friendly Fire|[b][*[&pctid]:t-name][/b] needs to make a [sheetbutton][*R:spellsave]::[*[&pctid]:character_name]::Saves[/sheetbutton] --]| --%| That PC save section is the only one with a sheetbutton. That is echoed to chat since the intention is that the player controlling the token that's adjacent to the Ice Knife target will roll their own save and adjust their own HP. All the NPC stuff is all contained in the ScriptCard itself and will either auto-roll the save or whisper to the DM with a re-entry button. EDIT: I should say that the way ScriptCards pagetokens filters work is by NPC and PC are linked to characters but PC have someone in the controlledby. So if your token and/or character can be controlled by a player in the game, it will show up in the PC section. In my games, NPC tokens are only controllable by me as the GM and have nothing in controlledby.
Ok, I see what you're saying. The script doesn't seem to roll the save for surrounding npcs, but does see them as friendly fire.
So my NPC tokens never have a player in the controlledby. I don't assign a player to my NPCs so I can rely on that. If you like having a player assigned to the NPC characters, you could not rely on the pc and npc filters for the pagetokens function and instead use the char function and separate them with the npc attribute on the character sheet; like so: ~TokenCount|array;pagetokens;TokenArr;[*C:playerpageid];char %ttid|foreach;TokenArr     -->CheckAdjacency|[*T:t-id];[&ttid];Adjacent     --?"[&Adjacent]" -eq 1|[         --?"[*[&ttid]:npc]" -eq 1|>NPCSection|>PCSection --]| --% Basically something like that moving the PC and NPC sections and relying on the 5e by Roll20 character sheet attributes to determine PC from NPC instead of the controlledby property. Just depends on how you set up your game.
Yeah, sorry about that. My reading comprehension and just comprehension overall has taken a nosedive. For some reason the group of npc's on the page I was testing were all set to be controlled by me. I'm not entirely sure why, no other NPC's were. Now it's working. Thanks
Hi Joshua, On closer inspection, I'm still having trouble with this macro. I don't know if the damage is supposed to be applied to the enemies who fail saves, but it only applies the damage to the initial target. The following section is throwing errors: --?[$saveRoll] -ge [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3]|&result;success --?[$saveRoll] -lt [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3]|&result;failure --?[$saveRoll] -ge [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3]|&result;successmixed --?[$saveRoll] -lt [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3]|&result;failuremixed It's giving the invalid clause joiner error. I don't know if it's affecting it or not, I've had that error and before and scripts still worked. But, I don't see where [&npcSTInfo3] is defined. Although, a lot of this is way over my head.
--~npcSTInfo|string;split;%;[&params] That line splits a string into different String variables on the percent symbol.  Per the wiki : string split string;split;delimiter;stringValue special Splits  stringValue  into pieces, breaking it at  delimiter . Returns a rollVariable with "Count" appended to the assigned variable name and a series of string variables with numbers appended (starting at 1) for each of the returned pieces. And the top of the NPCSavingThrow label I try to document what the parameters being passed in are: --:NPCSavingThrow|NPCTokenID;SaveType;DC So in this case npcSTInfo3 is the DC to check against which gets passed in here: -->NPCSavingThrow|[&adjacentTID];[*R:spellsave];[*R:roll_output_dc] So I'm not sure why that would give an invalid joiner clause for you. The most common way that would occur in the above is if there were a space in one of the variables cause they aren't quoted but these should all be numeric. The initial saving throw versus the Spell Save DC of the caster and then a secondary roll variable to be used if there were advantage or disadvantage applied to the saving throw; again against the spell save DC of the caster. This roll_output_dc is the saving throw retrieved from the properties of the Ice Knife spell in the repeating_spell section for level 1 spells: --Rfind|[*S:character_id];[&FeatureName];repeating_spell-[&MinimumSpellLevel];spellname But to clarify, it will auto-apply damage to secondary targets if this variable is set to true: --&AutoApplyDamage|false It should also whisper the results to you as the DM if you have &verboseOutput set to true. I don't get those errors even in my current testing. Here is an example if both AutoRollNPCSaves and autoApplyDamage are set to false. It will whisper buttons to the DM So the initial attack missed the Phase Spider and it prompted buttons for the Phase Spider and the adjacent Water Elemental. The Water Elemental failed its save and there was a button whispered to apply the damage and then applied 2 damage to the bar1_value of the Water Elemental's token. This is the output with both  AutoRollNPCSaves and autoApplyDamage set to true. This also has verboseOutput as true: This time the attack hit the Phase Spider and both the Phase Spider and Water Elemental failed their initial saves. autoApplyDamage will assume there is no advantage or disadvantage and apply if the initial save fails. In this case 17 damage was applied to bar1_value for the Phase Spider token for the initial attack damage and the splash damage and the Water Elemental's token got the 8 splash damage. I'm not sure that you are seeing or where it is breaking down for you but feel free to ask about any bits of the code you don't understand. I'll do my best to explain it so you can modify it to suit your needs.
Timothy, Since I cannot replicate the errors you are seeing, I added a new function to the original Ice Knife to send some Debug messages to the console:     --:DebugLog|DebugMessage --\|SC [&FeatureName] DEBUG: [%1%] --<| In this example it's set to true --&EnableDebugLogging|true !scriptcard {{ --/|Ice Knife has an attack and area of effect damage --/|Script can be set to auto-roll NPC token saves for AoE damage --/|Script can be set to auto-apply damage to NPC tokens --/|NOTE: PC tokens are always prompted for the player to roll the save. Never autorolls for a player --/|NPC saves also roll a second roll for advantage/disadvantage --/|Auto-applying damage will not use adv/disadvantage and only consider the initial saving throw --/|Requires: ScriptCards 2.3.8+ --/|TODO: allow upcasting --/|VARIABLES TO SET --&FeatureName|Ice Knife --&MinimumSpellLevel|1 --&TargetACAttribute|npc_ac --&AutoRollNPCSaves|false --&AutoApplyDamage|false --&HPBar|1 --&emoteMessage|conjures a sharp cold blade! --&verboseOutput|true --&EnableDebugLogging|true --#sourceToken|@{selected|token_id} --#targetToken|@{target|token_id} --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Found source token [*S:t-id] and target token [*T:t-id] --#title|[&FeatureName] --#emoteText|[*S:character_name] [&emoteMessage] --#reentryval|[&FeatureName][*S:character_id] --Rfind|[*S:character_id];[&FeatureName];repeating_spell-[&MinimumSpellLevel];spellname --?"[*R:spellname]" -eq "NoRepeatingAttributeLoaded"|>NotFound;[&FeatureName] --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Repeating spell lookup for [&FeatureName] returned [*R>] --/|Check spell slot remaining --=RemainingSlots|[*S:lvl[&MinimumSpellLevel]_slots_expended] --?[$RemainingSlots] -eq 0|InsufficientResources --:DeductResources| --!a:[*S:character_id]|lvl[&MinimumSpellLevel]_slots_expended:-=1 --:AttackRoll| --=AtkRoll|1d20 + [*S:spell_attack_bonus] [SPELLATTACK] --?[$AtkRoll] -lt [*T:[&TargetACAttribute]]|Miss --?[$AtkRoll.Base] -eq 20|Crit|Hit --:SplashDamage| --=SplashDamageRoll|[*R:spelldamage2] --+Splash Damage|Each creature in area needs to make a [*R:spellsave] vs DC [*R:roll_output_dc] or take [$SplashDamageRoll] [*R:spelldamagetype2] damage. [*R:spellsavesuccess][br] --:DetermineTokensInArea| --/|Check for friendly fire first --~pcTokenCount|array;pagetokens;PCTokenArr;[*C:playerpageid];pc --%pctid|foreach;PCTokenArr -->CheckAdjacency|[*T:t-id];[&pctid];PCAdjacent --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Checked PC [*[&pctid]:t-name] for adjacency and returned [&PCAdjacent] --?[&PCAdjacent] -eq 1|[ --+Friendly Fire|[b][*[&pctid]:t-name][/b] needs to make a [sheetbutton][*R:spellsave]::[*[&pctid]:character_name]::Saves[/sheetbutton] --]| --%| --~|array;define;AdjancentNPCTokenArr;[*T:t-id] --~npcTokenCount|array;pagetokens;NPCTokenArr;[*C:playerpageid];npc --%npctid|foreach;NPCTokenArr --?"[&npctid]" -eq "[*T:t-id]"|% -->CheckAdjacency|[*T:t-id];[&npctid];NPCAdjacent --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Checked NPC [*[&npctid]:t-name] ID [&npctid] for adjacency returned [&NPCAdjacent] --?[&NPCAdjacent] -eq 1|[ --~|array;add;AdjancentNPCTokenArr;[&npctid] --]| --%| --%adjacentTID|foreach;AdjancentNPCTokenArr --&damageType|[*R:spelldamagetype2] --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Adjacent [*[&adjacentTID]:t-name] [&adjacentTID] checking for [&damageType] immunity --?"[*[&adjacentTID]:npc_immunities]" -inc "[&damageType(tolowercase)]"|[ --*IMMUNE|[*[&adjacentTID]:t-name] is adjacent but immune to [&damageType(tolowercase)] --%| --]| --&buttonParams|[&adjacentTID]%[*R:spellsave]%[*R:roll_output_dc] --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;[&FeatureName] save type: [*R:spellsave] Save DC:[*R:roll_output_dc] --?"[&AutoRollNPCSaves]" -ne "true"|[ --*[*[&adjacentTID]:t-name]|[rbutton][*R:spellsave] Save::NPCSavingThrow;[&buttonParams][/rbutton] --]|[ -->NPCSavingThrow|[&adjacentTID];[*R:spellsave];[*R:roll_output_dc] --]| --%| --:Done| --X| --:Miss| --=AtkDamage|0 --+[&FeatureName]|Attack Roll:[$AtkRoll] misses![br] --^SplashDamage| --:Hit| --=AtkDamage|[*R:spelldamage] --+[&FeatureName]|Attack Roll:[$AtkRoll] hits for [$AtkDamage] [*R:spelldamagetype] damage[br] --^SplashDamage| --:Crit| --+[&FeatureName]|Attack Roll:[$AtkRoll] critical hit! --=DamageRoll|[*R:spelldamage] --=CritRoll|[*R:spelldamage] --=AtkDamage|[$DamageRoll] + [$CritRoll] [CRIT] --+Total Damage|[$DamageRoll] + [$CritRoll] critical damage for [$AtkDamage] total [*R:spelldamagetype] damage[br] --^SplashDamage| --:CheckAdjacency|TargetTokenID;AdjacentTokenID;StringVariableToSet --/|If the tokens overlap then they are also considered adjacent for the purposes of splash damage --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Checking adjacency and overlap for [%1%] and [%2%] -->DoTokensOverlap|[%1%];[%2%];0;[%3%] --<| --/|Check to see if two tokens overlap --/|The y coordinates are negative because roll20 0,0 is the top left corner of the page --/|that makes the y coordinates for tokens negative on a traditional grid --/|The PixelPadding is to allow checking if tokens overlap without sharing a border --/|Set PixelPadding to 1 to remove adjacent tokens that share a border --/|Set to 0 to also get adjacent tokens --:DoTokensOverlap|TokenID1;TokenID2;PixelPadding;StringVariableToSet --=T1HalfHeight|[*[%1%]:t-height] / 2 --=T1HalfWidth|[*[%1%]:t-width] / 2 --/|L1x is realLeft --=L1x|[*[%1%]:t-left] - [$T1HalfWidth] + [%3$] --/|L1y is negative realTop --=L1y|-[*[%1%]:t-top] + [$T1HalfHeight] - [%3%] --/|R1x is right --=R1x|[*[%1%]:t-left] + [$T1HalfWidth] - [%3%] --/|R1y is negative bottom --=R1y|-[*[%1%]:t-top] - [$T1HalfHeight] + [%3%] --=T2HalfHeight|[*[%2%]:t-height] / 2 --=T2HalfWidth|[*[%2%]:t-width] / 2 --/|L2x is realLeft --=L2x|[*[%2%]:t-left] - [$T2HalfWidth] + [%3%] --/|L2y is negative realTop --=L2y|-[*[%2%]:t-top] + [$T2HalfHeight] - [%3$] --/|R2x is right --=R2x|[*[%2%]:t-left] + [$T2HalfWidth] - [%3%] --/|R2y is negative bottom --=R2y|-[*[%2%]:t-top] - [$T2HalfHeight] + [%3%] --&[%4%]|1 --?[$L1x] -gt [$R2x] -or [$L2x] -gt [$R1x]|&[%4%];0 --?[$R1y] -gt [$L2y] -or [$R2y] -gt [$L1y]|&[%4%];0 --<| --X| --/|AutoRoll will use params and non-Auto will use re-entry buttons --:NPCSavingThrow|NPCTokenID;SaveType;DC --?"X[%1%]" -eq "X"|[ --&params|[&reentryval] --#hidecard|1 --]|[ --&params|[%1%]%[%2%]%[%3%] --]| --~npcSTInfo|string;split;%;[&params] --?"[&npcSTInfo2]" -eq "Strength"|&npcSTShortname;str --?"[&npcSTInfo2]" -eq "Dexterity"|&npcSTShortname;dex --?"[&npcSTInfo2]" -eq "Constitution"|&npcSTShortname;con --?"[&npcSTInfo2]" -eq "Intelligence"|&npcSTShortname;int --?"[&npcSTInfo2]" -eq "Wisdom"|&npcSTShortname;wis --?"[&npcSTInfo2]" -eq "Charisma"|&npcSTShortname;cha --~npcSTMod|math;max;[*[&npcSTInfo1]:npc_[&npcSTShortname]_save];[*[&npcSTInfo1]:[&npcSTInfo2(tolowercase)]_save_bonus] --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Making a [&npcSTInfo2] saving throw for [&npcSTInfo1] against DC [&npcSTInfo3] --=saveRoll|1d20 + [$npcSTMod] [MOD] --=advRoll|1d20 + [$npcSTMod] [MOD] --?[$saveRoll] -ge [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3]|&result;success --?[$saveRoll] -lt [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3]|&result;failure --?[$saveRoll] -ge [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3]|&result;successmixed --?[$saveRoll] -lt [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3]|&result;failuremixed --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;[&npcSTInfo1] rolled a primary save of [$saveRoll] and adv/dis roll of [$advRoll] --&tookDamage|0 --?"[*T:t-id]" -eq "[&npcSTInfo1]" -and [$AtkDamage] -gt 0|&initialDamage;1|&initialDamage;0 --?"[&result]" -inc "failure" -or [&initialDamage] -eq 1|&tookDamage;1 --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;[&npcSTInfo1] save result [&result] initial target was [*T:t-id] took damage [&tookDamage] --&msg|Sv:[$saveRoll] A/D:[$advRoll] [b][&result][/b] --?"[&verboseOutput]" -eq "true"|[ --*[*[&npcSTInfo1]:t-name]|[&msg] --]| --?"[&AutoApplyDamage]" -eq "true" -and "[&tookDamage]" -eq 1|>ApplyDamage;[&npcSTInfo1];[&result] --?"[&AutoApplyDamage]" -ne "true" -and "[&result]" -ne "success"|[ --*[*[&npcSTInfo1]:t-name]|[rbutton]Apply Damage::ApplyDamage;[&npcSTInfo1]%[&result][/rbutton] --]| --<| --/|Add a stop here so re-entry doesn't fallthrough when not set to auto --X| --:ApplyDamage|NPCTokenID;Result --?"X[%1%]" -eq "X"|[ --&adParams|[&reentryval] --#hidecard|1 --]|[ --&adParams|[%1%]%[%2%] --]| --&adTID|[&adParams(split,%,0)] --/|Check damage modifiers. Already checked immunity previously -->Lib5E_CheckDamageModifiers|ResistType;[*R:spelldamagetype2] --=TotalDamage|[$SplashDamageRoll] [&ResistType] --/|Check if initial target to add any piercing damage and check result so that successful saving throws don't get splash damage --?"[&adTID]" -eq "[*T:t-id]"|[ --?"[&AutoApplyDamage]" -eq "true" -and "[&adParams(split,%,1)]" -inc "success"|=TotalDamage;0 --=TotalDamage|[$TotalDamage] [SPLASH] + [$AtkDamage] [ATTACK] --]| --?"[&verboseOutput]" -eq "true"|[ --*Applying Damage|[$TotalDamage] damage to bar [&HPBar] of [b][*[&adTID]:t-name][/b] --]| --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Applying [$TotalDamage] Damage to [&adTID] bar[&HPBar]_value --!t:[&adTID]|bar[&HPBar]_value:-=[$TotalDamage] --<| --/|Add a stop here so re-entry doesn't fallthrough when not set to auto --X| --/|From Kurt J's dnd5elib library but porting over here to make scriptcard self contained --:Lib5E_CheckDamageModifiers|damageVariableName;damageType --&[%1%]| --?"[*T:npc_vulnerabilities]" -inc "[%2%]"|>_Lib5E_IsVulnerable;[%1%] --?"[*T:npc_resistances]" -inc "[%2%]"|>_Lib5E_IsResistant;[%1%] --?"[*T:npc_immunities]" -inc "[%2%]"|>_Lib5E_IsImmune;[%1%] --<| --:_Lib5E_IsVulnerable| --&[%1%]| * 2 [Vulnerable] --<| --:_Lib5E_IsResistant| --&[%1%]| \ 2 [Resistant] --<| --:_Lib5E_IsImmune| --&[%1%]| * 0 [Immune] --<| --:NotFound|FeatureName --#emoteText|Not Found --+ERROR|[%1%] not found. --^Done| --:InsufficientResources| --#emoteText|Out of spell slots --+Out of Uses|No more remaining spells for [&FeatureName] --^Done| --:DebugLog|DebugMessage --\|SC [&FeatureName] DEBUG: [%1%] --<| }} You should then see messages in console with information throughout the ScriptCard. Like these: "SC Ice Knife DEBUG: Checked NPC Deer ID -NnFU1TiudECunDoVGgF for adjacency returned 0" "SC Ice Knife DEBUG: Checking adjacency and overlap for -NnFE7_k-5tWlY0-t7iL and -NnaXlua7YcfrZOHfDe3" "SC Ice Knife DEBUG: Checked NPC Goblin ID -NnaXlua7YcfrZOHfDe3 for adjacency returned 0" "SC Ice Knife DEBUG: Adjacent Phase Spider -NnFE7_k-5tWlY0-t7iL checking for Cold immunity" "SC Ice Knife DEBUG: Ice Knife save type: Dexterity Save DC:14" "SC Ice Knife DEBUG: Adjacent Water Elemental -NiLez_P5mZ24KuBiiKt checking for Cold immunity" "SC Ice Knife DEBUG: Ice Knife save type: Dexterity Save DC:14" "SC Ice Knife DEBUG: Making a Dexterity saving throw for -NnFE7_k-5tWlY0-t7iL against DC 14" Hopefully those would help get more insight into what is going wrong with the script in your setup. You can also add any debug logging statements you think would be helpful. Just add: --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Whatever your message you think helpful including with [$RollVariables] [&StringVariables] Feel free to ask any questions you have about it. At its core, this makes an attack roll against a target NPC, uses some math to figure out if any tokens overlap with the initial target, and the roll saving throws and damage for the initial target and any additional NPC tokens.
Hi Joshua, Thanks for your patience with this, it's still way over my head but the debug information is helpful. It look like  [&npcSTInfo3] has no value. "SC Ice Knife DEBUG: Ice Knife save type: Dexterity Save DC:" "SC Ice Knife DEBUG: Making a Dexterity saving throw for -MOiVzELaItDBsHGFKAk against DC" "ScriptCards conditional error: Condition contains an invalid clause joiner on line. Only -and and -or are supported. Assume results are incorrect. 10 -ge -and 14 -ge " "ScriptCards conditional error: Condition contains an invalid clause joiner on line. Only -and and -or are supported. Assume results are incorrect. 10 -lt -and 14 -lt " "ScriptCards conditional error: Condition contains an invalid clause joiner on line. Only -and and -or are supported. Assume results are incorrect. 10 -ge -and 14 -lt " "ScriptCards conditional error: Condition contains an invalid clause joiner on line. Only -and and -or are supported. Assume results are incorrect. 10 -lt -and 14 -ge " Which is this from the macro: --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Making a [&npcSTInfo2] saving throw for [&npcSTInfo1] against DC [&npcSTInfo3] --=saveRoll|1d20 + [$npcSTMod] [MOD] --=advRoll|1d20 + [$npcSTMod] [MOD] --?[$saveRoll] -ge [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3]|&result;success --?[$saveRoll] -lt [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3]|&result;failure --?[$saveRoll] -ge [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3]|&result;successmixed --?[$saveRoll] -lt [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3]|&result;failuremixed Although, the reentry buttons do work to do damage, but the error still happens...
Hmm that is interesting. That comes from a property of the Ice Knife spell in this section of code:     --%adjacentTID|foreach;AdjancentNPCTokenArr --&damageType|[*R:spelldamagetype2] --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Adjacent [*[&adjacentTID]:t-name] [&adjacentTID] checking for [&damageType] immunity --?"[*[&adjacentTID]:npc_immunities]" -inc "[&damageType(tolowercase)]"|[ --*IMMUNE|[*[&adjacentTID]:t-name] is adjacent but immune to [&damageType(tolowercase)] --%| --]| --&buttonParams|[&adjacentTID]%[*R:spellsave]%[*R:roll_output_dc] --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;[&FeatureName] save type: [*R:spellsave] Save DC:[*R:roll_output_dc] --?"[&AutoRollNPCSaves]" -ne "true"|[ --*[*[&adjacentTID]:t-name]|[rbutton][*R:spellsave] Save::NPCSavingThrow;[&buttonParams][/rbutton] --]|[ -->NPCSavingThrow|[&adjacentTID];[*R:spellsave];[*R:roll_output_dc] --]| --%| In there both ways to get into the NPCSavingThrow block of code are set. One via the rbutton used in reentrant scripts  that prompts the GM when it's not set to AutoRollNPCSaves and one via the gosub call or procedure call  when AutoRollNPCSaves is set to true. I am not sure why roll_output_dc is a property on my spells and not yours but that can be replaced easy enough. One caveat being this might not be the correct DC for multiclass casters depending on what class you got the spell. !scriptcard {{ --/|Ice Knife has an attack and area of effect damage --/|Script can be set to auto-roll NPC token saves for AoE damage --/|Script can be set to auto-apply damage to NPC tokens --/|NOTE: PC tokens are always prompted for the player to roll the save. Never autorolls for a player --/|NPC saves also roll a second roll for advantage/disadvantage --/|Auto-applying damage will not use adv/disadvantage and only consider the initial saving throw --/|Requires: ScriptCards 2.3.8+ --/|TODO: allow upcasting --/|VARIABLES TO SET --&FeatureName|Ice Knife --&MinimumSpellLevel|1 --&TargetACAttribute|npc_ac --&AutoRollNPCSaves|false --&AutoApplyDamage|false --&HPBar|1 --&emoteMessage|conjures a sharp cold blade! --&verboseOutput|true --&EnableDebugLogging|true --#sourceToken|@{selected|token_id} --#targetToken|@{target|token_id} --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Found source token [*S:t-id] and target token [*T:t-id] --#title|[&FeatureName] --#emoteText|[*S:character_name] [&emoteMessage] --#reentryval|[&FeatureName][*S:character_id] --Rfind|[*S:character_id];[&FeatureName];repeating_spell-[&MinimumSpellLevel];spellname --?"[*R:spellname]" -eq "NoRepeatingAttributeLoaded"|>NotFound;[&FeatureName] --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Repeating spell lookup for [&FeatureName] returned [*R>] --/|Check spell slot remaining --=RemainingSlots|[*S:lvl[&MinimumSpellLevel]_slots_expended] --?[$RemainingSlots] -eq 0|InsufficientResources --:DeductResources| --!a:[*S:character_id]|lvl[&MinimumSpellLevel]_slots_expended:-=1 --:AttackRoll| --=AtkRoll|1d20 + [*S:spell_attack_bonus] [SPELLATTACK] --?[$AtkRoll] -lt [*T:[&TargetACAttribute]]|Miss --?[$AtkRoll.Base] -eq 20|Crit|Hit --:SplashDamage| --=SplashDamageRoll|[*R:spelldamage2] --+Splash Damage|Each creature in area needs to make a [*R:spellsave] vs DC [*R:roll_output_dc] or take [$SplashDamageRoll] [*R:spelldamagetype2] damage. [*R:spellsavesuccess][br] --:DetermineTokensInArea| --/|Check for friendly fire first --~pcTokenCount|array;pagetokens;PCTokenArr;[*C:playerpageid];pc --%pctid|foreach;PCTokenArr -->CheckAdjacency|[*T:t-id];[&pctid];PCAdjacent --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Checked PC [*[&pctid]:t-name] for adjacency and returned [&PCAdjacent] --?[&PCAdjacent] -eq 1|[ --+Friendly Fire|[b][*[&pctid]:t-name][/b] needs to make a [sheetbutton][*R:spellsave]::[*[&pctid]:character_name]::Saves[/sheetbutton] --]| --%| --~|array;define;AdjancentNPCTokenArr;[*T:t-id] --~npcTokenCount|array;pagetokens;NPCTokenArr;[*C:playerpageid];npc --%npctid|foreach;NPCTokenArr --?"[&npctid]" -eq "[*T:t-id]"|% -->CheckAdjacency|[*T:t-id];[&npctid];NPCAdjacent --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Checked NPC [*[&npctid]:t-name] ID [&npctid] for adjacency returned [&NPCAdjacent] --?[&NPCAdjacent] -eq 1|[ --~|array;add;AdjancentNPCTokenArr;[&npctid] --]| --%| --%adjacentTID|foreach;AdjancentNPCTokenArr --&damageType|[*R:spelldamagetype2] --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Adjacent [*[&adjacentTID]:t-name] [&adjacentTID] checking for [&damageType] immunity --?"[*[&adjacentTID]:npc_immunities]" -inc "[&damageType(tolowercase)]"|[ --*IMMUNE|[*[&adjacentTID]:t-name] is adjacent but immune to [&damageType(tolowercase)] --%| --]| --&buttonParams|[&adjacentTID]%[*R:spellsave]%[*S:spell_save_dc] --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;[&FeatureName] save type: [*R:spellsave] Save DC:[*S:spell_save_dc] --?"[&AutoRollNPCSaves]" -ne "true"|[ --*[*[&adjacentTID]:t-name]|[rbutton][*R:spellsave] Save::NPCSavingThrow;[&buttonParams][/rbutton] --]|[ -->NPCSavingThrow|[&adjacentTID];[*R:spellsave];[*S:spell_save_dc] --]| --%| --:Done| --X| --:Miss| --=AtkDamage|0 --+[&FeatureName]|Attack Roll:[$AtkRoll] misses![br] --^SplashDamage| --:Hit| --=AtkDamage|[*R:spelldamage] --+[&FeatureName]|Attack Roll:[$AtkRoll] hits for [$AtkDamage] [*R:spelldamagetype] damage[br] --^SplashDamage| --:Crit| --+[&FeatureName]|Attack Roll:[$AtkRoll] critical hit! --=DamageRoll|[*R:spelldamage] --=CritRoll|[*R:spelldamage] --=AtkDamage|[$DamageRoll] + [$CritRoll] [CRIT] --+Total Damage|[$DamageRoll] + [$CritRoll] critical damage for [$AtkDamage] total [*R:spelldamagetype] damage[br] --^SplashDamage| --:CheckAdjacency|TargetTokenID;AdjacentTokenID;StringVariableToSet --/|If the tokens overlap then they are also considered adjacent for the purposes of splash damage --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Checking adjacency and overlap for [%1%] and [%2%] -->DoTokensOverlap|[%1%];[%2%];0;[%3%] --<| --/|Check to see if two tokens overlap --/|The y coordinates are negative because roll20 0,0 is the top left corner of the page --/|that makes the y coordinates for tokens negative on a traditional grid --/|The PixelPadding is to allow checking if tokens overlap without sharing a border --/|Set PixelPadding to 1 to remove adjacent tokens that share a border --/|Set to 0 to also get adjacent tokens --:DoTokensOverlap|TokenID1;TokenID2;PixelPadding;StringVariableToSet --=T1HalfHeight|[*[%1%]:t-height] / 2 --=T1HalfWidth|[*[%1%]:t-width] / 2 --/|L1x is realLeft --=L1x|[*[%1%]:t-left] - [$T1HalfWidth] + [%3$] --/|L1y is negative realTop --=L1y|-[*[%1%]:t-top] + [$T1HalfHeight] - [%3%] --/|R1x is right --=R1x|[*[%1%]:t-left] + [$T1HalfWidth] - [%3%] --/|R1y is negative bottom --=R1y|-[*[%1%]:t-top] - [$T1HalfHeight] + [%3%] --=T2HalfHeight|[*[%2%]:t-height] / 2 --=T2HalfWidth|[*[%2%]:t-width] / 2 --/|L2x is realLeft --=L2x|[*[%2%]:t-left] - [$T2HalfWidth] + [%3%] --/|L2y is negative realTop --=L2y|-[*[%2%]:t-top] + [$T2HalfHeight] - [%3$] --/|R2x is right --=R2x|[*[%2%]:t-left] + [$T2HalfWidth] - [%3%] --/|R2y is negative bottom --=R2y|-[*[%2%]:t-top] - [$T2HalfHeight] + [%3%] --&[%4%]|1 --?[$L1x] -gt [$R2x] -or [$L2x] -gt [$R1x]|&[%4%];0 --?[$R1y] -gt [$L2y] -or [$R2y] -gt [$L1y]|&[%4%];0 --<| --X| --/|AutoRoll will use params and non-Auto will use re-entry buttons --:NPCSavingThrow|NPCTokenID;SaveType;DC --?"X[%1%]" -eq "X"|[ --&params|[&reentryval] --#hidecard|1 --]|[ --&params|[%1%]%[%2%]%[%3%] --]| --~npcSTInfo|string;split;%;[&params] --?"[&npcSTInfo2]" -eq "Strength"|&npcSTShortname;str --?"[&npcSTInfo2]" -eq "Dexterity"|&npcSTShortname;dex --?"[&npcSTInfo2]" -eq "Constitution"|&npcSTShortname;con --?"[&npcSTInfo2]" -eq "Intelligence"|&npcSTShortname;int --?"[&npcSTInfo2]" -eq "Wisdom"|&npcSTShortname;wis --?"[&npcSTInfo2]" -eq "Charisma"|&npcSTShortname;cha --~npcSTMod|math;max;[*[&npcSTInfo1]:npc_[&npcSTShortname]_save];[*[&npcSTInfo1]:[&npcSTInfo2(tolowercase)]_save_bonus] --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Making a [&npcSTInfo2] saving throw for [&npcSTInfo1] against DC [&npcSTInfo3] --=saveRoll|1d20 + [$npcSTMod] [MOD] --=advRoll|1d20 + [$npcSTMod] [MOD] --?[$saveRoll] -ge [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3]|&result;success --?[$saveRoll] -lt [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3]|&result;failure --?[$saveRoll] -ge [&npcSTInfo3] -and [$advRoll] -lt [&npcSTInfo3]|&result;successmixed --?[$saveRoll] -lt [&npcSTInfo3] -and [$advRoll] -ge [&npcSTInfo3]|&result;failuremixed --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;[&npcSTInfo1] rolled a primary save of [$saveRoll] and adv/dis roll of [$advRoll] --&tookDamage|0 --?"[*T:t-id]" -eq "[&npcSTInfo1]" -and [$AtkDamage] -gt 0|&initialDamage;1|&initialDamage;0 --?"[&result]" -inc "failure" -or [&initialDamage] -eq 1|&tookDamage;1 --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;[&npcSTInfo1] save result [&result] initial target was [*T:t-id] took damage [&tookDamage] --&msg|Sv:[$saveRoll] A/D:[$advRoll] [b][&result][/b] --?"[&verboseOutput]" -eq "true"|[ --*[*[&npcSTInfo1]:t-name]|[&msg] --]| --?"[&AutoApplyDamage]" -eq "true" -and "[&tookDamage]" -eq 1|>ApplyDamage;[&npcSTInfo1];[&result] --?"[&AutoApplyDamage]" -ne "true" -and "[&result]" -ne "success"|[ --*[*[&npcSTInfo1]:t-name]|[rbutton]Apply Damage::ApplyDamage;[&npcSTInfo1]%[&result][/rbutton] --]| --<| --/|Add a stop here so re-entry doesn't fallthrough when not set to auto --X| --:ApplyDamage|NPCTokenID;Result --?"X[%1%]" -eq "X"|[ --&adParams|[&reentryval] --#hidecard|1 --]|[ --&adParams|[%1%]%[%2%] --]| --&adTID|[&adParams(split,%,0)] --/|Check damage modifiers. Already checked immunity previously -->Lib5E_CheckDamageModifiers|ResistType;[*R:spelldamagetype2] --=TotalDamage|[$SplashDamageRoll] [&ResistType] --/|Check if initial target to add any piercing damage and check result so that successful saving throws don't get splash damage --?"[&adTID]" -eq "[*T:t-id]"|[ --?"[&AutoApplyDamage]" -eq "true" -and "[&adParams(split,%,1)]" -inc "success"|=TotalDamage;0 --=TotalDamage|[$TotalDamage] [SPLASH] + [$AtkDamage] [ATTACK] --]| --?"[&verboseOutput]" -eq "true"|[ --*Applying Damage|[$TotalDamage] damage to bar [&HPBar] of [b][*[&adTID]:t-name][/b] --]| --?"[&EnableDebugLogging]" -eq "true"|>DebugLog;Applying [$TotalDamage] Damage to [&adTID] bar[&HPBar]_value --!t:[&adTID]|bar[&HPBar]_value:-=[$TotalDamage] --<| --/|Add a stop here so re-entry doesn't fallthrough when not set to auto --X| --/|From Kurt J's dnd5elib library but porting over here to make scriptcard self contained --:Lib5E_CheckDamageModifiers|damageVariableName;damageType --&[%1%]| --?"[*T:npc_vulnerabilities]" -inc "[%2%]"|>_Lib5E_IsVulnerable;[%1%] --?"[*T:npc_resistances]" -inc "[%2%]"|>_Lib5E_IsResistant;[%1%] --?"[*T:npc_immunities]" -inc "[%2%]"|>_Lib5E_IsImmune;[%1%] --<| --:_Lib5E_IsVulnerable| --&[%1%]| * 2 [Vulnerable] --<| --:_Lib5E_IsResistant| --&[%1%]| \ 2 [Resistant] --<| --:_Lib5E_IsImmune| --&[%1%]| * 0 [Immune] --<| --:NotFound|FeatureName --#emoteText|Not Found --+ERROR|[%1%] not found. --^Done| --:InsufficientResources| --#emoteText|Out of spell slots --+Out of Uses|No more remaining spells for [&FeatureName] --^Done| --:DebugLog|DebugMessage --\|SC [&FeatureName] DEBUG: [%1%] --<| }} So that is the updated code with the [*R:roll_output_dc] references changed to [*S:spell_save_dc]. So that will use the same spell DC attribute on the character sheet that appears at the top of the spells tab on the 5e by Roll20 sheet. So while I can't really do much to explain why the spell properties were different between our games, hopefully that will fix your issues. And feel free to ask any questions you have. Hopefully you will understand more and more of it and then realize that you can do the same thing but way way better and easier.
Thanks Joshua, That's fixed it for me. I have weirdness occasionally and what seems randomly with all sorts of things. I've sometimes had to delete macros and recreate them because copying and pasting new code doesn't take. That definitely shouldn't be happening, so maybe have corruption happening somewhere. Luckily, I'm DM'ing for new players, so no one is going to be multi-classing.