From 5dfffe48a9df21d454dc81bd913a1a8551ad5d27 Mon Sep 17 00:00:00 2001 From: Paul Maskelyne Date: Mon, 18 Nov 2024 16:38:48 +0000 Subject: [PATCH] closes #952 --- RELEASE_NOTES.md | 2 +- i18n/en.yaml | 2 +- system/src/apps/RequestCheckSD.mjs | 6 +- system/src/documents/ActorSD.mjs | 112 +++++++++----------------- system/src/documents/ItemSD.mjs | 62 ++++++-------- system/src/system/ChatSD.mjs | 16 +++- system/templates/chat/use-ability.hbs | 22 ++++- 7 files changed, 104 insertions(+), 118 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index fcec82a52..3481560cd 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -27,6 +27,7 @@ * [#930] Class Abilities can't be used if they have no associated skill roll * [#936] Character Generator: Don't display empty alignment for Deities that don't have one * [#938] Unecessary line breaks in Farsight talent descriptions +* [#952] NPC Special Attacks always posted to chat in public * [#953] Dice So Nice not honouring roll modes in Shadowdark * [#955] Chat message icon missing on hovering over Ancestry and Level talents on character sheet @@ -40,7 +41,6 @@ *Many thanks to **AdamsGH** for contributing **Russian** translation data.* - --- # v3.1.3 diff --git a/i18n/en.yaml b/i18n/en.yaml index 57ea56076..c3d77544d 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -185,7 +185,7 @@ SHADOWDARK.chat.spell_learn.failure: "{name} failed to learn anything from the s SHADOWDARK.chat.spell_learn.success: "{name} successfully learnt the {spellName} spell" SHADOWDARK.chat.spell_learn.title: Learning Spell SHADOWDARK.chat.spell_roll.title: "{name}, DC {spellDC}" -SHADOWDARK.chat.use_ability.failure: "{name} failed to used the {ability} ability" +SHADOWDARK.chat.use_ability.failure: "{name} failed to use the {ability} ability" SHADOWDARK.chat.use_ability.success: "{name} successfully used the {ability} ability" SHADOWDARK.chat.use_ability.title: "Using Ability" SHADOWDARK.chat.welcome_message.arcane_library_button: Shop Shadowdark RPG diff --git a/system/src/apps/RequestCheckSD.mjs b/system/src/apps/RequestCheckSD.mjs index a55d7ccf9..d48abaa4a 100644 --- a/system/src/apps/RequestCheckSD.mjs +++ b/system/src/apps/RequestCheckSD.mjs @@ -45,8 +45,10 @@ export default class RequestCheckSD extends FormApplication { shadowdark.chat.renderRollRequestMessage( await shadowdark.utils.getCurrentActor(), { - title: game.i18n.localize("SHADOWDARK.check.requesting"), - body: `[[check ${dc} ${stat}]]`, + templateData: { + title: game.i18n.localize("SHADOWDARK.check.requesting"), + body: `[[check ${dc} ${stat}]]`, + }, }, CONST.DICE_ROLL_MODES.PUBLIC ); diff --git a/system/src/documents/ActorSD.mjs b/system/src/documents/ActorSD.mjs index 08368e699..b29987e78 100644 --- a/system/src/documents/ActorSD.mjs +++ b/system/src/documents/ActorSD.mjs @@ -1412,6 +1412,26 @@ export default class ActorSD extends Actor { async useAbility(itemId, options={}) { const item = this.items.get(itemId); + + if (item.type === "NPC Feature") item.displayCard(); + + // If the ability has limited uses, handle that first + if (item.system.limitedUses) { + if (item.system.uses.available <= 0) { + return ui.notifications.error( + game.i18n.format("SHADOWDARK.error.class_ability.no-uses-remaining"), + {permanent: false} + ); + } + else { + const newUsesAvailable = item.system.uses.available - 1; + + item.update({ + "system.uses.available": Math.max(0, newUsesAvailable), + }); + } + } + const abilityDescription = await TextEditor.enrichHTML( item.system.description, { @@ -1420,86 +1440,32 @@ export default class ActorSD extends Actor { relativeTo: this, } ); - // Default message values - let title = ""; - let message = ""; - let success = true; - - // NPC features - no title or checks required - if (item.type === "NPC Feature") { - message = `${abilityDescription}`; - } - else { - title = game.i18n.localize("SHADOWDARK.chat.use_ability.title"); - - // does ability use on a roll check? - if (item.system.ability) { - options = foundry.utils.mergeObject({target: item.system.dc}, options); - const result = await this.rollAbility( - item.system.ability, - options - ); - - success = result?.rolls?.main?.success ?? false; - } - - // does ability have limited uses? - if (item.system.limitedUses) { - if (item.system.uses.available > 0) { - item.update({ - "system.uses.available": item.system.uses.available - 1, - }); - } - else { - success = false; - ui.notifications.error( - game.i18n.format("SHADOWDARK.error.class_ability.no-uses-remaining"), - {permanent: false} - ); - } - } - - const messageType = success - ? "SHADOWDARK.chat.use_ability.success" - : "SHADOWDARK.chat.use_ability.failure"; - message = game.i18n.format( - messageType, - { - name: this.name, - ability: item.name, - } + let success = true; + // does ability use on a roll check? + if (item.system.ability) { + options = foundry.utils.mergeObject({target: item.system.dc}, options); + const result = await this.rollAbility( + item.system.ability, + options ); - if (success) { - message = `

