diff --git a/module/actor/actor-sheet.mjs b/module/actor/actor-sheet.mjs index 22a3ee4e..394234d0 100644 --- a/module/actor/actor-sheet.mjs +++ b/module/actor/actor-sheet.mjs @@ -952,6 +952,7 @@ export class HeroSystemActorSheet extends ActorSheet { error: resourceError, warning: resourceWarning, resourcesUsedDescription, + resourcesUsedDescriptionRenderedRoll, } = await userInteractiveVerifyOptionallyPromptThenSpendResources(item, strengthUsed); if (resourceError) { return ui.notifications.error(resourceError); @@ -976,7 +977,9 @@ export class HeroSystemActorSheet extends ActorSheet { item, targetEntangle: "true", - resourcesUsedDescription, + resourcesUsedDescription: resourcesUsedDescription + ? `Spent ${resourcesUsedDescription}${resourcesUsedDescriptionRenderedRoll}` + : "", // dice rolls renderedDamageRoll: damageRenderedResult, diff --git a/module/actor/actor.mjs b/module/actor/actor.mjs index aed53ab2..dba572bf 100644 --- a/module/actor/actor.mjs +++ b/module/actor/actor.mjs @@ -1290,7 +1290,7 @@ export class HeroSystem6eActor extends Actor { heroJson.CHARACTER.POWERS.length + heroJson.CHARACTER.SKILLS.length + heroJson.CHARACTER.TALENTS.length + - (this.type === "pc" || this.type === "npc" ? 1 : 0) + + (this.type === "pc" || this.type === "npc" ? 1 : 0) + // Free stuff 1 + // Validating adjustment and powers 1 + // Images 1 + // Final save @@ -1337,8 +1337,6 @@ export class HeroSystem6eActor extends Actor { } break; } - // Indicate we're processing this aspect of the HDC. If it crashes it should remain showing this progress note. - //uploadProgressBar.advance(`${this.name}: Adding ${itemTag} ${itemData.name}`); if (this.id) { itemsToCreate.push(itemData); @@ -1468,7 +1466,7 @@ export class HeroSystem6eActor extends Actor { uploadPerformance.itemPromiseArray = new Date() - uploadPerformance._d; uploadPerformance._d = new Date(); - // Do CSL's last so we can property select the attacks + // Do CSLs last so we can property select the attacks await Promise.all( this.items .filter((o) => !["COMBAT_LEVELS", "MENTAL_COMBAT_LEVELS"].includes(o.system.XMLID)) diff --git a/module/item/item-attack.mjs b/module/item/item-attack.mjs index 9b8bb246..93d05341 100644 --- a/module/item/item-attack.mjs +++ b/module/item/item-attack.mjs @@ -518,6 +518,7 @@ export async function AttackToHit(item, options) { warning: resourceWarning, resourcesRequired, resourcesUsedDescription, + resourcesUsedDescriptionRenderedRoll, } = await userInteractiveVerifyOptionallyPromptThenSpendResources(item, { ...options, ...{ noResourceUse: false }, @@ -720,7 +721,7 @@ export async function AttackToHit(item, options) { const chatData = { user: game.user._id, type: CONST.CHAT_MESSAGE_TYPES.OTHER, - content: resourcesUsedDescription, + content: `${resourcesUsedDescription}${resourcesUsedDescriptionRenderedRoll}`, whisper: ChatMessage.getWhisperRecipients("GM"), speaker, }; @@ -782,7 +783,7 @@ export async function AttackToHit(item, options) { // endurance useEnd: resourcesRequired.end, - resourcesUsedDescription, + resourcesUsedDescription: `${resourcesUsedDescription}${resourcesUsedDescriptionRenderedRoll}`, // misc tags: heroRoller.tags(), @@ -3055,6 +3056,7 @@ async function _calcKnockback(body, item, options, knockbackMultiplier) { * * @param {HeroSystem6eItem} item * @param {Object} options + * @param {boolean} options.noResourceUse - true to not consume resources but still indicate how many would have been consumed * * @returns Object discriminated union based on error or warning being falsy/truthy */ @@ -3124,7 +3126,7 @@ export async function userInteractiveVerifyOptionallyPromptThenSpendResources(it } // The actor is now committed to spending the resources to activate the power - const resourcesUsedDescription = await spendResourcesToUse( + const { resourcesUsedDescription, resourcesUsedDescriptionRenderedRoll } = await spendResourcesToUse( item, enduranceReserve, resourcesRequired.end, @@ -3134,8 +3136,8 @@ export async function userInteractiveVerifyOptionallyPromptThenSpendResources(it !useResources, ); - // Let users know what resources were not consumed - if (!useResources) { + // Let users know what resources were not consumed only if there were any to be consumed + if (!useResources && resourcesUsedDescription) { const speaker = ChatMessage.getSpeaker({ actor: actor, }); @@ -3144,7 +3146,7 @@ export async function userInteractiveVerifyOptionallyPromptThenSpendResources(it const chatData = { user: game.user._id, type: CONST.CHAT_MESSAGE_TYPES.OTHER, - content: `${game.user.name} is using SHIFT to override using ${resourcesUsedDescription} for ${item.name}`, + content: `${game.user.name} is using SHIFT to override using ${resourcesUsedDescription} for ${item.name}${resourcesUsedDescriptionRenderedRoll}`, whisper: whisperUserTargetsForActor(this), speaker, }; @@ -3153,9 +3155,14 @@ export async function userInteractiveVerifyOptionallyPromptThenSpendResources(it return { resourcesRequired, + + // Provide a wordy description of what was used. Indicate if resource use was overridden except if no would have been used. resourcesUsedDescription: useResources - ? `Spent ${resourcesUsedDescription}` - : `${resourcesUsedDescription} overridden with SHIFT`, + ? `${resourcesUsedDescription}` + : resourcesUsedDescription + ? `${resourcesUsedDescription} overridden with SHIFT` + : resourcesUsedDescription, + resourcesUsedDescriptionRenderedRoll, }; } @@ -3301,6 +3308,37 @@ async function rollStunForEnd(stunDice) { }; } +/** + * Parse the automation setting for the system. Return what should be automated for + * this actor type. + * + * none: "No Automation", + * npcOnly: "NPCs Only (end, stun, body)", + * pcEndOnly: "PCs (end) and NPCs (end, stun, body)", + * all: "PCs and NPCs (end, stun, body)" + * + * @typedef {Object} HeroSystem6eActorAutomation + * @param {boolean} endurance - Automate END use/tracking + * @param {boolean} stun - Automate STUN use/tracking + * @param {boolean} body - Automate BODY use/tracking + */ +/** + * @param {HeroSystem6eActor} actor + * @returns {HeroSystem6eActorAutomation} + */ +export function actorAutomation(actor) { + const automation = game.settings.get(HEROSYS.module, "automation"); + + return { + endurance: + automation === "all" || + (automation === "npcOnly" && actor.type == "npc") || + (automation === "pcEndOnly" && actor.type === "pc"), + stun: automation === "all" || (automation === "npcOnly" && actor.type == "npc"), + body: automation === "all" || (automation === "npcOnly" && actor.type == "npc"), + }; +} + /** * Spend all resources (END, STUN, charges) provided. Assumes numbers are possible. * @@ -3311,7 +3349,7 @@ async function rollStunForEnd(stunDice) { * @param {HeroRoller} stunToSpendRoller * @param {number} chargesToSpend * @param {boolean} noResourceUse - true if you would like to simulate the resources being used without using them (aka dry run) - * @returns + * @returns {Object} */ async function spendResourcesToUse( item, @@ -3323,29 +3361,26 @@ async function spendResourcesToUse( noResourceUse, ) { const actor = item.actor; - let resourceUsageDescription = ""; + const expectedAutomation = actorAutomation(actor); + const canSpendResources = !noResourceUse; + const canSpendEndurance = + canSpendResources && + actor.inCombat && // TODO: Not sure if we should have this or not. We had it in toggle() but not elsewhere. + expectedAutomation.endurance; + const canSpendStun = canSpendResources && expectedAutomation.stun; + const canSpendCharges = canSpendResources; + let resourcesUsedDescription = ""; + let resourcesUsedDescriptionRenderedRoll = ""; // Deduct endurance - // none: "No Automation", - // npcOnly: "NPCs Only (end, stun, body)", - // pcEndOnly: "PCs (end) and NPCs (end, stun, body)", - // all: "PCs and NPCs (end, stun, body)" - const automation = game.settings.get(HEROSYS.module, "automation"); - const actorInCombat = actor.inCombat; - const noEnduranceUse = - !actorInCombat && // TODO: Not sure if we should have this or not. We had it in toggle() but not elsewhere. - (automation === "all" || - (automation === "npcOnly" && actor.type == "npc") || - (automation === "pcEndOnly" && actor.type === "pc")); - - if (item.system.USE_END_RESERVE) { + if (item.system.USE_END_RESERVE && endToSpend) { if (enduranceReserve) { const reserveEnd = parseInt(enduranceReserve?.system.value || 0); const actorNewEndurance = reserveEnd - endToSpend; - resourceUsageDescription = `${endToSpend} END from Endurance Reserve`; + resourcesUsedDescription = `${endToSpend} END from Endurance Reserve`; - if (!noResourceUse && !noEnduranceUse) { + if (canSpendEndurance) { await enduranceReserve.update({ "system.value": actorNewEndurance, "system.description": enduranceReserve.system.description, @@ -3356,52 +3391,55 @@ async function spendResourcesToUse( const actorStun = actor.system.characteristics.stun.value; const actorEndurance = actor.system.characteristics.end.value; const actorNewEndurance = actorEndurance - endToSpend; - const actorChanges = {}; if (stunToSpend > 0) { - resourceUsageDescription = ` - - ${endToSpend} END and ${stunToSpend} STUN - - - `; - - const stunRenderedResult = await stunToSpendRoller.render(); - resourceUsageDescription += stunRenderedResult; - - await ui.notifications.warn(`${actor.name} used ${stunToSpend} STUN for ENDURANCE.`); - - // NOTE: Can have a negative END for reasons other than spending END (e.g. drains), however, spend END on - // an attack can't lower it beyond its starting value or 0 (whichever is smaller). - actorChanges["system.characteristics.stun.value"] = actorStun - stunToSpend; + resourcesUsedDescription = ` + + ${endToSpend} END and ${stunToSpend} STUN + + + `; + + resourcesUsedDescriptionRenderedRoll = await stunToSpendRoller.render(); + + if (canSpendStun) { + await ui.notifications.warn(`${actor.name} used ${stunToSpend} STUN for ENDURANCE.`); + + // NOTE: Can have a negative END for reasons other than spending END (e.g. drains), however, spend END on + // an attack can't lower it beyond its starting value or 0 (whichever is smaller). + actorChanges["system.characteristics.stun.value"] = actorStun - stunToSpend; + } } else { - resourceUsageDescription = `${endToSpend} END`; + resourcesUsedDescription = `${endToSpend} END`; } - actorChanges["system.characteristics.end.value"] = actorNewEndurance; + if (canSpendEndurance) { + actorChanges["system.characteristics.end.value"] = actorNewEndurance; - if (!noResourceUse && !noEnduranceUse) { await actor.update(actorChanges); } } // Spend charges if (chargesToSpend > 0) { - resourceUsageDescription = `${resourceUsageDescription}${ - resourceUsageDescription ? " and " : "" + resourcesUsedDescription = `${resourcesUsedDescription}${ + resourcesUsedDescription ? " and " : "" }${chargesToSpend} charge${chargesToSpend > 1 ? "s" : ""}`; - if (!noResourceUse) { + if (canSpendCharges) { const startingCharges = parseInt(item.system.charges?.value || 0); await item.update({ "system.charges.value": startingCharges - chargesToSpend }); } } - return resourceUsageDescription; + return { + resourcesUsedDescription, + resourcesUsedDescriptionRenderedRoll, + }; } diff --git a/module/item/item.mjs b/module/item/item.mjs index 65629342..ba57262f 100644 --- a/module/item/item.mjs +++ b/module/item/item.mjs @@ -469,8 +469,9 @@ export class HeroSystem6eItem extends Item { error: resourceError, warning: resourceWarning, resourcesUsedDescription, + resourcesUsedDescriptionRenderedRoll, } = await userInteractiveVerifyOptionallyPromptThenSpendResources(item, { - userForceOverride: !!event?.shiftKey, + noResourceUse: !!event?.shiftKey, }); if (resourceError) { return ui.notifications.error(resourceError); @@ -485,7 +486,7 @@ export class HeroSystem6eItem extends Item { const chatData = { user: game.user._id, type: CONST.CHAT_MESSAGE_TYPES.OTHER, - content: `${resourcesUsedDescription} to attempt to activate ${item.name} but attempt failed`, + content: `${resourcesUsedDescription ? `Spent ${resourcesUsedDescription} to attempt` : "Attempted"} to activate ${item.name} but attempt failed${resourcesUsedDescriptionRenderedRoll}`, whisper: whisperUserTargetsForActor(item.actor), speaker, }; @@ -499,13 +500,13 @@ export class HeroSystem6eItem extends Item { const chatData = { user: game.user._id, type: CONST.CHAT_MESSAGE_TYPES.OTHER, - content: `${resourcesUsedDescription} to activate ${item.name}`, + content: `${resourcesUsedDescription ? `Spent ${resourcesUsedDescription} to activate` : "Activated "} ${item.name}${resourcesUsedDescriptionRenderedRoll}`, whisper: whisperUserTargetsForActor(item.actor), speaker, }; await ChatMessage.create(chatData); - // A continuing charge's use is tracked by an active effect. Start it. + // A continuing charges use is tracked by an active effect. Start it. await _startIfIsAContinuingCharge(this); // Invisibility status effect for SIGHTGROUP? @@ -1443,8 +1444,10 @@ export class HeroSystem6eItem extends Item { this.system.charges = { ...this.system.charges, max: parseInt(CHARGES.OPTION_ALIAS), - recoverable: (CHARGES.ADDER || []).find((o) => o.XMLID == "RECOVERABLE") ? true : false, - continuing: (CHARGES.ADDER || []).find((o) => o.XMLID == "CONTINUING")?.OPTIONID, + recoverable: !!(CHARGES.ADDER || []).find((o) => o.XMLID === "RECOVERABLE"), + continuing: (CHARGES.ADDER || []).find((o) => o.XMLID === "CONTINUING")?.OPTIONID, + boostable: !!(CHARGES.ADDER || []).find((o) => o.XMLID === "BOOSTABLE"), + fuel: !!(CHARGES.ADDER || []).find((o) => o.XMLID === "FUEL"), }; if (this.system.charges?.value === undefined || this.system.charges?.value === null) { console.log("Invalid charges. Resetting to max", this); @@ -2283,6 +2286,15 @@ export class HeroSystem6eItem extends Item { if (this.system.XMLID === "WEAPONSMITH" && adderCost > 0) { adder.BASECOST_total = 1; } + // else if (this.system.XMLID === "ENTANGLE") { + // // 5E is just a straight 5 points per adder level, but + // // 6E costs 3 points for the first LEVEL and 2 points for the next and so on (typical cost of resistent defense). + // if (adder.XMLID === "ADDITIONALED" || adder.XMLID === "ADDITIONALPD") { + // const groupsOfTwo = Math.floor(adderLevels / 2); + // const cost = 5 * groupsOfTwo + 3 * (adderLevels - 2 * groupsOfTwo); + // adder.BASECOST_total = cost; + // } + // } } else { adder.BASECOST_total = 0; } diff --git a/module/item/skill.mjs b/module/item/skill.mjs index e64e4896..07ae0480 100644 --- a/module/item/skill.mjs +++ b/module/item/skill.mjs @@ -89,8 +89,8 @@ async function skillRoll(item, actor, target, event) { const { error: resourceError, warning: resourceWarning, - resourcesRequired, resourcesUsedDescription, + resourcesUsedDescriptionRenderedRoll, } = await userInteractiveVerifyOptionallyPromptThenSpendResources(item, { noResourceUse: event.shiftKey }); if (resourceError || resourceWarning) { const chatData = { @@ -167,8 +167,9 @@ async function skillRoll(item, actor, target, event) { }), rolls: skillRoller.rawRolls(), renderedRoll: rollHtml, - resourcesUsedDescription: - resourcesRequired.charges > 0 || resourcesRequired.end > 0 ? resourcesUsedDescription : undefined, + resourcesUsedDescription: resourcesUsedDescription + ? `Spent ${resourcesUsedDescription}${resourcesUsedDescriptionRenderedRoll}` + : "", user: game.user._id, speaker: speaker, }; diff --git a/templates/chat/item-toHit-block-card.hbs b/templates/chat/item-toHit-block-card.hbs index faf5af9a..c1f5b470 100644 --- a/templates/chat/item-toHit-block-card.hbs +++ b/templates/chat/item-toHit-block-card.hbs @@ -64,7 +64,7 @@