From df30f56cbaf382f02290042b803c1d8002eb5a76 Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 11:26:42 -0800 Subject: [PATCH 01/13] HANDTOHANDATTACK vs PD --- CHANGELOG.md | 4 +++- module/item/item.mjs | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f667adb..2a58fea9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Releases -## Version 4.0.4 [Hero System 6e (Unofficial) v2](https://github.com/dmdorman/hero6e-foundryvtt) +## Version 4.0.5 (So Far...) [Hero System 6e (Unofficial) v2](https://github.com/dmdorman/hero6e-foundryvtt) + +## Version 4.0.4 - Penalty Skill Levels now default to checked after upload. [#1359](https://github.com/dmdorman/hero6e-foundryvtt/issues/1359) - Mental attacks vs Entangles without Mental Defense now default to targeting actor, not entangle by default. [#1295](https://github.com/dmdorman/hero6e-foundryvtt/issues/1295) diff --git a/module/item/item.mjs b/module/item/item.mjs index bca0e027..b18fbbb3 100644 --- a/module/item/item.mjs +++ b/module/item/item.mjs @@ -4862,7 +4862,11 @@ export class HeroSystem6eItem extends Item { return "KB"; } - console.error(`Unable to determine defense for ${this.name}`); + if (this.system.XMLID === "HANDTOHANDATTACK") { + return "PD"; + } + + console.warn(`Unable to determine defense for ${this.name}`); return "PD"; // Default } From 2f6bec442f8d1a49e19719e7ca4a167ae223bffa Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 12:28:15 -0800 Subject: [PATCH 02/13] OCV/OMCV bonuses are no longer active effects #1285 --- CHANGELOG.md | 3 +++ module/actor/actor-sheet.mjs | 8 ++++---- module/item/item-attack-application.mjs | 2 +- module/item/item.mjs | 2 +- module/utility/attack.mjs | 5 ++++- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a58fea9..7a4a7404 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Version 4.0.5 (So Far...) [Hero System 6e (Unofficial) v2](https://github.com/dmdorman/hero6e-foundryvtt) +- OCV/OMCV bonuses are no longer active effects. They only work for a specific instant attack. (#1285)(https://github.com/dmdorman/hero6e-foundryvtt/issues/1285) +- Fixed rare issue where adding 6e powers to a 5e actor would prevent actor sheet from opening. + ## Version 4.0.4 - Penalty Skill Levels now default to checked after upload. [#1359](https://github.com/dmdorman/hero6e-foundryvtt/issues/1359) diff --git a/module/actor/actor-sheet.mjs b/module/actor/actor-sheet.mjs index 9d51b45b..c7747e2a 100644 --- a/module/actor/actor-sheet.mjs +++ b/module/actor/actor-sheet.mjs @@ -262,7 +262,7 @@ export class HeroSystemActorSheet extends ActorSheet { const pdContentsAttack = ` - `; + `; const pdAttack = await new HeroSystem6eItem( HeroSystem6eItem.itemDataFromXml(pdContentsAttack, defenseCalculationActor), { temporary: true, parent: defenseCalculationActor }, @@ -312,7 +312,7 @@ export class HeroSystemActorSheet extends ActorSheet { const edContentsAttack = ` - `; + `; const edAttack = await new HeroSystem6eItem( HeroSystem6eItem.itemDataFromXml(edContentsAttack, defenseCalculationActor), { temporary: true, parent: defenseCalculationActor }, @@ -363,7 +363,7 @@ export class HeroSystemActorSheet extends ActorSheet { - `; + `; const mdAttack = await new HeroSystem6eItem( HeroSystem6eItem.itemDataFromXml(mdContentsAttack, defenseCalculationActor), { temporary: true, parent: defenseCalculationActor }, @@ -411,7 +411,7 @@ export class HeroSystemActorSheet extends ActorSheet { - `; + `; const drainAttack = await new HeroSystem6eItem( HeroSystem6eItem.itemDataFromXml(drainContentsAttack, defenseCalculationActor), { temporary: true, parent: defenseCalculationActor }, diff --git a/module/item/item-attack-application.mjs b/module/item/item-attack-application.mjs index 4fc6b381..856d4144 100644 --- a/module/item/item-attack-application.mjs +++ b/module/item/item-attack-application.mjs @@ -138,7 +138,7 @@ export class ItemAttackFormApplication extends FormApplication { data.targetEntangle = data.entangleExists; // Mental attacks typically bypass entangles - if (item.attackDefenseVs === "MD" && entangles?.[0].flags.entangleDefense.rMD === 0) { + if (item.attackDefenseVs === "MD" && entangles?.[0]?.flags.entangleDefense.rMD === 0) { data.targetEntangle = false; } } diff --git a/module/item/item.mjs b/module/item/item.mjs index b18fbbb3..36a09f44 100644 --- a/module/item/item.mjs +++ b/module/item/item.mjs @@ -4876,7 +4876,7 @@ export class HeroSystem6eItem extends Item { // A backpack from MiscEquipment.hdp is a CUSTOMPOWER if (this.system.description.match(/can hold \d+kg/i)) return true; - return this.baseInfo.isContainer; + return this.baseInfo?.isContainer; } get killing() { diff --git a/module/utility/attack.mjs b/module/utility/attack.mjs index 7ae05b0a..c0cb79fd 100644 --- a/module/utility/attack.mjs +++ b/module/utility/attack.mjs @@ -8,7 +8,10 @@ export class Attack { const actor = action.system.actor; Attack.removeActionActiveEffects(actor); cvModifiers.forEach((cvModifier) => { - Attack.makeActionActiveEffect(action, cvModifier); + // Do not create an AE for OCV as it only works for an instant, no need to keep track of it. + if (!cvModifier.cvMod.ocv) { + Attack.makeActionActiveEffect(action, cvModifier); + } }); } // discontinue any effects for the action From 9d1a37ca664bad49466dbe458885b91e650beb07 Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 12:28:59 -0800 Subject: [PATCH 03/13] wordsmithing --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a4a7404..4871e6cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Version 4.0.5 (So Far...) [Hero System 6e (Unofficial) v2](https://github.com/dmdorman/hero6e-foundryvtt) - OCV/OMCV bonuses are no longer active effects. They only work for a specific instant attack. (#1285)(https://github.com/dmdorman/hero6e-foundryvtt/issues/1285) -- Fixed rare issue where adding 6e powers to a 5e actor would prevent actor sheet from opening. +- Fixed rare issue where adding some 6e powers to a 5e actor would prevent actor sheet from opening. ## Version 4.0.4 From ab8b0b0749f05d552c57267489df52d77778dc89 Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 13:28:18 -0800 Subject: [PATCH 04/13] Support for STR Minimum OCV penalty. #384 --- CHANGELOG.md | 1 + module/item/item-attack.mjs | 10 ++++++++++ module/item/item.mjs | 19 +++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4871e6cd..f37b98a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - OCV/OMCV bonuses are no longer active effects. They only work for a specific instant attack. (#1285)(https://github.com/dmdorman/hero6e-foundryvtt/issues/1285) - Fixed rare issue where adding some 6e powers to a 5e actor would prevent actor sheet from opening. +- Support for STR Minimum OCV penalty. (#384)(https://github.com/dmdorman/hero6e-foundryvtt/issues/384) ## Version 4.0.4 diff --git a/module/item/item-attack.mjs b/module/item/item-attack.mjs index 2f0c7dd3..d5683901 100644 --- a/module/item/item-attack.mjs +++ b/module/item/item-attack.mjs @@ -558,6 +558,16 @@ export async function AttackToHit(item, options) { dcv -= 4; } + // STRMINIMUM + const STRMINIMUM = item.findModsByXmlid("STRMINIMUM"); + if (STRMINIMUM) { + const strMinimumValue = parseInt(STRMINIMUM.OPTION_ALIAS.match(/\d+/)?.[0] || 0); + const extraStr = Math.max(0, parseInt(actor.system.characteristics.str.value)) - strMinimumValue; + if (extraStr < 0) { + heroRoller.addNumber(Math.floor(extraStr / 5), STRMINIMUM.ALIAS); + } + } + cvModifiers.forEach((cvModifier) => { if (cvModifier.cvMod.ocv) { heroRoller.addNumber(cvModifier.cvMod.ocv, cvModifier.name); diff --git a/module/item/item.mjs b/module/item/item.mjs index 36a09f44..a6c148fe 100644 --- a/module/item/item.mjs +++ b/module/item/item.mjs @@ -1412,6 +1412,25 @@ export class HeroSystem6eItem extends Item { item.flags.tags.dcv += `-5 Haymaker`; } + // STRMINIMUM + const STRMINIMUM = item.findModsByXmlid("STRMINIMUM"); + if (STRMINIMUM) { + const strMinimumValue = parseInt(STRMINIMUM.OPTION_ALIAS.match(/\d+/)?.[0] || 0); + const extraStr = + Math.max(0, parseInt(item.actor?.system.characteristics.str.value || 0)) - strMinimumValue; + if (extraStr < 0) { + const adjustment = Math.floor(extraStr / 5); + item.system.ocvEstimated = `${parseInt(item.system.ocvEstimated) + adjustment}`; + + if (item.flags.tags.ocv) { + item.flags.tags.ocv += "\n"; + } else { + item.flags.tags.ocv = ""; + } + item.flags.tags.ocv += `${adjustment.signedString()} ${STRMINIMUM.ALIAS}`; + } + } + item.system.phase = item.system.PHASE; } From 68bfc7c182990bc367e866a6ca67d01b7a8843ca Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 14:22:32 -0800 Subject: [PATCH 05/13] Allow for STR0 attacks. Check for resources earlier in AttackToHit --- module/item/item-attack-application.mjs | 1 + module/item/item-attack.mjs | 34 ++++++++++---------- templates/attack/item-attack-application.hbs | 2 +- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/module/item/item-attack-application.mjs b/module/item/item-attack-application.mjs index 856d4144..ce53f5b3 100644 --- a/module/item/item-attack-application.mjs +++ b/module/item/item-attack-application.mjs @@ -109,6 +109,7 @@ export class ItemAttackFormApplication extends FormApplication { data.omcvMod ??= parseInt(item.system.ocv); //TODO: May need to make a distinction between OCV/OMCV data.dmcvMod ??= parseInt(item.system.dcv); data.effectiveStr ??= parseInt(data.str); + data.effectiveStr = Math.max(0, data.effectiveStr); data.effectiveLevels ??= parseInt(data.item.system.LEVELS); // Penalty Skill Levels diff --git a/module/item/item-attack.mjs b/module/item/item-attack.mjs index d5683901..fec99cfb 100644 --- a/module/item/item-attack.mjs +++ b/module/item/item-attack.mjs @@ -373,6 +373,23 @@ export async function AttackToHit(item, options) { return ui.notifications.error(`Attack details are no longer available.`); } + // Make sure there are enough resources and consume them + const { + error: resourceError, + warning: resourceWarning, + resourcesRequired, + resourcesUsedDescription, + resourcesUsedDescriptionRenderedRoll, + } = await userInteractiveVerifyOptionallyPromptThenSpendResources(item, { + ...options, + ...{ noResourceUse: false }, + }); + if (resourceError) { + return ui.notifications.error(resourceError); + } else if (resourceWarning) { + return ui.notifications.warn(resourceWarning); + } + const action = Attack.getActionInfo(item, Array.from(game.user.targets), options); item = action.system.item[action.current.itemId]; const targets = action.system.currentTargets; @@ -612,23 +629,6 @@ export async function AttackToHit(item, options) { // (so we can be sneaky and not tell the target's DCV out loud). heroRoller.addDice(-3); - // Make sure there are enough resources and consume them - const { - error: resourceError, - warning: resourceWarning, - resourcesRequired, - resourcesUsedDescription, - resourcesUsedDescriptionRenderedRoll, - } = await userInteractiveVerifyOptionallyPromptThenSpendResources(item, { - ...options, - ...{ noResourceUse: false }, - }); - if (resourceError) { - return ui.notifications.error(resourceError); - } else if (resourceWarning) { - return ui.notifications.warn(resourceWarning); - } - const aoeModifier = item.getAoeModifier(); const aoeTemplate = game.scenes.current.templates.find((template) => template.flags.itemId === item.id) || diff --git a/templates/attack/item-attack-application.hbs b/templates/attack/item-attack-application.hbs index 192b5387..669abb5b 100644 --- a/templates/attack/item-attack-application.hbs +++ b/templates/attack/item-attack-application.hbs @@ -222,7 +222,7 @@
{{!-- --}} - {{numberInput effectiveStr name="effectiveStr" step=1 min=1 max=999}} + {{numberInput effectiveStr name="effectiveStr" step=1 min=0 max=999}}
From 55aa47f61e4537c2146e0da0c3db97186e12bfe8 Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 15:39:00 -0800 Subject: [PATCH 06/13] upport for STR 0 rolls including DCV and movement penalties. #1401 --- CHANGELOG.md | 5 +- module/actor/actor.mjs | 89 +++++++++++++++++++++++++ module/item/item-attack-application.mjs | 1 - module/item/item-attack.mjs | 23 ++++++- module/item/item.mjs | 54 +++++++++++++-- 5 files changed, 163 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f37b98a7..9b9d5715 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,10 @@ ## Version 4.0.5 (So Far...) [Hero System 6e (Unofficial) v2](https://github.com/dmdorman/hero6e-foundryvtt) -- OCV/OMCV bonuses are no longer active effects. They only work for a specific instant attack. (#1285)(https://github.com/dmdorman/hero6e-foundryvtt/issues/1285) +- OCV/OMCV bonuses are no longer active effects. They only work for a specific instant attack. [#1285](https://github.com/dmdorman/hero6e-foundryvtt/issues/1285) - Fixed rare issue where adding some 6e powers to a 5e actor would prevent actor sheet from opening. -- Support for STR Minimum OCV penalty. (#384)(https://github.com/dmdorman/hero6e-foundryvtt/issues/384) +- Support for STR Minimum OCV penalty. [#384](https://github.com/dmdorman/hero6e-foundryvtt/issues/384) +- Support for STR 0 rolls including DCV and movement penalties. [#1401](https://github.com/dmdorman/hero6e-foundryvtt/issues/1401) ## Version 4.0.4 diff --git a/module/actor/actor.mjs b/module/actor/actor.mjs index 02f86e31..449901b7 100644 --- a/module/actor/actor.mjs +++ b/module/actor/actor.mjs @@ -886,6 +886,13 @@ export class HeroSystem6eActor extends Actor { } await this.createEmbeddedDocuments("ActiveEffect", [activeEffect]); + + // If we have control of this token, re-acquire to update movement types + const myToken = this.getActiveTokens()?.[0]; + if (canvas.tokens.controlled.find((t) => t.id == myToken.id)) { + myToken.release(); + myToken.control(); + } return; } @@ -894,6 +901,88 @@ export class HeroSystem6eActor extends Actor { } else if (prevActiveEffect && prevActiveEffect.name != name) { await prevActiveEffect.update({ name: name }); } + + // At STR 0, halve the character’s Running, + // Leaping, Swimming, Swinging, Tunneling, and + // Flight based on muscle power (such as most types + // of wings). The GM may require the character to + // succeed with STR Rolls just to stand up, walk, and + // perform similar mundane exertions. + // At STR 0, halve the character’s DCV. + // For every 2x mass a character has above the + // standard human mass of 100 kg, the effects of STR + // 0 on movement and DCV occur 5 points of STR + // sooner. + const massMultiplier = this.items + .filter((o) => o.system.XMLID === "DENSITYINCREASE" && o.system.active) + .reduce((p, a) => p + parseInt(a.system.LEVELS), 0); + const minStr = massMultiplier * 5; + + const prevStr0ActiveEffect = this.effects.find((o) => o.flags?.str0); + if (this.system.characteristics.str.value <= minStr && !prevStr0ActiveEffect) { + const str0ActiveEffect = { + name: "STR0", + id: "STR0", + icon: `systems/${HEROSYS.module}/icons/encumbered.svg`, + changes: [ + { + key: "system.characteristics.dcv.value", + value: 0.5, + mode: CONST.ACTIVE_EFFECT_MODES.MULTIPLY, + }, + { + key: "system.characteristics.running.value", + value: 0.5, + mode: CONST.ACTIVE_EFFECT_MODES.MULTIPLY, + }, + { + key: "system.characteristics.leaping.value", + value: 0.5, + mode: CONST.ACTIVE_EFFECT_MODES.MULTIPLY, + }, + { + key: "system.characteristics.swimming.value", + value: 0.5, + mode: CONST.ACTIVE_EFFECT_MODES.MULTIPLY, + }, + { + key: "system.characteristics.swinging.value", + value: 0.5, + mode: CONST.ACTIVE_EFFECT_MODES.MULTIPLY, + }, + { + key: "system.characteristics.tunneling.value", + value: 0.5, + mode: CONST.ACTIVE_EFFECT_MODES.MULTIPLY, + }, + ], + origin: this.uuid, + // duration: { + // seconds: 3.154e7 * 100, // 100 years should be close to infinity + // }, + flags: { + str0: true, + }, + }; + + await this.createEmbeddedDocuments("ActiveEffect", [str0ActiveEffect]); + // If we have control of this token, re-acquire to update movement types + const myToken = this.getActiveTokens()?.[0]; + if (canvas.tokens.controlled.find((t) => t.id == myToken.id)) { + myToken.release(); + myToken.control(); + } + } else { + if (prevStr0ActiveEffect && this.system.characteristics.str.value > minStr) { + await prevStr0ActiveEffect.delete(); + // If we have control of this token, re-acquire to update movement types + const myToken = this.getActiveTokens()?.[0]; + if (canvas.tokens.controlled.find((t) => t.id == myToken.id)) { + myToken.release(); + myToken.control(); + } + } + } } async FullHealth() { diff --git a/module/item/item-attack-application.mjs b/module/item/item-attack-application.mjs index ce53f5b3..04d8d8f4 100644 --- a/module/item/item-attack-application.mjs +++ b/module/item/item-attack-application.mjs @@ -330,7 +330,6 @@ export class ItemAttackFormApplication extends FormApplication { } if (updates) { const actualUpdates = await this.data.actor.updateEmbeddedDocuments("Item", updates); - console.log(actualUpdates); } // Take all the data we updated in the form and apply it. diff --git a/module/item/item-attack.mjs b/module/item/item-attack.mjs index fec99cfb..d5bed0c8 100644 --- a/module/item/item-attack.mjs +++ b/module/item/item-attack.mjs @@ -11,7 +11,7 @@ import { } from "../utility/damage.mjs"; import { performAdjustment, renderAdjustmentChatCards } from "../utility/adjustment.mjs"; import { getRoundedDownDistanceInSystemUnits, getSystemDisplayUnits } from "../utility/units.mjs"; -import { HeroSystem6eItem, RequiresASkillRollCheck } from "../item/item.mjs"; +import { HeroSystem6eItem, RequiresASkillRollCheck, RequiresACharacteristicRollCheck } from "../item/item.mjs"; import { ItemAttackFormApplication } from "../item/item-attack-application.mjs"; import { DICE_SO_NICE_CUSTOM_SETS, HeroRoller } from "../utility/dice.mjs"; import { clamp } from "../utility/compatibility.mjs"; @@ -397,6 +397,25 @@ export async function AttackToHit(item, options) { const actor = item.actor; let effectiveItem = item; + // STR 0 character must succeed with + // a STR Roll in order to perform any Action that uses STR, such + // as aiming an attack, pulling a trigger, or using a Power with the + // Gestures Limitation. + if (actor && (effectiveItem.system.usesStrength || effectiveItem.findModsByXmlid("GESTURES"))) { + if (parseInt(actor.system.characteristics.str.value) <= 0) { + if ( + !(await RequiresACharacteristicRollCheck( + actor, + "str", + `Actions that use STR or GESTURES require STR roll when at 0 STR`, + )) + ) { + await ui.notifications.warn(`${actor.name} failed STR 0 roll. Action with ${item.name} failed.`); + return; + } + } + } + // Create a temporary item based on effectiveLevels if (options?.effectiveLevels && parseInt(item.system.LEVELS) > 0) { options.effectiveLevels = parseInt(options.effectiveLevels) || 0; @@ -815,7 +834,7 @@ export async function AttackToHit(item, options) { } } - if (!(await RequiresASkillRollCheck(item))) { + if (!(await item)) { const speaker = ChatMessage.getSpeaker({ actor: item.actor }); speaker["alias"] = item.actor.name; const chatData = { diff --git a/module/item/item.mjs b/module/item/item.mjs index a6c148fe..5dd24f9e 100644 --- a/module/item/item.mjs +++ b/module/item/item.mjs @@ -621,6 +621,11 @@ export class HeroSystem6eItem extends Item { break; } + // DENSITYINCREASE can affect Encumbrance & Movements + if (this.system.XMLID === "DENSITYINCREASE") { + await this.actor.applyEncumbrancePenalty(); + } + // Charges expire // if (charges) { // // Find the active effect @@ -4953,7 +4958,47 @@ export function getItem(id) { return null; } -export async function RequiresASkillRollCheck(item, event) { +export async function RequiresACharacteristicRollCheck(actor, characteristic, reasonText) { + console.log(characteristic, this); + const successValue = parseInt(actor?.system.characteristics[characteristic.toLowerCase()].roll) || 8; + const activationRoller = new HeroRoller().makeSuccessRoll(true, successValue).addDice(3); + await activationRoller.roll(); + let succeeded = activationRoller.getSuccess(); + const autoSuccess = activationRoller.getAutoSuccess(); + const total = activationRoller.getSuccessTotal(); + const margin = successValue - total; + + const flavor = `${reasonText ? `${reasonText}. ` : ``}${characteristic.toUpperCase()} roll ${successValue}- ${ + succeeded ? "succeeded" : "failed" + } by ${autoSuccess === undefined ? `${Math.abs(margin)}` : `rolling ${total}`}`; + let cardHtml = await activationRoller.render(flavor); + + // FORCE success + if (!succeeded && overrideCanAct) { + const overrideKeyText = game.keybindings.get(HEROSYS.module, "OverrideCanAct")?.[0].key; + ui.notifications.info(`${actor.name} succeeded roll because override key.`); + succeeded = true; + cardHtml += `

Succeeded roll because ${game.user.name} used ${overrideKeyText} key to override.

`; + } + + const token = actor.token; + const speaker = ChatMessage.getSpeaker({ actor: actor, token }); + speaker.alias = actor.name; + + const chatData = { + type: CONST.CHAT_MESSAGE_TYPES.ROLL, + rolls: activationRoller.rawRolls(), + user: game.user._id, + content: cardHtml, + speaker: speaker, + }; + + await ChatMessage.create(chatData); + + return succeeded; +} + +export async function RequiresASkillRollCheck(item) { // Toggles don't need a roll to turn off //if (item.system?.active === true) return true; @@ -5067,10 +5112,11 @@ export async function RequiresASkillRollCheck(item, event) { let cardHtml = await activationRoller.render(flavor); // FORCE success - if (!succeeded && event?.ctrlKey) { - ui.notifications.info(`${item.actor.name} succeeded roll because ${game.user.name} used CTRL key.`); + if (!succeeded && overrideCanAct) { + const overrideKeyText = game.keybindings.get(HEROSYS.module, "OverrideCanAct")?.[0].key; + ui.notifications.info(`${item.actor.name} succeeded roll because override key.`); succeeded = true; - cardHtml += `

Succeeded roll because ${game.user.name} used CTRL key.

