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

5E Shaped Sheet - Question regarding repeating macros

This is on the 5e Shaped Sheet by Kryx. I'm curious if it's possible to reference a repeating section, let's say Offense, and have it run through and test each repeating field for a condition? For example, let me write a simple IF statement to get across my question more clearly. I'm not necessarily wanting to create such a statement, just to know if it's possible and if so, what would be the syntax for testing each repeating macro instance? IF(repeating_offense_$#_name != "Crossbow") {   repeating_offense_$0 = repeating_offense_$0 + 1; //to get to the next attack in this macro, and then retest } else   return repeating_offense_$#; //to get the "repeating_offense_$" macro that is named "Crossbow" I don't care if it's a single line macro, a script call or something else entirely, if it can be done. And how! I don't like the idea of writing out macros with every possible combination and it would be nice to allow a repeating section to "search" through its fields for the correct line or lines).
1606671065
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
Yes it can be done. A script can do it, but not a normal macro. Check out the  Universal Chat Menus  script. It contains the looping and filtering features you are looking for.
I already tried that, and I can't seem to make it do anything . Not even post an error in the API Sandbox. Reinstalled the script from scratch and retried, same issue. The code I tried was as simple as I could make it, nothing. If you enter a code that has a problem, should the script at least punch out an error in the Sandbox or something in the game chat? And I do see the potential for using it to display menus, but I don't quite see how to make it handle the functionality I'm seeking. The closest I see is a fourth variable that acts as a conditional display, if =1 then it shows, if =0 then it doesn't. I would need it to check a field for a specific word/phrase, not 1/0. I wanted so bad for this to fit my needs but I think it just falls short...or I don't understand the functionality well enough.  -_- 
1606697090

