Advertisement Create a free account

[Script, Call For Testers] Universal Chat Menus

1558616040

Edited 1566552450
GiGs
Pro
Sheet Author
Here is a script that will build chat menus for any sheet, if the sheet designer made sure to give each button a unique name.  I've only tested it on the Pathfinder by Roll20 sheet, and my own homebrew sheet, but in principle it should work for any sheet if the buttons are named properly. It uses the standard default roll template for now, but i have ideas on how to support individual sheet's rolltemplates. Here's an example output: The script is in the next post. Here's how you use it: An Example Command !chatmenu @{selected|character_id} @{selected|character_name}'s Chat Menu --title:Weapons --repeating_weapons|weaponName|btnWeapon --title:Skills --Acrobatics|Bluff|Climb|Diplomacy|Tumbling Beginning The Command !chatmenu id header !chatmenu @{selected|character_id} @{selected|character_name}'s Chat Menu You must supply an id - selected is a good way to get it. chatmenu, id, and header must be separated by spaces, but header can contain spaces. In the above example, the menu will have as header:  @{selected|character_name}'s Chat Menu Title You can supply a title, in the format --title:A title --title:Weapons --title:Social Skills When you supply a title, you create a menu group, that contains everything following, up to the next title. In the screenshot above, Attacks, Abilities, Spells, and skills were created using a title. It must begin with --title: and anything after that becomes the title, up to the next --. Repeating Sets You can include one or more repeating sets. The menu will generate a button for every item in the set. --repeating_section|label|button --repeating_weapons|weaponName|btnWeapon Any section beginning with --repeating_  will be treated as repeating section.  The label is the name of the attribute in the repeating set that contains the item's name. The button is the name of the button, without the roll_ part. You can find the name of a button by dragging it to your macro bar. It'll look something like this: You just need the bit I've highlighted above.  Active / Prepared You can include an optional 4th parameter, like so --repeating_spells|spellname|btnspell|prepared This must be the name of an attribute used to show whether an item is equipped, a spell isn't prepared, or similar. the attribute will be checked, and if it doesnt exist or has a value of 0, the item wont be displayed. Other Attributes You can also include lists of attributes that aren't in repeating sets. If you have a section that starts with -- and isnt a repeating section or a title, it will be treated as a list of attributes. Use this format: --Acrobatics|Bluff|Climb|Diplomacy|Tumbling --Acrobatics,skill_acrobatics|Bluff,skill_bluff|Climb,skill_climb|Diplomacy,skill_diplomacy|Tumbling,skill_tumbling .If the button name and label are the same, you can write it once (as in the first example). If button name and label are different, you need to separate them with a comma. You can include a third parameter for checking whether an item is prepared, active, equipped, etc. If so, you must include both label and buttom even if they are named the same: --title:Weapons --Pistol,pistol,pistol_equipped|Rifle,rifle,rifle_equipped|Sword,sword,sword_equipped This isn't likely to be needed very often - its usually a feature of repeating sets. But some sheets do this, so it's there if you need it. Non-Roll Buttons Sometimes you want a button to show descriptions, like say the text of a feat, or description of a spell.  Normally when you create an attribute, it is in this format: Languages,languages| If you put an ! sign on the end of button name, it wont look for a button, it will instead look for the named attribute, and create a button to display its contents.  Languages,languages!| This might not look as pretty, but it works. Maximum Flexibility You can include any number of attribute lists, repeating set groups, and titles, and arrange them however you like. They'll be printed in the order they are listed in the command you send. Here's a large example tested on the Pathfinder by Roll20 sheet (linebreaks included for readability - remove those if you use it): !chatmenu @{selected|character_id} @{selected|character_name} Action Menu --title:Attacks --repeating_attacks|atkname|fullattack --title:Abilities --repeating_spell-like|spelldisplay|spell --title:Spells --repeating_spell-0|spelldisplay|spell|spellprepared --repeating_spell-1|spelldisplay|spell|spellprepared --repeating_spell-2|spelldisplay|spell|spellprepared --repeating_spell-3|spelldisplay|spell|spellprepared --repeating_spell-4|spelldisplay|spell|spellprepared --repeating_spell-5|spelldisplay|spell|spellprepared --repeating_spell-6|spelldisplay|spell|spellprepared --repeating_spell-7|spelldisplay|spell|spellprepared --repeating_spell-8|spelldisplay|spell|spellprepared --repeating_spell-9|spelldisplay|spell|spellprepared --title:Skills --Appraise,appraise|Bluff,Bluff|Climb,climb|Craft,craft|Diplomacy,diplomacy Note the spells are all lumped into one group. Ideally you'd separate them by titles showing spell level, but I wanted to show how you can group different sets under one title. Also the spells have a 4th parameter: with this version, spells will only be shown if they are prepared. Roll Template Support You can enter the rolltemplate for your sheet. I've only tested this with pathfinder, so it's likely not very robust. Let me know of any problems. Syntax is mostly the same as before. You start with the command and header, like so !chatmenu @{selected|character_id} header text But now you can include the full text of a rolltemplate in the header text. So for the pathfinder sheet you might have !chatmenu @{selected|character_id} {template:pc}{{charname=@{selected|character_name}}} {{type=ability}} {{smallname=ACTIONS}} {{showchar=[1]}} {{descflag=1}} {{desc=CHATMENU}}  Notice that last entry: CHATMENU. If the header text contains that, thats where the menu buttons will be grouped, in a single rolltemplate section. If no CHATMENU is included, it will work like the default rolltemplate, and create a separate rolltemplate section for each group, with a title equal to the group name.  Important Use the Rolltemplate code as normal, but Drop the initial & before &{template if the template has any inline rolls [[ ]] convert them to single brackets [ ]  (so they aren't processed before the script gets them) So for pathfinder, the code is normally as below. &{template:pc}{{charname=@{selected|character_name}}} {{type=ability}} {{smallname=ACTIONS}} {{showchar=[[1]]}} {{descflag=1}} {{desc=CHATMENU}} {{desc= whatever you want here }} For this script, that becomes: (notice the {template missing its usual & , and showchar brackets cropping from [[1]] to [1] !chatmenu @{selected|character_id} {template:pc}{{charname=@{selected|character_name}}} {{type=ability}} {{smallname=ACTIONS}} {{showchar=[1]}} {{descflag=1}} {{desc=CHATMENU}} --title:Attacks --repeating_attacks|atkname|fullattack Here's what the pathfinder sheet looks like: Filtering When adding a menu button rule, you can add rules to test, and only show those buttons that pass. Say you have a list of feats and only want to show the combat ones: -title:Feats --repeating_feats|name|feat|type=combat Or you have equipment and only want to show the ones that are equipped (own 1 or more), and have magic in the name. -title:Magic Gear --repeating_gear|name|desc!|name=magic|count>0 This is for a hypothetical sheet which has a desc field, and a quantity field. The ! after the desc field means its not a button. See above. You can include any number of tests. The rules are: = (an equals sign): tests if the attribute being tested is equal to the given value. If its a text field, it accepts partial matches (so if you supply a test of "=comb" it will match "combat"). ! (exclamation mark, not equal to):  the reverse of the above. For numbers, it tests if the value is different. ( !0  is not equal to zero, handy). Go text, it checks if the test is NOT included in the attribute value. spellname!detect will exclude any spells with detect in their name, for instance. > (at least equal) : checks if the attribute's value is at least equal to the given match value. Only for numbers. So >0 will include all numbers 0 or greater. >10 includes all numbers 10 or greater. < (less than):  Less than. <10 will only show values 9 and below. No Symbol:  if you just include an attribute to test, but enter no comparison, it is treated as !0 . You are looking for any attribute value that exists and is not equal to 0 or "". This is handy for showing prepared spells, active powers or gear, and the like. Notice the behaviour of > and < is slightly different. > includes the number supplied, and < doesnt. I think this is the most likely needed behaviour, let me know if this is wrong.  Minor Bits: Separators, Extra Text Along with  --title  and the various  --attributes  there are other parameters: Separator --separator:| By default, the buttons in the chat menu are separated by spaces. You can pick a different separator. If you want to use a multi-character separator, that ends on a space, use a second : to end the separator. Like so: --separator: | : Footer You can add another section (bare text, another rolltemplate section, etc) and this will be added to end of the menu.   --footer:{{end=some end text}} --footer:some end text Note that you dont usually need this. You can include extra text in the initial declaration before titles, separators etc, like so: !chatmenu @{selected|character_id} {template:npcaction}{{rname=@{selected|character_name}}} {{description=CHATMENU}} {{name=@{selected|npc_type} @{selected|race} @{selected|class_display}}} The generated button menu will appear wherever you place the CHATMENU text, and it doesnt have to be at the end of the rolltemplate.  Please Test With Your Favourite Sheet There are a LOT of sheets and I can't test them all. If you test this script and it has problems, please report back with the sheet you tried it on, and the commands you used that didnt work or caused crashes.
1558616278

Edited 1559111522
GiGs
Pro
Sheet Author
Here's the script. Hope you enjoy it! v. 0.2.1 Major Update: rolltemplate support, and rudimentary attribute field support. v0.2.3 Minor bugfix:  on --footer v0.2.3 Minor Bigfix on the bigfix... v0.3.5 Added Filtering var universalChatMenu = universalChatMenu || (function () { 'use strict'; const version = '0.3.5', scriptName = 'Universal Chat Menu', lastUpdate = 1559109603598, //defaultTitle = 'Actions', defaultHeader = (who, actions = 'Actions') => `${who} ${actions} Menu`, BUTTONSYMBOL = '!', MENUPLACE = 'CHATMENU', SEPARATOR = ' ', checkInstall = () => { log('-=> ' + scriptName + ' v' + version + ' <=- [' + (new Date(lastUpdate)) + ']'); }, ch = function (c) { var entities = { '<': 'lt', '>': 'gt', "'": '#39', '@': '#64', '{': '#123', '|': '#124', '}': '#125', '[': '#91', ']': '#93', '"': 'quot', '-': 'mdash', ' ': 'nbsp' }; if (_.has(entities, c)) { return ('&' + entities[c] + ';'); } return ''; }, // build attribute name for a repeating section repname = (section, id, name) => `repeating_${section}_${id}_${name}`, // get an array of attributes on this character - handy to shorten code elsewhere. getAttributes = (cid) => { return findObjs({ _type: 'attribute', _characterid: cid }); }, // find a list of all the row ids in a repeating section. getRepeatingIDs = (cid, section, attribute) => { const repeating = getAttributes(cid) .filter((obj) => { return obj.get('name').startsWith(`repeating_${section}`) && obj.get('name').endsWith(attribute); }) .map(obj => obj.get('name').replace(`repeating_${section}_`, '').replace('_' + attribute, '').trim()); return repeating; }, checkFilter = (check, cid, prefix = '') => { // prefix is for repeating stats, supply 'repeating_${section}_${id}_' as the prefix //how to filter attributes //check=test,owned>0,magic!magic; or might just be check=checkbox; log('=== FILTER ===='); log(`check: ${check} prefix: ${prefix}`); let tests = check.toString().split(','); let rules = []; let pass = 1; // if tests are passed, return 1, otherwise 0; tests.forEach(test => { // need to check it has the three results, if only one, use rule.match = 0, and rule.type = '>' let s = test.split(/[!<>=]/); let rule = {}; rule.stat = s[0]; rule.match = s.length > 1 ? s[1] : 0; rule.type = s.length > 1 ? test.replace(s[0], '').replace(s[1], '') : '!'; rules.push(rule); // rules is an array of objects. don't need a key since will just be looping through it. }); log(`rules: ${JSON.stringify(rules)}`); // now have a set of rules can use to test the filters // will return true or false for (let rule of rules) { let value = getAttrByName(cid, `${prefix}${rule.stat}`) || 0; // should this be 1 if not exist, to ensure its not filtered if no default set? log(`rule: ${JSON.stringify(rule)}`); log(`value: ${value}`); log(`match: ${rule.match}`); switch (rule.type) { case '=': if(isNaN(value)) { pass = value.toString().toLowerCase().includes(rule.match.toString().toLowerCase()); } else { pass = (+value || 0) === (+rule.match || 0); } break; case '>': pass = (+value || 0) > (+rule.match || 0); break; case '<': pass = (parseInt(value, 10) || 0) < (parseInt(rule.match, 10) || 0); break; case '!': if(isNaN(value)) { pass = !value.toString().toLowerCase().includes(rule.match.toString().toLowerCase()); } else { pass = (+value || 0) !== (+rule.match || 0); } break; } if (!pass) break; } // at this point, pass will be 0 or 1, true or false; log(`pass: ${pass}`); return pass; }, buildButtons = (cid, who, args) => { let parameters = {}; let title = ''; parameters[title] = []; let settings_allowed = ['title', 'footer', 'separator']; for (let arg of args) { let settings = arg.split(':'); let setting = settings[0].toLowerCase(); let setting_details = settings.length > 0 ? settings[1] : ''; if (settings_allowed.includes(setting)) { // this is a config setting; only one supported right now is title // if new title, create an empty array in parameters to hold the following attributes. switch (setting) { case 'title': title = setting_details; // dont need defaulttitle here any more //|| defaultTitle; if (!parameters.hasOwnProperty(title)) parameters[title] = []; break; case 'footer': case 'separator': parameters[setting] = setting_details; break; } } else if (arg.toLowerCase().startsWith('repeating_')) { // this is a repeating section, need to split on "|" const repeatingAttribute = arg.split('|'); if (repeatingAttribute.length < 3) { // not valid, skip it parameters['error'] = 'Error in Repeating Attributue: ' + arg; break; } const section = repeatingAttribute[0].replace('repeating_', '').trim(); //repeating section name const display = repeatingAttribute[1].trim() || 'N/A'; //display name let button = repeatingAttribute[2].trim() || 'N/A'; let buttonAttr = getButtonType(button); if (buttonAttr) button = button.split(BUTTONSYMBOL)[0].trim(); if (display === 'N/A' || button === 'N/A') { // this will likely never trigger, it just wont print that section. parameters['error'] = 'Error in Repeating Attributue: ' + arg; break; } let repeating = getRepeatingIDs(cid, section, display); let checkAttribute = repeatingAttribute.length > 3 ? repeatingAttribute.slice(3) : []; // build chat menu buttons repeating.forEach(id => { let showButton = 1; if (checkAttribute.length > 0) { showButton = checkFilter(checkAttribute,cid, `repeating_${section}_${id}_`); } if (showButton > 0) { parameters[title].push( getButtonCode(getAttrByName(cid, repname(section, id, display)), who, repname(section, id, button), buttonAttr)); } }); } else { // this is a non-repeating set of attributes, split on | and then , let singleAttributes = arg.split('|'); singleAttributes.forEach(attr => { let attr_array = attr.split(','); let label = attr_array[0]; let button = attr_array[1] || label; let check = attr_array.length > 2 ? attr_array.slice(2) : []; let buttonAttr = getButtonType(button); if (buttonAttr) button = button.split(BUTTONSYMBOL)[0].trim(); // if check doesnt exist, or if it does exist but has a value of zero, print it. Otherwise don';'t let pass = 1; if(check.length > 0) { log(`${label}: ${check}`); pass = checkFilter(check,cid,''); } if (pass) { parameters[title].push( getButtonCode(label, who, button, buttonAttr)); } /* if (undefined === check || (getAttrByName(cid, check) && '0' !== getAttrByName(cid, check).toString())) { parameters[title].push( getButtonCode(label, who, button, buttonAttr)); } */ }); } } return parameters; }, getButtonType = (button) => button.split(BUTTONSYMBOL).length > 1, getButtonCode = (label, who, button, attribute = false) => { let code = `[${label}](`; if (attribute) { code += `!
/w gm &${ch('{')}template:default${ch('}')}${ch('{')}${ch('{')}name=${ch('@')}${ch('{')}selected|character_name${ch('}')} ${label}${ch('}')}${ch('}')}${ch('{')}${ch('{')}=${ch('@')}${ch('{')}selected${ch('|')}${button}${ch('}')}${ch('}')}${ch('}')})`; } else { code += `~${who}|${button})`; } return code; }, extractFrom = (buttons, field, fallback) => { let found = fallback; if (buttons.hasOwnProperty(field)) { found = buttons[field]; delete buttons[field]; } return found; }, handleInput = (msg) => { // copy the body of your on(chat:message) event to this function if ('api' === msg.type && msg.content.toLowerCase().startsWith('!chatmenu ')) { let args = msg.content.split(/\s+--/); // get character and heading let parameters = args[0].split(/\s+/); const cid = parameters[1] || ''; //character id if (cid === '') { sendChat(scriptName, 'No recognised character.'); return; } const who = getAttrByName(cid, 'character_name'); if (!who) { sendChat(scriptName, 'No recognised character.'); return; } const sender = 'character|' + cid; const caller = (getObj('player', msg.playerid) || { get: () => 'API' }).get('_displayname'); let header = parameters.slice(2).join(' ') || defaultHeader(who); // if this starts with {template: it is a rolltemplate // and if it contains CHATMENU, the menu buttons are placed there. Otherwise they are placed at the end. // get buttons let combineSections = false; // if false, defaultTemplate sections; if true, combine all sections into one args.shift(); let buttons = buildButtons(cid, who, args); // buttons will be an object, each item key is a section title, and each item value is an array of buttons in that section // build chat menu buttons if (buttons.hasOwnProperty('error')) { sendChat(scriptName, buttons['error'].toString()); return; } let headerPrint = `&{template:default}{{name=${header}}}`; if (header.includes('template:')) { headerPrint = `&${header}`; } headerPrint = headerPrint.replace(/(\[)/g, '[[').replace(/(\])/g, ']]'); // make sure no inline rolls present let footer = extractFrom(buttons, 'footer', ''); let separator = extractFrom(buttons, 'separator', SEPARATOR); let output = ''; let sections = Object.keys(buttons); if (headerPrint.includes(MENUPLACE)) combineSections = true; for (let section of sections) { if (buttons[section].length > 0) { output += (combineSections ? `**${section.toUpperCase()}**\n` : `{{**${section}**=`); output += buttons[section].join(separator); output += (combineSections ? `\n` : `}}`); } } let print = combineSections ? headerPrint.replace(MENUPLACE, output) : headerPrint + output; print += footer ? `\n/w ${caller} ${footer}` : ''; //log(print); sendChat(sender, `/w ${caller} ${print}`); log('====================================='); } }, registerEventHandlers = () => { on('chat:message', handleInput); }; return { CheckInstall: checkInstall, RegisterEventHandlers: registerEventHandlers }; }()); on('ready', () => { 'use strict'; universalChatMenu.CheckInstall(); universalChatMenu.RegisterEventHandlers(); });
1558617453
keithcurtis
Forum Champion
API Scripter
I'll take a look at this. I've been playtesting something very similar.
1558620530

Edited 1558620563
keithcurtis
Forum Champion
API Scripter
Working up a statblock and running into a problem on the OGL sheet. This is the built in macro for running Action #1: %{selected|repeating_npcaction_$1_npc_action} Here is the command I am using in UCM: --title:Actions --repeating_npcaction|name|npc_action but the error message I am receiving is: Universal Chat Menu: Error in Repeating Attributue: repeating_npcaction|name|npcaction What should I be doing differently?
1558622768
Ooh, very cool.  I’ll test it on Infinity and Monster of the Week. Maybe Call of Cthulhu. (too bad the script stuff I’m working on goes way off the sheet buttons, otherwise this would auto-solve many of my issues, lol.
1558623674

Edited 1558629498
keithcurtis
Forum Champion
API Scripter
Here is what I have so far, for NPCs on the OGL sheet. Note that I am  using a heavily Stylus-tweaked roll template , or the number and size of buttons would make this unusable:
1558655725

Edited 1558655781
GiGs
Pro
Sheet Author
Thanks for the reminder about that template, Keith. i might see about adding it to the script.  I'll check out the DnD sheet issue in a bit. You are referring to the D&D 5e sheet by roll20?
1558656177
keithcurtis
Forum Champion
API Scripter
I am indeed.
1558658120

Edited 1558659476
GiGs
Pro
Sheet Author
I found the cause Keith. My code got a bit over-zealous in my checking for errors, and found one where there was none :) It should work now. PS: Nice set of stats for that NPC. By the way, in case you don't realise, you can include calls to all spell levels. If the character doesnt have spells of a particular level, that section wont get shown. That makes it more universal. If you like the look better, you could also do the stats like this:  --title:STR --@{strength} (@{strength_mod}),npc_str --title:DEX --@{dexterity} (@{dexterity_mod}),npc_dex
1558664015

Edited 1558664120
keithcurtis
Forum Champion
API Scripter
Yes, I saw that nice feature. My template shows up to level 9 if such exist. I used the "|prepared" 4th criterion as well, so it only shows available spells. I'll post the code once I get a chance to check. I have a few more ideas to add. Thanks! Oh, and I set the Ability scores and mods in the left hand field mostly so the buttons would line up more nicely. 
1558665884
keithcurtis
Forum Champion
API Scripter
The actions are showing up fine now, but for some reason, they are appearing out of order, at the top of the roll template. I also put in slots available and known for each level. Here is the screen shot, followed by the code: !chatmenu @{selected|character_id} @{selected|character_name} --title:Str @{selected|strength} *(@{selected|strength_mod})* --Roll,strength|Save,npc_str_save --title:Dex @{selected|dexterity} *(@{selected|dexterity_mod})* --Roll,dexterity|Save,npc_dex_save --title:Con @{selected|constitution} *(@{selected|constitution_mod})* --Roll,constitution|Save,npc_con_save --title:Int @{selected|intelligence} *(@{selected|intelligence_mod})* --Roll,intelligence|Save,npc_int_save --title:Wis @{selected|wisdom} *(@{selected|wisdom_mod})* --Roll,wisdom|Save,npc_wis_save --title:Cha @{selected|charisma} *(@{selected|charisma_mod})* --Roll,charisma|Save,npc_cha_save  --title:Skills --Acrobatics,npc_Acrobatics|Arcana,npc_Arcana|Athletics,npc_Athletics|Deception,npc_Deception|History,npc_History|Intimidation,npc_Intimidation|Investigation,npc_Investigation|Medicine,npc_Medicine|Nature,npc_Nature|Perception,npc_Perception|Persuasion,npc_Persuasion|Sleight of Hand,npc_Sleight_of_Hand|Survival,npc_survival --title:Actions --repeating_npcaction|name|npc_action --title:Cantrips --repeating_spell-cantrip|spellname|spell --title:Lvl-1 *(@{selected|lvl1_slots_expended}/@{selected|lvl1_slots_total})* --repeating_spell-1|spellname|spell|prepared --title:Lvl 2 *(@{selected|lvl2_slots_expended}/@{selected|lvl2_slots_total})* --repeating_spell-2|spellname|spell|prepared --title:Lvl 3 *(@{selected|lvl3_slots_expended}/@{selected|lvl3_slots_total})* --repeating_spell-3|spellname|spell|prepared --title:Lvl 4 *(@{selected|lvl4_slots_expended}/@{selected|lvl4_slots_total})* --repeating_spell-4|spellname|spell|prepared --title:Lvl 5 *(@{selected|lvl5_slots_expended}/@{selected|lvl5_slots_total})* --repeating_spell-5|spellname|spell|prepared --title:Lvl 6 *(@{selected|lvl6_slots_expended}/@{selected|lvl6_slots_total})* --repeating_spell-6|spellname|spell|prepared --title:Lvl 7 *(@{selected|lvl7_slots_expended}/@{selected|lvl7_slots_total})* --repeating_spell-7|spellname|spell|prepared --title:Lvl 8 *(@{selected|lvl8_slots_expended}/@{selected|lvl8_slots_total})* --repeating_spell-8|spellname|spell|prepared --title:Lvl 9 *(@{selected|lvl9_slots_expended}/@{selected|lvl9_slots_total})* --repeating_spell-9|spellname|spell|prepared
1558666035
keithcurtis
Forum Champion
API Scripter
This is very nice, by the way. Just have to set that up as a universal token macro, and it's everywhere.
1558666809
GiGs
Pro
Sheet Author
Thanks!  I see why actions is appearing first. I created a default menu title at the start, which appears first in the list, and it happens to match your title. I'll fix that. In the meantime, if you change that section name to something other than Actions, it'll sequence properly.
1558667283
keithcurtis
Forum Champion
API Scripter
Cool. The statblock macros are one of the things I like most about the shaped sheet. This gives similar capability to nearly any sheet. I really would love a more versatile Roll20 default roll template, though.
Tools like this tempts me to upgrade to Pro...
1558668383

Edited 1558683001
GiGs
Pro
Sheet Author
aisforanagrams said: Tools like this tempts me to upgrade to Pro... Honestly, I cant imagine using roll20 without Pro. Scripts like tokenmod and chatsettattr and so many others make such a massive difference to GMs. If you can persuade your players to contribute, its worth it.  keithcurtis said: Cool. The statblock macros are one of the things I like most about the shaped sheet. This gives similar capability to nearly any sheet. I really would love a more versatile Roll20 default roll template, though. I do think this is the most useful script I've created. I agree on the roll20 template. I havent decided whether to do my own inbuilt formatting like so many scripts do, or provide a way for people to use their own sheet's rolltemplates. Both present their own challenges (mainly being that html and css is not my forte, haha - it's doable, it's just not fun for me like scripting is).
1558668988
GiGs
Pro
Sheet Author
Fixed the Actions title issue. Keep finding those little issues Keith :)
GiGs said: Honestly, I cant imagine using roll20 without Pro. Scripts like tokenmod and chatsettattr and so many others make such a massive difference to GMs. If you can persuade your players to contribute, its worth it.  My players have already gifted me compendiums and modules (they're freaking awesome!).  I wouldn't dare ask them to gift me a subscription!  I'd consider upgrading as I've been spending more time on this hobby of late, but I'd want to wait until I land a more permanent job situation (my current contract is up in the fall).
1558670620
keithcurtis
Forum Champion
API Scripter
Feature request . I now have a template that reports appropriately for both PCs and NPCs. However, NPCs have a Traits section that has no built in reporting macro, just a name field and a desc field, both text only. Any way this could be engineered so that when the resulting button is clicked, a roll template is produced that is something like "/w gm (template:default} {{name=name_field}}{{=desc_field}}". With name_field and desc_field being defined similarly to the other repeating fields that do  have macros? Perhaps a different declaration, like "--title:Traits --list|repeating_npctrait|name|desc". There are use cases for any repeating field with no button: Traits, Feats, Equipment, Magic Items, etc. --Sheet-dependent of course.
1558671501

Edited 1558681692
Seph
Pro
Nice work, GiGs! Would you recommend I use this over the original PF one you made? It's great that you've included skills, I also wondered if this would work for repeating skill fields (repeating_skillcraft, repeating_skillknowledge, repeating_skillperform, repeating_skillprofession, repeating_skillcustom) EDIT : got that last bit figured out! Except for how to get a repeating craft/perform/etc. to make a button that says ' Craft (item) ' instead of just ' item '. And like keith said above, this will be great for magic items, feats, etc. Is there a way to change the format to match PF? I tried changing the value of 'template' in the script to 'pc' like it was in the PF version, but I imagine it's more complicated than that, because all it gave me was a blank PF header. I mainly ask because I like how that template puts the titles directly over the buttons, and includes a scroll feature. Also, is there a way to have a specific skill not print if its ranks equal zero? Ex. Disable Device checks to see if disable_device_ranks > 0; if not, it doesn't print
1558682840
GiGs
Pro
Sheet Author
This one can include more stuff than the PF-specific menu, but because its designed to work with almost any sheet, it cant do the PF-specific formatting (yet). i do intend to add more controllable formatting, but I need to think about how I want to do that, so it'll be a little while. That's a good point about the repeating crafts, professions, etc. I can add something for that.  Regarding not printing if rank is zero: try putting the name of the attribute that holds the rank as a third parameter. --Disable Device,disabledevice,disable_device_ranks|
1558683364
GiGs
Pro
Sheet Author
keithcurtis said: Feature request . I now have a template that reports appropriately for both PCs and NPCs. However, NPCs have a Traits section that has no built in reporting macro, just a name field and a desc field, both text only. Any way this could be engineered so that when the resulting button is clicked, a roll template is produced that is something like "/w gm (template:default} {{name=name_field}}{{=desc_field}}". With name_field and desc_field being defined similarly to the other repeating fields that do  have macros? Perhaps a different declaration, like "--title:Traits --list|repeating_npctrait|name|desc". There are use cases for any repeating field with no button: Traits, Feats, Equipment, Magic Items, etc. --Sheet-dependent of course. Good idea. I've essentially already done the same process in the PF-specific script, accessing attribute values instead of buttons. I'll have a look at adding that over the weekend.
1558683667
Seph
Pro
Tried that, but it doesn't seem to work for those skills. Only for the repeating ones, I'm guessing since they use '|'
1558684331
GiGs
Pro
Sheet Author
Can you post the full command you are using? i'll try it out myself later and see why it isnt working.
1558684371
Seph
Pro
And a question regarding Magic Items: could the fourth parameter be set to only print items with a specific non-number value? Ex: --repeating_gear|name|rollShowGear|special, where the fourth parameter searches for the word 'aura' and prints the item only if that word is present.
1558684627
Seph
Pro
Here's the command as I currently have it written !chatmenu @{selected|character_id} @{selected|character_name} Skills Menu --title:Background Skills --repeating_skillcustom|name|skill --Appraise,appraise|Craft,craft --repeating_skillcraft|name|skill --Handle Animal,handleanimal|Knowledge (Engineering),knowledgeengineering,knowledge_engineering_ranks|Knowledge (Geography),knowledgegeography,knowledge_geography_ranks|Knowledge (History),knowledgehistory,knowledge_history_ranks|Knowledge (Nobility),knowledgenobility,knowledge_nobility_ranks --repeating_skillknowledge|name|skill|ranks --Linguistics,linguistics,linguistics_ranks|Perform,perform --repeating_skillperform|name|skill --Profession,profession --repeating_skillprofession|name|skill --Sleight of Hand,sleightofhand,sleight_of_hand_ranks
1558695869
GiGs
Pro
Sheet Author
Thanks for that Seph. Script is updated to fix single attributes not being hidden when you supply that third parameter.  That magic item feature: not sure about that. Every feature added means more code that needs to be maintained, and I don't want to go too far beyond the scripts original purpose (giving a handy menu of buttons from the sheet, organised as the sheet is organised). What's the use case for this feature? If I see it as being generally useful, and not just for niche purposes, I'll add it.
1558710230

Edited 1558710623
Seph
Pro
Since the Pathfinder sheet has all mundane and magic gear lumped into one list, I was looking for a way to have the chat menu only show magic items, or items with 'uses'. That may be too niche, though. Plus, I think I can accomplish it instead by making sure items I want printed have ein number at the beginning of the field specified in the fourth parameter. Not sure if this will work the way I think it will, I'll have to test it later.
1558722557

Edited 1558725766
You mentioned that this only works if each kind of button has a unique name.  So if the sheet author named all of the skill rolls "roll_skill," in a non-repeating section it won't work? Having trouble getting this to fire in Infinity.  Am I doing this right? The section on the character sheet: <div class="sheet-repitem"> <fieldset class="repeating_Weapons"> <table border="1" style="width:100%">     <td>Name<input style=width:140px type="text" name="attr_weapon_Name_rep" />                 <td>Range<input style=width:60px type="text" name="attr_weapon_Range_rep" />                 <td>Attribute Bonus Dice<input style=width:30px  type="text" name="attr_weapon_Attribute_rep" value='0'/>                 <td>Base Damage<input style=width:30px type="text" name="attr_weapon_Damagebonus_rep" value='0'>                 <td>Damage<input style=width:30px type="text" name="attr_weapon_Damage_rep" value='0'><button type='roll' name="attr_Combat-Dice" value="/me rolls @{weapon_Name_rep} [[(?{Modifier to Damage Dice|0}+@{weapon_Damage_rep}+@{weapon_Attribute_rep})t[Combat-Dice]]]+@{weapon_Damagebonus_rep} Hits , Hover mouse over result to get full result"></button></td> And my basic macro: !chatmenu @{selected|character_id} @{selected|character_name}'s Chat Menu --title:Agility --Acrobatics,skill|Stealth,skill --title:Weapons --repeating_Weapons|weapon_Name_rep|Combat-Dice This displays buttons for Acroabtics and Stealth, but no title or buttons for Weapons.  Is this because the weapon buttons are titled with "attr" rather than "roll"?
1558723214

Edited 1558723315
Scott C.
Forum Champion
Sheet Author
API Scripter
If the sheet author named all their roll buttons the same, they will only work from the sheet regardless of this script; token actions and macro bar calls also won't work. You should contact the sheet author and submit that as a bug. All roll buttons need to have unique names.
1558725824

Edited 1558725885
Kraynic
Pro
Sheet Author
I expect that is right, Ryan B.  I didn't have roll in front of any of the button names on my sheet and they wouldn't work correctly with this script until I edited that into a few for testing.  It does work after doing so, however. The sheet I started from didn't have any name for the roll buttons at all and it was only recently I realized that lack of a name was why they wouldn't drag to the macro quick bar.  So I learned a new thing today (which means more work!).  Nice script!  If I am eventually able to use my menu roll template for it, that would be fantastic.
1558726942
Yes, I will let the sheet author know.  In the meantime, I have it as a custom sheet so I can do whatever I want. I've got the rolls working for non-repeating skills after giving them unique names, but I still can't get results for that weapons section.  I tried changing the button name "attr_Combat-Dice" to "roll_Combat-Dice" - no luck.
1558727677
Kraynic
Pro
Sheet Author
Something you may try is to change to all lower case (may not matter for this script) and replace the "-" with "_" (which might be the problem).  I only am mentioning that because the combat section of my sheet works for me and those are differences I see.
1558728367
Davemania
KS Backer
Sheet Author
API Scripter
This is super cool and handy. Works without a hitch with the Low Fantasy Gaming sheet.
1558736767
Kraynic, that was it, I had to get rid of any capitals in the roll button name to make it work.
1558737276
GiGs
Pro
Sheet Author
I'm happy to see more people trying out this script. Thanks for letting us know Low fantasy works fine, Davemania. Ryan, I have noticed issues with capital letters in repeating section names (just the first part, immediately following repeating_), I hadnt noticed it in button names, but it wouldn't surprise me. Roll20's official character sheet guidelines were recently updated to recommend all attributes be named in lower case, so it is better to stick to that when you are making your own sheets. And yes, button names have to use roll_ in place of attr_ in the sheet html.
1558737396
GiGs
Pro
Sheet Author
Seph said: Since the Pathfinder sheet has all mundane and magic gear lumped into one list, I was looking for a way to have the chat menu only show magic items, or items with 'uses'. That may be too niche, though. Plus, I think I can accomplish it instead by making sure items I want printed have ein number at the beginning of the field specified in the fourth parameter. Not sure if this will work the way I think it will, I'll have to test it later. That doesnt sound too niche. When you said 'aura', I was definitely thinking it was something a bit too niche, but something like this might be a common requirement. I'll think about it. In the meantime, picking a field for the 4th parameter that has a number in it and setting it to zero for non magic, and 1+ for magic, should work.
1558739907
keithcurtis
Forum Champion
API Scripter
GiGs said: I'm happy to see more people trying out this script. It's a great idea. I've grown very dependent on Shaped for this built-in feature. I've wanted to run an OGL sheet campaign for a while, just to get a better understanding of the sheet, and this helps remove a big stumbling block.
1558779042

Edited 1559111422
GiGs
Pro
Sheet Author
Just added two features to test: RollTemplate Support You can enter the rolltemplate for your sheet. I've only tested this with pathfinder, so it's likely not very robust. Let me know of any problems. Syntax is mostly the same as before. You start with the command and header, like so !chatmenu @{selected|character_id} header text But now you can include the full text of a rolltemplate in the header text. So for the pathfinder sheet you might have !chatmenu @{selected|character_id} {template:pc}{{charname=@{selected|character_name}}} {{type=ability}} {{smallname=ACTIONS}} {{showchar=[1]}} {{descflag=1}} {{desc=CHATMENU}}  Notice that last entry: CHATMENU. If the header text contains that, thats where the menu buttons will be grouped, in a single rolltemplate section. If no CHATMENU is included, it will work like the default rolltemplate, and create a separate rolltemplate section for each group, with a title equal to the group name.  Important Use the Rolltemplate code as normal, but Drop the initial & before &{template if the template has any inline rolls [[ ]] convert them to single brackets [ ]  (so they aren't processed before the script gets them) So for pathfinder, the code is normally as below. &{template:pc}{{charname=@{selected|character_name}}} {{type=ability}} {{smallname=ACTIONS}} {{showchar=[[1]]}} {{descflag=1}} {{desc=CHATMENU}} {{desc= whatever you want here }} For this script, that becomes: (notice the {template missing its usual &, and showchar brackets cropping from [[1]] to [1] !chatmenu @{selected|character_id} {template:pc}{{charname=@{selected|character_name}}} {{type=ability}} {{smallname=ACTIONS}} {{showchar=[1]}} {{descflag=1}} {{desc=CHATMENU}} --title:Attacks --repeating_attacks|atkname|fullattack Here's what the pathfinder sheet looks like: Non-Button Buttons Keith asked for buttons to load attribute contents in a rolltemplate. I have a first version of this, but it looks like when you have a button like this, it overrides the custom rolltemplate. I don't know if that can be overcome, I'll investigate. Look at the picture above, the Language button at the bottom. Normally when you create an attribute, it is in this format: Languages,languages| If you put an = sign on the end of button name, it wont look for a button, it will instead look for the named attribute. Languages,languages=| I may add the ability to use custom templates, the parameters will go after the = sign. But for now, it just works with the default roll template. Edit: just realised the button will whisper the attribute only to the GM, not the player who triggered it. Oops. I'll fix that later, it's bedtime now. Minor Bits: Separators, Extra Text Along with --title  and the various --attributes  there are two new parameters: --separator:| By default, the buttons in the chat menu are separated by spaces. You can pick a different separator. If you want to use a multi-character separator, that ends on a space, use a second : to end the separator. Like so: --separator: | : You can add another section (bare text, another rolltemplate section, etc) and this will be added to end of the menu. This is mainly or the default rolltemplate, if you want to add one or more sections with descriptive text, but might be useful for some rolltemplates.  --footer:{{end=some end text}} --footer:some end text Note that you dont usually need this. You can include extra text in the initial declaration before titles, separators etc, like so: !chatmenu @{selected|character_id} {template:npcaction}{{rname=@{selected|character_name}}} {{description=CHATMENU}} {{name=@{selected|npc_type} @{selected|race} @{selected|class_display}}} The CHATMENU insert controls where the generated buttons will be placed, and it doesnt have to be at the end of the rolltemplate. Final Notes I also plan to add Seph's requested feature of more flexible filtering on what attributes get shown. Probably just a simple =(some value), or a list of values.  Let me know if the new features cause problems.
1558779204
GiGs
Pro
Sheet Author
If you havent seen Scott's approach to menus, check it out here:&nbsp; <a href="https://app.roll20.net/forum/post/7478947/script-menu-maker" rel="nofollow">https://app.roll20.net/forum/post/7478947/script-menu-maker</a> &nbsp;and pick the one that's best for you.
1558798182
keithcurtis
Forum Champion
API Scripter
Wow. You read my mind and added the things I was actually afraid to ask for. The ability to use other roll templates is amazing. I will have to do some more tweaking, since the default template can waste a lot of space. And separators!
1558822828

Edited 1558822984
Kraynic
Pro
Sheet Author
Just a heads up, there is a repeat in your roll template support codes.&nbsp; Probably just a copy/paste error. !chatmenu @{selected|character_id} !chatmenu @{selected|character_id} It is nice to have the ability to use another template.&nbsp; I set up a template for spellbook use and it works well with this script. I chose a character with a bunch of skills for the test. There are several buttons in each repeating combat section, but they can be split up nicely. And of course: Nice working script!&nbsp; I may be forced to add this to my games soon.
1558824441
GiGs
Pro
Sheet Author
I'm glad you like it Keith. The default template does take up so much space. I will probably add my own more compact version of it as a third formatting option, for sheets without rolltemplates of their own. Those look great, Kraynic. Which sheet is that for? Thanks for noticing that copypaste error, Kryanic. I made the mistake of posting just before bed, and was a bit tired. I also left out two important instryctions for converting rolltemplate code for use with this script, but you figured it out, so great. I've added the instructions to the above post (will add them to the OP later), but in short, convert any inline rolls [[ ]] to single brackets [ ], and leave off the &amp; before {template.
1558825252
Kraynic
Pro
Sheet Author
Originally it was the Palladium-Megaverse sheet, but I have been working on it for a while for use with the Palladium Fantasy 1E RPG.&nbsp; It has very little left of the original other than the structure of the tabs at the top of the sheet.&nbsp; I'm using the roll template that Jakob wrote up and posted a while back, with a few edits I have made over time.&nbsp; I have the basic template used by all the sheet rolls of various colors, and then a version that is specifically for menus, which is what I am using with your script.
1558825911
GiGs
Pro
Sheet Author
Very nice work. Was that jakob's multi-coloured default rolltemplate? If so, you have radically transformed its look.
1558826576
Kraynic
Pro
Sheet Author
Yep, that is the one!
1558828801

Edited 1558828964
keithcurtis
Forum Champion
API Scripter
Revised Roll template, PC and NPC shown. These use the npcattack roll template for the 5e OGL sheet, which I feel has the best flexibility for display. Again, I have modded it with Stylus, code found here . These take up much less room. The Traits now report nicely with no fuss. I arranged the ability scores a bit differently, since making them into sections by Str, Dex, etc, took up way too much vertical space. Some filtering as you mention above would be great, allowing the attacks to be separated into melee, ranged and spell (in the case of 5e, at least). I have the criteria needed to separate them from doing Scott's template. I do like that if an attribute is empty or does not exist, the script just passes over it. I was able to create the line that gives the Bullywug's creature type or the PCs class info with a call on the new footer parameter that asks for both but only passes the ones that exist. Another possibility: a new parameter called --freetext, which just passes whatever text comes after it, provided it is not empty.&nbsp; Here is the code: !chatmenu @{selected|character_id} {template:npcaction}{{rname=@{selected|character_name}}}{{description=CHATMENU}} --separator: |&amp;nbsp; --title:Ability Rolls --**Str&nbsp;@{selected|strength}**&nbsp;*(@{selected|strength_mod})* ,strength|**Dex&nbsp;@{selected|dexterity}**&nbsp;*(@{selected|dexterity_mod})*,dexterity|**Con&nbsp;@{selected|constitution}**&nbsp;*(@{selected|constitution_mod})* ,constitution|**Int&nbsp;@{selected|intelligence}**&nbsp;*(@{selected|intelligence_mod})* ,intelligence|**Wis&nbsp;@{selected|wisdom}**&nbsp;*(@{selected|wisdom_mod})*,wisdom|**Cha&nbsp;@{selected|charisma}**&nbsp;*(@{selected|charisma_mod})*,charisma --title:Saving Throws --Str,npc_str_save|Dex,npc_dex_save|Con,npc_con_save|Int,npc_int_save|Wis,npc_wis_save|Cha,npc_cha_save --title:Skills --Acrobatics,npc_Acrobatics|Arcana,npc_Arcana|Athletics,npc_Athletics|Deception,npc_Deception|History,npc_History|Intimidation,npc_Intimidation|Investigation,npc_Investigation|Medicine,npc_Medicine|Nature,npc_Nature|Perception,npc_Perception|Persuasion,npc_Persuasion|Sleight of Hand,npc_Sleight_of_Hand|Survival,npc_survival --title:Traits --repeating_npctrait|name|desc= --title:Actions --repeating_npcaction|name|npc_action --title:Traits --repeating_traits|name|output --title:Attacks --repeating_attack|atkname|attack --title:Cantrips --repeating_spell-cantrip|spellname|spell --title:Lvl-1 *(@{selected|lvl1_slots_expended}/@{selected|lvl1_slots_total})* --repeating_spell-1|spellname|spell|prepared --title:Lvl 2 *(@{selected|lvl2_slots_expended}/@{selected|lvl2_slots_total})* --repeating_spell-2|spellname|spell|prepared --title:Lvl 3 *(@{selected|lvl3_slots_expended}/@{selected|lvl3_slots_total})* --repeating_spell-3|spellname|spell|prepared --title:Lvl 4 *(@{selected|lvl4_slots_expended}/@{selected|lvl4_slots_total})* --repeating_spell-4|spellname|spell|prepared --title:Lvl 5 *(@{selected|lvl5_slots_expended}/@{selected|lvl5_slots_total})* --repeating_spell-5|spellname|spell|prepared --title:Lvl 6 *(@{selected|lvl6_slots_expended}/@{selected|lvl6_slots_total})* --repeating_spell-6|spellname|spell|prepared --title:Lvl 7 *(@{selected|lvl7_slots_expended}/@{selected|lvl7_slots_total})* --repeating_spell-7|spellname|spell|prepared --title:Lvl 8 *(@{selected|lvl8_slots_expended}/@{selected|lvl8_slots_total})* --repeating_spell-8|spellname|spell|prepared --title:Lvl 9 *(@{selected|lvl9_slots_expended}/@{selected|lvl9_slots_total})* --repeating_spell-9|spellname|spell|prepared --footer:{{name=@{selected|npc_type} @{selected|race} @{selected|class_display}}}
1558829082
keithcurtis
Forum Champion
API Scripter
Also note: in order to get the pipe to display with spaces to either side, it was necessary to define the separator as&nbsp; --separator: |&amp;nbsp; I don't think there's any easy way around that, but it's simple enough to do.
1558829626

Edited 1558829993
keithcurtis
Forum Champion
API Scripter
Hmm. I'm also seeing that I did not realize that the OGL sheet has an acrobatics roll, and an NPC acrobatics roll. Asking a PC to roll NPC acrobatics doesn't take proficiency into account, but it doesn't throw up an error, either. I'll either need to split these into two macros (not difficult), or wait to see what you come up with for filtering. I'll edit my posted code accordingly. Awesome job! Edit: Situation also applies to Saves. I don't think that having two statblock reporters is that huge an obstacle, though. I can make the PC version a universal token macro for my players, and reserve the NPC one for DM use.
1558836641

Edited 1558843013
GiGs
Pro
Sheet Author
You're a great beta tester Keith! I'll think about the separator space issue, and see if I can come up with a good way to resolve it. People naturally use spaces as separators when writing commands, so it's trickier than it seems - how to distinguish between spaces that are just spacing, and spaces that are part of the command? I can add an "end this command section" character, if i can think of a one that isnt too clunky.&nbsp; Edit: &nbsp;actually it should be easy, I'm already planning to use = in a couple of places, so i could use that as an optional end character. You would then use --separator: | = inserting the equal to end the separator text. Edit 2: &nbsp;even better, you can use a second ':' and it will work already, no extra code needed. --separator: | : Regarding the footer and free text, you might not need one. Correct me if I'm not understanding you properly. The CHATMENU section doesnt need to be the last entry in the initial template text. You can add any text there. The start of your macro: !chatmenu @{selected|character_id} {template:npcaction}{{rname=@{selected|character_name}}}{{description=CHATMENU}}&nbsp; --separator: |&amp;nbsp; Could be: !chatmenu @{selected|character_id} {template:npcaction}{{rname=@{selected|character_name}}} {{description=CHATMENU}}&nbsp;{{name=@{selected|npc_type} @{selected|race} @{selected|class_display}}} anytext you want here --separator: |&amp;nbsp; (line break added for clarity) The footer was mainly designed for the default template, which lets you create arbitrary rolltemplate sections with any name. Turns out it probably isnt needed there, either. Does this work for you, or is there some subtlety I'm not grasping (very possible!)? Edit 3: &nbsp;I discovered that you cant add naked text like the above "any text here" example, so redesigned the footer to work with that. The above would now read: !chatmenu @{selected|character_id} {template:npcaction}{{rname=@{selected|character_name}}} {{description=CHATMENU}}&nbsp;{{name=@{selected|npc_type} @{selected|race} @{selected|class_display}}} --footer:anytext you want here --separator: |&amp;nbsp;
1558836716
GiGs
Pro
Sheet Author
keithcurtis said: Hmm. I'm also seeing that I did not realize that the OGL sheet has an acrobatics roll, and an NPC acrobatics roll. Asking a PC to roll NPC acrobatics doesn't take proficiency into account, but it doesn't throw up an error, either. I'll either need to split these into two macros (not difficult), or wait to see what you come up with for filtering. I'll edit my posted code accordingly. Awesome job! Edit: Situation also applies to Saves. I don't think that having two statblock reporters is that huge an obstacle, though. I can make the PC version a universal token macro for my players, and reserve the NPC one for DM use. I'll have to look at the D&amp;D5e sheet and how it does this. What sort of filtering would be needed to make this work properly?