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

Is there any best practice to minimize "dropped" API Script commands, other than minimizing concurrent installed scripts?

I've tried to do some googling on this and mostly what I've found is "Minimize the number of concurrently-running API scripts installed".  For reference: I am running MetaScript Toolbox, ChatSetAttr, TokenMod, and Dealer. I am only using the API for the following activities: 1) Using chatsetattr for a "Full Refresh" standalone macro that resets a token's stats 2) Using TokenMod in power macros that apply status effects to characters, to automatically set the target's status markers 3) Using Dealer in power macros that apply status effects to also give a status effect card to the target creature's controller 4) Using Dealer in power macros to take power cards from users to reflect that they have been used up 5) Using Dealer in a standalone macro to "Refresh" a player's hand of their default loadout of power cards. The most complex simultaneous usage of the API would be a power macro that uses token-mod to set a marker, dealer to give a status card, and dealer to take a power card from a player. But even that is just three consecutive what-I-assume-to-be low-intensity usages. Even with this pretty modest usage, I occasionally get "dropped" API commands--a power macro will execute but one of these commands will simply not run. It happens infrequently enough that I might not see it at all during testing, but then when we actually start a game it'll happen to players like 5-10% of the time. Is there some best practice I can do to minimize the chance of this happening? I'm not sure if there is a "Wait" command I can place inbetween API commands inside of macros, if that's part of the problem, or if it'd be smart to chain macros together to trigger these.
1696951131
The Aaron
Roll20 Production Team
API Scripter
Can you post some examples of your power macros? 
1696951170
timmaugh
Forum Champion
API Scripter
Yep. This is a known bug . You can use ZeroFrame (already installed as a part of the MetaScriptToolbox) to get around it. Use the batching syntax to turn multiple command lines into a single command line, which ZeroFrame then issues individually. ZeroFrame batching looks like this: !{{ ...individual commands here ...new lines are new commands ...lines beginning with a bang will be sent to the Script Moderator ...lines NOT beginning with a bang will pass through the metascripts, then be output to chat }} Doing this also allows (or forces?) you to reuse rolls between commands. I say "forces" because all rolls are available for all individual commands. So if you  are turning a three command series into a batch, and each of the lines has their OWN unique roll... then if they were individual commands you could re-reference the rolls within the same command line using $[[0]]. However, once they're a batch, if the second command line wanted to re-reference its own roll, that would no longer be $[[0]] -- that would refer to the first roll; the roll in the first line. Instead, the second line would have to reference roll $[[1]], and the third line (to reference its own roll) would have to reference $[[2]]. You can also use deferral characters to hide constructions during the period when the whole message is evaluated (the batched set), exposing them only when the individual command lines are run... but I'm not sure you'll run into a need for that. If you have trouble getting this to work, share your macros and I'll lend a hand.
1696956293