Edited 1606697928
Oosh
Sheet Author
API Scripter
What's the end goal once you've found the string "crossbow"? You can certainly do what you want, though it's not particularly straight-forward. If you're wanting to do anything other than create a menu button, the script Keith has suggested won't be much help run from the command line - it only does what it does. You'll need to actually go through the code, find the bits you want, and write your own script. As he said, it contains the functions you're looking for for finding repeating sections, grabbing rowIDs, grabbing the attributes, then operating on the values - but the script only uses those to build menu buttons.
1606698734
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
That fact that it's not doing anything points to a likely problem in the macro code. You could try posting what you are using and maybe we can see an issue. And filtering should work on words. I use it in this macro:  Spell Filter . And yeah, Oosh pointed out something important I had overlooked. The solution I suggested makes a chat menu. If you are looking to actually run the action in one step, that wold likely require a custom script. The API can absolutely do this, but I know of nothing off the shelf.
If I'm doing it wrong, could you give me a simple example that will work regardless of the system? The most simple out just to test to make certain it works? Everything I've tried does nothing. For exactly what I'm doing, I'm running a post-apocalyptic campaign with firearms. Buried somewhere in the Shaped Sheet (and it's accompanying API) is what I'm seeking, but I have absolutely no idea how to find it...to my knowledge it isn't something that is available to the average user. I am using the SetChatAttribute API that allows direct edits on sheet values, and that functionality is NOT an issue, each value can be changed at will with no issues on that front. Red/Blue uses are Current/Max ammo. Each time any given Ranged Weapon Attack macro is selected, the Brown "per use" is subtracted from the Current number of uses. It works with the Shaped sheet by default and even tells the user when ammo (uses) are insufficient. If you enable the accompanying API, then if you set the Yellow "per use" to a value (and switch on the variable to do "auto ammo deplete", then that number, which is set to the Green field "Ammo type", is automatically drawn from the correct ammo down below in Purple. The script goes through each ammo listed in the Ammo section, matches the names, and everything. I'm trying to figure out how so I can replicate it to finish off my firearm macros. I want to create a macro that knows which Ammo types the selected character has on their sheet, and display the types in a dropdown box, which will set the ranged weapon to that ammo type. (At the start of this macro, I'd like the current ammo be returned to its rightful ammo type). Also, I want to create a macro that will handle reloading: Macro looks at Green and fills Red to the max value (Blue) from Purple. If I can figure those out, I could probably create some simple macros that will handle burst-fire, and other similar skills, but currently I just need to understand how all of this could theoretically work. I'm no stranger to coding, and I've coded a firearms system before for 5e using a different sheet (the Community one, that doesn't use repeating sections), but the system had to be modified every time a new ammo type showed up, or weapon type. Every single thing had to be coded, with all options available hard-coded as well. This is much more elegant and SIMPLE. I messaged the sheet creator hoping to gleam some of the hidden functions off him, but so far I'm just trying to be patient and see what that yields. (I'm also trying to create Chat Buttons that only show buttons for Ranged Weapons. It's all starting to feel a bit daunting and confusing for something that ALMOST works the way I want by default - with the API script of course.)
1606723872

Edited 1606724143
Oosh
Sheet Author
API Scripter
I haven't really played around with Universal Chat Menu, but I think this should work for the shaped sheet: !chatmenu @{selected|character_id} @{selected|character_name} --title:Ranged Attacks --repeating_offense|name|roll|attack_type=RANGED One of the sheet authors will know better, but I believe someone said that creating a dynamic drop-down on the sheet is either very difficult, or impossible. That's also going to require customising the sheet itself, you can't do that with an API script. I could be misremembering though, maybe it can be done. You could add that functionality into an API reloading script instead, it could search the repeating_ammo section, grab all the results and make a button from each one. Or you could do a Query pop-up to choose from, which would give you a drop down list. So, player runs the reload script by clicking a button, it then gives them a list of their weapons to choose from, then another dialog asking which ammo to use. It posts the result to chat and does the math with the ammo. Is that kind of what you're looking for? Changing ammo types is mechanically the same as reloading, so the script would cover both. The ammo part of the Companion script looks like it starts on line 5416. It's not overly long, but it has a _.chain function which can be difficult to follow. I'm not sure which bits you're hoping to steal: consumeAmmo(options) { if (!this.roll20.checkCharacterFlag(options.character.id, 'ammo_auto_use')) { return; } const ammoAttr = _.chain(this.roll20.findObjs({ type: 'attribute', characterid: options.character.id })) .filter(attribute => attribute.get('name').indexOf('repeating_ammo') === 0) .groupBy(attribute => attribute.get('name').replace(/(repeating_ammo_[^_]+).*/, '$1')) .find(attributeList => _.find(attributeList, attribute => attribute.get('name').match(/.*name$/) && attribute.get('current') === options.ammoName) ) .find(attribute => attribute.get('name').match(/.*qty$/)) .value(); if (!ammoAttr) { this.logger.error('No ammo attribute found corresponding to name $$$', options.ammoName); return; } if (options.ammo) { const ammoRemaining = parseInt(options.ammo, 10); if (ammoRemaining >= 0) { const current = parseInt(ammoAttr.get('current'), 10); ammoAttr.setWithWorker('current', ammoRemaining); const ammoTracking = this.getAmmoTracking(); if (ammoTracking) { ammoTracking[ammoAttr.id] = (ammoTracking[ammoAttr.id] || 0) + current - ammoRemaining; } } else { this.reportResult('Ammo Police', `${options.characterName} can't use ${options.title} because ` + `they don't have enough ${options.ammoName} left`, options); } } } getAmmoTracking() { if (this.roll20.getCampaign().get('initiativepage')) { this.myState.ammoTracking = this.myState.ammoTracking || {}; return this.myState.ammoTracking; } return null; } reportTotalAmmoUse() { if (!this.myState.config.sheetEnhancements.ammoRecovery) { return; } const recoveryStrings = _.chain(this.myState.ammoTracking) .map((used, id) => { const ammoAttr = this.roll20.getObj('attribute', id); if (!ammoAttr) { return null; } const ammoName = this.roll20.getAttrByName(ammoAttr.get('characterid'), ammoAttr.get('name').replace(/_qty/, '_name')); const char = this.roll20.getObj('character', ammoAttr.get('characterid')); return `${char.get('name')} used ${used} ${ammoName}. <a href="!shaped-recover-ammo ` + `--ammoAttr ${id} --qty ?{Quantity to recover|${Math.floor(used / 2)}}">Recover</a>`; }) .compact() .value(); if (!_.isEmpty(recoveryStrings)) { const msg = `<ul><li>${recoveryStrings.join('</li><li>')}</li></ul>`; this.reportPlayer('Ammo Recovery', msg); } } recoverAmmo(options) { const ammoName = this.roll20.getAttrByName(options.ammoAttr.get('characterid'), options.ammoAttr.get('name').replace(/_qty/, '_name')); options.ammoAttr.setWithWorker({ current: options.ammoAttr.get('current') + options.qty }); this.reportCharacter('Ammo Recovery', `You recover ${options.qty} ${ammoName}`, options.ammoAttr.get('characterid')); } }
Oh my god. Huge blond moment for me. I completely forgot, at every point, that there was code I could look at for the companion script! Thank you so much for this breakthough! I'm sure this is what I need, though it'll take some time to go through as I have to focus on finalizing prep for tonight's session. You really saved my sanity on this. I'll poke my head back in here in a day or two and let you all know the outcome, which should be favorable. Thanks to everyone that gave advise, it is so very appreciated. 
So I starting dissecting the actual function I was looking for, and just as I started to make progress in understanding the logic behind the "consumeAmmo()" function, I realized...if I could figure out the way each ranged weapon section was fed to the function in order for it to run, I could just call the entire function and have it do the heavy lifting! I found the call, but I'm not quite savvy enough to make sense of it. I understand it only on a basic level, anyone think they can figure out how to pass along the correct info? registerChatListeners(chatWatcher) { chatWatcher.registerChatListener(['ammoName', 'character'], this.consumeAmmo.bind(this)); } This directly calls the function in question. Essentially, I would have a macro that would pick which ranged weapon to use. The trick is knowing how to call the function correctly so that it knows which weapon's fields to use. ANY assistance on this is greatly appreciated. I had almost given up when I located this, and feel it's basically just a single line or two (essentially just a function call using the correct format/variables) away! 
1606956647

Edited 1606957060
Oosh
Sheet Author
API Scripter
It might be easier to just insert your own input listener to activate with a chat command, like !reloadShaped, and chuck that in the script somewhere, much like the handleInput(msg) block in most one-click scripts. if (msg.type === 'api' && msg.content.search(/^!reloadshaped\s/i) !== -1) { // call required function return; } the script is designed to react to sheet changes & chat output automatically, so there's probably no manual way to activate those function currently, except by faking an attack output. Looks like you might also have to tweak the function to take an integer for number of ammo to decrement. That's the way I'd look at doing it anyway, give yourself an easy way into the script via macros. I've woken up still drunk from yesterday, and have a lunch to get to (which won't fix my current debuff whatsoever) and don't have time to look at it, sorry. Alternatively, I've got the beginnings of an ammo/weapon manager for the R20 sheet - it wouldn't be much work to switch it to Shaped, though it still needs some work (only does reloading so far). Happy to share that if it looks useful. Gif:
I had a chance to play around with it, and I think you're right. I would have to figure a way to simulate an attack in order for the "consumeAmmo()" function to receive the proper info. As much as I was hoping it would be something more simple than that, I suppose I shouldn't be surprised. I think it's worth the time, assuming I eventually figure out a way to get the function to take the info for whichever Ranged Weapon reload button is pressed and deduct ammo from the correct ammo section. That being said, I don't have an advanced programmers mind and working on this simple feature is kicking me in the face. Repeatedly. Thank you so much for the advice and assistance, though. I'd honestly be completely lost if you hadn't set me on the right direction twice now. After even more playing with it, I think if I modify the roll function so that  if(ammo_auto_use == 0) {   DISPLAY ATTACK MACRO NORMALLY } else   CLEAR THE ATTACK THAT GETS SENT TO CHAT I'm going to test that out tomorrow to see if adding in that clause can make it work. If it does, then all I'll have to do is give players a macro button that switches 'ammo_auto_use' to 1, then when they press any ranged weapon's attack button, it'll do the reload instead of attacking. I'll have to add a few simple lines to the companion script, but hopefully I can look at the existing logic and work them in without too much headache. We'll see though, since I barely follow the logic of chain functions.  -_-