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

What's the best way to do Advantage/Disadvantage/Normal/Elven Advantage rolls within api macros?

I'll preface this by saying that I have made a concerted effort to avoid asking my players to click through several menus to do basic things like attacking. As such, unless it is absolutely necessary, I avoid prompting my players with a dropdown menu from a roll query, since it tends to slow things down. When I am writing macros, I am trying to facilitate game play so players can focus more on the game than trying to navigate the vagaries of the mechanics. So the most ideal attack for me would look something like this: Using power cards, I produce a neat, easy to read menu of options for them to see what they can do in combat. In this case, if my player clicks Eldritch Blast, it rolls to attack. If that attack hits, then it rolls damage. The damage is automatically applied and the information is concisely presented for everyone to read. Nice. Except, in this case, if my player has advantage, he's going to have to click the attack twice, since I don't know whether or not he has advantage when I wrote this macro. In the example below, my player rolls eldritch blast twice, missing once and hitting the second time. Honestly, the cool solution seems to be to check the @{player|AdvantageToggle} attribute. But that doesn't give you a simple value. If you do that you get something like "{{query=1}} {{advantage=1}} {{r2=[[1d20" in the chat window. I can't use that in a macro. I've considered having a few attributes called Advantage, Normal, Disadvantage, and Elven Advantage on the character sheets. In theory, ChatSetAttr could handle advantage/disadvantage so long as my players use that. But that only works if I am willing to basically build a whole character sheet worth of macros for every skill and save and tool and attack and spell all using these attributes. It's an enormous task to handle. There's gotta be a way to handle advantage and disadvantage without displaying advantage as always on (which could affect my auto damage rolls and just generally tidiness of my macros), adding another menu for my players to click through, or redoing the entire sheet. What's the answer here?
1620662353

Edited 1620662372
timmaugh
Forum Champion
API Scripter
The meta-toolbox could probably help, here... for instance, the following would utilize Fetch and APILogic (and ZeroFrame to make sure those scripts are properly ordered) to read the AdvantageToggle property: {& if @(selected.AdvantageToggle)~advantage} ...stuff for advantage... {& elseif @(selected.AdvantageToggle)~normal} ...stuff for normal... {&else} ...stuff for disadvantage {&end} All of that would run before your macro. You could use it to designate a variable, if the macro allowed it... {& if @(selected.AdvantageToggle)~advantage} advantage {& elseif @(selected.AdvantageToggle)~normal} normal Or you could use it to actually branch your macro by determining which parts would be kept in the macro (only those in the "true" branch would survive). Hopefully that helps. If it isn't clear, I can probably speak more specifically if you share your actual macro.
1620664465