${message}

${abilityDescription}`; + success = result?.rolls?.main?.success ?? false; + + if (!success && item.system.loseOnFailure) { + item.update({"system.lost": true}); } } - // construct and create chat message - const cardData = { - actor: this, - item: item, - message, - }; - - let template = "systems/shadowdark/templates/chat/use-ability.hbs"; - - const content = await renderTemplate(template, cardData); - - await ChatMessage.create({ - title, - content, - flags: { "core.canPopout": true }, - flavor: title, - speaker: ChatMessage.getSpeaker({actor: this, token: this.token}), - type: CONST.CHAT_MESSAGE_STYLES.OTHER, - user: game.user.id, + return shadowdark.chat.renderUseAbilityMessage(this.actor, { + flavor: game.i18n.localize("SHADOWDARK.chat.use_ability.title"), + templateData: { + abilityDescription, + actor: this, + item: item, + success, + }, }); - - if (!success && item.system.loseOnFailure) { - item.update({"system.lost": true}); - } } diff --git a/system/src/documents/ItemSD.mjs b/system/src/documents/ItemSD.mjs index d113a5142..2eebbcf41 100644 --- a/system/src/documents/ItemSD.mjs +++ b/system/src/documents/ItemSD.mjs @@ -10,10 +10,12 @@ export default class ItemSD extends Item { ].includes(this.type); } + get typeSlug() { return this.type.slugify(); } + get usesAmmunition() { return (game.settings.get("shadowdark", "autoConsumeAmmunition") && this.isOwned @@ -55,12 +57,14 @@ export default class ItemSD extends Item { } } + availableAmmunition() { if (this.usesAmmunition) { return this.actor.ammunitionItems(this.system.ammoClass); } } + async getChatData(htmlOptions={}) { const description = await this.getEnrichedDescription(); @@ -87,33 +91,15 @@ export default class ItemSD extends Item { return data; } - async displayCard(options={}) { - // Render the chat card template - const token = this.actor.token; - - const templateData = await this.getChatData(); - - const template = this.getItemTemplate("systems/shadowdark/templates/chat/item"); - - const html = await renderTemplate(template, templateData); - - const chatData = { - user: game.user.id, - type: CONST.CHAT_MESSAGE_STYLES.OTHER, - content: html, - flavor: this.name, - speaker: ChatMessage.getSpeaker({actor: this.actor, token}), - flags: { "core.canPopout": true }, - }; - - ChatMessage.applyRollMode(chatData, options.rollMode ?? game.settings.get("core", "rollMode")); - - const card = (options.createMessage !== false) - ? await ChatMessage.create(chatData) : chatData; - return card; + async displayCard(options={}) { + shadowdark.chat.renderItemCardMessage(this.actor, { + template: this.getItemTemplate("systems/shadowdark/templates/chat/item"), + templateData: await this.getChatData(), + }); } + async getBaseItemName() { if (this.type === "Armor") { if (this.system.baseArmor === "") return ""; @@ -159,6 +145,7 @@ export default class ItemSD extends Item { return await renderTemplate(templatePath, data); } + async getEnrichedDescription() { return await TextEditor.enrichHTML( this.system.description, @@ -168,6 +155,7 @@ export default class ItemSD extends Item { ); } + getItemTemplate(basePath) { switch (this.type) { case "Armor": @@ -185,19 +173,20 @@ export default class ItemSD extends Item { } } + lightRemainingString() { if (this.type !== "Basic" && !this.system.light.isSource) return; - const timeRemaining = Math.ceil( - this.system.light.remainingSecs / 60 - ); - if (this.system.light.remainingSecs < 60) { this.lightSourceTimeRemaining = game.i18n.localize( "SHADOWDARK.inventory.item.light_seconds_remaining" ); } else { + const timeRemaining = Math.ceil( + this.system.light.remainingSecs / 60 + ); + this.lightSourceTimeRemaining = game.i18n.format( "SHADOWDARK.inventory.item.light_remaining", { timeRemaining } @@ -205,31 +194,36 @@ export default class ItemSD extends Item { } } + async reduceAmmunition(amount) { const newAmount = Math.max(0, this.system.quantity - amount); this.update({"system.quantity": newAmount}); } + setLightRemaining(remainingSeconds) { this.update({"system.light.remainingSecs": remainingSeconds}); } + /* -------------------------------------------- */ /* Roll Methods */ /* -------------------------------------------- */ - async rollNpcAttack(parts, data, options={}) { - options.dialogTemplate = "systems/shadowdark/templates/dialog/roll-npc-attack-dialog.hbs"; + async rollItem(parts, data, options={}) { + options.dialogTemplate = "systems/shadowdark/templates/dialog/roll-item-dialog.hbs"; options.chatCardTemplate = "systems/shadowdark/templates/chat/item-card.hbs"; await CONFIG.DiceSD.RollDialog(parts, data, options); } - async rollItem(parts, data, options={}) { - options.dialogTemplate = "systems/shadowdark/templates/dialog/roll-item-dialog.hbs"; + + async rollNpcAttack(parts, data, options={}) { + options.dialogTemplate = "systems/shadowdark/templates/dialog/roll-npc-attack-dialog.hbs"; options.chatCardTemplate = "systems/shadowdark/templates/chat/item-card.hbs"; await CONFIG.DiceSD.RollDialog(parts, data, options); } + async rollSpell(parts, data, options={}) { options.dialogTemplate = "systems/shadowdark/templates/dialog/roll-spell-dialog.hbs"; options.chatCardTemplate = "systems/shadowdark/templates/chat/item-card.hbs"; @@ -250,9 +244,6 @@ export default class ItemSD extends Item { return roll; } - /* -------------------------------------------- */ - /* Methods */ - /* -------------------------------------------- */ async hasProperty(property) { property = property.slugify(); @@ -364,7 +355,6 @@ export default class ItemSD extends Item { return propertyItems; } - // Duration getters /** * Returns the total duration depending on the type diff --git a/system/src/system/ChatSD.mjs b/system/src/system/ChatSD.mjs index 292280e0b..ee215594f 100644 --- a/system/src/system/ChatSD.mjs +++ b/system/src/system/ChatSD.mjs @@ -6,7 +6,7 @@ export default class ChatSD { template, mode ) { - const html = await renderTemplate(template, data); + const html = await renderTemplate(template, data.templateData); if (!mode) { mode = game.settings.get("core", "rollMode"); @@ -15,11 +15,12 @@ export default class ChatSD { const chatData = { content: html, flags: { "core.canPopout": true }, + flavor: data.flavor ?? undefined, rollMode: mode, speaker: ChatMessage.getSpeaker({ actor: actor, }), - type: CONST.CHAT_MESSAGE_STYLES.OTHER, + type: data.type ?? CONST.CHAT_MESSAGE_STYLES.OTHER, user: game.user.id, }; @@ -35,10 +36,21 @@ export default class ChatSD { ); } + static async renderItemCardMessage(actor, data, mode) { + this._renderChatMessage(actor, data, data.template, mode); + } + static async renderRollRequestMessage(actor, data, mode) { this._renderChatMessage(actor, data, "systems/shadowdark/templates/chat/roll-request.hbs", mode ); } + + static async renderUseAbilityMessage(actor, data, mode) { + this._renderChatMessage(actor, data, + "systems/shadowdark/templates/chat/use-ability.hbs", + mode + ); + } } diff --git a/system/templates/chat/use-ability.hbs b/system/templates/chat/use-ability.hbs index 4bcd46777..4b4ba67ce 100644 --- a/system/templates/chat/use-ability.hbs +++ b/system/templates/chat/use-ability.hbs @@ -4,11 +4,27 @@ data-item-id="{{item._id}}" >
- -

{{item.name}}

+ +

+ {{item.name}} +

- {{{message}}} + + {{#if success}} + {{localize "SHADOWDARK.chat.use_ability.success" + ability=item.name + name=actor.name + }} + {{else}} + {{localize "SHADOWDARK.chat.use_ability.failure" + ability=item.name + name=actor.name + }} + {{/if}} + + + {{{abilityDescription}}}