Last active
September 18, 2021 05:45
-
-
Save birdbrainiac/4e92f46651b3d8cf8db917e4f7b09a7c to your computer and use it in GitHub Desktop.
[Roll20] Pacesetter Dice Rolls Script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Structure | |
Input is in the form !pace --name: --type: --score: --defense: --defensecolumn: | |
name: is optyional, and can be anything, of any length, just avoid including -- characetrs, | |
Can use attributes, like @{selected|token_name} | |
type: armed, unarmed or fear; can also use a or u or f. | |
score: a number, the score you are rolling against; can be an attribute, like @{selected|blaster} | |
use only one of the following: | |
defense/defence: the score you are rolling against - will be converted into a column | |
defensecolumn/defencecolumn: the column you are rolling against. Can supply this instead of a score. | |
Create a macro: | |
!pace --type:?{Attack type?|Armed|Unarmed|Fear} --score:?{Attack Value?|0} --defense:?{Defense Value?|0} | |
*/ | |
const pacesetter = (() => { | |
'use strict'; | |
/* CONFIG SETTINGs */ | |
const defence = 'defense'; // use defence or defense - the spelling used for defense/defense, and defencecolumn/defensecolumn in the abobe | |
const hidedefence = 0; // 0 shows defence row, 1 hides it | |
const terseKB = 1; // 0 shows full knockdown text, 1 shows a terser version. | |
const failtext = 'Fail'; // enter the text you want to show on a failed or missed roll. | |
/* dont change anything below this line */ | |
const effectTable = [ | |
['LK', 'L', 'L', 'L', 'L', 'S', 'S', 'S', 'S', 'S'], | |
['M', 'M', 'L', 'LK', 'L', 'LK', 'L', 'S', 'S', 'S'], | |
['H', 'MK', 'M', 'L', 'L', 'L', 'L', 'L', 'LK', 'S'], | |
['C', 'H', 'MK', 'M', 'M', 'M', 'LK', 'L', 'L', 'L'], | |
['C', 'C', 'H', 'M', 'MK', 'M', 'M', 'MK', 'L', 'LK'], | |
['CK', 'CK', 'C', 'H', 'H', 'H', 'M', 'M', 'M', 'M', ], | |
['CK', 'CK', 'CK', 'CK', 'H', 'H', 'H', 'H', 'M', 'M', ], | |
['CK', 'CK', 'CK', 'CK', 'CK', 'HK', 'H', 'H', 'H', 'H', ], | |
['CK', 'CK', 'CK', 'CK', 'CK', 'CK', 'HK', 'HK', 'H', 'H', ], | |
['CK', 'CK', 'CK', 'CK', 'CK', 'CK', 'CK', 'HK', 'HK', 'H'] | |
]; | |
const tableResults = { | |
a: { | |
S: {type: 'Scratch', stun: '1d2', wounds: 1}, | |
L: {type: 'Light Wound', stun: '1d10*2', wound: 2}, | |
M: {type: 'Medium Wound', stun: '2d10*2', wound: 3}, | |
H: {type: 'Heavy Wound', stun: '3d10*2', wound: 4}, | |
C: {type: 'Crippling Wound', stun: '3d10*2', wound: 5}, | |
K: ['{{Knockdown=Defender knocked down and back}}', | |
'{{Knockdown=Down and back.}}',] | |
}, | |
u: { | |
S: {type: 'Scant Damage', stun: '1d2', wound: 0}, | |
L: {type: 'Light Damage', stun: '1d10*2', wound: 0}, | |
M: {type: 'Medium Damage', stun: '2d10*2', wound: 0}, | |
H: {type: 'Harsh Damage', stun: '3d10*2', wound: 1}, | |
C: {type: 'Crushing Damage', stun: '3d10*2', wound: 2}, // or called shot | |
K: ['{{Knockdown=Defender knocked down and back 5 feet; all uncompleted actions this round are cancelled.}}', | |
'{{Knockdown=Down and back 5 feet, lose action.}}'] | |
}, | |
f: { | |
S: {type: 'Scared', stun: '1d10', wound: 0, text: 'Runs away as fast as possible or hides for 1 round'}, | |
L: {type: 'Loathing', stun: '1d10/2', wound: 0, text: 'Runs away as fast as possible or hides for 1 round'}, | |
M: {type: 'Misgivings', stun: '1d10/2', wound: 0, text: 'Does not run away'}, | |
H: {type: 'Heroism', stun: '0', wound: 0, text: 'All fear is overcome'}, | |
C: {type: 'Heroism', stun: '0', wound: 0, text: 'All fear is overcome'} | |
} | |
}; | |
const attackIndex = margin => margin >= 100 ? 9 : [0, 4, 9, 29, 49, 69, 89, 94, 99].findIndex(element => margin <= element); | |
const getSender = msg => { | |
const character = findObjs({ type: 'character', name: msg.who })[0]; | |
const player = getObj('player', msg.playerid); | |
if (character) return 'character|'+character.id; | |
else return 'player|'+player.id; | |
}; | |
const getPlayer = msg => getObj('player', msg.playerid).get('displayname'); | |
const getFromArgs = (args, property, notfound = 0) => { | |
const found = args.find(element => element.includes(`${property}:`)); | |
const result = (found && found.includes(':')) ? found.split(':')[1] : notfound; | |
return result; | |
}; | |
const defaultName = type => type === 'u' ? 'Unarmed Attack' : type === 'a' ? 'Armed Attack' : 'Fear Check'; | |
const handleInput = (msg) => { | |
if (msg.type === 'api' && msg.content.startsWith('!pace') ) | |
{ | |
const sender = getSender(msg); | |
const args = msg.content.split(/\s+--/); | |
const props = {}; | |
const validTypes = ['a', 'u', 'f']; | |
props.attacktype = validTypes.includes(getFromArgs(args,'type')[0].toLowerCase()) ? getFromArgs(args,'type')[0].toLowerCase() : 'u'; | |
props.attackvalue = +getFromArgs(args,'score', 0); | |
props.defence = +getFromArgs(args,defence, 0); | |
props.defencecolumn = props.defence > 0 ? Math.ceil(props.defence /15) : +getFromArgs(args,'column', 0); | |
props.name = getFromArgs(args,'name', defaultName(props.attacktype)); | |
if (!props.hasOwnProperty('defencecolumn') || !props.hasOwnProperty('attackvalue') || !validTypes.includes(props.attacktype)) { | |
sendChat(`Pacesetter`, `/w "${getPlayer(msg)}" Error in Script Values`); // | |
return; | |
} | |
sendChat("PaceRoll", "[[1d100]]", function(ops) { | |
const output = {}; | |
output.roll = ops[0].inlinerolls[0].results.rolls[0].results[0].v; | |
output.attackvalue = props.attackvalue - output.roll; | |
output.damageresult = output.attackvalue >= 0 ? effectTable[attackIndex(output.attackvalue)][props.defencecolumn -1] : failtext; | |
const table = tableResults[props.attacktype][output.damageresult[0]]; | |
output.template = `&{template:default} {{name=${props.name} (${props.name !== defaultName(props.attacktype) ? `${defaultName(props.attacktype).split(' ')[0]}:` : ''} ${props.attackvalue})}} {{Roll=**${output.roll}**}}` | |
if(output.damageresult === failtext) { | |
if(props.attacktype === 'f') { | |
output.template += `{{Result=${failtext}}} `; | |
output.template += `{{action=Runs away as fast as possible or hides for 1 round}} `; | |
output.template += `{{Willpower=[[2d10]]}}`; | |
} else { | |
output.template += `{{Result=${failtext} }}`; | |
} | |
// neeed to put willpower roll here if type = f | |
} else { | |
if(props.attacktype === 'f') { | |
output.template += hidedefence ? '' : `{{${defence}=${props.defence > 0 ? `${props.defence} [` : ''} ${props.defencecolumn}${props.defence > 0 ? ` ]` : ''} }}` ; | |
output.template += `{{Result=${table.type} (${output.damageresult})}} {{action=${table.text}}} {{Willpower=[[ceil(${table.stun})]]}}`; | |
} else { | |
output.template += hidedefence ? '' : `{{${defence}=${props.defence > 0 ? `${props.defence} [` : ''} ${props.defencecolumn}${props.defence > 0 ? ` ]` : ''} }}` ; | |
output.template += `{{Result=${table.type} (${output.damageresult}) }} {{STA=[[${table.stun}]]}}`; | |
output.template += table.wound > 0 ? `{{Wounds=**${table.wound}** }}` : ''; | |
output.template += output.damageresult.includes('K') ? tableResults[props.attacktype].K[terseKB] : ''; | |
} | |
} | |
sendChat(sender,output.template); | |
}); | |
} | |
}; | |
const registerEventHandlers = () => { | |
on('chat:message', handleInput); | |
}; | |
on('ready', () => { | |
'use strict'; | |
registerEventHandlers(); | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment