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

Help With Custom Sheet Macros

Using example of Skill = d10 + d4, Initiative = d20 + d6 I want the rolls to come up in the chat for Skill to NOT SHOW crit/fail but still able to hover in the chat and see the roll and for Initiative to SHOW crit/fail for the d20 but NOT for the d6 and still be able to hover in the chat to see the roll.  Is this possible? Do I need a script? Is it too complicated to attempt? Could I call the roll into the template separately from the roll total to view both without needing to hover of the total? Any advice would be very much appreciated lol
1746723954
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
There's several ways to do this. The best way is going to depend on what you are doing. Are you building a character sheet (e.g. html, css, javascript)? Or writing a macro for an existing sheet or without a sheet?
It's for a character sheet I've been working on so to put it into perspective there are a lot of skills that are similar to the example that don't use a d20 and then just initiative and attack rolls that use a d20 with another die. The friend I'm making this for basically only wants to see crit/fail on d20s only and nothing else.
1746743480
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
gotcha, with a custom sheet there's a couple ways to do it: use a custom roll template and change the styling of the template so that rolls in a given field aren't styled for crit/fumble change the crit/fumble points on the roll expression (e.g. 1d10cs<0cf<0 would prevent any result styling from being applied.
How do I use a custom rolltemplate to accomplish this? I have a rolltemplate. I have seen the code that styles the crit/fail of the die and changed the green and red that shows up in the chat to black so that it doesn't show but it applies that to all dice (d2, d4... d20) how can I tell it to only change colors for a d20 and not the other dice? I have seen the cs<0cf<0 thing before but that's a lot of nothing to put into the code and the rolls are a bit complicated 😅 Example... <button class="roll-button skill-button" type="roll" name="roll_skill" value="@{whispertoggle}&{template:custom}{{title=@{character_name}}} {{Roll=[[[[d0 + @{disguise_dice} + ?{Select Ability|None, 0|Strength, @{strength_dice}|Agility, @{agility_dice}|Alertness, @{alertness_dice}|Endurance, @{endurance_dice}|Mind, @{mind_dice}|Charisma, @{charisma_dice}}]]]] | [[[[d0 + @{disguise_dice} + ?{Select Ability|None, 0|Strength, @{strength_dice}|Agility, @{agility_dice}|Alertness, @{alertness_dice}|Endurance, @{endurance_dice}|Mind, @{mind_dice}|Charisma, @{charisma_dice}}]]]]}}{{desc2=**Disguise Check**}} {{status= }} @{blind} @{burning} @{charmed} @{chilled} @{deafened} @{distracted} @{drained} @{frightened} @{frozen} @{incapacitated} @{paralyzed} @{poisoned} @{prone} @{restrained} @{stunned} @{unconscious}">Disguise</button> In the example above, it's using script at the end of my sheet because your die value changes depending on how many points you put into a skill. As well as that the die value shows and changes on the sheet so if I change the script to have cs<0cf<0 at the end of all the dice then it'll look bad on the sheet.  So, I'm more interested in trying to see how it works in the rolltemplate but I don't see a lot about how rolls work anywhere.
1746808205
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
actually, thinking about it, doing the roll template styling isn't going to work because you have multiple dice in a single roll. To get around the display issue, you can do a couple things: make a hidden version of the attribute that is the "roll version" of the attribute and uses a sheetworker to append the appropriate cs/cf modifications depending on if it's a d20 or not. Use custom roll parsing to send the roll and do the logic for whether to add the crit/fumble customization at time of roll Also, there is another way to prevent the crit fumble detection in Roll20. You can wrap the dice in inline brackets within the overarching inline roll. This will stop those rolls from triggering all crit/fumble effects. However, it also changes the tooltip hover for the roll so that the dice result just looks like a standard number instead of a rolled value.
I am already using the extra [[ [[roll]] ]] around all my rolls. We are currently settling for this but my friend really wants to be able to see the rolled dice result. Which of the other two suggestions would be easier to do? Cause I have very little coding experience so much of what you are saying is going over my head lol but very much appreciate it still. How would I be able to apply this to my sheet?
1746828006

Edited 1746829523
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
I'd probably go with the custom roll parsing method. It avoids bloating the sheet with a bunch of extra attributes and will allow additional features if you decide you want to do more complex things. And it will be only slightly more complex to do than the roll version of the attribute. EDIT: Hit submit too soon, meant to add some demo code.
1746829406

Edited 1746830495
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
The first step for this is to change all the buttons that are going to be used for this into action buttons (note the change to the button name): <button class="roll-button skill-button" type="action" name="act_disguise" >Disguise</button> All of the roll's macro stuff will now happen down your script field. Something like the following would be included in your script tag at the end of your sheet (will need to be adjusted if used with repeating sections or other rolls that use a different chat output than that for disguise): // array that holds all the names of the skill buttons const skills = ['disguise']; skills.forEach(skill => { // listen for the click on each button on(`clicked:${skill}`,() => { // create an array of attribute names to get with getAttrs // I'd just put all the attribute names that you might need for any skill roll in here. This demo just has the ones for your disguise roll const getArr = ['disguise_dice','agility_dice','strength_dice','alertness_dice','endurance_dice','mind_dice','charisma_dice']; // get the attribute values. Note the async tag on the callback function. This will allow us to use await with the startRoll sheetworker. getAttrs(getArr,async (attributes) => { // process the skill name into it's plain text name const textName = capitalize(skill.replace(/-+|_+/g,' ')); // ask the user what dice pool they want to use const abilityName = await extractQueryResult('Select Ability|None,none|Strength,strength|Agility,agility,Alertness,alertness|Endurance,endurance|Mind,mind|Charisma,charisma'); // get the dice value to use const abilityDice = attributes[abilityName] || '0'; // decide whether to append the cs/cf customization to it const modifiedAbilityDice = abilityDice === '0' || /d20/.test(abilityDice) ? attributes[abilityName] : `${attributes[abilityName]}cs<0cf<0`; // create the text that is going to be sent to chat const rollText = `@{whispertoggle}&{template:custom}{{title=@{character_name}}} {{Roll=[[d0 + ${ modifiedSkillDice } + ${ modifiedAbilityDice } ]] | [[d0 + ${ modifiedSkillDice } + ${ modifiedAbilityDice } ]]}} {{desc2=** ${ textName } check**}} {{status= }} @{blind} @{burning} @{charmed} @{chilled} @{deafened} @{distracted} @{drained} @{frightened} @{frozen} @{incapacitated} @{paralyzed} @{poisoned} @{prone} @{restrained} @{stunned} @{unconscious}` ; const roll = await startRoll(rollText); finishRoll(roll.rollId); }) }) }); // function to allow us to query the user for information const extractQueryResult = async function(query){ const rollObj = { query:`[[0[response=?{${query}}]]]` }; const msg = `{{query=[[0[response=?{${query}}]]]}}`; const roll = await startRoll(msg); finishRoll(roll.rollId); return roll.results.query.expression.replace(/^.+?response=|\]$/g,''); }; // capitalize function pulled from the K-scaffold const capitalize = function(string){ return string.replace(/(?:^|\s+|\/)[a-z]/ig,(letter)=>letter.toUpperCase()); }; EDIT: Had forgotten to actually put the roll of the dice in. EDIT: If you want some more info and examples of using startRoll, I recommend GiGs' blog . GiGs prefers the callback syntax of startRoll over the async/await that I used here, but that is a minor difference.
I appreciate your help with this. I have inserted the script code into my sheet, changed all Roll Buttons to Action Buttons in both HTML and CSS. I added just the skill and ability names into some of the script but I'm not sure what else needs to be changed or added? Also I hope I understand you correctly. Will this change I make not work for buttons in a fieldset? Cause that is where attack rolls come from, inside a fieldset lol So far everything looks good but the buttons won't roll anything. This is what I have added to the script you made: // array that holds all the names of the skill buttons const skills = ["genathletics", "gencovert", "gendomestic", "geninfluence", "genperception", "genpilot", "genscience", "genmelee", "genheavy", "genknow", "genmedical", "genmilitary", "genstarships", "gentech", "nature", "genranged", "climbing", "gym", "spo", "swim", "weight", "xspo", "conceal", "disguise", "escape", "forgery", "picklock", "pickpocket", "soh", "sneak", "streetsmart", "acting", "art", "cook", "dance", "dac", "mixology", "plantcare", "pmi", "sewing", "sing", "barter", "deception", "diplomacy", "empathy", "interrogation", "intimidation", "persuasion", "seduction", "detecttraps", "gambling", "gaming", "investigation", "sense", "surveillance", "aerovehicles", "aircraft", "watervehicles", "landvehicles", "detectambush", "detectconcealment", "dpa", "biology", "chemistry", "physics", "xenobio", "axes", "knives", "maces", "spears", "special", "staffs", "swords", "unarmed", "grenades", "heavyauto", "heavyrifle", "rocketlauncher", "throwerweapons", "bandits", "business", "cularbor", "culcouncil", "culouter", "culsector", "hisarbor", "hiscouncil", "hisouter", "hissector", "lawarbor", "lawcouncil", "lawouter", "lawsector", "relarbor", "relcouncil", "relouter", "relsector", "rememberinfo", "firstaid", "forensics", "pathology", "psychiatry", "surgery", "toxicology", "xenopathology", "armourer", "blacksmith", "demolitions", "findcont", "gunsmith", "militaryknowledge", "womd", "xenoweapons", "starshipeng", "starshipgen", "starshipnav", "spl", "sps", "starshiprepair", "sss", "starshipweapons", "advancedfirearms", "bbss", "comsystems", "computers", "electronics", "mechanics", "robotics", "fishing", "fauna", "flora", "landnav", "trackanimals", "trapping", "wac", "wildernesssurvival", "xenoanimalcare", "automaticrifles", "bows", "longrangerifles", "pistols", "shotguns", "submachineguns", "thrownweapons"]; skills.forEach(skill => { // listen for the click on each button on(`clicked:${skill}`,() => { // create an array of attribute names to get with getAttrs // I'd just put all the attribute names that you might need for any skill roll in here. This demo just has the ones for your disguise roll const getArr = ["genathletics", "gencovert", "gendomestic", "geninfluence", "genperception", "genpilot", "genscience", "genmelee", "genheavy", "genknow", "genmedical", "genmilitary", "genstarships", "gentech", "nature", "genranged", "climbing", "gym", "spo", "swim", "weight", "xspo", "conceal", "disguise", "escape", "forgery", "picklock", "pickpocket", "soh", "sneak", "streetsmart", "acting", "art", "cook", "dance", "dac", "mixology", "plantcare", "pmi", "sewing", "sing", "barter", "deception", "diplomacy", "empathy", "interrogation", "intimidation", "persuasion", "seduction", "detecttraps", "gambling", "gaming", "investigation", "sense", "surveillance", "aerovehicles", "aircraft", "watervehicles", "landvehicles", "detectambush", "detectconcealment", "dpa", "biology", "chemistry", "physics", "xenobio", "axes", "knives", "maces", "spears", "special", "staffs", "swords", "unarmed", "grenades", "heavyauto", "heavyrifle", "rocketlauncher", "throwerweapons", "bandits", "business", "cularbor", "culcouncil", "culouter", "culsector", "hisarbor", "hiscouncil", "hisouter", "hissector", "lawarbor", "lawcouncil", "lawouter", "lawsector", "relarbor", "relcouncil", "relouter", "relsector", "rememberinfo", "firstaid", "forensics", "pathology", "psychiatry", "surgery", "toxicology", "xenopathology", "armourer", "blacksmith", "demolitions", "findcont", "gunsmith", "militaryknowledge", "womd", "xenoweapons", "starshipeng", "starshipgen", "starshipnav", "spl", "sps", "starshiprepair", "sss", "starshipweapons", "advancedfirearms", "bbss", "comsystems", "computers", "electronics", "mechanics", "robotics", "fishing", "fauna", "flora", "landnav", "trackanimals", "trapping", "wac", "wildernesssurvival", "xenoanimalcare", "automaticrifles", "bows", "longrangerifles", "pistols", "shotguns", "submachineguns", "thrownweapons", 'agility_dice', 'strength_dice', 'alertness_dice', 'endurance_dice', 'mind_dice', 'charisma_dice']; // get the attribute values. Note the async tag on the callback function. This will allow us to use await with the startRoll sheetworker. getAttrs(getArr,async (attributes) => { // process the skill name into it's plain text name const textName = capitalize(skill.replace(/-+|_+/g,' ')); // ask the user what dice pool they want to use const abilityName = await extractQueryResult('Select Ability|None,none|Strength,strength|Agility,agility,Alertness,alertness|Endurance,endurance|Mind,mind|Charisma,charisma'); // get the dice value to use const abilityDice = attributes[abilityName] || '0'; // decide whether to append the cs/cf customization to it const modifiedAbilityDice = abilityDice === '0' || /d20/.test(abilityDice) ? attributes[abilityName] : `${attributes[abilityName]}cs<0cf<0`; // create the text that is going to be sent to chat const rollText = `@{whispertoggle}&{template:custom}{{title=@{character_name}}} {{Roll=[[d0 + ${modifiedSkillDice} + ${modifiedAbilityDice}]] | [[d0 + ${modifiedSkillDice} + ${modifiedAbilityDice}]]}} {{desc2=**${textName} check**}} {{status= }} @{blind} @{burning} @{charmed} @{chilled} @{deafened} @{distracted} @{drained} @{frightened} @{frozen} @{incapacitated} @{paralyzed} @{poisoned} @{prone} @{restrained} @{stunned} @{unconscious}`; const roll = await startRoll(rollText); finishRoll(roll.rollId); }) }) }); // function to allow us to query the user for information const extractQueryResult = async function(query){ const rollObj = { query:`[[0[response=?{${query}}]]]` }; const msg = `{{query=[[0[response=?{${query}}]]]}}`; const roll = await startRoll(msg); finishRoll(roll.rollId); return roll.results.query.expression.replace(/^.+?response=|\]$/g,''); }; // capitalize function pulled from the K-scaffold const capitalize = function(string){ return string.replace(/(?:^|\s+|\/)[a-z]/ig,(letter)=>letter.toUpperCase()); }; This is what the rest of my sheetworker looks like if that is necessary: <!-- SHEETWORKER --> <script type="text/worker"> // Script that does something I don't understand const int = score => parseInt(score, 10) || 0; // Script work for Attributes (uses the word stat) const stat = ["strength", "agility", "alertness", "endurance", "mind", "charisma"]; stat.forEach(stat => { on(`change:${stat}`, () => { getAttrs([stat], values => { const stat_points = int(values[stat]); console.log(stat_points); let stat_dice = "0"; if (stat_points >= 30) stat_dice = "d12 + d10"; else if (stat_points >= 29) stat_dice = "d12 + d8"; else if (stat_points >= 28) stat_dice = "d12 + d8"; else if (stat_points >= 27) stat_dice = "d12 + d6"; else if (stat_points >= 26) stat_dice = "d12 + d6"; else if (stat_points >= 25) stat_dice = "d12 + d4"; else if (stat_points >= 24) stat_dice = "d12 + d4"; else if (stat_points >= 23) stat_dice = "d12 + d2"; else if (stat_points >= 22) stat_dice = "d12 + d2"; else if (stat_points >= 21) stat_dice = "d12"; else if (stat_points >= 20) stat_dice = "d12"; else if (stat_points >= 19) stat_dice = "d10"; else if (stat_points >= 18) stat_dice = "d10"; else if (stat_points >= 17) stat_dice = "d8"; else if (stat_points >= 16) stat_dice = "d8"; else if (stat_points >= 15) stat_dice = "d6"; else if (stat_points >= 14) stat_dice = "d6"; else if (stat_points >= 13) stat_dice = "d4"; else if (stat_points >= 12) stat_dice = "d4"; else if (stat_points >= 11) stat_dice = "d2"; else if (stat_points >= 10) stat_dice = "d2"; else if (stat_points >= 9) stat_dice = "-d2"; else if (stat_points >= 8) stat_dice = "-d2"; else if (stat_points >= 7) stat_dice = "-d4"; else if (stat_points >= 6) stat_dice = "-d4"; else if (stat_points >= 5) stat_dice = "-d6"; else if (stat_points >= 4) stat_dice = "-d6"; else if (stat_points >= 3) stat_dice = "-d8"; else if (stat_points >= 2) stat_dice = "-d8"; else if (stat_points >= 1) stat_dice = "-d10"; else stat_dice = "0"; setAttrs({ [`${stat}_dice`]: stat_dice }); }); }); }); // Script work for skills (uses the word skill) const skill = ["genathletics", "gencovert", "gendomestic", "geninfluence", "genperception", "genpilot", "genscience", "genmelee", "genheavy", "genknow", "genmedical", "genmilitary", "genstarships", "gentech", "nature", "genranged", "climbing", "gym", "spo", "swim", "weight", "xspo", "conceal", "disguise", "escape", "forgery", "picklock", "pickpocket", "soh", "sneak", "streetsmart", "acting", "art", "cook", "dance", "dac", "mixology", "plantcare", "pmi", "sewing", "sing", "barter", "deception", "diplomacy", "empathy", "interrogation", "intimidation", "persuasion", "seduction", "detecttraps", "gambling", "gaming", "investigation", "sense", "surveillance", "aerovehicles", "aircraft", "watervehicles", "landvehicles", "detectambush", "detectconcealment", "dpa", "biology", "chemistry", "physics", "xenobio", "axes", "knives", "maces", "spears", "special", "staffs", "swords", "unarmed", "grenades", "heavyauto", "heavyrifle", "rocketlauncher", "throwerweapons", "bandits", "business", "cularbor", "culcouncil", "culouter", "culsector", "hisarbor", "hiscouncil", "hisouter", "hissector", "lawarbor", "lawcouncil", "lawouter", "lawsector", "relarbor", "relcouncil", "relouter", "relsector", "rememberinfo", "firstaid", "forensics", "pathology", "psychiatry", "surgery", "toxicology", "xenopathology", "armourer", "blacksmith", "demolitions", "findcont", "gunsmith", "militaryknowledge", "womd", "xenoweapons", "starshipeng", "starshipgen", "starshipnav", "spl", "sps", "starshiprepair", "sss", "starshipweapons", "advancedfirearms", "bbss", "comsystems", "computers", "electronics", "mechanics", "robotics", "fishing", "fauna", "flora", "landnav", "trackanimals", "trapping", "wac", "wildernesssurvival", "xenoanimalcare", "automaticrifles", "bows", "longrangerifles", "pistols", "shotguns", "submachineguns", "thrownweapons"]; skill.forEach(skill => { on(`change:${skill}`, () => { getAttrs([skill], values => { const skill_points = int(values[skill]); console.log(skill_points); let skill_dice = "0"; if (skill_points >= 10) skill_dice = "d12+d8"; else if (skill_points >= 9) skill_dice = "d12+d6"; else if (skill_points >= 8) skill_dice = "d12+d4"; else if (skill_points >= 7) skill_dice = "d12+d2"; else if (skill_points >= 6) skill_dice = "d12"; else if (skill_points >= 5) skill_dice = "d10"; else if (skill_points >= 4) skill_dice = "d8"; else if (skill_points >= 3) skill_dice = "d6"; else if (skill_points >= 2) skill_dice = "d4"; else if (skill_points >= 1) skill_dice = "d2"; else if (skill_points >= 0) skill_dice = "0"; else if (skill_points >= -1) skill_dice = "-d2"; else if (skill_points >= -2) skill_dice = "-d4"; else if (skill_points >= -3) skill_dice = "-d6"; else if (skill_points >= -4) skill_dice = "-d8"; else if (skill_points >= -5) skill_dice = "-d10"; else if (skill_points >= -6) skill_dice = "-d12"; else if (skill_points >= -7) skill_dice = "-d12-d2"; else if (skill_points >= -8) skill_dice = "-d12-d4"; else if (skill_points >= -9) skill_dice = "-d12-d6"; else if (skill_points >= -10) skill_dice = "-d12-d8"; else skill_dice = "0"; setAttrs({ [`${skill}_dice`]: skill_dice }); }); }); }); on("change:classname", function() { console.log("Set ClassCheck = ClassName if not already equal.") getAttrs(["ClassName","ClassCheck"], function(values) { if (values.ClassCheck !== values.ClassName) { setAttrs({ ClassCheck: values.ClassName }); }; }); console.log("ClassCheck = ClassName whenever changed."); }); on("change:npcclassname", function() { console.log("Set npcClassCheck = npcClassName if not already equal.") getAttrs(["npcClassName","npcClassCheck"], function(values) { if (values.npcClassCheck !== values.npcClassName) { setAttrs({ npcClassCheck: values.npcClassName }); }; }); console.log("npcClassCheck = npcClassName whenever changed."); });
1746905116
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Ah, the reason it isn't working is that for some reason I copy/pasted in a version of the code that was missing the setup for the modified skill dice. Here's the code with that corrected: // array that holds all the names of the skill buttons const skills = ['disguise']; skills.forEach(skill => { // listen for the click on each button on(`clicked:${skill}`,() => { debugger; // create an array of attribute names to get with getAttrs // I'd just put all the attribute names that you might need for any skill roll in here. This demo just has the ones for your disguise roll const getArr = ['disguise_dice','agility_dice','strength_dice','alertness_dice','endurance_dice','mind_dice','charisma_dice']; // get the attribute values. Note the async tag on the callback function. This will allow us to use await with the startRoll sheetworker. getAttrs(getArr,async (attributes) => { // process the skill name into it's plain text name const textName = capitalize(skill.replace(/-+|_+/g,' ')); // ask the user what dice pool they want to use const abilityName = await extractQueryResult('Select Ability|None,none|Strength,strength|Agility,agility,Alertness,alertness|Endurance,endurance|Mind,mind|Charisma,charisma'); // get the dice value to use const abilityDice = attributes[abilityName] || '0'; const skillDice = attributes[skill] || '0'; // decide whether to append the cs/cf customization to it const modifiedAbilityDice = abilityDice === '0' || /d20/.test(abilityDice) ? abilityDice : `${abilityDice}cs<0cf<0`; const modifiedSkillDice = skillDice === '0' || /d20/.test(skillDice) ? skillDice : `${skillDice}cs<0cf<0`; // create the text that is going to be sent to chat const rollText = `@{whispertoggle}&{template:default}{{title=@{character_name}}} {{Roll=[[d0 + ${modifiedSkillDice} + ${modifiedAbilityDice}]] | [[d0 + ${modifiedSkillDice} + ${modifiedAbilityDice}]]}} {{desc2=**${textName} check**}} {{status= }} @{blind} @{burning} @{charmed} @{chilled} @{deafened} @{distracted} @{drained} @{frightened} @{frozen} @{incapacitated} @{paralyzed} @{poisoned} @{prone} @{restrained} @{stunned} @{unconscious}`; const roll = await startRoll(rollText); finishRoll(roll.rollId); }) }) }); // function to allow us to query the user for information const extractQueryResult = async function(query){ const rollObj = { query:`[[0[response=?{${query}}]]]` }; const msg = `!{{query=[[0[response=?{${query}}]]]}}`; const roll = await startRoll(msg); finishRoll(roll.rollId); return roll.results.query.expression.replace(/^.+?response=|\]$/g,''); }; // capitalize function pulled from the K-scaffold const capitalize = function(string){ return string.replace(/(?:^|\s+|\/)[a-z]/ig,(letter)=>letter.toUpperCase()); }; I should also note, that this does assume that the button names and the attribute names for the skills are the same. As for the repeating section fieldsets, this current version of the code doesn't work with them, but it can be adapted to do so.
1747017611

Edited 1747018214
Super excited that I got it to roll into the chat but now it isn't using the Rolltemplate that I styled lol Do I have to use something different in the CSS? Also the roll is only rolling d0 + 0 + 0 and not putting in the die values. (the d0 is so that I can have "negative" die values)
// get the dice value to use const abilityDice = attributes[abilityName] || '0'; const skillDice = attributes[skill] || '0'; I'm not sure what I'm supposed to write in these spots^ for the dice values when they can be different depending on how many points someone spends for the skill. // Script work for skills (uses the word skill) const skill = ["genathletics", "gencovert", "gendomestic", "geninfluence", "genperception", "genpilot", "genscience", "genmelee", "genheavy", "genknow", "genmedical", "genmilitary", "genstarships", "gentech", "nature", "genranged", "climbing", "gym", "spo", "swim", "weight", "xspo", "conceal", "disguise", "escape", "forgery", "picklock", "pickpocket", "soh", "sneak", "streetsmart", "acting", "art", "cook", "dance", "dac", "mixology", "plantcare", "pmi", "sewing", "sing", "barter", "deception", "diplomacy", "empathy", "interrogation", "intimidation", "persuasion", "seduction", "detecttraps", "gambling", "gaming", "investigation", "sense", "surveillance", "aerovehicles", "aircraft", "watervehicles", "landvehicles", "detectambush", "detectconcealment", "dpa", "biology", "chemistry", "physics", "xenobio", "axes", "knives", "maces", "spears", "special", "staffs", "swords", "unarmed", "grenades", "heavyauto", "heavyrifle", "rocketlauncher", "throwerweapons", "bandits", "business", "cularbor", "culcouncil", "culouter", "culsector", "hisarbor", "hiscouncil", "hisouter", "hissector", "lawarbor", "lawcouncil", "lawouter", "lawsector", "relarbor", "relcouncil", "relouter", "relsector", "rememberinfo", "firstaid", "forensics", "pathology", "psychiatry", "surgery", "toxicology", "xenopathology", "armourer", "blacksmith", "demolitions", "findcont", "gunsmith", "militaryknowledge", "womd", "xenoweapons", "starshipeng", "starshipgen", "starshipnav", "spl", "sps", "starshiprepair", "sss", "starshipweapons", "advancedfirearms", "bbss", "comsystems", "computers", "electronics", "mechanics", "robotics", "fishing", "fauna", "flora", "landnav", "trackanimals", "trapping", "wac", "wildernesssurvival", "xenoanimalcare", "automaticrifles", "bows", "longrangerifles", "pistols", "shotguns", "submachineguns", "thrownweapons"]; skill.forEach(skill => { on(`change:${skill}`, () => { getAttrs([skill], values => { const skill_points = int(values[skill]); console.log(skill_points); let skill_dice = "0"; if (skill_points >= 10) skill_dice = "d12+d8"; else if (skill_points >= 9) skill_dice = "d12+d6"; else if (skill_points >= 8) skill_dice = "d12+d4"; else if (skill_points >= 7) skill_dice = "d12+d2"; else if (skill_points >= 6) skill_dice = "d12"; else if (skill_points >= 5) skill_dice = "d10"; else if (skill_points >= 4) skill_dice = "d8"; else if (skill_points >= 3) skill_dice = "d6"; else if (skill_points >= 2) skill_dice = "d4"; else if (skill_points >= 1) skill_dice = "d2"; else if (skill_points >= 0) skill_dice = "0"; else if (skill_points >= -1) skill_dice = "-d2"; else if (skill_points >= -2) skill_dice = "-d4"; else if (skill_points >= -3) skill_dice = "-d6"; else if (skill_points >= -4) skill_dice = "-d8"; else if (skill_points >= -5) skill_dice = "-d10"; else if (skill_points >= -6) skill_dice = "-d12"; else if (skill_points >= -7) skill_dice = "-d12-d2"; else if (skill_points >= -8) skill_dice = "-d12-d4"; else if (skill_points >= -9) skill_dice = "-d12-d6"; else if (skill_points >= -10) skill_dice = "-d12-d8"; else skill_dice = "0"; setAttrs({ [`${skill}_dice`]: skill_dice }); }); }); });
1747021585

Edited 1747021723
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Sirius said: Super excited that I got it to roll into the chat but now it isn't using the Rolltemplate that I styled lol Do I have to use something different in the CSS? Also the roll is only rolling d0 + 0 + 0 and not putting in the die values. (the d0 is so that I can have "negative" die values) The template is because it is currently using the default template. See the const rollText = ... line. This is essentially your original macro text. I just used the default template so I could test it. Sirius said: // get the dice value to use const abilityDice = attributes[abilityName] || '0'; const skillDice = attributes[skill] || '0'; I'm not sure what I'm supposed to write in these spots^ for the dice values when they can be different depending on how many points someone spends for the skill. You shouldn't need to put anything here. It's going to pull from whatever the currently set dice for the ability/skill is, which I thought was already calculated on the sheet in an attribute. However, I apparently screwed up the reference to the dice values in the function. These two lines: const abilityDice = attributes[abilityName] || '0'; const skillDice = attributes[skill] || '0'; should be this: const abilityDice = attributes[`${abilityName}_dice`] || '0'; const skillDice = attributes[`${skill}_dice`] || '0'; at least assuming that the skill and ability score attribute names all follow the same pattern of attributeName_dice . Which, based on your last code snippet, it looks like they do.
1747081803

Edited 1747081872
Okay, I have everything working now but I have one last problem. The name="" part of the button is not the same as the actual name of the skill for many buttons. I abbreviated some due to their length and not knowing if having spaces would mess up the code. Example Code: <button class="roll-button skill-button" type="action" name="act_bandits">Bandits and Hostile Races</button> Example Roll to Chat: It should say Bandits and Hostile Races Check. Any ideas for this?
1747167294
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
There's a few potential methods, but the one I'd recommend is to create an object to look up the appropriate text name. The code would look like this: const skillNameLookup = { genathletics: 'General Athletics', gencovert: 'General Covert', // Add additional key/value pairs as needed. Key is the name of the skill attribute, value is the text that should be output. }; skills.forEach(skill => { // listen for the click on each button on(`clicked:${skill}`,() => { // create an array of attribute names to get with getAttrs // I'd just put all the attribute names that you might need for any skill roll in here. This demo just has the ones for your disguise roll const getArr = ['disguise_dice','agility_dice','strength_dice','alertness_dice','endurance_dice','mind_dice','charisma_dice']; // get the attribute values. Note the async tag on the callback function. This will allow us to use await with the startRoll sheetworker. getAttrs(getArr,async (attributes) => { // process the skill name into it's plain text name const textName = skillNameLookup[skill]; // ask the user what dice pool they want to use const abilityName = await extractQueryResult('Select Ability|None,none|Strength,strength|Agility,agility,Alertness,alertness|Endurance,endurance|Mind,mind|Charisma,charisma'); // get the dice value to use const abilityDice = attributes[abilityName] || '0'; const skillDice = attributes[skill] || '0'; // decide whether to append the cs/cf customization to it const modifiedAbilityDice = abilityDice === '0' || /d20/.test(abilityDice) ? abilityDice : `${abilityDice}cs<0cf<0`; const modifiedSkillDice = skillDice === '0' || /d20/.test(skillDice) ? skillDice : `${skillDice}cs<0cf<0`; // create the text that is going to be sent to chat const rollText = `@{whispertoggle}&{template:default}{{title=@{character_name}}} {{Roll=[[d0 + ${modifiedSkillDice} + ${modifiedAbilityDice}]] | [[d0 + ${modifiedSkillDice} + ${modifiedAbilityDice}]]}} {{desc2=**${textName} check**}} {{status= }} @{blind} @{burning} @{charmed} @{chilled} @{deafened} @{distracted} @{drained} @{frightened} @{frozen} @{incapacitated} @{paralyzed} @{poisoned} @{prone} @{restrained} @{stunned} @{unconscious}`; const roll = await startRoll(rollText); finishRoll(roll.rollId); }) }) });