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 it possible to write an API-free macro that creates macros and attributes on the same sheet it's on?

1455497220
AliceSteel
Sheet Author
API Scripter
I use a lot of similar abilities on a lot of characters in a fair number of campaigns. I can't afford Pro level membership right now and many of the campaigns I join aren't made by Pro members either. Such a macro would be quite useful for me. If it's possible what commands would I use to do it?
1455497509
vÍnce
Pro
Sheet Author
I think that's something that's only possible using an API script.  ;-( You can always keep an external doc handy and use the old copy and paste method though.
1455499048
AliceSteel
Sheet Author
API Scripter
Failing that, is it possible to write a macro to have a drop-down list of attributes and/or other macros on the sheet to fill in slots/call? Also, can a macro adjust the value stored in an attribute and/or have an alternate effect when an attribute is set to 0?
1455499929
vÍnce
Pro
Sheet Author
Michiyo S. said: Failing that, is it possible to write a macro to have a drop-down list of attributes and/or other macros on the sheet to fill in slots/call? Also, can a macro adjust the value stored in an attribute and/or have an alternate effect when an attribute is set to 0? There are lot's of macros that people have made that include multiple drop-down queries for all kinds of rolls. Other than the complexity of what you might be trying to accomplish, this is definitely possible.  Silvyre is exceptionally good at nailing down complex multi-tiered, query-based, macros.  ;-)  That's a tongue twister... I've seen examples of macros setup using boolean logic that accomplish very complicated things "within the macro".   Again, these type of macros and/or logic only affects the outcome of the roll.  You would ultimately need API access and the proper script(s) to modify your attributes directly.  
1455501000
Silvyre
Forum Champion
...Ah-choo! Yes; sounds like Roll Queries are right up your alley, Michiyo S. (They can be surprisingly versatile.) Feel welcome to post up an example of the macros you're currently working with, and what exactly you wish to achieve with them.
1455503036
AliceSteel
Sheet Author
API Scripter
One of the macros I'm wanting to upgrade is this 3.5 D&D Healing Belt macro: &{template:default} {{name=Healing Belt}} {{Action=Standard Action}} {{Range=Touch }} {{Effect=Target recovers [[ (1 + ?{Charges Used|1})d8]] HP (Undead take damage instead)}} {{Charges Spent=[[?{Charges Used|1}]]}} (line breaks added for readability, they aren't in the real thing) Currently I have an attribute on my sheet to keep track of my charges manually. If possible I'd like to have it automatically reduce the current value of that attribute by the "Charges Used" number. Another thing I'd like to have it do is not send the template to chat but instead whisper an error message to me if I try to use more charges than I have. Another type of macro I might like to set up would be to have a "Level 1 Spell" macro that would check and adjust my current number of slots in an attribute before calling another macro based on a drop-down selection. As this would be basically worthless (outside of saving some macro bar space I suppose) without the attribute checking/adjusting code I've not written anything for it yet.
Thanks. I have also this problem. :)
1455506406
Silvyre
Forum Champion
As Vince said, it's unfortunately not possible for a macro to create Attributes or adjust their values without an API Script. Sorry. :(
1455507734
AliceSteel
Sheet Author
API Scripter
How would I do it if I did have API access? My current DM is a Plus member and I might be able to give them the difference for a few months with what little funding I have coming in next Friday.
1455509276

Edited 1455510404
Silvyre
Forum Champion
One of the most popular Attribute-altering API Scripts is Ammo . It also outputs error messages in certain circumstances. If the Attribute you're using to track charges is named HealingBeltCharges and the Character that contains this Attribute is named Michiyo, your macro might look like the following: &{template:default}  {{name=Healing Belt}} {{Action=Standard Action}}  {{Range=Touch }} {{Effect=Target recovers [[ [[1 + {?{Charges Used|1}, @{Michiyo|HealingBeltCharges}}kl1]]d8 ]] HP (Undead take damage instead)}} {{Charges Spent=[[ {?{Charges Used}, @{Michiyo|HealingBeltCharges}}kl1 ]]}} !ammo @{Michiyo|character_id} HealingBeltCharges [[ -1 * {?{Charges Used}, @{Michiyo|HealingBeltCharges}}kl1 ]] (I added a limiting factor to the inline rolls within your Roll Template.)
1455511553
AliceSteel
Sheet Author
API Scripter
Is it not possible to directly use code within the macro to do it or is it just easier with the prewritten script to call? What exactly is the kl1 doing here?
1455517402

