diff --git a/module/documents/item.mjs b/module/documents/item.mjs index f5a6dbc..6ab68ed 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -188,10 +188,31 @@ export class OrdemItem extends Item { * Rely upon the d20Roll logic for the core implementation */ async rollAttack(options={}) { - if ( !this.system.formulas.attackFormula.formula ) throw new Error('This Item does not have a formula to roll!'); + if ( !this.system.formulas.attackFormula.attr || !this.system.formulas.attackFormula.skill) throw new Error('This Item does not have a formula to roll!'); + + const attack = this.system.formulas.attackFormula; + + const bonus = attack.bonus && '+' + attack.bonus; + let attr = attack.attr; + let skill = attack.skill; + let rollMode = 'kh'; + + for (const [i, attrParent] of Object.entries(this.parent.system.attributes)) { + console.log(i + attr + attrParent.value); + if (i == attr) { + if (attrParent.value == 0) { + attr = 2; + rollMode = 'kl'; + } else attr = attrParent.value; + } + } + + for (const [i, skillParent] of Object.entries(this.parent.system.skills)) { + if (i == skill) skill = '+' + skillParent.value; + } const rollConfig = { - formula: this.system.formulas.attackFormula.formula, + formula: attr + 'd20' + rollMode + skill + bonus, data: this.getRollData(), chatMessage: true }; @@ -201,7 +222,7 @@ export class OrdemItem extends Item { * A hook event that fires before a formula is rolled for an Item. * @function ordemparanormal.preRollFormula * @memberof hookEvents - * @param {Item5e} item Item for which the roll is being performed. + * @param {OrdemItem} item Item for which the roll is being performed. * @param {object} config Configuration data for the pending roll. * @param {string} config.formula Formula that will be rolled. * @param {object} config.data Data used when evaluating the roll. @@ -226,8 +247,8 @@ export class OrdemItem extends Item { * A hook event that fires after a formula has been rolled for an Item. * @function ordemparanormal.rollFormula * @memberof hookEvents - * @param {Item5e} item Item for which the roll was performed. - * @param {Roll} roll The resulting roll. + * @param {OrdemItem} item Item for which the roll was performed. + * @param {Roll} roll The resulting roll. */ Hooks.callAll('ordemparanormal.rollFormula', this, roll); @@ -240,19 +261,28 @@ export class OrdemItem extends Item { */ async rollDamage(options={}) { if ( !this.system.formulas.damageFormula.formula ) throw new Error('This Item does not have a formula to roll!'); - + + const damage = this.system.formulas.damageFormula; + const bonus = damage.bonus && '+' + damage.bonus; + const formula = damage.formula; + let attr = damage.attr; + + for (const [i, attrParent] of Object.entries(this.parent.system.attributes)) { + if (i == attr) attr = '+' + attrParent.value; + } + const rollConfig = { - formula: this.system.formulas.damageFormula.formula, - data: this.getRollData(), - chatMessage: true + formula: formula + attr + bonus, + data: this.getRollData(), + chatMessage: true }; - // if ( spellLevel ) rollConfig.data.item.level = spellLevel; + // if ( spellLevel ) rollConfig.data.item.level = spellLevel; /** * A hook event that fires before a formula is rolled for an Item. * @function ordemparanormal.preRollFormula * @memberof hookEvents - * @param {Item5e} item Item for which the roll is being performed. + * @param {OrdemItem} item Item for which the roll is being performed. * @param {object} config Configuration data for the pending roll. * @param {string} config.formula Formula that will be rolled. * @param {object} config.data Data used when evaluating the roll. @@ -277,14 +307,23 @@ export class OrdemItem extends Item { * A hook event that fires after a formula has been rolled for an Item. * @function ordemparanormal.rollFormula * @memberof hookEvents - * @param {Item5e} item Item for which the roll was performed. - * @param {Roll} roll The resulting roll. + * @param {Ordemitem} item Item for which the roll was performed. + * @param {Roll} roll The resulting roll. */ Hooks.callAll('ordemparanormal.rollFormula', this, roll); return roll; } + /** + * Trigger an item usage, optionally creating a chat message with followup actions. + **/ + async use(config={}, options={}) { + const item = this; + const is = item.system; + const as = item.actor.system; + } + /** * Handle clickable rolls. * @param {Event} event The originating click event diff --git a/module/documents/macro.mjs b/module/documents/macro.mjs new file mode 100644 index 0000000..177d5b1 --- /dev/null +++ b/module/documents/macro.mjs @@ -0,0 +1,94 @@ +/** + * Attempt to create a macro from the dropped data. Will use an existing macro if one exists. + * @param {object} dropData The dropped data + * @param {number} slot The hotbar slot to use + */ +export async function createOPMacro(dropData, slot) { + const macroData = { type: 'script', scope: 'actor' }; + switch ( dropData.type ) { + case 'Item': { + const itemData = await Item.implementation.fromDropData(dropData); + if ( !itemData ) return ui.notifications.warn(game.i18n.localize('MACRO.opUnownedWarn')); + foundry.utils.mergeObject(macroData, { + name: itemData.name, + img: itemData.img, + command: `ordemparanormal.documents.macro.rollItem('${itemData.name}')`, + flags: {'dnd5e.itemMacro': true} + }); + break; + } + case 'ActiveEffect': { + const effectData = await ActiveEffect.implementation.fromDropData(dropData); + if ( !effectData ) return ui.notifications.warn(game.i18n.localize('MACRO.opUnownedWarn')); + foundry.utils.mergeObject(macroData, { + name: effectData.label, + img: effectData.icon, + command: `ordemparanormal.documents.macro.toggleEffect('${effectData.label}')`, + flags: {'dnd5e.effectMacro': true} + }); + break; + } + default: + return; + } + + // Assign the macro to the hotbar + const macro = game.macros.find(m => { + return (m.name === macroData.name) && (m.command === macroData.command) && m.isAuthor; + }) || await Macro.create(macroData); + game.user.assignHotbarMacro(macro, slot); +} + +/* -------------------------------------------- */ + +/** + * Find a document of the specified name and type on an assigned or selected actor. + * @param {string} name Document name to locate. + * @param {string} documentType Type of embedded document (e.g. "Item" or "ActiveEffect"). + * @returns {Document} Document if found, otherwise nothing. + */ +function getMacroTarget(name, documentType) { + let actor; + const speaker = ChatMessage.getSpeaker(); + if ( speaker.token ) actor = game.actors.tokens[speaker.token]; + actor ??= game.actors.get(speaker.actor); + if ( !actor ) return ui.notifications.warn(game.i18n.localize('MACRO.opNoActorSelected')); + + const collection = (documentType === 'Item') ? actor.items : actor.effects; + const nameKeyPath = (documentType === 'Item') ? 'name' : 'label'; + + // Find item in collection + const documents = collection.filter(i => foundry.utils.getProperty(i, nameKeyPath) === name); + const type = game.i18n.localize(`DOCUMENT.${documentType}`); + if ( documents.length === 0 ) { + return ui.notifications.warn(game.i18n.format('MACRO.opMissingTargetWarn', { actor: actor.name, type, name })); + } + if ( documents.length > 1 ) { + ui.notifications.warn(game.i18n.format('MACRO.opMultipleTargetsWarn', { actor: actor.name, type, name })); + } + + return documents[0]; +} + +/* -------------------------------------------- */ + +/** + * Trigger an item to roll when a macro is clicked. + * @param {string} itemName Name of the item on the selected actor to trigger. + * @returns {Promise} Roll result. + */ +export function rollItem(itemName) { + return getMacroTarget(itemName, 'Item'); +} + +/* -------------------------------------------- */ + +/** + * Toggle an effect on and off when a macro is clicked. + * @param {string} effectName Name of the effect to be toggled. + * @returns {Promise} The effect after it has been toggled. + */ +export function toggleEffect(effectName) { + const effect = getMacroTarget(effectName, 'ActiveEffect'); + return effect?.update({disabled: !effect.disabled}); +} \ No newline at end of file