Edited 1696956412
The Aaron said: Can you post some examples of your power macros?  Sure, here's a Power that ended up with a dropped command, even though it only has one API action: /em @{power-5-name} ``**TARGET:**`` @{target|Enemy|token_name} ``**DEFENSE:**`` [[@{target|Enemy|ref}]] Reflex ``**DAMAGE 1️⃣:**`` [[floor(floor(((3d6)+@{power-5-damage})*((((1d20+@{power-5-attack}+?{ Do you or the target have any Accuracy or Defense cards? (Positive Values = + Accuracy or - Defense)|0|1|2|3|4|5|-1|-2|-3|-4|-5}+?{Is the enemy bloodied? (1=Yes)|0|1}+?{Are you using Demonic Attack? (1=Yes)?|0|1}) - (@{target|Enemy|ref}))*0.1)+1)))]] / ``[[(18)+@{power-5-damage}]]``  Fire ``**DAMAGE 2️⃣:**`` [[floor(floor(((3d6)+@{power-5-damage})*((((1d20+@{power-5-attack}+?{ Do you or the target have any Accuracy or Defense cards? (Positive Values = + Accuracy or - Defense)|0|1|2|3|4|5|-1|-2|-3|-4|-5}+?{Is the enemy bloodied? (1=Yes)|0|1}+?{Are you using Demonic Attack? (1=Yes)?|0|1}) - (@{target|Enemy|ref}))*0.1)+1)))]] / ``[[(18)+@{power-5-damage}]]``  Fire ``**EFFECT:**`` All creatures adjacent to your target take [[@{charisma}+@{intelligence}]]  fire damage. !deal {&select @{selected|token_id} } --take --Peacemaker|Explosive Bullet @{power-99-macro} /fx Bloodsplat @{target|Enemy|token_id} /em @{power-5-name} Here's probably the most complicated power macro I have, which has 3 API commands in it: /em @{power-6-name} ``**TARGET:**`` @{target|Enemy|token_name} ``**DEFENSE:**`` [[@{target|Enemy|ref}]] Reflex ``**DAMAGE 1️⃣:**`` [[floor(floor(((3d10)+@{power-6-damage})*((((1d20+@{power-6-attack}+?{ Do you or the target have any Accuracy or Defense cards? (Positive Values = + Accuracy or - Defense)|0|1|2|3|4|5|-1|-2|-3|-4|-5}+?{Is the enemy bloodied? (1=Yes)|0|1}+?{Are you using Demonic Attack? (1=Yes)?|0|1}) - (@{target|Enemy|ref}))*0.1)+1)))]] / ``[[(30)+@{power-6-damage}]]``  Fire ``**DAMAGE 2️⃣:**`` [[floor(floor(((3d10)+@{power-6-damage})*((((1d20+@{power-6-attack}+?{ Do you or the target have any Accuracy or Defense cards? (Positive Values = + Accuracy or - Defense)|0|1|2|3|4|5|-1|-2|-3|-4|-5}+?{Is the enemy bloodied? (1=Yes)|0|1}+?{Are you using Demonic Attack? (1=Yes)?|0|1}) - (@{target|Enemy|ref}))*0.1)+1)))]] / ``[[(30)+@{power-6-damage}]]``  Fire ``**EFFECT:**`` The target gains 10  **Burning Bullet** cards. ``**BURNING BULLET:**`` At the start of your turn, and for each action you take, you take [[@{charisma}]] fire damage. !token-mod --set statusmarkers|Burning-Bullet:0 --ids @{target|Enemy|token_id} !deal {&select @{target|Enemy|token_id} } --give 10 --Effects|Burning Bullet !deal {&select @{selected|token_id} } --take --Peacemaker|Burning Bullet @{power-99-macro} /fx Bloodsplat @{target|Enemy|token_id} /em @{power-6-name} Both of these have occasionally resulted in dropped commands. Yep. This is a  known bug . You can use ZeroFrame (already installed as a part of the MetaScriptToolbox) to get around it. Use the batching syntax to turn multiple command lines into a single command line, which ZeroFrame then issues individually. ZeroFrame batching looks like this: Thanks @timmaugh! So as you can see above, it looks like all I'd need to do is put a !{{ ... }} on both sides of my blocks of !token-mod and !deal commands? So like this: !{{ !deal {&select @{target|Enemy|token_id} } --give 10 --Effects|Burning Bullet !deal {&select @{selected|token_id} } --take --Peacemaker|Burning Bullet !token-mod --set statusmarkers|Burning-Bullet:0 --ids @{target|Enemy|token_id} }} This command-dropping problem is the main reason I haven't tried doing some more complex things like automatically processing Advantage/Disadvantage, but if the solution is as simple as containing everything in !{{...}} then maybe I'll try that!
1698559293

Edited 1698562517
This fix seemed to help but I'm weirdly having dropped API commands again. Did it look like I did it right in the above post? Some things will very consistently work (!token-mod commands), others will almost never work (!deal commands), the commands that don't work work fine if I copy and paste them directly into chat. Having other issues this evening so maybe it's a general problem with the API? EDIT: Seems like something went wrong with the !{{...}} ZeroFrame batching in the last couple weeks, maybe an update broke it? Commands I have nested in this format suddenly either don't work or are inconsistent. Any ideas? Relevant thread, before I realized these were part of the same problem: <a href="https://app.roll20.net/forum/post/11650692/is-there-any-best-practice-to-minimize-dropped-api-script-commands-other-than-minimizing-concurrent-installed-scripts" rel="nofollow">https://app.roll20.net/forum/post/11650692/is-there-any-best-practice-to-minimize-dropped-api-script-commands-other-than-minimizing-concurrent-installed-scripts</a>
1698600911

