diff --git a/module/actor/actor-sheet.js b/module/actor/actor-sheet.js index d8b3ed7..831930c 100644 --- a/module/actor/actor-sheet.js +++ b/module/actor/actor-sheet.js @@ -3,9 +3,7 @@ import { DGPercentileRoll, DGLethalityRoll, - sendDamageRollToChat, - showModifyDamageRollDialogue, - sendSanityDamageToChat + DGDamageRoll, } from "../roll/roll.js" /** * Extend the basic ActorSheet with some very simple modifications @@ -608,7 +606,7 @@ export class DeltaGreenActorSheet extends ActorSheet { const element = event.currentTarget; const { dataset } = element; - if(event && event.which === 2){ + if(event && event.which === 2) { // probably don't want rolls to trigger from a middle mouse click so just kill it here return; } @@ -635,13 +633,12 @@ export class DeltaGreenActorSheet extends ActorSheet { roll = new DGLethalityRoll("1D100", {}, rollOptions) break; case "damage": { - let diceFormula = dataset.roll; - let skillType = dataset.key; + let diceFormula = dataset.formula; + const skillType = dataset.skill; if(this.actor.type === 'agent' && (skillType === 'unarmed_combat' || skillType === 'melee_weapons')){ diceFormula += this.actor.system.statistics.str.meleeDamageBonusFormula; } - showModifyDamageRollDialogue(this.actor, diceFormula); - sendDamageRollToChat(this.actor, diceFormula, game.settings.get("core", "rollMode")); + roll = new DGDamageRoll(diceFormula, {}, rollOptions) break; } case "sanitydamage":{ diff --git a/module/deltagreen.js b/module/deltagreen.js index 455ae8f..a4918ac 100644 --- a/module/deltagreen.js +++ b/module/deltagreen.js @@ -5,7 +5,7 @@ import { DeltaGreenActor } from "./actor/actor.js"; import { DeltaGreenActorSheet } from "./actor/actor-sheet.js"; import { DeltaGreenItem } from "./item/item.js"; import { DeltaGreenItemSheet } from "./item/item-sheet.js"; -import { sendDamageRollToChat, DGPercentileRoll, DGLethalityRoll } from "./roll/roll.js"; +import * as DGRolls from "./roll/roll.js"; import { registerSystemSettings } from "./settings.js" import { preloadHandlebarsTemplates } from "./templates.js"; import { ParseDeltaGreenStatBlock } from "./other/stat-parser-macro.js"; @@ -32,7 +32,7 @@ Hooks.once('init', async function() { }; // Register custom dice rolls - CONFIG.Dice.rolls.push(DGPercentileRoll, DGLethalityRoll); + Object.values(DGRolls).forEach((cls) => CONFIG.Dice.rolls.push(cls)) // Register System Settings registerSystemSettings(); diff --git a/module/item/item.js b/module/item/item.js index c3541ba..5b35e7e 100644 --- a/module/item/item.js +++ b/module/item/item.js @@ -1,4 +1,3 @@ -import { sendDamageRollToChat } from "../roll/roll.js" /** * Extend the basic Item with some very simple modifications. diff --git a/module/roll/roll.js b/module/roll/roll.js index 90a6a9c..0eed152 100644 --- a/module/roll/roll.js +++ b/module/roll/roll.js @@ -2,9 +2,62 @@ import { localizeWithFallback } from "../other/utility-functions.js" -export class DGPercentileRoll extends Roll { +export class DGRoll extends Roll { /** - * In order for all of our custom data to persist, our constructor must use the same parameters as its parent class. + * NOTE: This class will rarely be called on its own. It should generally be extended. Look to DGPercentileRoll as an example. + * + * Customize our roll with some useful information, passed in the `options` Object. + * + * @param {string} formula Unused - The string formula to parse (from Foundry) + * @param {Object} data Unused - The data object against which to parse attributes within the formula + * @param {Object} [options] Additional data which is preserved in the database + * @param {Number} [options.rollType] The type of roll (stat, skill, sanity, damage, etc). + * @param {String} [options.key] The key of the skill, stat, etc. to use as a basis for this roll. + * @param {DeltaGreenActor} [options.actor] The actor that this roll originates from. + * @param {DeltaGreenItem} [options.item] Optional - The item from which the roll originates. + */ + constructor(formula, data = {}, options) { + super(formula, data, options); + const { rollType, key, actor, item } = options; + this.type = rollType; + this.key = key; + this.actor = actor; + this.item = item; + this.modifier = 0; + } + + /** + * Simple function that actually creates the message and sends it to chat. + * + * Broke this out so multiple functions can use it. + * + * @returns {Promise} - the created chat message. + */ + async createMessage(content, flavor, rollMode) { + let chatData = { + speaker: ChatMessage.getSpeaker({ actor: this.actor }), + content, + flavor, + type: 5, //CHAT_MESSAGE_TYPES.ROLL, + roll: this, + rollMode + }; + + // play the dice rolling sound, like a regular in-chat roll + AudioHelper.play({src: "sounds/dice.wav", volume: 0.8, autoplay: true, loop: false}, true); + return ChatMessage.create(chatData); + } +} + +export class DGPercentileRoll extends DGRoll { + /** + * Creates D100 rolls, the base die of the system. + * + * This constructor embeds the following info into the roll: + * 1. Target number that the roll needs to beat. + * 2. Localized name for the roll. + * + * Note: In order for all of our custom data to persist, our constructor must use the same parameters as its parent class. * So, even though percentile rolls will always have a formula of "1d100" and we don't use the `data` object, * we still have to keep them as parameters. * @@ -18,46 +71,40 @@ export class DGPercentileRoll extends Roll { */ constructor(formula = "1D100", data = {}, options) { super("1D100", {}, options); - const { rollType, key, actor, item } = options; - this.type = rollType; - this.key = key; - this.actor = actor; - this.item = item; - this.modifier = 0; - const skillKeys = Object.keys(actor.system.skills); - const typedSkillKeys = Object.keys(actor.system.typedSkills); - const statKeys = Object.keys(actor.system.statistics); + const skillKeys = Object.keys(this.actor.system.skills); + const typedSkillKeys = Object.keys(this.actor.system.typedSkills); + const statKeys = Object.keys(this.actor.system.statistics); - switch (rollType) { + switch (this.type) { case "stat": - this.target = actor.system.statistics[key].x5; - this.localizedKey = game.i18n.localize(`DG.Attributes.${key}`); + this.target = this.actor.system.statistics[this.key].x5; + this.localizedKey = game.i18n.localize(`DG.Attributes.${this.key}`); break; case "skill": - if (skillKeys.includes(key)) { - this.target = actor.system.skills[key].proficiency; - this.localizedKey = game.i18n.localize(`DG.Skills.${key}`); + if (skillKeys.includes(this.key)) { + this.target = this.actor.system.skills[this.key].proficiency; + this.localizedKey = game.i18n.localize(`DG.Skills.${this.key}`); } - if (typedSkillKeys.includes(key)) { - this.target = actor.system.typedSkills[key].proficiency; - this.localizedKey = game.i18n.localize(`DG.Skills.${key}`); + if (typedSkillKeys.includes(this.key)) { + this.target = this.actor.system.typedSkills[this.key].proficiency; + this.localizedKey = game.i18n.localize(`DG.Skills.${this.key}`); } break; case "sanity": - this.target = actor.system.sanity.value; + this.target = this.actor.system.sanity.value; this.localizedKey = game.i18n.localize("DG.Attributes.SAN"); break; case "weapon": - if (key === "custom") { - this.target = item.system.customSkillTarget; + if (this.key === "custom") { + this.target = this.item.system.customSkillTarget; this.localizedKey = game.i18n.localize("DG.ItemWindow.Custom"); - } else if (skillKeys.includes(key)) { - this.target = actor.system.skills[key].proficiency; - this.localizedKey = game.i18n.localize(`DG.Skills.${key}`); - } else if (statKeys.includes(key)) { - this.target = actor.system.statistics[key].x5; - this.localizedKey = game.i18n.localize(`DG.Attributes.${key}`); + } else if (skillKeys.includes(this.key)) { + this.target = this.actor.system.skills[this.key].proficiency; + this.localizedKey = game.i18n.localize(`DG.Skills.${this.key}`); + } else if (statKeys.includes(this.key)) { + this.target = this.actor.system.statistics[this.key].x5; + this.localizedKey = game.i18n.localize(`DG.Attributes.${this.key}`); } break; case "luck": @@ -129,7 +176,9 @@ export class DGPercentileRoll extends Roll { } /** - * Evaluates and sends a roll to chat. + * Prepares data for a chat message and then passes that data + * to a method that actually creates a ChatMessage. + * * Lays out and styles message based on outcome of the roll. * * @returns {Promise} - the created chat message. @@ -195,26 +244,6 @@ export class DGPercentileRoll extends Roll { return this.createMessage(html, label, rollMode); } - /** - * Broke this out so multiple functions can use it. - * - * @returns {Promise} - the created chat message. - */ - async createMessage(content, flavor, rollMode) { - let chatData = { - speaker: ChatMessage.getSpeaker({ actor: this.actor }), - content, - flavor, - type: 5, //CHAT_MESSAGE_TYPES.ROLL, - roll: this, - rollMode - }; - - // play the dice rolling sound, like a regular in-chat roll - AudioHelper.play({src: "sounds/dice.wav", volume: 0.8, autoplay: true, loop: false}, true); - return ChatMessage.create(chatData); - } - /** * "Inhuman" stat being rolled, logic is different per page 188 of the Handler's Guide. * Note - originally implemented by Uriele, but my attempt at merging conficts went poorly, so re-implementing. @@ -294,7 +323,9 @@ export class DGLethalityRoll extends DGPercentileRoll { } /** - * Evaluates and sends a roll to chat. + * Prepares data for a chat message and then passes that data + * to a method that actually creates a ChatMessage. + * * Lays out and styles message based on outcome of the roll. * * Overrides `DGPercentileRoll.toChat()` @@ -387,42 +418,28 @@ export class DGLethalityRoll extends DGPercentileRoll { } } -export async function sendDamageRollToChat(actor, label, diceFormula, rollMode){ - - if(rollMode == null || rollMode === ""){ - rollMode = game.settings.get("core", "rollMode"); +export class DGDamageRoll extends DGRoll { + /** + * Prepares data for a chat message and then passes that data + * to a method that actually creates a ChatMessage. + * + * @returns {Promise} - the created chat message. + * @override + */ + async toChat() { + const rollMode = this.options.rollMode || game.settings.get("core", "rollMode"); + let label = this.formula; + try{ + label = `${this.item.name}: ${game.i18n.localize("DG.Roll.Rolling")} ${game.i18n.localize("DG.Roll.Damage").toUpperCase()} ${game.i18n.localize("DG.Roll.For")} ${label.toUpperCase()}`; + } + catch{ + label = `Rolling DAMAGE for ${label.toUpperCase()}`; + } + return this.createMessage(this.total, label, rollMode); } - - //diceFormula += actor.data.data.statistics.str.meleeDamageBonusFormula; - - let roll = new Roll(diceFormula, actor.system); - - await roll.evaluate({async: true}); - - try{ - label = `${game.i18n.localize("DG.Roll.Rolling")} ${game.i18n.localize("DG.Roll.Damage").toUpperCase()} ${game.i18n.localize("DG.Roll.For")} ${label.toUpperCase()}`; - } - catch{ - label = `Rolling DAMAGE for ${label.toUpperCase()}`; - } - - let chatData = { - speaker: ChatMessage.getSpeaker({actor: actor}), - content: roll.total, - flavor: label, - type: 5, //CHAT_MESSAGE_TYPES.ROLL, - roll: roll, - rollMode: rollMode - }; - - // play the dice rolling sound, like a regular in-chat roll - AudioHelper.play({src: "sounds/dice.wav", volume: 0.8, autoplay: true, loop: false}, true); - - ChatMessage.create(chatData, {}); - } -export async function sendSanityDamageToChat(actor, label, lowFormula, highFormula, rollMode){ +async function sendSanityDamageToChat(actor, label, lowFormula, highFormula, rollMode){ if(rollMode == null || rollMode === ""){ rollMode = game.settings.get("core", "rollMode"); @@ -461,7 +478,7 @@ export async function sendSanityDamageToChat(actor, label, lowFormula, highFormu ChatMessage.create(chatData, {}); } -export async function showModifyDamageRollDialogue(actor, label, originalFormula){ +async function showModifyDamageRollDialogue(actor, label, originalFormula){ let template = "systems/deltagreen/templates/dialog/modify-damage-roll.html"; let backingData = { @@ -513,7 +530,7 @@ export async function showModifyDamageRollDialogue(actor, label, originalFormula }).render(true); } -export function skillIsStatTest(skillName){ +function skillIsStatTest(skillName){ try{ if(skillName.toUpperCase() === game.i18n.localize("DG.Attributes.str").toUpperCase()){ return true; diff --git a/templates/actor/actor-sheet.html b/templates/actor/actor-sheet.html index 2748477..8a3a80b 100644 --- a/templates/actor/actor-sheet.html +++ b/templates/actor/actor-sheet.html @@ -335,11 +335,8 @@ {{#unless item.system.isLethal}}