/** * Roll Dice. * @param {int} dice_amount * @param {string} attribute_name * @param {string} position * @param {string} effect */ export async function bladesRoll(dice_amount, attribute_name = "", position = "risky", effect = "standard") { // ChatMessage.getSpeaker(controlledToken) let zeromode = false; if ( dice_amount < 0 ) { dice_amount = 0; } if ( dice_amount == 0 ) { zeromode = true; dice_amount = 2; } let r = new Roll( `${dice_amount}d6`, {} ); // show 3d Dice so Nice if enabled r.roll(); showChatRollMessage(r, zeromode, attribute_name, position, effect); } /** * Shows Chat message. * * @param {Roll} r * @param {Boolean} zeromode * @param {String} attribute_name * @param {string} position * @param {string} effect */ async function showChatRollMessage(r, zeromode, attribute_name = "", position = "", effect = "") { let speaker = ChatMessage.getSpeaker(); let isBelow070 = isNewerVersion('0.7.0', game.data.version); let rolls = []; let attribute_label = BladesHelpers.getAttributeLabel(attribute_name); // Backward Compat for rolls. if (isBelow070) { rolls = (r.parts)[0].rolls; } else { rolls = (r.terms)[0].results; } // Retrieve Roll status. let roll_status = getBladesRollStatus(rolls, zeromode); let result; if (BladesHelpers.isAttributeAction(attribute_name)) { let position_localize = ''; switch (position) { case 'controlled': position_localize = 'BITD.PositionControlled' break; case 'desperate': position_localize = 'BITD.PositionDesperate' break; case 'risky': default: position_localize = 'BITD.PositionRisky' } let effect_localize = ''; switch (effect) { case 'limited': effect_localize = 'BITD.EffectLimited' break; case 'great': effect_localize = 'BITD.EffectGreat' break; case 'standard': default: effect_localize = 'BITD.EffectStandard' } result = await renderTemplate("systems/blades-in-the-dark/templates/chat/action-roll.html", {rolls: rolls, roll_status: roll_status, attribute_label: attribute_label, position: position, position_localize: position_localize, effect: effect, effect_localize: effect_localize}); } else { let stress = getBladesRollStress(rolls, zeromode); result = await renderTemplate("systems/blades-in-the-dark/templates/chat/resistance-roll.html", {rolls: rolls, roll_status: roll_status, attribute_label: attribute_label, stress: stress}); } let messageData = { speaker: speaker, content: result, type: CONST.CHAT_MESSAGE_TYPES.ROLL, roll: r } CONFIG.ChatMessage.entityClass.create(messageData, {}) } /** * Get status of the Roll. * - failure * - partial-success * - success * - critical-success * @param {Array} rolls * @param {Boolean} zeromode */ export function getBladesRollStatus(rolls, zeromode = false) { // Dice API has changed in 0.7.0 so need to keep that in mind. let isBelow070 = isNewerVersion('0.7.0', game.data.version); let sorted_rolls = []; // Sort roll values from lowest to highest. if (isBelow070) { sorted_rolls = rolls.map(i => i.roll).sort(); } else { sorted_rolls = rolls.map(i => i.result).sort(); } let roll_status = "failure" if (sorted_rolls[0] === 6 && zeromode) { roll_status = "critical-success"; } else { let use_die; let prev_use_die = false; if (zeromode) { use_die = sorted_rolls[0]; } else { use_die = sorted_rolls[sorted_rolls.length - 1]; if (sorted_rolls.length - 2 >= 0) { prev_use_die = sorted_rolls[sorted_rolls.length - 2] } } // 1,2,3 = failure if (use_die <= 3) { roll_status = "failure"; } // if 6 - check the prev highest one. else if (use_die === 6) { // 6,6 - critical success if (prev_use_die && prev_use_die === 6) { roll_status = "critical-success"; } // 6 - success else { roll_status = "success"; } } // else (4,5) = partial success else { roll_status = "partial-success"; } } return roll_status; } /** * Get stress of the Roll. * @param {Array} rolls * @param {Boolean} zeromode */ export function getBladesRollStress(rolls, zeromode = false) { var stress = 6; // Dice API has changed in 0.7.0 so need to keep that in mind. let isBelow070 = isNewerVersion('0.7.0', game.data.version); let sorted_rolls = []; // Sort roll values from lowest to highest. if (isBelow070) { sorted_rolls = rolls.map(i => i.roll).sort(); } else { sorted_rolls = rolls.map(i => i.result).sort(); } let roll_status = "failure" if (sorted_rolls[0] === 6 && zeromode) { stress = -1; } else { let use_die; let prev_use_die = false; if (zeromode) { use_die = sorted_rolls[0]; } else { use_die = sorted_rolls[sorted_rolls.length - 1]; if (sorted_rolls.length - 2 >= 0) { prev_use_die = sorted_rolls[sorted_rolls.length - 2] } } if (use_die === 6 && prev_use_die && prev_use_die === 6) { stress = -1; } else { stress = 6 - use_die; } } return stress; } /** * Call a Roll popup. */ export async function simpleRollPopup() { new Dialog({ title: `Simple Roll`, content: `

${game.i18n.localize("BITD.RollSomeDice")}

${game.i18n.localize("BITD.RollTokenDescription")}

`, buttons: { yes: { icon: "", label: `Roll`, callback: (html) => { let diceQty = html.find('[name="qty"]')[0].value; bladesRoll(diceQty); }, }, no: { icon: "", label: game.i18n.localize('Cancel'), }, }, default: "yes" }).render(true); }