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

Token Mod / ChatSetAttr] Having Some Difficulty Writing a Macro For Swarm Encounters.

1616425576

Edited 1616448783
Saving you a Click. I have posted the working version at the top of this post here. I have kept the rest of the post in tact so you may read if you like. I'll save everyone a scroll. I have my updated macro right here. It's working as it is. It relies on a character attribute called "NumberOfVillagers" to be on the character named "Mob of Villagers". If you want to implement this in your own games, just change those to be whatever you'd like. You'll also need a rollable table with a number of sides equal to the number of members of your mob/swarm/etc. The Mob of Villagers in my game has 96 health and every 4 health represents 1 villager (for a total of 24 villagers). !token-mod {{     --set         bar3_value|[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]]         width|[[[[{[[([[[[[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]]/@{selected|bar3|max}]]-0.0001]])d1]],[[{[[(1d[[[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]]]])-4]],0}>1]],1}>1]]*70]]         height|[[[[{[[([[[[[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]]/@{selected|bar3|max}]]-0.0001]])d1]],[[{[[(1d[[[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]]]])-4]],0}>1]],1}>1]]*70]] currentside|[[{[[@{selected|NumberOfVillagers|Max}-[[floor((@{selected|bar3|max}-[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]])/4)]]]],[[0d0]]}kh1]]     --order         toback }} !setattr --name Mob of Villagers --NumberOfVillagers|[[{[[@{selected|NumberOfVillagers|Max} - [[floor((@{selected|bar3|max}-[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]])/4)]]]],[[0d0]]}kh1]] /desc The Mob of Villagers took [[{?{How much damage?},@{selected|bar3}}kl1]] damage. [[{[[[[floor((@{selected|bar3|max}-[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]])/4)]]-[[@{selected|NumberOfVillagers|max}-@{selected|NumberOfVillagers}]]]],0}kh1]] of them were killed. The mob now has [[{[[@{selected|NumberOfVillagers|Max}-floor((@{selected|bar3|max}-@{selected|bar3})/4)]],[[0d0]]}kh1]] villagers left. Tl;dr: I made a macro that applies damage to a swarm enemy, killing some number of the individuals in the swarm per x HP. The macro to apply the damage and calculate the new swarm size isn't working because (I think) Token-Mod isn't finished updating the HP value by the time ChatSetAttr is attempting to update the swarm size. I'm not sure how to fix this. Additionally, I wrote some cool stuff to change the size of the token of the swarm and I also want to update which sheet the token represents based on the size of the swarm... but I have some questions. I am working to help my DM make a macro for swarms. (In this case, it's a mob of angry villagers, but that's besides the point.) The premise is simple enough. There are 25 angry villagers in a mob. Each villager has 4 hit points. Each time the mob of angry villagers is attacked, I want to figure out how many villagers are left in the mob. I have a rollable table with a number of sides equal to the maximum number of angry villagers and each element of the table has a token with a number displaying the number of surviving angry villagers. I want to use token mod to set the token side equal to the correct number of villagers. To help deal with numbers that aren't multiples of 4, I have created an attribute on the Mob of Villagers sheet that shows the number of villagers. @{Mob of Villagers|NumberOfVillagers} refers to the current number of survivors. @{Mob of Villagers|NumberOfVillagers|Max} refers to the max number of villagers in the mob. To accomplish this, I wrote a macro using ChatSetAttr and Token-Mod together. Because I'm a dev and I can't stand uncommented code, I'll be "commenting" the code below to clarify its function using "//" followed by a number that references the comments below the macro section (this looks easier to read this way). !token-mod {{ --set // [1] bar3_value|[[@{selected|bar3} -[[{?{How much damage?}, @{selected|bar3}}kl1]] --report // [2]     all|"/desc The {name} took [[{?{How much damage?}, @{selected|bar3}}kl1]] damage and [[{ [[ [[floor((@{selected|bar3|max} - @{selected|bar3}) / 4)]] - [[@{selected|NumberOfVillagers|max} - @{selected|NumberOfVillagers}]] ]], 0}kh1]] of them were killed. The mob now has [[{[[@{selected|NumberOfVillagers} - floor((@{selected|bar3|max} - @{selected|bar3}) / 4)]], [[0d0]]}kh1]] villagers left." }} // [3] !setattr --name Mob of Villagers --silent --NumberOfVillagers|[[{[[@{selected|NumberOfVillagers|Max} - floor((@{selected|bar3|max} - @{selected|bar3}) / 4)]], [[0d0]]}kh1]] // [4] !token-mod --set currentside|@{selected|NumberOfVillagers} }} [1] I am applying damage here. I want to set the current health equal to the mob's current health, minus the damage they're about to take.          The maximum damage the mob can take is its current hit points. [2] I want to post to chat the amount of damage these villagers took, how many were killed, and how many remain. The maximum damage the mob can take is equal to their remaining hitpoints. [3] Here, I want to update the number of villagers into the NumberOfVillagers attribute on their character sheet. I will reference this number to see how many villagers have been killed so far. This is supposed to help me keep track of the number of remaining villagers. It also avoids situations where player 1 deals 9 damage, killing 2 villagers with a remainder of 3 damage. Player 2 deals 8 damage, killing 2 villagers, still holding a remainder of 3 damage. Then player 3 deals 3 damage, killing the 1 with the remainder (even though each villager has 4 hitpoints). To do this, I am taking the max number of villagers and I am subtracting the total number of villagers that the players have killed so far. To get this, I take the total number of villagers and subtract their current health, divided by 4, rounded down. [4] Now that I have updated the NumberOfVillagers in the Attributes section, I want to use that number to determine which token "side" I want from my rollable table. The number here should be the same number as the number of living villagers. Alright, so here's my problem. When I run the macro I posted above here's what happens. First, it prompts me to ask how much damage the Mob of Villagers has taken. I'll apply 9 damage to my mob here.  Next, it correctly applies the damage to the token and reports the information... incorrectly. It shows that 0 of the villagers were killed and that the mob has 24 villagers left. (The Mob of Villagers "3" is just a numbered tokens thing.) Additionally, the Attribute NumberOfVillagers has not been updated and it remains at 24/24.  At this point, any of the macros I call now will correctly work. I can go ahead and type my ChatSetAttr command and it will correctly function now. After I call that, individually, I can call the Token-Mod set current side macro and it will work.  So why can't I call these together? From what I understand, Roll20 parses and executes macros line by line until they are completed. But it looks like the bar3 value is taking too long to update, so by the time Roll20 has submitted the line of code for execution, the ChatSetAttr line is being submitted. At the time that it's sent, damage hasn't been applied yet so it can't update the NumberOfVillagers attribute. Because I can't assign that value, by the time I'm reaching the next macro, the value is still just "24". That's at least my interpretation of it. But Since You're Here... Can I get a second opinion on this macro? And while you're here. Is there a better way to handle this? Ideally, I wanted to even change the "Represents Character" field to change from the Mob of Villagers, to a Group of Villagers at 12 members and at 1 member I'd want it to change to a single villager. I can do the macrowork manually, but that defeats the fun of automating stuff. Like, I can make a button that the DM presses at the health thresholds, but... yuck. Most of the reason for macro work is to automate cool stuff. !token-mod {{     --set         currentside|@{Mob of Villagers|NumberOfVillagers}         width|[[ [[ { [[ ( [[ [[ @{selected|bar3} / @{selected|bar3|max} ]] - 0.0001 ]] )d1 ]], [[ { [[ 1d [[ @{selected|bar3} ]] - 4]], 0 } > 1 ]], 1 } > 1 ]] * 70]]         height|[[ [[ { [[ ( [[ [[ @{selected|bar3} / @{selected|bar3|max} ]] - 0.0001 ]] )d1 ]], [[ { [[ 1d [[ @{selected|bar3} ]] - 4]], 0 } > 1 ]], 1 } > 1 ]] * 70]]     --order         toback }} This took some work, but the above macro should try to see if the swarm/mob meets 3 conditions. 1) If you divide its current hp by its max hp and you subtract a TINY bit, and you roll THAT many dice, it should roll a 1 if you are above half health. It will roll a 0 if you are below. 2) If you subtract 4 from your current health (the amount of health for each member of the mob), you should be left with a number of mobs greater than 0. If this is true, you roll a 1, if it is false you roll a 0. 3) Free Space Since I needed my mob to be huge, I can multiply my token's height and width by that equation * 70 to reach 210, which is giant sized. And when they're at half their strength, they are large. When they are down to 4 health or less, it's just the 1 dude. It's no longer a mob. Also, it sends it all to back, since no one wants to cover their players' beautiful, shining faces as they are mauled and engulfed in the endless flesh fiesta of your swarm. I've tried creating a character with token actions called "1", "2", and "3" so that I could use the return value from that macro above to call his token actions to call a macro snippet like !token-mod --set represents|@{Mob of Villagers|character_id} But predictably, they don't allow you to call a token action with something like %{CharacterName|AwesomeMacro[[X]]}. Nor do they allow the [[math]] in macros or character references (or whatever the @{stuff} is called}. You also can't use an element from a rollable table like @{CharacterName|AwesomeMacro[[1t[Wow]]]}. And yet, I still want to believe that there's a way to change the "Represents Character"... It's quickly becoming my white whale.
1616434557
The Aaron
Roll20 Production Team
API Scripter
I haven't fully grokked this, but my first thought is that I'd store the number of villagers, rather than the total hit points.  That would make the math much easier. Something to be aware of is that all attribute references, dice rolls, and roll queries are evaluated before any messages are sent to the API.  Try taking the ! off the front of all the API commands and see if what is sent to chat looks like what you would expect.
Thanks, @TheAaron.  I've confirmed that the values produce expected results when I strip the ! from my macro. The trouble seems to be that the 2nd line of my macro relies on the 1st macro being completed. (The 1st macro needs to update their bar3 value. Then the 2nd macro uses [(Bar3|Max - Bar3) - (NumberOfVillagers|Max - NumberOfVillagers)].  When I run the macro, the numbers in the report are wrong because the health value hasn't changed by the time the report is being executed (IE, it correctly shows how much damage the target takes, but it doesn't calculate the number of dead villagers correctly because it's calculating it before the hp values have updated... I think) Also, I am currently storing the number of villagers. But that number is dependent on the current health value (bar3), which I need to update.
1616441070
The Aaron
Roll20 Production Team
API Scripter
This: [[{[[@{selected|NumberOfVillagers|Max} - floor((@{selected|bar3|max} - @{selected|bar3}) / 4)]] Will be evaluated before TokenMod changes bar3, that's fine?
You should be able to use the Delay script to add some process ordering to your command, which should allow for the Bar3 to be updated before the 2nd macro runs.
This may help! In this picture, I left the ! on the first macro (I'll post the macro below). Here, you can see that it incorrectly reports the numbers, as it shows 0 deaths with 17 damage (4 hp per individual). And it also incorrectly shows that the mob still have 24 members (which is the maximum number). !token-mod {{     --set         bar3_value|[[@{selected|bar3} - [[{?{How much damage?}, @{selected|bar3}}kl1]]]]     --report all|"/desc The {name} took [[{?{How much damage?}, @{selected|bar3}}kl1]] damage and [[{ [[ [[floor((@{selected|bar3|max} - @{selected|bar3}) / 4)]] - [[@{selected|NumberOfVillagers|max} - @{selected|NumberOfVillagers}]] ]], 0}kh1]] of them were killed. The mob now has [[{[[@{selected|NumberOfVillagers} - floor((@{selected|bar3|max} - @{selected|bar3}) / 4)]], [[0d0]]}kh1]] villagers left."  }} After running the macro, I ran the full macro (again, posted below) but I removed all of the "!"s so that we could see the calculations. I have made no adjustments or changes. But here, you can see that the numbers are all working correctly. • The bar3 value is 62 (which is the correct number, since the 17 damage was applied the 1st time, and I am calculating 17 more). • You can see that there are 4 deaths (because it's only calculating based on the 1st 17 damage, not the 2nd) • You can also see that there are 20 living villagers... which jives with the 4 deaths.  • ChatSetAttr is correctly being told to set the number of remaining villagers to 20. • The Token-Mod CurrentSide is being set to 24... which is actually fine, since I have never updated the NumberOfVillagers attribute, since I removed the "!"s from the macro calls to check the calculations. So every thing is calculating correctly. I am wondering if the calculations are being made before the macro finishes executing, so the numbers are wrong, since I need the data to update when the target takes damage. !token-mod {{     --set         bar3_value|[[@{selected|bar3} - [[{?{How much damage?}, @{selected|bar3}}kl1]]]]     --report all|"/desc The {name} took [[{?{How much damage?}, @{selected|bar3}}kl1]] damage and [[{ [[ [[floor((@{selected|bar3|max} - @{selected|bar3}) / 4)]] - [[@{selected|NumberOfVillagers|max} - @{selected|NumberOfVillagers}]] ]], 0}kh1]] of them were killed. The mob now has [[{[[@{selected|NumberOfVillagers} - floor((@{selected|bar3|max} - @{selected|bar3}) / 4)]], [[0d0]]}kh1]] villagers left."  }} !setattr --name Mob of Villagers --silent --NumberOfVillagers|[[{[[@{selected|NumberOfVillagers|Max} - floor((@{selected|bar3|max} - @{selected|bar3}) / 4)]], [[0d0]]}kh1]] !token-mod --set currentside|@{selected|NumberOfVillagers} !token-mod --set currentside|[[{[[@{selected|NumberOfVillagers|Max} - floor((@{selected|bar3|max} - @{selected|bar3}) / 4)]], [[0d0]]}kh1]]
1616441523

Edited 1616441774
Correction: That isn't fine. How can I get Token-Mod to update bar3 before executing that line? Edit: Now I know what you meant by " Something to be aware of is that all attribute references, dice rolls, and roll queries are evaluated before any messages are sent to the API.  Try taking the ! off the front of all the API commands and see if what is sent to chat looks like what you would expect." It seems I have an order of operations mistake here. How embarassing! Is there a valid way of forcing roll20 to execute the first step (applying the damage) before  moving on to start crunching the updated numbers?
And JarrenK, I am looking at your script you've suggested now. It seems like a delay command would do what I am wanting. If I can wait until the bar has been updated before doing any other work, that would fix it, I think.
1616442216
The Aaron
Roll20 Production Team
API Scripter
You can't, that's the nature of the interaction between the Client and the API.  Usually people end up either doing all the calculations up front in both places, or writing a script that does what they want directly without trying to use multiple existing scripts. Tim's MetaScripting things might be able to help with this, I'll poke him...
@TheAaron, I suppose I could always make my calculations more verbose and do it all up front. The calculation for the # of dead villagers is just floor((MaxHP - CurrentHP) / 4). I have been trying to update CurrentHP to use that for the math, but I could just as easily recalculate it by taking their actual current hp and just subtract the damage they took from it. This has some really cool potential. If I can get something like this working on a macro side, then I can potentially start running swarms using this template. I may have to resort to writing a script for running swarms, since all of this stuff is calculable but difficult to execute with a macro.
I've updated the macro. It's now redundant, but at least all the calculations are happening in line, rather than relying on values to update from the API and trying to use those new values. !token-mod {{     --set         bar3_value|[[@{selected|bar3} - [[{?{How much damage?}, @{selected|bar3}}kl1]]]]   --report all|"/desc The {name} took [[{?{How much damage?}, @{selected|bar3}}kl1]] damage." }} !setattr --name Mob of Villagers --NumberOfVillagers|[[{[[@{selected|NumberOfVillagers|Max} - [[floor((@{selected|bar3|max} - [[@{selected|bar3} - [[{?{How much damage?}, @{selected|bar3}}kl1]]]]) / 4)]]]], [[0d0]]}kh1]] /desc [[{ [[ [[floor((@{selected|bar3|max} - [[@{selected|bar3} - [[{?{How much damage?}, @{selected|bar3}}kl1]]]]) / 4)]] - [[@{selected|NumberOfVillagers|max} - @{selected|NumberOfVillagers}]] ]], 0}kh1]] of them were killed. The mob now has [[{[[@{selected|NumberOfVillagers} - floor((@{selected|bar3|max} - @{selected|bar3}) / 4)]], [[0d0]]}kh1]] villagers left. !token-mod --set currentside|[[{[[@{selected|NumberOfVillagers|Max} - [[floor((@{selected|bar3|max} - [[@{selected|bar3} - [[{?{How much damage?}, @{selected|bar3}}kl1]]]]) / 4)]]]], [[0d0]]}kh1]]
1616443566

Edited 1616443620
David M.
Pro
API Scripter
EDIT: cross-posted. It's working now? A scriptcard might work, too. Create a variable for what the updated bar3 value will become, then use that variable in your api calls instead of @{selected|bar3...}. Would probably need to use  timmaugh's SelectManager script, too (with a forselected command), but on first glance it seems this should work. If the delay script approach fails, I can take a look at this and see if we can come up with a scriptcard.
1616444516

Edited 1616448523
@DavidM. I have something working now. This will apply damage determined by user input to the token. Then it will change the side of the token to match with the corresponding size of the mob. Then it checks to see if the mob size has reduced enough to warrant a size change (at 50% health, then again at 4 hp). Then it puts the mob at the back (since swarms can occupy the spaces of characters). Then it updates the number of villagers so that it will correctly report the number of villagers killed next time they take damage. Then it reports all the relevant information. It's pretty handy.  I can't think of a good way to change the character sheet at mob size increments without a script, but I could always just have 1 sheet with abilities that are unlocked at certain sizes. !token-mod {{ --set bar3_value|[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]] width|[[[[{[[([[[[[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]]/@{selected|bar3|max}]]-0.0001]])d1]],[[{[[(1d[[[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]]]])-4]],0}>1]],1}>1]]*70]] height|[[[[{[[([[[[[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]]/@{selected|bar3|max}]]-0.0001]])d1]],[[{[[(1d[[[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]]]])-4]],0}>1]],1}>1]]*70]] currentside|[[{[[@{selected|NumberOfVillagers|Max}-[[floor((@{selected|bar3|max}-[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]])/4)]]]],[[0d0]]}kh1]] --order toback }} !setattr --name Mob of Villagers --NumberOfVillagers|[[{[[@{selected|NumberOfVillagers|Max} - [[floor((@{selected|bar3|max}-[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]])/4)]]]],[[0d0]]}kh1]] /desc The Mob of Villagers took [[{?{How much damage?},@{selected|bar3}}kl1]] damage. [[{[[[[floor((@{selected|bar3|max}-[[@{selected|bar3}-[[{?{How much damage?},@{selected|bar3}}kl1]]]])/4)]]-[[@{selected|NumberOfVillagers|max}-@{selected|NumberOfVillagers}]]]],0}kh1]] of them were killed. The mob now has [[{[[@{selected|NumberOfVillagers|Max}-floor((@{selected|bar3|max}-@{selected|bar3})/4)]],[[0d0]]}kh1]] villagers left. Edit: I have updated the code above. I had a typo that caused a calculation error. It's been resolved.
1616445416
timmaugh
Forum Champion
API Scripter
I think this is a great candidate for the meta scripting stuff I've been working on... The ability to do inline math Read a variable store a variable get sheet info ...in whatever order you need, as many times as you need... and all before it ever reaches token-mod or chatsetattr (or whatever script is the intended recipient. Right now a lot of that functionality is in APILogic, and we could probably make it work, but the loop order is more constrained having them all in the same script bed. I'm breaking them out into their own component parts to give you more control. I hate to keep saying, "It's coming..." but... it's coming. It's very close to being ready!
@Timmaugh, that sounds incredible! I am super looking forward to it! I could do a lot of stuff with the ability to use an API to change values on a sheet, then being able reference those new values. I mean at that point, that's just an overly complicated way to make variables. And if you could store variables, you could store... well, anything! I could see your script being used to store results of rolls and all kinds of stuff!
1616457139
David M.
Pro
API Scripter
If you want to store variables for later use/modification and implement simple or complex loops and conditionals, I'll put another word in for Kurt's Scriptcards ;) Sounds like APILogic is going to work for you, but I having multiple ways to solve something is always a good "problem" to have!