Edited 1620664524
Oh right, I didn't actually post the macro. Let's use that Eldritch Blast as an example: !power {{  --name|Eldritch Blast  --leftsub|Ranged Spell Attack  --rightsub|120 ft. Range  --orowbg|#ffffff  --erowbg|#ffffff  --Attack:|[[ [$Atk] 1d20 + @{spell_attack_bonus}]]  --?? $Atk >= @{target|npc_AC} ?? Damage:|[[ [$Dmg] 1d10 + @{charisma_mod}]]  --?? $Atk.total >= @{target|npc_ac} ?? alterbar1|_target|@{target|token_id} _bar|3 _amount|-[^Dmg] _show|all  --soundfx|_audio,play,nomenu|Eldritch Blast  --vfx_opt|@{selected|token_id} @{target|token_id} Laser }} I really appreciate your help here on this. Are you saying that I should use an api like Fetch, ApiLogic and  Zero Frame  so I can pass the advantage state to the api call? I'm not really sure how that works in Roll20, but it sounds super interesting.
1620675946

Edited 1620678544
timmaugh
Forum Champion
API Scripter
It would be one way to do it, yes. As I understand the advantage mechanic (not really a DnD player), the only change to your roll formula for advantage, normal, or disadvantage is: Normal : 1d20 Advantage : 2d20kh1 Disadvantage : 2d20kl1 If that is accurate, and if you have the sheet set to use the advantage toggle, then you can change the line in your macro that reads:  --Attack:|[[ [$Atk] 1d20 + @{spell_attack_bonus}]] ...to instead read:  --Attack:|[\][\] [$Atk] {& if @(selected.AdvantageToggle)~normal}1d20{& elseif @(selected.AdvantageToggle)~disadvantage}2d20kl1{&else}2d20kh1{&end} + @{spell_attack_bonus}\]\] By the time PowerCards reads that line, the value will have been substituted. It will effectively see one of these formations:  --Attack:|[[ [$Atk] 1d20 + @{spell_attack_bonus}]]  --Attack:|[[ [$Atk] 2d20kl1 + @{spell_attack_bonus}]]  --Attack:|[[ [$Atk] 2d20kh1 + @{spell_attack_bonus}]] Also, now that I think about it, this might also require SelectManager... EDIT: Forgot to defer the inline roll until after APILogic completes its work.
Geez, that's slick. Forgive me because I'm a bit confused on this. And yeah, you've got advantage/disadavantage correct. You're saying the completed macro should be like this: !power {{  --name|Eldritch Blast  --leftsub|Ranged Spell Attack  --rightsub|120 ft. Range  --orowbg|#ffffff  --erowbg|#ffffff  --Description:|A beam of crackling energy streaks toward a creature within range.    --Attack:|[\][\] [$Atk] {& if @(selected.AdvantageToggle)~normal}1d20{& elseif @(selected.AdvantageToggle)~disadvantage}2d20kl1{&else}2d20kh1{&end} + @{selected|spell_attack_bonus}\]\]  --?? $Atk >= @{target|npc_AC} ?? Damage:|[[ [$Dmg] 1d10 + @{charisma_mod}]] force damage  --?? $Atk < @{target|npc_ac} ?? !Missed|**You missed!**  --?? $Atk.total >= @{target|npc_ac} ?? alterbar1|_target|@{target|token_id} _bar|3 _amount|-[^Dmg] _show|all  --?? $Atk.base == 20 ?? Critical Hit:|[[ [$CritDmg] 2d10+@{charisma_mod}]] force damage  --?? $Atk.base == 20 ?? alterbar2|_target|@{target|token_id} _bar|3 _amount|-[^CritDmg] _show|GM  --soundfx|_audio,play,nomenu|Eldritch Blast  --vfx_opt|@{selected|token_id} @{target|token_id} Laser }} So, yeah, I've added some additional stuff to this macro. I've bolded the section where the attack roll is determined. When I attempt to run this, I get a TypeError: h is undefined.
1620697452

Edited 1620697857
timmaugh
Forum Champion
API Scripter
Hmm... well let's make at least one change to the roll. First, to be clear, you should have ZeroFrame, Fetch, SelectManager, and APILogic installed. ZeroFrame also requires a script called libInline. If you have those, then this line should result in a real return for you (copy/paste into chat with the token selected): !The character's advantage state is currently {& if @(selected.AdvantageToggle)~normal}normal{& elseif @(selected.AdvantageToggle)~disadvantage}disadvantage{&else}advantage{&end}. They would roll {& if @(selected.AdvantageToggle)~normal}1d20{& elseif @(selected.AdvantageToggle)~disadvantage}2d20kl1{&else}2d20kh1{&end}. If I rolled it right now, that would be [\][\]{& if @(selected.AdvantageToggle)~normal}1d20{& elseif @(selected.AdvantageToggle)~disadvantage}2d20kl1{&else}2d20kh1{&end}\]\]{&simple} Should get you something output like: The character's advantage state is currently advantage. They would roll 2d20kh1. If I rolled it right now, that would be  14 If that all works, good. Go ahead and change the line to reverse the [$Atk] and the IF block. These rolls are now going to be coming from the API (rather than the initial call from the user in chat), so we have to avoid certain structures that break the parser.  --Attack:|[\][\] {& if @(selected.AdvantageToggle)~normal}1d20{& elseif @(selected.AdvantageToggle)~disadvantage}2d20kl1{&else}2d20kh1{&end} [$Atk] + @{spell_attack_bonus}\]\] That should work. Give that a try and report back. EDIT: Thanks to Aaron for pointing me in the right direction on this.
I'm feeling a bit like a dummy here. So I'll post what I've got. I realized that ZeroFrame wasn't identifying any of the APIs that I got from the script library. I disabled those and went to GitHub and got Fetch, SelectManager, APILogic and libline. Now that I've saved those scripts, ZeroFrame is correctly identifying them (which was probably the 1st problem I was having). Now, if I run that first macro, it correctly reports whether or not my character has advantage or not. It also correctly rolls and that's all well and good. But I'm still running into errors with the power cards api. !power {{ --name|Eldritch Blast --leftsub|Ranged Spell Attack --rightsub|120 ft. Range --orowbg|#ffffff --erowbg|#ffffff --Description:|A beam of crackling energy streaks toward a creature within range. --Attack:|[[ [$Atk] 1d20 + @{spell_attack_bonus}]] --?? $Atk >= @{target|npc_AC} ?? Damage:|[[ [$Dmg] 1d10 + @{charisma_mod}]] force damage --?? $Atk < @{target|npc_ac} ?? !Missed|**You missed!** --?? $Atk.total >= @{target|npc_ac} ?? alterbar1|_target|@{target|token_id} _bar|3 _amount|-[^Dmg] _show|all --?? $Atk.base == 20 ?? Critical Hit:|[[ [$CritDmg] 2d10+@{charisma_mod}]] force damage --?? $Atk.base == 20 ?? alterbar2|_target|@{target|token_id} _bar|3 _amount|-[^CritDmg] _show|GM --soundfx|_audio,play,nomenu|Eldritch Blast --vfx_opt|@{selected|token_id} @{target|token_id} Laser }} This is the base macro. It was working prior to my installation of the apis above. Now, it reports a syntax error. If I run the macro with the updated line for the [$Atk], I get the same error. !power {{ --name|Eldritch Blast --leftsub|Ranged Spell Attack --rightsub|120 ft. Range --orowbg|#ffffff --erowbg|#ffffff --Description:|A beam of crackling energy streaks toward a creature within range. --Attack:|[\][\] {& if @(selected.AdvantageToggle)~normal}1d20{& elseif @(selected.AdvantageToggle)~disadvantage}2d20kl1{&else}2d20kh1{&end} [$Atk] + @{selected|spell_attack_bonus}\]\] --?? $Atk >= @{target|npc_ac} ?? Damage:|[[ [$Dmg] 1d10 + @{selected|charisma_mod}]] force damage --?? $Atk < @{target|npc_ac} ?? !Missed|**You missed!** --?? $Atk.total >= @{target|npc_ac} ?? alterbar1|_target|@{target|token_id} _bar|3 _amount|-[^Dmg] _show|all --?? $Atk.base == 20 ?? Critical Hit:|[[ [$CritDmg] 2d10+@{selected|charisma_mod}]] force damage --?? $Atk.base == 20 ?? alterbar2|_target|@{target|token_id} _bar|3 _amount|-[^CritDmg] _show|GM --soundfx|_audio,play,nomenu|Eldritch Blast --vfx_opt|@{selected|token_id} @{target|token_id} Laser }}         Changes Bolded You had mentioned that I may need to change the line to reverse the $Atk and the IF block, but I'm unclear what you're referring to there. I suspect you mean that the $Atk variable is populated after the if block, as you show in your 2nd example. If the macro I am running above is incorrect, I'd appreciate if you could show me where it needs to be changed. Notable Information. !power {{ --name|Eldritch Blast --leftsub|Ranged Spell Attack --rightsub|120 ft. Range --orowbg|#ffffff --erowbg|#ffffff --Description:|A beam of crackling energy streaks toward a creature within range. --Attack:|[\][\] {& if @(selected.AdvantageToggle)~normal}1d20{& elseif @(selected.AdvantageToggle)~disadvantage}2d20kl1{&else}2d20kh1{&end} [$Atk] + @{spell_attack_bonus} \]\] --?? $Atk >= @{target|npc_ac} ?? Damage:|[[ [$Dmg] 1d10 + @{charisma_mod} ]] force damage --?? $Atk < @{target|npc_ac} ?? !Missed|**You missed!** --?? $Atk.total >= @{target|npc_ac} ?? alterbar1|_target|@{target|token_id} _bar|3 _amount|-[^Dmg] _show|all --?? $Atk.base == 20 ?? Critical Hit:|[[ [$CritDmg] 2d10+ @{charisma_mod} ]] force damage --?? $Atk.base == 20 ?? alterbar2|_target|@{target|token_id} _bar|3 _amount|-[^CritDmg] _show|GM --soundfx|_audio,play,nomenu|Eldritch Blast --vfx_opt|@{selected|token_id} @{target|token_id} Laser }}         Changes bolded If I type the macro using @{charisma_mod} or @{spell_attack_bonus} as I did above, it will say type h is undefined. then it will loop through that error like a hundred times really quickly. Shown here.
1620767430
timmaugh
Forum Champion
API Scripter
Sorry about that, Jon... First, yes, it's good that you got the scripts from my personal repo. I'd been letting people use the new scripts from there to shake out any bugs, and just today I submitted them to the one-click install. They should be there next week. Right now, I would suggest you regrab libInline as I did catch an error in that library and corrected it (see (E), below). Second, I did some testing on my end and there were a couple of problems in what I sent you last time. 1) the field we were looking for was advantagetoggle (lower case), so Fetch wasn't returning anything.  2) the returned data from that field was forcing funny breaks in the IF parser; we can get around that by wrapping the returned text in either a single quote ('), double quotes ("), or tick (`). The below macro worked for me in my test game: !power {{  --name|Eldritch Blast  --leftsub|Ranged Spell Attack  --rightsub|120 ft. Range  --orowbg|#ffffff  --erowbg|#ffffff  --Description:|A beam of crackling energy streaks toward a creature within range.   --Attack:|[\][\] {& if "@{selected|advantagetoggle}"~normal}1d20{& elseif "@{selected|advantagetoggle}"~disadvantage}2d20kl1{&else}2d20kh1{&end} [$Atk] + @{selected|spell_attack_bonus}\]\]   --?? $Atk >= @{target|npc_ac} ?? Damage:|[[ [$Dmg] 1d10 + @{selected|charisma_mod}]] force damage  --?? $Atk < @{target|npc_ac} ?? !Missed|**You missed!**  --?? $Atk.total >= @{target|npc_ac} ?? alterbar1|_target|@{target|token_id} _bar|3 _amount|-[^Dmg] _show|all  --?? $Atk.base == 20 ?? Critical Hit:|[[ [$CritDmg] 2d10+@{selected|charisma_mod}]] force damage  --?? $Atk.base == 20 ?? alterbar2|_target|@{target|token_id} _bar|3 _amount|-[^CritDmg] _show|GM  --soundfx|_audio,play,nomenu|Eldritch Blast  --vfx_opt|@{selected|token_id} @{target|token_id} Laser }} The line we've been working on is bolded there. Highlighting a couple of things happening so you can replicate for other macros: A) we're using the default Roll20 construction to retrieve the data: @{selected|advantagetoggle} That will be detected and retrieved immediately. In our case, it doesn't matter when it is detected and replaced with the appropriate data. If, however, you need to schedule this more to your liking, or if you wanted to supply a default value for the attribute in case nothing was provided, you could use the Fetch construction: @(selected.advantagetoggle) B) we wrap the returned data in quotation marks "@{selected|advantagetoggle}" This tells the IF block parser "this whole thing" is what we need to compare. Do this if you return any text with spaces, an equals or a brace. In our case, the field had all 3. Everywhere we feed this text to an IF, we need to do this (that includes the IF and the ELSEIF). C) We use the "includes" test The tests available to the IF block are listed on the APILogic wiki article . We use the "~" to indicate we want to know if the left side of the comparison includes the text on the right. D) defer the inline roll We want to return the value from the IF block to build the equation for the inline roll, so we need to delay Roll20 detecting it. The escape character for ZeroFrame is the backslash, except for the front end of an inline roll, which also must have a closing bracket. So an inline roll that is deferred one time will be: [\][\] .... \]\] Two deferrals would be: [\\][\\] ... \\]\\] E) Move the roll label to be later in the inline roll equation For an inline roll originating from the API (which these will become, using this technique), there are certain formations we have to stay away from. One is having a roll label as the first thing the parser sees in an equation. There are two ways to handle that... you can move it away from being the first thing inside the double brackets -- this is what we did above. The other thing you can do is to insert a 0d20 type formation before it. Not sure if that changes the way PowerCards lets you do .base or .total references, but the Roll20 parser allows it (and now so does libInline).