ENH: Chat card buttons

master
U~man 2020-07-04 13:41:17 +02:00
parent 07fff9b20a
commit 86bb4333e2
5 changed files with 135 additions and 12 deletions

View File

@ -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>`;

View File

@ -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;
}
}

View File

@ -63,3 +63,6 @@ Hooks.once("setup", function () {
}, {});
}
});
Hooks.on("renderChatLog", (app, html, data) => OseItem.chatListeners(html));

View File

@ -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>

View File

@ -1,11 +1,11 @@
<div class="ose chat-card item-card" data-actor-id="{{actor._id}}" data-item-id="{{item._id}}"
{{#if tokenId}}data-token-id="{{tokenId}}"{{/if}}>
{{#if tokenId}}data-token-id="{{tokenId}}" {{/if}}>
<header class="card-header flexrow">
<img src="{{item.img}}" title="{{item.name}}" width="36" height="36"/>
<img src="{{item.img}}" title="{{item.name}}" width="36" height="36" />
<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>