ENH: Chat card buttons
parent
07fff9b20a
commit
86bb4333e2
|
@ -29,6 +29,7 @@ export class OseDice {
|
|||
return details;
|
||||
}
|
||||
details = `<div class='roll-result'><b>Hits AC ${Math.clamped(thac - roll.total,-3,9)}</b> (${thac})</div>`;
|
||||
// ADD DAMAGE ROLL
|
||||
}
|
||||
} else if (data.rollData.type == "Save") {
|
||||
// SAVING THROWS
|
||||
|
@ -47,7 +48,7 @@ export class OseDice {
|
|||
details = `<div class='roll-result roll-fail'><b>Failure</b> (${sc})</div>`;
|
||||
}
|
||||
} else if (data.rollData.type == "Exploration") {
|
||||
// Exploration Checks
|
||||
// EXPLORATION CHECKS
|
||||
let sc = data.data.exploration[data.rollData.stat];
|
||||
if (roll.total <= sc) {
|
||||
details = `<div class='roll-result roll-success'><b>Success!</b> (${sc})</div>`;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,3 +63,6 @@ Hooks.once("setup", function () {
|
|||
}, {});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Hooks.on("renderChatLog", (app, html, data) => OseItem.chatListeners(html));
|
|
@ -12,7 +12,7 @@
|
|||
</div>
|
||||
<div class="attribute-bonuses">
|
||||
{{localize 'OSE.Melee'}} ({{mods.str}})<br/>
|
||||
{{localize 'OSE.exploration.OpenDoor.long'}}
|
||||
{{localize 'OSE.exploration.od.long'}}
|
||||
<span class="attribute-mod"><a><i class="fas fa-minus"></i></a></span>
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<h3 class="item-name">{{item.name}}</h3>
|
||||
</header>
|
||||
|
||||
<div class="card-content">
|
||||
<div class="card-content" style="display:none;">
|
||||
{{{data.description}}}
|
||||
</div>
|
||||
|
||||
|
@ -14,8 +14,11 @@
|
|||
|
||||
{{#if hasDamage}}
|
||||
<button data-action="damage">
|
||||
{{#if isHealing}}{{ localize "OSE.Healing" }}
|
||||
{{else}}{{localize "OSE.Damage" }}{{/if}}
|
||||
{{#if isHealing}}
|
||||
{{ localize "OSE.Healing" }}
|
||||
{{else}}
|
||||
{{localize "OSE.Damage" }}
|
||||
{{/if}}
|
||||
</button>
|
||||
{{/if}}
|
||||
|
||||
|
@ -25,8 +28,8 @@
|
|||
</button>
|
||||
{{/if}}
|
||||
|
||||
{{#if data.formula}}
|
||||
<button data-action="formula">{{ localize "OSE.OtherFormula"}}</button>
|
||||
{{#if data.roll}}
|
||||
<button data-action="formula">{{ localize "OSE.Roll"}}</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Reference in New Issue