The following code is untested. What should happen: when a player uses the command "!hero", the system will spend a hero point on their character and perform a re-roll of the last roll made by that character, whether it was with the /roll command or with an [[inline]] roll. If the character used multiple inline rolls in a single chat message, the last one in the message will be used. The portion of the script recording the rolls doesn't explicitly make the assumption that the roll is in the form 1d20+mod; rather, it takes the total of the roll and subtracts the value of all dice involved in the roll. So, if the player did 1d8+1d6+1d4+10+1, the script would subtract the results of the d8/d6/d4 and end up with 11. This part is the most likely place where an error will occur, particularly if a player gets into some of the stranger dice roll syntax; the dice engine is complicated. (Again, untested code.) This script assumes hero points are stored in an attribute named "Hero Points". Change the heroPoints = findObjs(...) call if that's not the case. on('chat:message', function(msg) { if(!(msg.type == 'api' && msg.content.toLowerCase() == '!hero')) return; var character = findObjs({ type: 'character', name: msg.who })[0]; var player = findObjs({ type: 'player', displayname: msg.who.indexOf(' (GM)') > -1 ? msg.who.substring(0, msg.who.indexOf(' (GM)')) : msg.who })[0]; if (!character) { // If the user didn't post the message speaking as a character, pick the first character they control character = findObjs({ type: 'character', controlledby: player.id })[0]; if (!character) { sendChat('SYSTEM', 'Could not find character for ' + msg.who); return; } } var heroPoints = findObjs({ type: 'attribute', characterid: character.id, name: 'Hero Points' })[0]; if (!heroPoints) { // We must have the hero points attribute to function sendChat('SYSTEM', 'Could not find hero points for ' + character.get('name')); return; } if (parseInt(heroPoints.get('current')) < 1) { // Character must have a hero point to spend sendChat('SYSTEM', character.get('name') + ' does not have any hero points to spend'); return; } if (!state.henry_w) { // We need a previous roll to compare to state.henry_w = { Mods: {} }; sendChat('SYSTEM', character.get('name') + ' has not made any recorded rolls'); return; } if (!state.henry_w.Mods[character.id]) { // We need *this character* to have a previous roll sendChat('SYSTEM', character.get('name') + ' has not made any recorded rolls'); return; } // Now we're ready to spend that hero point! sendChat('character|'+character.id, '/me uses a hero point to re-roll.'); var lastRollMod = state.henry_w.Mods[character.id]; var dieTotal = randomInteger(20); dieTotal = dieTotal <= 10 ? dieTotal + 10 : dieTotal; heroPoints.set('current', parseInt(heroPoints.get('current')) - 1); sendChat('SYSTEM', character.get('name') + '\'s hero point re-roll was ' + (dieTotal + lastRollMod) + '!'); }); on('chat:message', function(msg) { if (!(msg.type == 'rollresult' || msg.inlinerolls)) return; var character = findObjs({
type: 'character',
name: msg.who
})[0];
var player = findObjs({
type: 'player',
displayname: msg.who.indexOf(' (GM)') > -1 ? msg.who.substring(0, msg.who.indexOf(' (GM)')) : msg.who
})[0];
if (!character) {
// If the user didn't post the message speaking as a character, pick the first character they control
character = findObjs({
type: 'character',
controlledby: player.id
})[0];
if (!character) {
// Fail silently
return;
}
} if (!state.henry_w) { state.henry_w = { Mods: {} }; } var mod, roll; if (msg.type == 'rollresult') { roll = JSON.parse(msg.content); mod = roll.total; _.each(roll.rolls, function(r) { _.each(r.results, function(d) { mod -= d.v; }); }); } else { roll = msg.inlinerolls[msg.inlinerolls.length - 1]; mod = roll.results.total; _.each(roll.results.rolls, function(r) { _.each(r.results, function(d) { mod -= d.v }); }); }
state.henry_w.Mods[character.id] = mod; });