Edited 1455517734
Silvyre
Forum Champion
Michiyo S. said: Is it not possible to directly use code within the macro to do it or is it just easier with the prewritten script to call? Both, really. API Scripts that use text chat commands do their own thing on their own separate lines preceded by an exclamation mark. So, they can't really mingle with Roll Templates. On the other hand, we do have wonderful scripts like PowerCards , which is effectively Roll Templates on steroids. Michiyo S. said: What exactly is the kl1 doing here? That's part of the syntax of the keep/drop function (i.e. the aformentioned limiting factor). The kl1 in {A, B}kl1 stands for k eep l owest 1 . {?{Charges Used|1}, @{Michiyo|HealingBeltCharges}}kl1 keeps the lower of ?{Charges Used|1} and @{Michiyo|HealingBeltCharges}
1455525465
AliceSteel
Sheet Author
API Scripter
Alright, I think I can use the information thus far to improve my macros once I get the DM on a Pro membership. Last thing I'd like to know is if I can make a macro to easily refill my various daily charge attributes all at once. Oh, and if you might know how to write a 3.5 Barbarian Rage toggle macro (or a pair if a smart toggling macro isn't possible) that would automatically add in the appropriate temporary stat modifications it would really help me aid another player in our group speed up his turns.
1455563726

Edited 1455564018
Silvyre
Forum Champion
Michiyo S. said: Last thing I'd like to know is if I can make a macro to easily refill my various daily charge attributes all at once. Yup; here's a basic template: !ammo @{Michiyo|character_id} HealingBeltCharges [[ @{Michiyo|HealingBeltCharges|max} - @{Michiyo|HealingBeltCharges} ]] Michiyo S. said: Oh, and if you might know how to write a 3.5 Barbarian Rage toggle macro (or a pair if a smart toggling macro isn't possible) that would automatically add in the appropriate temporary stat modifications it would really help me aid another player in our group speed up his turns. I'm presuming you're using the 3.5 Edition Character Sheet? If so, before you can use the below with !ammo, you'll have to locate the fields of the Character Sheet which correspond to each of the four Attributes used (str-temp, con-temp, willtempmod, miscac1bonus) and edit them (or at least click into them) such that they pop up on the Attributes & Abilities tab of the Character. If you can't find one, it should be okay to just create an Attribute with that name. Rage on: !ammo @{AnotherPlayer|character_id} str-temp +4 !ammo @{AnotherPlayer|character_id} con-temp +4 !ammo @{AnotherPlayer|character_id} willtempmod +2 !ammo @{AnotherPlayer|character_id} miscac1bonus -2 /me flies into a rage! Rage off: !ammo @{AnotherPlayer|character_id} str-temp -4 !ammo @{AnotherPlayer|character_id} con-temp -4 !ammo @{AnotherPlayer|character_id} willtempmod -2 !ammo @{AnotherPlayer|character_id} miscac1bonus +2 /me ceases their rage. With API Command Buttons : Rage: [On](!ammo @{AnotherPlayer|character_id} str-temp +4 
!ammo @{AnotherPlayer|character_id} con-temp +4 
!ammo @{AnotherPlayer|character_id} willtempmod +2 
!ammo @{AnotherPlayer|character_id} miscac1bonus -2 
/me flies into a rage!) [Off](!ammo @{AnotherPlayer|character_id} str-temp -4 
!ammo @{AnotherPlayer|character_id} con-temp -4 
!ammo @{AnotherPlayer|character_id} willtempmod -2 
!ammo @{AnotherPlayer|character_id} miscac1bonus +2 
/me ceases their rage.) Note: this last macro contains HTML entities and thus should be saved as an Ability. (At least, until !ammo gets multi-line support.)
1455568163
AliceSteel
Sheet Author
API Scripter
When I say macro in regards to Roll20 I actually usually mean Ability. Perhaps I should have mentioned that earlier. All of your examples that include the character name in them can omit the character name if they're written as Abilities on the character in question's sheet right?
1455569425
Silvyre
Forum Champion
Michiyo S. said: All of your examples that include the character name in them can omit the character name if they're written as Abilities on the character in question's sheet right? Yup!
1456184825

Edited 1456184986
AliceSteel
Sheet Author
API Scripter
How would I go about using the Ammo script to use the same ability with 2 or more possible attributes each with a different adjustment amount? I'm sure it would use Roll Queries but I'm not entirely sure how to write them for this. Base ability: &{template:default} {{name=Blade of Blood}} {{Spell Level=1}} {{Caster Level=@{level} }} {{Action=Swift Action}} {{Deal extra damage to next living creature attacked within [[@{level}]] rounds.}} Possible attributes (Adjustment): Level1SpellSlots (-1), Level0SpellSlots (-2)
1456185579

Edited 1456185643
Silvyre
Forum Champion
You'd have something like {{Spell Level=?{Spell Level|0|1}}} etc. within your Roll Template, and then, below, something like !ammo @{Michiyo|character_id} Level?{Spell Level}SpellSlots [[ ?{Spell Level} - 2 ]]
1456187774

Edited 1456190745
AliceSteel
Sheet Author
API Scripter
So then how would you do a Level 2 spell? (Cost options: 1 SL2, 2 SL1, 4 SL0) Trying to implement my Versatile Spellcaster feat (D&D 3.5 Races of the Dragon page ~100ish) that lets me spend 2 slots of a level to make 1 slot of the next level up. BTW: That refilling macro you gave me isn't working.
1456196436
Silvyre
Forum Champion
Michiyo S. said: So then how would you do a Level 2 spell? (Cost options: 1 SL2, 2 SL1, 4 SL0) {{Spell Level=?{Spell Level|0|1|2}}} !ammo @{Michiyo|character_id} Level?{Spell Level}SpellSlots [[ {4 - 2 * ?{Spell Level}, 1}kh1 ]]
1456196501

