From 86bb4333e29a5d11609c3b02f740b4b7e5bfd1fe Mon Sep 17 00:00:00 2001 From: U~man Date: Sat, 4 Jul 2020 13:41:17 +0200 Subject: [PATCH] ENH: Chat card buttons --- src/module/dice.js | 3 +- src/module/item/entity.js | 118 +++++++++++++++++- src/ose.js | 5 +- .../partials/character-attributes-tab.html | 2 +- src/templates/chat/item-card.html | 19 +-- 5 files changed, 135 insertions(+), 12 deletions(-) diff --git a/src/module/dice.js b/src/module/dice.js index 8850e5b..25dc5fc 100644 --- a/src/module/dice.js +++ b/src/module/dice.js @@ -29,6 +29,7 @@ export class OseDice { return details; } details = `
Hits AC ${Math.clamped(thac - roll.total,-3,9)} (${thac})
`; + // ADD DAMAGE ROLL } } else if (data.rollData.type == "Save") { // SAVING THROWS @@ -47,7 +48,7 @@ export class OseDice { details = `
Failure (${sc})
`; } } else if (data.rollData.type == "Exploration") { - // Exploration Checks + // EXPLORATION CHECKS let sc = data.data.exploration[data.rollData.stat]; if (roll.total <= sc) { details = `
Success! (${sc})
`; diff --git a/src/module/item/entity.js b/src/module/item/entity.js index 7e1444b..9c62396 100644 --- a/src/module/item/entity.js +++ b/src/module/item/entity.js @@ -13,6 +13,11 @@ export class OseItem extends Item { super.prepareData(); } + static chatListeners(html) { + html.on('click', '.card-buttons button', this._onChatCardAction.bind(this)); + html.on('click', '.item-name', this._onChatCardToggleContent.bind(this)); + } + getChatData(htmlOptions) { const data = duplicate(this.data.data); @@ -53,12 +58,32 @@ export class OseItem extends Item { return false; } + async rollFormula(options={}) { + if ( !this.data.data.roll ) { + throw new Error("This Item does not have a formula to roll!"); + } + + // Define Roll Data + const rollData = { + item: this.data.data + }; + const title = `${this.name} - Roll`; + + // Invoke the roll and submit it to chat + const roll = new Roll(rollData.item.roll, rollData).roll(); + roll.toMessage({ + speaker: ChatMessage.getSpeaker({actor: this.actor}), + flavor: this.data.data.chatFlavor || title, + rollMode: game.settings.get("core", "rollMode") + }); + return roll; + } + /** * Roll the item to Chat, creating a chat card which contains follow up attack or damage roll options * @return {Promise} */ async roll({ configureDialog = true } = {}) { - console.log(this.data) if (this.data.type == 'weapon') { if (this.rollWeapon()) return; } @@ -101,4 +126,95 @@ export class OseItem extends Item { // Create the chat message return ChatMessage.create(chatData); } + + /** + * Handle toggling the visibility of chat card content when the name is clicked + * @param {Event} event The originating click event + * @private + */ + static _onChatCardToggleContent(event) { + event.preventDefault(); + const header = event.currentTarget; + const card = header.closest(".chat-card"); + const content = card.querySelector(".card-content"); + content.style.display = content.style.display === "none" ? "block" : "none"; + } + + + static async _onChatCardAction(event) { + event.preventDefault(); + + // Extract card data + const button = event.currentTarget; + button.disabled = true; + const card = button.closest(".chat-card"); + const messageId = card.closest(".message").dataset.messageId; + const message = game.messages.get(messageId); + const action = button.dataset.action; + + // Validate permission to proceed with the roll + const isTargetted = action === "save"; + if ( !( isTargetted || game.user.isGM || message.isAuthor ) ) return; + + // Get the Actor from a synthetic Token + const actor = this._getChatCardActor(card); + if ( !actor ) return; + + // Get the Item + const item = actor.getOwnedItem(card.dataset.itemId); + if ( !item ) { + return ui.notifications.error(`The requested item ${card.dataset.itemId} no longer exists on Actor ${actor.name}`) + } + + // Get card targets + let targets = []; + if ( isTargetted ) { + targets = this._getChatCardTargets(card); + if ( !targets.length ) { + ui.notifications.warn(`You must have one or more controlled Tokens in order to use this option.`); + return button.disabled = false; + } + } + + // Attack and Damage Rolls + else if ( action === "damage" ) await item.rollDamage({event}); + else if ( action === "formula" ) await item.rollFormula({event}); + + // Saving Throws for card targets + else if ( action === "save" ) { + for ( let t of targets ) { + await t.rollAbilitySave(button.dataset.ability, {event}); + } + } + + // Re-enable the button + button.disabled = false; + } + + static _getChatCardActor(card) { + + // Case 1 - a synthetic actor from a Token + const tokenKey = card.dataset.tokenId; + if (tokenKey) { + const [sceneId, tokenId] = tokenKey.split("."); + const scene = game.scenes.get(sceneId); + if (!scene) return null; + const tokenData = scene.getEmbeddedEntity("Token", tokenId); + if (!tokenData) return null; + const token = new Token(tokenData); + return token.actor; + } + + // Case 2 - use Actor ID directory + const actorId = card.dataset.actorId; + return game.actors.get(actorId) || null; + } + + static _getChatCardTargets(card) { + const character = game.user.character; + const controlled = canvas.tokens.controlled; + const targets = controlled.reduce((arr, t) => t.actor ? arr.concat([t.actor]) : arr, []); + if ( character && (controlled.length === 0) ) targets.push(character); + return targets; + } } diff --git a/src/ose.js b/src/ose.js index 2836cb1..7011227 100644 --- a/src/ose.js +++ b/src/ose.js @@ -62,4 +62,7 @@ Hooks.once("setup", function () { return obj; }, {}); } -}); \ No newline at end of file +}); + + +Hooks.on("renderChatLog", (app, html, data) => OseItem.chatListeners(html)); \ No newline at end of file diff --git a/src/templates/actors/partials/character-attributes-tab.html b/src/templates/actors/partials/character-attributes-tab.html index 3dcae5a..19a7688 100644 --- a/src/templates/actors/partials/character-attributes-tab.html +++ b/src/templates/actors/partials/character-attributes-tab.html @@ -12,7 +12,7 @@
{{localize 'OSE.Melee'}} ({{mods.str}})
- {{localize 'OSE.exploration.OpenDoor.long'}} + {{localize 'OSE.exploration.od.long'}}
diff --git a/src/templates/chat/item-card.html b/src/templates/chat/item-card.html index fc1d239..902acc7 100644 --- a/src/templates/chat/item-card.html +++ b/src/templates/chat/item-card.html @@ -1,11 +1,11 @@
+ {{#if tokenId}}data-token-id="{{tokenId}}" {{/if}}>
- +

{{item.name}}

-
+ @@ -14,8 +14,11 @@ {{#if hasDamage}} {{/if}} @@ -25,8 +28,8 @@ {{/if}} - {{#if data.formula}} - + {{#if data.roll}} + {{/if}}
@@ -35,4 +38,4 @@ {{this}} {{/each}} -
+ \ No newline at end of file