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

Deathwatch Psy-Focus roller

okay so I have the macro for this built, looking for an easy clean way to highlight three things; 1) doubles, as that triggers Warp shenanigans, 2) crit fail on a 91 or higher 3) crit success on 1's, and I'd like each to have a unique colour I know I can write this into the macro but cs1cf>91cf11cf22cf33cf44cf55cf66cf77cf88 looks ugly as shit when your trying to quickly read the roll
1649228353

Edited 1649228404
Oosh
Sheet Author
API Scripter
Just to clear a few things up, since there's a few ways to nail this particular cat to the wall: - is this the Warhammer Deathwatch community sheet on Roll20? I can only see one. - is it a core rule? This could be implemented using Custom Roll Parsing, which is embedded in the sheet and can be used by everyone. This wouldn't be appropriate if it's not part of the core rules. - is this roll coming from a button on the sheet, or are you building your own macro? Is it in a roll template already, or just a raw roll?
Oosh said: Just to clear a few things up, since there's a few ways to nail this particular cat to the wall: - is this the Warhammer Deathwatch community sheet on Roll20? I can only see one. - is it a core rule? This could be implemented using Custom Roll Parsing, which is embedded in the sheet and can be used by everyone. This wouldn't be appropriate if it's not part of the core rules. - is this roll coming from a button on the sheet, or are you building your own macro? Is it in a roll template already, or just a raw roll? yes I believe so Psy-Focus is a Librarian only roll and from what I can see doesn't exists as a in built roll on the sheet I built the macro from scratch using the default template, and no API's
1649235784
Oosh
Sheet Author
API Scripter
Ah ok, API is probably the way to go then. This might be something ScriptCards can handle for you, but I've never really poked around in it so not 100% sure. If you can post the macro, it shouldn't be too hard to write a small custom script (unless someone chimes in with a ScriptCards solution). Are you happy with the default template, or would you prefer to use one of the Deathwatch ones so it matches the other sheet rolls?
1649239571

Edited 1649240561
Oosh said: Ah ok, API is probably the way to go then. This might be something ScriptCards can handle for you, but I've never really poked around in it so not 100% sure. If you can post the macro, it shouldn't be too hard to write a small custom script (unless someone chimes in with a ScriptCards solution). Are you happy with the default template, or would you prefer to use one of the Deathwatch ones so it matches the other sheet rolls? this is the macro I have currently: &{template:default}{{name=Psy-Focus}}{{?{Power Level| Fettered,Degree(s) of success = [[(@{Daemart Stormhair|Willpower} + ?{Sit Mod | 0} + (ceil(@{Daemart Stormhair|PsyRating}/2)*5) + @{Daemart Stormhair|advanceWP} - 1d100) / 10]] | Unfettered, Degree(s) of success = [[(@{Daemart Stormhair|Willpower} + ?{Sit Mod | 0} + (@{Daemart Stormhair|PsyRating}*5) + @{Daemart Stormhair|advanceWP} - 1d100) / 10]] | Pushing,Degree(s) of success=[[(@{Daemart Stormhair|Willpower} + ?{Sit Mod | 0} + ((@{Daemart Stormhair|PsyRating} + ?{Extra Strength | 0})*5) + @{Daemart Stormhair|advanceWP} - 1d100) / 10]]}} only the middle one needs the double indication just FYI if you could make it look like this that's be most epic, including the little subtitle for the power level please
1649240202
Oosh
Sheet Author
API Scripter
Just to clarify a few things:     - Sit Mod is always input with a Query, and can't be read from the sheet?     - Same with Power level - you always want the Query popping up for that?     - when you say "including the little subtitle for the power level please", do you mean where it says "Smite" in the screenshot, you'd want it to say e.g. "Fettered"     - only "Unfettered" power level needs to check for doubles. Is the  d100 roll in this 00-99, or 01 - 100? As in, does "00" count as a double?     - crit on a 1 - that's just the exact value "01", as in a 1% chance? Not anything else ending in 1 or involving a 1?     - is it "psi" or "psy"? :)
Oosh said: Just to clarify a few things:     - Sit Mod is always input with a Query, and can't be read from the sheet?     - Same with Power level - you always want the Query popping up for that?     - when you say "including the little subtitle for the power level please", do you mean where it says "Smite" in the screenshot, you'd want it to say e.g. "Fettered"     - only "Unfettered" power level needs to check for doubles. Is the  d100 roll in this 00-99, or 01 - 100? As in, does "00" count as a double?     - crit on a 1 - that's just the exact value "01", as in a 1% chance? Not anything else ending in 1 or involving a 1?     - is it "psi" or "psy"? :) - power level determins how the characteristics interact with the roll, and sit mod is always a option for the GM the throw at us - yes, or what ever power level I choose for the roll, if that's a pain in the ass to code it's optional - yes 00 counts as a double and is the highest and worse roll you can get - crits are only 01 - Psy, I confuse myself playing both 40k and starcraft
1649248250

Edited 1649248280
Oosh
Sheet Author
API Scripter
Ok, one last question - do you (or the GM or other players) use 3d dice?
Oosh said: Ok, one last question - do you (or the GM or other players) use 3d dice? no, just the results in chat
1649248558
Oosh
Sheet Author
API Scripter
Oh.... well I wasted my time finding a hack to get 3d dice working via the API then :)
1649329480