`; + cardHtml += `

Succeeded roll because ${game.user.name} used ${overrideKeyText} key to override.

`; } const actor = item.actor; From f8d466046bd681a82aed2b396d4b301868f29666 Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 15:55:48 -0800 Subject: [PATCH 07/13] Fixed issue where FLIGHT was impacting KB rolls even when FLIGHT was turned off. #1400 --- CHANGELOG.md | 1 + module/item/item-attack.mjs | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b9d5715..30391ce2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Fixed rare issue where adding some 6e powers to a 5e actor would prevent actor sheet from opening. - Support for STR Minimum OCV penalty. [#384](https://github.com/dmdorman/hero6e-foundryvtt/issues/384) - Support for STR 0 rolls including DCV and movement penalties. [#1401](https://github.com/dmdorman/hero6e-foundryvtt/issues/1401) +- Fixed issue where FLIGHT was impacting KB rolls even when FLIGHT was turned off. [#1400](https://github.com/dmdorman/hero6e-foundryvtt/issues/1400) ## Version 4.0.4 diff --git a/module/item/item-attack.mjs b/module/item/item-attack.mjs index d5bed0c8..b1451137 100644 --- a/module/item/item-attack.mjs +++ b/module/item/item-attack.mjs @@ -3199,13 +3199,23 @@ async function _calcKnockback(body, item, options, knockbackMultiplier) { // Target is in the air -1d6 // TODO: This is perhaps not the right check as they could just have the movement radio on. Consider a flying status // when more than 0m off the ground? This same effect should also be considered for gliding. - if (options.targetToken?.actor?.flags?.activeMovement === "flight") { - knockbackDice -= 1; - knockbackTags.push({ - value: "-1d6KB", - name: "target is in the air", - title: "Knockback Modifier", - }); + const activeMovement = options.targetToken?.actor?.flags?.activeMovement; + if (["flight", "gliding"].includes(activeMovement)) { + // Double check to make sure FLIGHT or GLIDING is still on + if ( + options.targetToken.actor.items.find( + (o) => o.system.XMLID === activeMovement.toUpperCase() && o.system.active, + ) + ) { + knockbackDice -= 1; + knockbackTags.push({ + value: "-1d6KB", + name: "target is in the air", + title: `Knockback Modifier ${options.targetToken?.actor?.flags?.activeMovement}`, + }); + } else { + console.warn(`${activeMovement} selected but that power is not active.`); + } } // TODO: Target Rolled With A Punch -1d6 From e0d0f60336536b70e06d0bb65412624408e2aedf Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 16:00:04 -0800 Subject: [PATCH 08/13] Fixed issue where full END for an attack was used even when lowering Effective Strength. #1399 --- CHANGELOG.md | 1 + module/item/item-attack.mjs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30391ce2..6d3f2df6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Support for STR Minimum OCV penalty. [#384](https://github.com/dmdorman/hero6e-foundryvtt/issues/384) - Support for STR 0 rolls including DCV and movement penalties. [#1401](https://github.com/dmdorman/hero6e-foundryvtt/issues/1401) - Fixed issue where FLIGHT was impacting KB rolls even when FLIGHT was turned off. [#1400](https://github.com/dmdorman/hero6e-foundryvtt/issues/1400) +- Fixed issue where full END for an attack was used even when lowering Effective Strength. [#1399](https://github.com/dmdorman/hero6e-foundryvtt/issues/1399) ## Version 4.0.4 diff --git a/module/item/item-attack.mjs b/module/item/item-attack.mjs index b1451137..1b310580 100644 --- a/module/item/item-attack.mjs +++ b/module/item/item-attack.mjs @@ -373,6 +373,13 @@ export async function AttackToHit(item, options) { return ui.notifications.error(`Attack details are no longer available.`); } + const action = Attack.getActionInfo(item, Array.from(game.user.targets), options); + item = action.system.item[action.current.itemId]; + const targets = action.system.currentTargets; + + const actor = item.actor; + let effectiveItem = item; + // Make sure there are enough resources and consume them const { error: resourceError, @@ -380,7 +387,7 @@ export async function AttackToHit(item, options) { resourcesRequired, resourcesUsedDescription, resourcesUsedDescriptionRenderedRoll, - } = await userInteractiveVerifyOptionallyPromptThenSpendResources(item, { + } = await userInteractiveVerifyOptionallyPromptThenSpendResources(effectiveItem, { ...options, ...{ noResourceUse: false }, }); @@ -390,13 +397,6 @@ export async function AttackToHit(item, options) { return ui.notifications.warn(resourceWarning); } - const action = Attack.getActionInfo(item, Array.from(game.user.targets), options); - item = action.system.item[action.current.itemId]; - const targets = action.system.currentTargets; - - const actor = item.actor; - let effectiveItem = item; - // STR 0 character must succeed with // a STR Roll in order to perform any Action that uses STR, such // as aiming an attack, pulling a trigger, or using a Power with the From ad56e627d8c745ad1b6a850253b2407c4a182ec3 Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 16:00:40 -0800 Subject: [PATCH 09/13] lint --- module/item/item-attack-application.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/item/item-attack-application.mjs b/module/item/item-attack-application.mjs index 04d8d8f4..58836f72 100644 --- a/module/item/item-attack-application.mjs +++ b/module/item/item-attack-application.mjs @@ -329,7 +329,7 @@ export class ItemAttackFormApplication extends FormApplication { } } if (updates) { - const actualUpdates = await this.data.actor.updateEmbeddedDocuments("Item", updates); + await this.data.actor.updateEmbeddedDocuments("Item", updates); } // Take all the data we updated in the form and apply it. From bf0fddb69795b8c2929bcb467b45824032134561 Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 16:09:36 -0800 Subject: [PATCH 10/13] Support for PRE 0 rolls. #1403 --- CHANGELOG.md | 1 + module/item/item-attack.mjs | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d3f2df6..dc130924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Fixed rare issue where adding some 6e powers to a 5e actor would prevent actor sheet from opening. - Support for STR Minimum OCV penalty. [#384](https://github.com/dmdorman/hero6e-foundryvtt/issues/384) - Support for STR 0 rolls including DCV and movement penalties. [#1401](https://github.com/dmdorman/hero6e-foundryvtt/issues/1401) +- Support for PRE 0 rolls. [#1403](https://github.com/dmdorman/hero6e-foundryvtt/issues/1403) - Fixed issue where FLIGHT was impacting KB rolls even when FLIGHT was turned off. [#1400](https://github.com/dmdorman/hero6e-foundryvtt/issues/1400) - Fixed issue where full END for an attack was used even when lowering Effective Strength. [#1399](https://github.com/dmdorman/hero6e-foundryvtt/issues/1399) diff --git a/module/item/item-attack.mjs b/module/item/item-attack.mjs index 1b310580..e42846a1 100644 --- a/module/item/item-attack.mjs +++ b/module/item/item-attack.mjs @@ -401,7 +401,12 @@ export async function AttackToHit(item, options) { // a STR Roll in order to perform any Action that uses STR, such // as aiming an attack, pulling a trigger, or using a Power with the // Gestures Limitation. - if (actor && (effectiveItem.system.usesStrength || effectiveItem.findModsByXmlid("GESTURES"))) { + // Not all token types (base) will have STR + if ( + actor && + actor.system.characteristics.str && + (effectiveItem.system.usesStrength || effectiveItem.findModsByXmlid("GESTURES")) + ) { if (parseInt(actor.system.characteristics.str.value) <= 0) { if ( !(await RequiresACharacteristicRollCheck( @@ -416,6 +421,24 @@ export async function AttackToHit(item, options) { } } + // PRE 0 + // At PRE 0, a character must attempt an PRE Roll to take any + // offensive action, or to remain in the face of anything even + // remotely threatening. + // Not all token types (base) will have PRE + if (actor && actor.system.characteristics.pre && parseInt(actor.system.characteristics.pre.value) <= 0) { + if ( + !(await RequiresACharacteristicRollCheck( + actor, + "pre", + `Offensive actions when at PRE 0 requires PRE roll, failure typically results in actor avoiding threats`, + )) + ) { + await ui.notifications.warn(`${actor.name} failed PRE 0 roll. Action with ${item.name} failed.`); + return; + } + } + // Create a temporary item based on effectiveLevels if (options?.effectiveLevels && parseInt(item.system.LEVELS) > 0) { options.effectiveLevels = parseInt(options.effectiveLevels) || 0; From 97d0b773dc2cd8eb0dd96a2c831594b6b668d6d3 Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 16:55:50 -0800 Subject: [PATCH 11/13] AUTOMATION's can't use STUN in place of END. #1398 --- CHANGELOG.md | 1 + module/item/item-attack.mjs | 8 ++++++++ module/item/item.mjs | 5 +++++ 3 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc130924..519281ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Support for PRE 0 rolls. [#1403](https://github.com/dmdorman/hero6e-foundryvtt/issues/1403) - Fixed issue where FLIGHT was impacting KB rolls even when FLIGHT was turned off. [#1400](https://github.com/dmdorman/hero6e-foundryvtt/issues/1400) - Fixed issue where full END for an attack was used even when lowering Effective Strength. [#1399](https://github.com/dmdorman/hero6e-foundryvtt/issues/1399) +- AUTOMATION's can't use STUN in place of END. [#1398](https://github.com/dmdorman/hero6e-foundryvtt/issues/1398) ## Version 4.0.4 diff --git a/module/item/item-attack.mjs b/module/item/item-attack.mjs index e42846a1..7222a4b0 100644 --- a/module/item/item-attack.mjs +++ b/module/item/item-attack.mjs @@ -3365,6 +3365,14 @@ export async function userInteractiveVerifyOptionallyPromptThenSpendResources(it } } else { if (resourcesRequired.end > actorEndurance && useResources) { + // Auotmation + const AUTOMATON = actor.items.find((o) => o.system.XMLID === "AUTOMATON"); + if (AUTOMATON) { + return { + warning: `${item.name} needs ${resourcesRequired.end} END but ${actor.name} only has ${actorEndurance} END. AUTOMATION's cannot use STUN for END.`, + }; + } + // Is the actor willing to use STUN to make up for the lack of END? const potentialStunCost = calculateRequiredStunDiceForLackOfEnd(actor, resourcesRequired.end); diff --git a/module/item/item.mjs b/module/item/item.mjs index 5dd24f9e..4d3c2965 100644 --- a/module/item/item.mjs +++ b/module/item/item.mjs @@ -4873,6 +4873,11 @@ export class HeroSystem6eItem extends Item { return "PD"; } + // STRIKE + if (this.system.EFFECT?.includes("STR")) { + return "PD"; + } + if (this.system.XMLID === "TELEKINESIS") { return "PD"; } From e694e4215eee148fec1881da2d2bc17a18594cc9 Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 17:13:49 -0800 Subject: [PATCH 12/13] More generic check for lack of STUN #1398 --- module/actor/actor-sheet.mjs | 3 ++- module/item/item-attack.mjs | 8 ++++---- module/utility/util.mjs | 8 +++++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/module/actor/actor-sheet.mjs b/module/actor/actor-sheet.mjs index c7747e2a..5798fd92 100644 --- a/module/actor/actor-sheet.mjs +++ b/module/actor/actor-sheet.mjs @@ -567,7 +567,8 @@ export class HeroSystemActorSheet extends ActorSheet { // Not all actor types have END & STUN data.hasEND = powers.find((o) => o.key === "END"); - data.hasSTUN = powers.find((o) => o.key === "STUN"); + data.hasSTUN = + powers.find((o) => o.key === "STUN") && !this.actor.items.find((o) => o.system.XMLID === "AUTOMATON"); // Endurance Reserve data.endReserve = this.actor.items.find((o) => o.system.XMLID === "ENDURANCERESERVE"); diff --git a/module/item/item-attack.mjs b/module/item/item-attack.mjs index 7222a4b0..24476b1d 100644 --- a/module/item/item-attack.mjs +++ b/module/item/item-attack.mjs @@ -3365,11 +3365,11 @@ export async function userInteractiveVerifyOptionallyPromptThenSpendResources(it } } else { if (resourcesRequired.end > actorEndurance && useResources) { - // Auotmation - const AUTOMATON = actor.items.find((o) => o.system.XMLID === "AUTOMATON"); - if (AUTOMATON) { + // Auotmation or other actor without STUN + const hasSTUN = getCharacteristicInfoArrayForActor(actor).find((o) => o.key === "STUN"); + if (!hasSTUN) { return { - warning: `${item.name} needs ${resourcesRequired.end} END but ${actor.name} only has ${actorEndurance} END. AUTOMATION's cannot use STUN for END.`, + warning: `${item.name} needs ${resourcesRequired.end} END but ${actor.name} only has ${actorEndurance} END. This actor cannot use STUN for END.`, }; } diff --git a/module/utility/util.mjs b/module/utility/util.mjs index 18a34932..0d0ea4e1 100644 --- a/module/utility/util.mjs +++ b/module/utility/util.mjs @@ -109,7 +109,13 @@ export function getCharacteristicInfoArrayForActor(actor) { const isCharOrMovePowerForActor = _isNonIgnoredCharacteristicsAndMovementPowerForActor(actor); const powerList = actor?.system?.is5e ? CONFIG.HERO.powers5e : CONFIG.HERO.powers6e; - const powers = powerList.filter(isCharOrMovePowerForActor); + let powers = powerList.filter(isCharOrMovePowerForActor); + const AUTOMATION = actor.items.find((o) => o.system.XMLID === "AUTOMATON"); + if (AUTOMATION && powers.find((o) => o.key === "STUN" || o.key === "EGO" || o.key === "OMCV" || o.key === "DMCV")) { + console.warn("Wrong actor type", actor); + // TODO: change actor type to AUTOMATION or whatever is appropriate? + powers = powers.filter((o) => o.key !== "STUN" && o.key !== "EGO" && o.key !== "OMCV" && o.key !== "DMCV"); + } return powers; } From 70b12de66de6b897aa43c864964253009819bed7 Mon Sep 17 00:00:00 2001 From: aeauseth Date: Fri, 8 Nov 2024 17:14:57 -0800 Subject: [PATCH 13/13] unnecessary AUTOMATION STUN check --- module/actor/actor-sheet.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/module/actor/actor-sheet.mjs b/module/actor/actor-sheet.mjs index 5798fd92..c7747e2a 100644 --- a/module/actor/actor-sheet.mjs +++ b/module/actor/actor-sheet.mjs @@ -567,8 +567,7 @@ export class HeroSystemActorSheet extends ActorSheet { // Not all actor types have END & STUN data.hasEND = powers.find((o) => o.key === "END"); - data.hasSTUN = - powers.find((o) => o.key === "STUN") && !this.actor.items.find((o) => o.system.XMLID === "AUTOMATON"); + data.hasSTUN = powers.find((o) => o.key === "STUN"); // Endurance Reserve data.endReserve = this.actor.items.find((o) => o.system.XMLID === "ENDURANCERESERVE");