Edited 1698689028
timmaugh
Forum Champion
API Scripter
OK... sorry, I missed this update where you shared the command you're using. To understand what is going wrong, you have to think about WHEN the metascript constructs are going to process. ZeroFrame detecting the batch commands it needs to dispatch happens with the speed of a normal script, which means there is a metascript loop that happens while the message is on its way to ZeroFrame. Then ZeroFrame parses out the commands it needs to send, and each of those are sent -- probably to scripts that will operate at normal-script speed, so there is another opportunity for a metascript loop for each of these messages before they reach their intended destination (i.e., Dealer or TokenMod, etc.). So when you do this: !{{ !deal {&amp;select @{target|Enemy|token_id} } --give 10 --Effects|Burning Bullet !deal {&amp;select @{selected|token_id} } --take --Peacemaker|Burning Bullet !token-mod --set statusmarkers|Burning-Bullet:0 --ids @{target|Enemy|token_id} }} ...what happens is Roll20 does any parsing it needs to do (things like resolving the targeting statements and the @{selected|token_id} call... or any inline rolls, though you don't have any of those). Then we have the metascript loop while the initial message is on its way to ZeroFrame. During this loop, every one of the metascripts look for work they have to do. Is anything exposed that they will detect? Yep. SelectManager will see the {&amp;select...} statements, and process them immediately. What is the result of that? The first {&amp;select} statement, since it is a select and not an inject statement, will overwrite the currently selected tokens with what you provide (in this case, the targeting statement which has resolved to a token id). Remember, {&amp;select} starts fresh and only selects what you tell it too; {&amp;inject} adds to the currently selected set of tokens. At this point, you only have the targeted token selected. Next, the second {&amp;select} statement, since it is a select and not an inject statement, will also overwrite the currently selected tokens (losing what you just did with the targeting statement), and it will select the list you provide. In this case, it will see the token id that was returned by the Roll20 formation @{selected|token_id}, which resolved before the metascripts ever got involved. So, at this point, you only have the first token which was selected when you sent the message AS your selected token. That's the end of the metascript work, so the script reaches ZeroFrame for batch-dispatch... and it makes sure that every message has the same starting selected tokens, inline rolls, etc. So every message will operate on that last state of your selected tokens. The Fix Hopefully all of that made sense, because the fix you need is to "hide" your metascript constructions until after ZeroFrame has dispatched the individual messages. You want them to resolve during the metascript loop that precedes the message landing with Dealer or TokenMod. To do that, we use line-deferral characters. These are characters you define, then you use them to disguise (or "break up") metascript constructions that you don't want to be recognized immediately. Then ZeroFrame, as a part of dispatching each message, will look to remove those characters from the command line (in effect, reconstructing the metascript constructions so they are detectable). You can read this post (specifically the part on Deferral Characters) for more info on how to use them, but for now, either of these should work. The first uses batch-level deferral (using the same character for all batch lines), while the second will use individual line-level deferrals: !{{(^) !deal {^&amp;select @{target|Enemy|token_id} } --give 10 --Effects|Burning Bullet !deal {^&amp;select @{selected|token_id} } --take --Peacemaker|Burning Bullet !token-mod --set statusmarkers|Burning-Bullet:0 --ids @{target|Enemy|token_id} }} ...second form... !{{ (^)!deal {^&amp;select @{target|Enemy|token_id} } --give 10 --Effects|Burning Bullet (^)!deal {^&amp;select @{selected|token_id} } --take --Peacemaker|Burning Bullet !token-mod --set statusmarkers|Burning-Bullet:0 --ids @{target|Enemy|token_id} }} Hopefully that helps. I think the problem is less that&nbsp; you're getting dropped commands, but that you're trying to act on the wrong token, and either can't (because it doesn't have the prerequisite conditions/properties), or you're getting unexpected results that maybe you're not noticing.
Thanks for the help! I think I get the gist of what you're saying, it's tough not being as-immersed in the way Roll20 works under-the-hood. I'll apply the fixes you suggested, but can you help me simplify when I do and don't need deferral characters? Is this only when I'm using &amp;select &nbsp;commands? Right now, the most complicated stuff I do with the API is deal and take cards, set token attribute values, set and remove marker values. If that's the only thing I'm doing do I only ever need ^ characters for the select statement lines? Thanks!
Ok, so I need some help still with this simple refresh macro. !token-mod --set statusmarkers|=blue|-blue --ids @{selected|token_id} !setattr --charid @{selected|character_id} --hp|%hp-max% !setattr --charid @{selected|character_id} --tmp-hp|0 Where would I put the deferral characters in this instance? When I run this by itself, it works (although sometimes it drops lines). When I put it in !{{...}} (with no deferral characters), it used to work a couple weeks ago, but now the second line always fails and returns this: Setting attributes Setting hp to %hp-max for character Ned. This might help me intuit what I need to do for future issues better. Thanks!
1698676008
timmaugh
Forum Champion
API Scripter
Marstead said: Thanks for the help! I think I get the gist of what you're saying, it's tough not being as-immersed in the way Roll20 works under-the-hood. I'll apply the fixes you suggested, but can you help me simplify when I do and don't need deferral characters? Is this only when I'm using &amp;select &nbsp;commands? Right now, the most complicated stuff I do with the API is deal and take cards, set token attribute values, set and remove marker values. If that's the only thing I'm doing do I only ever need ^ characters for the select statement lines? Thanks! Specifically in reference to batching lines (to avoid dropped commands), here are the basic ideas to consider when you think about deferral characters: 1) Does any part of your message (any of the multiple batched lines or any un-batched lines) contain a targeting statement? If so, you will not have a "selected" property -- that is, a script won't recognize any tokens are selected. (This is a bug that goes back to the original programming of Roll20 with the expectation that users would never want/need to have both selected tokens and targeted tokens.) 1A) Do you have tokens that need to be selected for all of the batched messages? That is, will all of the messages share the same selected tokens? If so, you can include a {&amp;select} statement anywhere in the message and it will take effect during the first cycle -- before ZeroFrame dispatches the individual batched lines. 1B) Will messages require different tokens to be selected? If so, you will need to include individual {&amp;select} statements to each line that needs to be different. Since you need these to be different, you will have to defer them so they don't resolve until the individual command line is dispatched. 2) Do you include a Fetch construction to retrieve token information from a selected token? Remember, Roll20 constructions for retrieving information from a selected token (like @{selected|token_id} ) will resolve before the message is even handed off to scripts, so it will act on the first token selected when you run the overall command -- before any {&amp;select} statement would alter what tokens were selected. In fact, this Roll20 construction will resolve even if you have a targeting statement; you just won't have a selected token when the message reaches your scripts. Fetch constructions, on the other hand, run during the metascript cycle, and they run after {&amp;select} statements (so long as ZeroFrame is installed, too). That way, you can use {&amp;select} statements to alter what you have selected, then still use a Fetch construction to return data&nbsp; from the token, i.e. : @(selected.token_id) Fetch constructions also make more datapoints available than standard Roll20 constructions (Fetch lets you get things like rotation, Turn Order initiative value, aura distance, status markers, etc.), so you might need a Fetch construction even if you don't use a {&amp;select} statement to select the tokens you want. 2A) If you are using a Fetch construction that will use the "selected" designation (like the example just above), is the correct token selected for the top level message (either by physical selection on the game board without a {&amp;select} statement changing that information, or by an non-deferred {&amp;select} statement)? If so, you don't need to defer the Fetch construction. It will act on the first selected token and will resolve before any individual message is dispatched. 2B) If the correct token is NOT selected until you are within a specific, individual command line (a batched command line), then you will need to defer the Fetch construction until you are in that line. You can use the same deferral as the {&amp;select} statement would get, since during any metascript loop (the overall message loop or a loop for any given individual batched command line) Fetch runs after SelectManager by default. For instance, this batched command line (imagine this is within the !{{...}} enclosure): &nbsp; (^)!token-mod --set bar1|@^(selected.tracker) {^&amp;select Volcano Man} That would only resolve the metascript constructions when this individual command was dispatched. At that point, the token named "Volcano Man" would be selected. Fetch would then use the selected token reference to return the Turn Order initiative value for the selected token. Since Fetch had to wait for the token to be selected, it also needed to be deferred. Finally, with all of that meta-work done, TokenMod can go about setting bar1 of the selected token (Volcano Man) to the value Fetch has returned. 3) Do you have to take individualized action on each of the selected tokens? For instance, perhaps you want to set a bar value equal to that token's tracker value, with each token having their own entry in the Turn Order. In that case, you will need to run forselected, and use forselected deferrals. If you need to select tokens for this batched sub-command (that is, this command will require different selected tokens than the overall command which reaches ZeroFrame, that {&amp;select} statement will require ZeroFrame batch deferral, but the Fetch construction within the line can take the forselected deferral: (^)!forselected(~) token-mod --set bar1|@~(selected.tracker {^&amp;select Goblin*} Nothing in the above line would register during the initial metascript loop while the message is on the way to ZeroFrame. Only when this line is dispatched will ZeroFrame detect the line-level deferral (the caret), and remove it from the downstream command it issues. When that message is received, it will pass through another metascript loop where the now-exposed {&amp;select...} statement is detected, selecting all Goblins on the page. That will end the metascript work for that message, and it will pass to SelectManager (catching the forselected handle). SelectManager now has a message with all of the Golbins selected, and it is being asked to dispatch a TokenMod call for each Goblin token, iterating over them. As a part of this, SelectManager detects the deferral character declaration (the tilde), and it removes it from the command line it will dispatch. This, in effect, exposes the Fetch construction to retrieve the token's tracker value. Each of these iterated messages will have only 1 token selected (the next one in the series), so the Fetch construction referencing "selected" will now act on the correct token when each message registers finally to TokenMod. This is just a primer on the timing of things and when/why you might need to defer constructions, and how you would go about doing that. Any individual case might present complications, however, so if you run into trouble don't hesitate to post a question and I can try to answer.
1698676983
timmaugh
Forum Champion
API Scripter
Marstead said: Ok, so I need some help still with this simple refresh macro. !token-mod --set statusmarkers|=blue|-blue --ids @{selected|token_id} !setattr --charid @{selected|character_id} --hp|%hp-max% !setattr --charid @{selected|character_id} --tmp-hp|0 Where would I put the deferral characters in this instance? When I run this by itself, it works (although sometimes it drops lines). When I put it in !{{...}} (with no deferral characters), it used to work a couple weeks ago, but now the second line always fails and returns this: Setting attributes Setting hp to %hp-max for character Ned. This might help me intuit what I need to do for future issues better. Thanks! Working through the points I included in my previous post, you'll find that there are no deferral characters required for this. You don't have a targeting statement, which means you WILL have a set of selected tokens (even a set of just 1 token). Your selected token is universal for all lines that would be included in the batch, so there is no need to include a deferred {&amp;select} statement for any of them, and you can just rely on the Roll20 constructions to retrieve the token id and character id. As for what is going on in that line where it is dropping the last % symbol... neither ZeroFrame nor ChatSetAttr have received an update recently that would change that behavior. In fact, I just tried your commands (but batched up in !{{...}} ) in my game, and everything worked fine. (I had to change the name of "hp-max" since I didn't have an attribute named that -- I just pointed it at another attribute I knew I had.) Could it be possible that you have an older version of ZeroFrame installed? Just wondering if there was a version that had an error with parsing the batched lines. If you have ScriptInfo, run: !scriptinfo ...and report back with your ZeroFrame version, if you would. That will help me narrow down whether it's a bug with ZeroFrame or not.
1698679765

Edited 1698679861
Thanks, here's !scriptinfo -- I just tested the macro again and it works without !{{...}} but breaks with the same error if I add !{{...}}. Double checked the script installation screen and these are all set to "Latest" Script Information SCRIPT NAME START / LINES TokenMod (0.8.77) 798 / (4081) MathOps (1.0.8) 4879 / (341) Plugger (1.0.6) 5220 / (551) libTable (1.0.0) 5770 / (159) Muler (2.0.2) 5929 / (641) SelectManager (1.1.7) 6570 / (1054) checkLightLevel 10135 / (376) libTokenMarkers (0.1.2) 10511 / (194) Messenger (1.0.1) 10705 / (641) Fetch (2.0.9) 11346 / (1445) APILogic (2.0.8) 12792 / (771) libInline (1.0.6) 13563 / (352) ZeroFrame (1.1.6) 13915 / (996) MetaScriptToolbox (0.0.1) 14911 / (234) ScriptInfo (0.0.2) 15311 / (143)
1698686540
timmaugh
Forum Champion
API Scripter
And what do you get if you select that same token and just enter into the chat: @{selected|hp-max} I'm betting at some point you've overwritten that field so it might currently read: %hp-max Or, run this command and it will return any tokens that might have that value: !forselected(^) @^(selected.token_name) has a malformed hp-max attribute.{^&amp;simple}{^&amp;end}{&amp;select *, @hp-max=%hp-max}
1698688796

Edited 1698689558
If I select the token and type that in chat, it works (it returns the numerical value contained in hp-max in the character sheet, not %hp-max). If I run the macro without the !{{...}} covering it, it works. It happens regardless of the token I use it on, but only when I contain the macro in !{{...}} As for the !forselected command, it returns tokens that I've tried to use the !{{...}} Full Refresh macro on. It **won't** return other tokens until I use the !{{...}} Full Refresh Macro on them. If I remove !{{...}} from the Full Refresh Macro, the macro works on all tokens without issue. That token will have its HP set appropriately and will not show up as having a malformed attribute when I run that !forselected command. This all didn't start happening until this past weekend so I'm still not sure what's broken. I dove into the Character Sheets for the affected characters and hp-max looks fine (has the number value I expect to see).
1698689798

Edited 1698691052
timmaugh
Forum Champion
API Scripter
All of the tokens returned by the line: !forselected(^) @^(selected.token_name) has a malformed hp-max attribute.{^&amp;simple}{&amp;select *, @hp-max=%hp-max} ...have a bad setup that you need to FIX. Not sure if we have a misunderstanding at this point, so my apologies if I explain something you already understand... Your ChatSetAttr line is setting a character's hp attribute to the current value of their hp-max attribute. If that character currently has the text string "%hp-max" in their hp-max attribute, then that is what the hp attribute will be updated to, and ChatSetAttr will happily report that it set the hp attribute to "%hp-max" (the current value of the hp-max attribute)... just the same way it would report that it had set the hp to 17 if the contents of the hp-max attribute said "17". In other words, I think your ChatSetAttr line is working as intended, it's just that for certain characters (those returned by the forselected statement I provided), the source value is corrupted and must be repaired. (Incidentally, the forselected line might not catch ALL of the offending characters... it can only detect characters attached to the tokens it can scoop up on the page with the {&amp;select} statement -- so characters who have tokens on the page. I'd think that would account for all of your problem characters unless you changed what tokens were on the map in the last couple of weeks.) So, repair one character's hp-max value (it doesn't even have to be accurate -- just give it a number), and then run this command for it: !{{ !token-mod --set statusmarkers|=blue|-blue --ids @{selected|token_id} !setattr --charid @{selected|character_id} --hp|%hp-max% !setattr --charid @{selected|character_id} --tmp-hp|0 }} I am betting it will then work for you, and you'll just have to correct the rest of the characters who are similarly corrupted. If there is a macro you are running which then *corrupts* these characters, post that, and we might be able to track down what is going wrong.
1698689947

Edited 1698690054
Do you know how I can detect or clear the corrupted value? When I look in Attributes, there's just a number in there. I can delete it, replace it, there's nothing in there but the number "100": Is there somewhere else I can check to see that the value has been corrupted? I expect I can just click in those boxes, ctrl+a to make sure it selects everything in the box, delete, type 100 again. But I do that and the Macro still fails the same way.
1698690228

Edited 1698690417
Here's a screenshot of the error. Again, this only happens if I contain my macro in !{{ ... }} ZeroFrame brackets!! &nbsp;If I run the macro normally, it works just fine and correctly sets the HP . When I look at this character's max-hp attribute it shows a numerical value, it does NOT say %hp-max at any point. EDIT: Here's a screenshot of the macro working fine provided I remove the !{{ ... }} ZeroFrame brackets from around it : I can click the macro over and over and so long as I don't have it in !{{ ... }} ZeroFrame brackets, it works every time. As soon as I put it in ! {{ ... }} ZeroFrame brackets, it returns the %hp-max error.
1698690358
timmaugh
Forum Champion
API Scripter
Hmm... can you invite/promote me so I can take a look?
timmaugh said: Hmm... can you invite/promote me so I can take a look? Sure, give me a few mins to set that up. Thank you!
1698692934

Edited 1698692998
Thanks a ton @timmaugh for the help, always a little embarrassing to have people peek in your campaign 😂 It does look like it was a bona fide bug, but a very weird one! For anyone who happens to stumble on this thread searching because they encountered a similar issue, the under-the-hood issue is complex (timmaugh can likely explain in full detail) but the gist of it is that when you have a line break in a block, it can break things like the %hp-max% construction I have in the sample macro. THE FIX (From @Timmaugh): Simply add a space to the end of any line where %attr% are being called by chatsetattr in a ZeroFrame construction and it works. Very simple fix! So for example, in my problematic macro: !{{ !token-mod --set statusmarkers|=blue|-blue --ids @{selected|token_id} !setattr --charid @{selected|character_id} --hp|%hp-max%&nbsp; !setattr --charid @{selected|character_id} --tmp-hp|0 }} There needs to be a space after %hp-max% above, or the line break causes problems. Thanks again @timmaugh!