Edited 1649329734
Oosh
Sheet Author
API Scripter
See if this does what you want it to. It's possible I got something wrong, math-wise. The command to trigger the script is: !psyroll {{name=Psy-Focus}} {{power=?{Power Level?|Fettered|Unfettered|Pushing,Pushing}} {{strength=?{Extra Strength|0}}}} {{mods=Advance WP|8,Willpower|11,Situation|?{Sit Mod?|0}}} {{rating=4}} {{name}} can be changed to whatever you want the name of the roll to be. {{power}} recreates the Query you had in your macro (with nested Query for Pushing). {{mods}} contains whatever else you want to add on to the roll, mods separated by comma and label|value separated by pipe. The example above is hard-coded numbers, you'd want to change those back to your @{charname|willpower} references. {{rating}} is just for the {psyrating} attribute, separate to mods since it has different math applied ot it. {{roll}} isn't in the example, but you can change the core roll if you want, {{roll=1d4}}. Doubt you'll need this. You can also supply {{test=1}} (or any characters after the '=') to enable test mode - this will trigger loads of crits, fails and warps for testing. I have no idea what you want the Warp effect to look like (I just chucked some blue stuff at it), happy to change it to whatever you want. It looks a bit erm.... placeholder-y: :) And the script. /* globals on, sendChat, log */ const psyRoll = (() => { //eslint-disable-line no-unused-vars const scriptName = 'psyRoll'; // !psyroll {{name=Psy-Focus}} {{power=?{Power Level?|Fettered|Unfettered|Pushing,Pushing}} {{strength=?{Extra Strength|0}}}} {{mods=Advance WP|8,Willpower|11,Situation|?{Sit Mod?|0}}} {{rating=4}} const styles = { outer: `margin-left: -7px; border: 1px solid #000099;`, header: `background-color: #000099; color: white; text-align: left; padding: 5px;`, title: `font-size: 1.1em;`, subtitle: `font-size: 0.9em;`, body: `line-height: 1.4em; padding: 5px; background-color: white;`, row: `text-align: center;`, rowKey: `font-weight: bold; padding-right: 10px; text-align: right; padding: 5px;`, rowValue: `font-weight: bold;`, footer: `display: none; padding: 5px;`, roll: `background-color: #fef68e;border: 2px solid #fef68e;padding: 0px 3px 0px 3px;font-weight: bold;cursor: help;font-size: 1.1em;`, warp: `padding: 2px 6px 2px 6px; border: 1px #8f8ff9 solid; border-radius: 3px; background-color: #a4cddb;`, warpText: `color: #006c8b; margin: 0px 0px 0px 5px; text-align: center;`, crit: `border: 2px solid #3fb315;`, fail: `border: 2px solid #b31515;`, critAndFail: `border: 2px solid darkblue;` } const outputTemplate = ` <div class="psyroll" style="${styles.outer}"> <div class="psyroll-header" style="${styles.header}"> <div class="psyroll-title" style="${styles.title}">%title%</div> <div class="psyroll-subtitle" style="${styles.subtitle}">%subtitle%</div> </div> <div class="psyroll-body" style="${styles.body}"> %body% </div> <div class="psyroll-footer" style="${styles.footer}">adsf</div> </div> `; const rowTemplate = `<div class="psyroll-row" style="${styles.row}"> <span class="row-key" style="${styles.rowKey}">%key%</span> <span class="row-value" style="${styles.rowValue}">%value%</span> </div>` const splitMacroData = async (msgContent) => { let output = { name: 'Psy Roll', mods: [], rating: 1, roll: '[[1d100]]', strength: 0 }; const dataArray = msgContent.split(/\s*\{\{\s*/g); dataArray.shift(); dataArray.forEach(v => { const parts = v.replace(/\s*\}\}\s*$/, '').split(/=/); if (parts && parts.length > 1) { if (/mods/i.test(parts[0])) { const rollMods = parts[1].split(/\s*,\s*/g); rollMods.forEach(rm => { const parts = rm.split(/\s*\|\s*/); if (parts && parts.length > 1) output.mods.push({ label: parts[0], value: parseInt(parts[1]) }); }); } else { output[parts[0]] = /(strength|rating)/i.test(parts[0]) ? parseInt(parts[1]) : parts[1]; } } }); output.roll = /\s*\[\[/.test(output.roll) && /\]\]\s*$/.test(output.roll) ? output.roll : `[[${output.roll}]]`; return output; } const buildTemplate = async (rollData) => { const critStyle = (rollData.crit && rollData.fail) ? styles.critAndFail : rollData.crit ? styles.crit : rollData.fail ? styles.fail : ``, warpStyle = rollData.warpRoll ? styles.warp : ``, warpText = rollData.warpRoll ? `<span class="warptext" style="${styles.warpText}">W</span>` : ``; const data = { title: rollData.name, subtitle: rollData.power, rows: [ { label: `Degree(s) of success`, value: `<div class="warp" style="display:inline-block;${warpStyle}"><span class="showtip" style="${styles.roll}${critStyle}" title="${rollData.tooltip}">${rollData.finalResult}</span>${warpText}</div>` } ] }; data.body = data.rows.map(r => rowTemplate.replace(/%key%/, r.label||'').replace(/%value%/, r.value||'')).join('<br>'); let chatTemplate = outputTemplate; for (let prop in data) { chatTemplate = chatTemplate.replace(`%${prop}%`, data[prop]) } return chatTemplate.replace(/%\w+%/g, ''); // remove any leftover placeholders } const buildPsyRoll = async (rollData) => { const ratingCalc = /push/i.test(rollData.power) ? (rollData.rating + rollData.strength)*5 : /unfett/i.test(rollData.power) ? (Math.ceil(rollData.rating/2))*5 : rollData.rating*5; let ratingCalcString = /push/i.test(rollData.power) ? `((${rollData.rating} + ${rollData.strength})` : /unfett/i.test(rollData.power) ? `(ceil(${rollData.rating}/2)` : `(${rollData.rating}`; ratingCalcString += `*5)[Psy Rating Calc]`; const modsString = rollData.mods.map(v => `${v.value}[${v.label}]`), rollString = `${modsString.join(' + ')} + ${ratingCalcString} - ${rollData.result}[${rollData.roll.replace(/[[\]]/g, '')}])/10`; const modSubtotal = rollData.mods.reduce((m,v) => m + v.value||0, 0), rollResult = (modSubtotal + ratingCalc - rollData.result)/10; // log(modSubtotal, rollResult); Object.assign(rollData, { finalResult: rollResult, tooltip: rollString, warpRoll: (rollData.result % 11 === 0 || rollData.result === 100) ? true : false, crit: rollData.result === 1 ? true : false, fail: rollData.result > 90 ? true : false, }); if (rollData.test) { Object.assign(rollData, { warpRoll: rollData.result % 2 === 0 ? true : false, crit: rollData.result > 65 ? true : false, fail: rollData.result < 34 ? true : false, }); } // log(rollData); return rollData; } const handleInput = async (msg) => { if (/^!psyroll\s/i.test(msg.content)) { const rollInput = await splitMacroData(msg.content); if (!rollInput || !rollInput.power) return helpers.toChat(`${scriptName} error: required input {{power}} not found.`, msg.who); const rollResult = await helpers.sendRoll(rollInput.roll); if (!rollResult || isNaN(rollResult)) return helpers.toChat(`${scriptName} error: bad roll result: ${rollResult}`, msg.who); rollInput.result = rollResult; rollInput.fullRoll = await buildPsyRoll(rollInput); let chatMsg = await buildTemplate(rollInput); chatMsg = chatMsg.replace(/\n/g, ''); helpers.toChat(chatMsg, null, msg.who); } } const helpers = (() => { const sendRoll = async (roll) => new Promise(res => { sendChat('', `${roll}`, v => res(v[0].inlinerolls[0].results.total)) }); const toChat = (content, target, who) => { const prefix = target ? `/w "${target}" ` : ``; sendChat((who || who === '') ? who : scriptName, `${prefix}${content}`); } return { sendRoll, toChat } })(); on('ready', () => on('chat:message', handleInput)); })();
see wall of text, bugger me