Edited 1456196717
Silvyre
Forum Champion
Michiyo S. said: BTW: That refilling macro you gave me isn't working. This one? !ammo @{Michiyo|character_id} HealingBeltCharges [[ @{Michiyo|HealingBeltCharges|max} - @{Michiyo|HealingBeltCharges} ]] What happens when you paste @{Michiyo|HealingBeltCharges|max} and @{Michiyo|HealingBeltCharges} into the text chat (with Michiyo replaced with the name of your Character, of course)?
1456214563
AliceSteel
Sheet Author
API Scripter
Well, it works that way. I don't quite understand why it didn't work without the name and bars in an ability but oh well, at least it works now.
1456217568
Silvyre
Forum Champion
Cool.
I use a different script to change attributes.  Its older and I cant remember where I got it from, I think it was Aaron. !alterattr adds or subtracts a value from a attribute, make sure you either note the original value or use a attribute that has a default of 0..  I like to use it on the temporary values so I can use a macro to clear them quickly. !alterattr @{selected|token_id} STR-temp 4 !setattr overrides the current value of an attribute.  Useful for restoring temp attributes to 0 quickly with a 1 click macro !setattr @{selected|token_id} STR-temp 0 !incrattr and !decattr either increment or decriment an attribute.  Useful for ammo scripts I suppose; I've never really used ammo tracking.  I dont know what the proper syntax is. There are several other commands inside of the code that I have seen.  I dont know how to use them though, !settint !addtracker !incattrforselected !decattrforselected.  Have fun, and I hope that helps. var ModUtils = ModUtils || {}; ModUtils.setAttribute = function(char_id, attr_name, newVal) {     var attribute = findObjs({         _type: "attribute",         _characterid: char_id,         _name: attr_name     })[0];     //log(attribute);     if (attribute == undefined) {         createObj("attribute", {         name: attr_name,         current: newVal,         characterid: char_id         });         } else {     attribute.set("current", newVal.toString());     }     //log(attribute); } ModUtils.decrementAttribute = function(char_id, attr_name, curVal) {     var attribute = findObjs({         _type: "attribute",         _characterid: char_id,         _name: attr_name     })[0];     if (attribute == undefined) {         ModUtils.setAttribute(char_id, attr_name, -1);         return;     }     //log(attribute);     attribute.set("current", (parseInt(curVal) - 1).toString());     //log(attribute); } ModUtils.incrementAttribute = function(char_id, attr_name, curVal) {     var attribute = findObjs({         _type: "attribute",         _characterid: char_id,         _name: attr_name     })[0];     if (attribute == undefined) {         ModUtils.setAttribute(char_id, attr_name, 1);         return;     }     //log(attribute);     attribute.set("current", (parseInt(curVal) + 1).toString());     //log(attribute); } ModUtils.alterAttribute = function(char_id, attr_name, modVal) {     var attribute = findObjs({         _type: "attribute",         _characterid: char_id,         _name: attr_name     })[0];     if (attribute == undefined) {         ModUtils.setAttribute(char_id, attr_name, modVal);         return;     }     //log(attribute);     attribute.set("current", (parseInt(attribute.get("current")) + modVal).toString());     //log(attribute); } ModUtils.alterBar = function(tokenID, barNum, modVal) {     var token = getObj("graphic", tokenID);     if (token == undefined || barNum < 1 && barNum > 3) return;     var bar = "bar" + barNum;     var barVal = parseInt(token.get(bar + "_value"));     var barMax = parseInt(token.get(bar + "_max"));     barVal += modVal;     if (barVal > barMax) barVal = barMax;     token.set(bar + "_value", barVal); }; on("chat:message", function (msg) {     //log(msg.content);     if (msg.type != "api") return;     var args = msg.content.split(" ");     var command = args.shift();     if (command === "!decattr") {         var token = getObj("graphic", args[0]);         var char_id = token.get("represents");         if (char_id != "") {             var attr_name = args[1];             var curVal = getAttrByName(char_id, attr_name);             //log(!char_id);             //log(attr_name);             //log(curVal);             ModUtils.decrementAttribute(char_id, attr_name, curVal);         }     } else if (command === "!incattr") {         var token = getObj("graphic", args[0]);         var char_id = token.get("represents");         if (char_id != "") {             var attr_name = args[1];             var curVal = getAttrByName(char_id, attr_name);             //log(char_id);             //log(attr_name);             //log(curVal);             ModUtils.incrementAttribute(char_id, attr_name, curVal);         }     } else if (command === "!alterattr") {         var token = getObj("graphic", args[0]);         var char_id = token.get("represents");         if (char_id != "") {             var attr_name = args[1];             var modVal = args[2];             modVal = parseInt(modVal);             //log(char_id);             //og(attr_name);             //log(modVal);             ModUtils.alterAttribute(char_id, attr_name, modVal);         }     } else if (command === "!setattr") {         var token = getObj("graphic", args[0]);         var char_id = token.get("represents");         if (char_id != "") {             var attr_name = args[1];             var newVal = args[2];             //log(char_id);             //log(attr_name);             //log(newVal);             ModUtils.setAttribute(char_id, attr_name, newVal);         }     } else if(command === "!settint") {         var token = getObj("graphic", args[0]);         var color = args[1];         //log(color);         token.set("tint_color", color);     } else if (command === "!addtracker") {         var turnorder = JSON.parse(Campaign().get("turnorder"));                  var token = getObj("graphic", args[0]);         var char_id = token.get("represents");         var rounds = args[1];         var condition = args[2];         var CUorCD = args[3];                  log(turnorder);         log(char_id);         log(rounds);         log(condition);         log(CUorCD);                  if (char_id != "") {             turnorder.push({                 id: "-1",                 pr: rounds,                 custom: token.get("name") + "\'s " + condition + "|" + CUorCD             });             Campaign().set("turnorder", JSON.stringify(turnorder));         }         log(turnorder);     } else if (command === "!decattrforselected") {         var attr_name = args[0];         _.each(msg.selected, function(selected) {         //log(selected)         var char_id = getObj("graphic", selected._id).get("represents");             if (char_id != "") {                 var curVal = getAttrByName(char_id, attr_name);                 //log(char_id);                 //log(attr_name);                 //log(curVal);                 ModUtils.decrementAttribute(char_id, attr_name, curVal);             }         });     } else if (command === "!incattrforselected") {         var attr_name = args[0];         _.each(msg.selected, function(selected) {             //log(selected)             var char_id = getObj("graphic", selected._id).get("represents");             if (char_id != "") {                 var curVal = getAttrByName(char_id, attr_name);                 //log(char_id);                 //log(attr_name);                 //log(curVal);                 ModUtils.incrementAttribute(char_id, attr_name, curVal);             }         });     } });