From 413f7690e43d7543773f0bff561f436435583022 Mon Sep 17 00:00:00 2001 From: phBalance Date: Sat, 23 Nov 2024 18:39:26 -0700 Subject: [PATCH 1/3] docs(README): update user -> author for v12 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dab11765..b84c0629 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ const cardHtml = await damageRoller.render("Knockback roll"); const chatData = { style: CONST.CHAT_MESSAGE_STYLES.OTHER, rolls: damageRoller.rawRolls(), - user: game.user._id, + author: game.user._id, content: cardHtml, }; From dd56585d36aca41690fa9dabde87fcc854f3ea0b Mon Sep 17 00:00:00 2001 From: phBalance Date: Sat, 23 Nov 2024 18:41:56 -0700 Subject: [PATCH 2/3] feat(interaction skills): Striking appearance now a potential modifier --- module/item/skill.mjs | 82 +++++++++++++++++++++++---- templates/pop-out/item-skill-card.hbs | 73 ++++++++---------------- 2 files changed, 94 insertions(+), 61 deletions(-) diff --git a/module/item/skill.mjs b/module/item/skill.mjs index dbdef364..29a8f9df 100644 --- a/module/item/skill.mjs +++ b/module/item/skill.mjs @@ -3,39 +3,97 @@ import { HeroRoller } from "../utility/dice.mjs"; import { userInteractiveVerifyOptionallyPromptThenSpendResources } from "./item-attack.mjs"; import { overrideCanAct } from "../settings/settings-helpers.mjs"; +export function isAgilitySkill(item) { + return item.system.CHARACTERISTIC === "DEX"; +} + +export function isBackgroundSkill(item) { + switch (item.system.XMLID) { + case "KNOWLEDGE_SKILL": + case "LANGUAGES": + case "PROFESSIONAL_SKILL": + case "SCIENCE_SKILL": + case "TRANSPORT_FAMILIARITY": + return true; + } + + return false; +} + +export function isCombatSkill(item) { + if ( + item.system.XMLID === "AUTOFIRE_SKILLS" || // Autofire Skills + item.system.XMLID === "COMBAT_LEVELS" || // CSLs + item.system.XMLID === "MENTAL_COMBAT_LEVELS" || + item.system.XMLID === "DEFENSE_MANEUVER" || // Defense maneuver + item.system.XMLID === "MANEUVER" || // Martial Arts + item.system.XMLID === "PENALTY_SKILL_LEVELS" || // PSLs + item.system.XMLID === "RAPID_ATTACK_HTH" || // Rapid Attack + item.system.XMLID === "RAPID_ATTACK_RANGED" || + item.system.XMLID === "TWO_WEAPON_FIGHTING_HTH" || // Two Weapon Fighting + item.system.XMLID === "TWO_WEAPON_FIGHTING_RANGED" || + item.system.XMLID === "WEAPON_FAMILIARITY" // Weapon Familiarity + ) { + return true; + } + + return false; +} + +export function isIntellectSkill(item) { + return item.system.CHARACTERISTIC === "INT"; +} + +export function isInteractionSkill(item) { + return item.system.CHARACTERISTIC === "PRE"; +} + async function _renderSkillForm(item, actor, stateData) { const token = actor.token; // Skill Levels (in most cases it will apply so check it) - let skillLevels = Array.from(actor.items.filter((o) => o.system.XMLID === "SKILL_LEVELS")); - for (let s of skillLevels) { - s.system.checked = false; - s.system.active = true; + const skillLevels = actor.items.filter((o) => o.system.XMLID === "SKILL_LEVELS"); + for (const skill of skillLevels) { + skill.system.checked = false; + skill.system.active = true; // OPTION_ALIAS has name of skill - if (s.system.OPTION_ALIAS.toUpperCase().indexOf(item.name.toUpperCase()) > -1) { - s.system.checked = true; + if (skill.system.OPTION_ALIAS.toUpperCase().indexOf(item.name.toUpperCase()) > -1) { + skill.system.checked = true; } // OPTION_ALIAS has XMLID of skill - if (s.system.OPTION_ALIAS.toUpperCase().indexOf(item.system.XMLID) > -1) { - s.system.checked = true; + if (skill.system.OPTION_ALIAS.toUpperCase().indexOf(item.system.XMLID) > -1) { + skill.system.checked = true; } // CHARACTERISTIC match - if (s.name.toUpperCase().indexOf(item.system.CHARACTERISTIC) > -1) { - s.system.checked = true; + if (skill.name.toUpperCase().indexOf(item.system.CHARACTERISTIC) > -1) { + skill.system.checked = true; } // INTERACTION match (really PRE match) - if (s.name.toUpperCase().indexOf("INTERACTION") > -1 && item.system.CHARACTERISTIC === "PRE") { - s.system.checked = true; + if (skill.name.toUpperCase().indexOf("INTERACTION") > -1 && item.system.CHARACTERISTIC === "PRE") { + skill.system.checked = true; + } + } + + // Certain skills/talents/powers may help or hinder this roll + const helperItems = []; + if (isInteractionSkill(item)) { + // Striking appearance may help + const strikingAppearance = actor.items.filter((item) => item.system.XMLID === "STRIKING_APPEARANCE"); + for (const saTalent of strikingAppearance) { + saTalent.system.checked = false; + saTalent.system.active = true; + helperItems.push(saTalent); } } // Enhanced Perception + Skill Levels const skillMods = [ ...skillLevels, + ...helperItems, ...(item.system.XMLID === "PERCEPTION" ? actor.items.filter((o) => o.system.XMLID === "ENHANCEDPERCEPTION") : []), diff --git a/templates/pop-out/item-skill-card.hbs b/templates/pop-out/item-skill-card.hbs index 9a0199bd..f4013137 100644 --- a/templates/pop-out/item-skill-card.hbs +++ b/templates/pop-out/item-skill-card.hbs @@ -8,28 +8,8 @@ - {{!-- {{#if skillLevels}} -
-
-

Verify the following Skill Levels apply to this skill.

-
- - {{#each skillLevels}} - - - - - {{/each}} -
- - - {{this.name}}: {{this.system.description}} -
-
- {{/if}} --}} -
- +
@@ -37,34 +17,29 @@
- {{#each skillMods}} - {{#if this.system.active}} - - - - - {{else}} - - - - - {{/if}} - {{/each}} + {{#each skillMods}} + {{#if this.system.active}} + + + + + {{else}} + + + + + {{/if}} + {{/each}}
- - - {{this.system.description}} -
- - - {{this.system.description}} -
+ + + {{this.system.description}} +
+ + + {{this.system.description}} +
-
+ {{/if}} - -
-
-

(- is harder, + is easier)

-
\ No newline at end of file From 292593b4b27e139ac7b3b714632dabee6f80fa38 Mon Sep 17 00:00:00 2001 From: phBalance Date: Sat, 23 Nov 2024 19:45:43 -0700 Subject: [PATCH 3/3] feat(presence): allow striking appearance to affect presence attacks --- CHANGELOG.md | 3 +- FEATURES.md | 4 +- module/item/skill.mjs | 1 - module/utility/presence-attack.mjs | 55 +++++++++++++++---- .../chat/presence-attack-result-card.hbs | 14 +++++ templates/pop-out/item-skill-card.hbs | 2 +- templates/pop-out/presence-attack-card.hbs | 30 ++++++++++ 7 files changed, 93 insertions(+), 16 deletions(-) create mode 100644 templates/chat/presence-attack-result-card.hbs diff --git a/CHANGELOG.md b/CHANGELOG.md index 61cf4cf4..24f456e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # Releases -## Version 4.0.8 [Hero System 6e (Unofficial) v2](https://github.com/dmdorman/hero6e-foundryvtt) +## Version 4.0.8 (So far...) [Hero System 6e (Unofficial) v2](https://github.com/dmdorman/hero6e-foundryvtt) +- Support striking appearance. Can be optionally enabled for presence and interaction skill rolls. [#1509](https://github.com/dmdorman/hero6e-foundryvtt/issues/1509) - Improved DEADLYBLOW so it does not apply to adjustment powers, sense-affecting powers, or ENTANGLES. GM still has to confirm DEADLYBLOW with applicable powers. [#1493](https://github.com/dmdorman/hero6e-foundryvtt/issues/1493) ## Version 4.0.7 diff --git a/FEATURES.md b/FEATURES.md index f998bbc1..a3ba9b37 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -116,14 +116,14 @@ Our support ratings: | EIDETIC MEMORY | D | | | ENVIRONMENTAL MOVEMENT | D | | | LIGHTNING CALCULATOR | D | | -| LIGHTNING REFLEXES | B | A second token in the combat tracker is shown, but up to player/gm to determine when to use | +| LIGHTNING REFLEXES | B | A second token in the combat tracker is shown, but up to player/GM to determine when to use | | LIGHTSLEEP | D | | | OFF-HAND DEFENSE | D | | | PERFECT PITCH | D | | | RESISTANCE | D | | | SIMULATE DEATH | D | | | SPEED READING | D | | -| STRIKING APPEARANCE | D | | +| STRIKING APPEARANCE | B | Up to player/GM to determine when to use | | UNIVERSAL TRANSLATOR | D | | | WEAPONMASTER | B | | diff --git a/module/item/skill.mjs b/module/item/skill.mjs index 29a8f9df..9e956db8 100644 --- a/module/item/skill.mjs +++ b/module/item/skill.mjs @@ -90,7 +90,6 @@ async function _renderSkillForm(item, actor, stateData) { } } - // Enhanced Perception + Skill Levels const skillMods = [ ...skillLevels, ...helperItems, diff --git a/module/utility/presence-attack.mjs b/module/utility/presence-attack.mjs index 22562a70..796ae9ce 100644 --- a/module/utility/presence-attack.mjs +++ b/module/utility/presence-attack.mjs @@ -4,41 +4,76 @@ import { HeroRoller } from "./dice.mjs"; async function _renderForm(actor, stateData) { const token = actor.token; + // Certain skills/talents/powers may help or hinder this roll + const attackMods = []; + // Striking appearance may help + const strikingAppearance = actor.items.filter((item) => item.system.XMLID === "STRIKING_APPEARANCE"); + for (const saTalent of strikingAppearance) { + saTalent.system.checked = false; + saTalent.system.active = true; + attackMods.push(saTalent); + } + const templateData = { actor: actor.system, tokenId: token?.uuid || null, state: stateData, + preAttackMods: attackMods, }; - var path = `systems/${HEROSYS.module}/templates/pop-out/presence-attack-card.hbs`; - + const path = `systems/${HEROSYS.module}/templates/pop-out/presence-attack-card.hbs`; return await renderTemplate(path, templateData); } async function presenceAttackRoll(actor, html) { const form = html[0].querySelector("form"); const rollModifier = parseFloat(form.mod.value); - const presence = parseInt(actor.system.characteristics.pre.value); const presenceDice = presence / 5; const partialDice = presenceDice % 1; - const heroRoller = new HeroRoller() + const preAttackRoller = new HeroRoller() .makeBasicRoll() .addDice(Math.trunc(presenceDice), "Presence Attack") - .addHalfDice(Math.sign(partialDice) * (Math.abs(partialDice) >= 0.5 ? 1 : 0), "Presence Attack") + .addHalfDice(Math.sign(partialDice) * (Math.abs(partialDice) >= 0.5 ? 1 : 0), "Presence Attack Half Dice") .addDice(Math.trunc(rollModifier), "Roll Modifier") .addHalfDice(Math.abs(rollModifier) % 1 >= 0.5 ? 1 : 0, "Roll Modifier"); - await heroRoller.roll(); - const cardHtml = await heroRoller.render("Presence Attack"); + // Presence Attack Modifiers + const preAttackModInputs = form.querySelectorAll("INPUT:checked"); + for (const preAttackInput of preAttackModInputs) { + const preAttack = actor.items.get(preAttackInput.id); + const levels = parseInt(preAttack.system.LEVELS || 0); + if (levels > 0) { + preAttackRoller.addDice(levels, preAttack.name); + } + } + + await preAttackRoller.roll(); + + const rollHtml = await preAttackRoller.render("Presence Attack"); + const tags = await preAttackRoller.tags(); + + // render card const token = actor.token; const speaker = ChatMessage.getSpeaker({ actor: actor, token }); speaker.alias = actor.name; + const cardData = { + tags: tags.map((tag) => { + return { ...tag, value: tag.value.signedString() }; + }), + rolls: preAttackRoller.rawRolls(), + renderedRoll: rollHtml, + user: game.user._id, + speaker: speaker, + }; + const template = `systems/${HEROSYS.module}/templates/chat/presence-attack-result-card.hbs`; + const cardHtml = await renderTemplate(template, cardData); + const chatData = { style: CONST.CHAT_MESSAGE_STYLES.OOC, - rolls: heroRoller.rawRolls(), + rolls: preAttackRoller.rawRolls(), author: game.user._id, content: cardHtml, speaker: speaker, @@ -47,7 +82,7 @@ async function presenceAttackRoll(actor, html) { return ChatMessage.create(chatData); } -async function presenceAttackPopOut(actor) { +export async function presenceAttackPopOut(actor) { const content = await _renderForm(actor, {}); // Attack Card as a Pop Out @@ -72,5 +107,3 @@ async function presenceAttackPopOut(actor) { new Dialog(data, options).render(true); }); } - -export { presenceAttackPopOut }; diff --git a/templates/chat/presence-attack-result-card.hbs b/templates/chat/presence-attack-result-card.hbs new file mode 100644 index 00000000..f7cc9d82 --- /dev/null +++ b/templates/chat/presence-attack-result-card.hbs @@ -0,0 +1,14 @@ +{{ log 'HEROSYS presence-attack-result-card' this }} +
+
+ {{#each tags as |tag id|}} + {{tag.name}} + {{tag.value}} + + {{/each}} +
+ +
+
{{{ renderedRoll }}}
+
+
\ No newline at end of file diff --git a/templates/pop-out/item-skill-card.hbs b/templates/pop-out/item-skill-card.hbs index f4013137..a15986ca 100644 --- a/templates/pop-out/item-skill-card.hbs +++ b/templates/pop-out/item-skill-card.hbs @@ -15,7 +15,7 @@ {{#if skillMods.length}}
- + {{#each skillMods}} {{#if this.system.active}} diff --git a/templates/pop-out/presence-attack-card.hbs b/templates/pop-out/presence-attack-card.hbs index ff8e9a1f..c4d6b243 100644 --- a/templates/pop-out/presence-attack-card.hbs +++ b/templates/pop-out/presence-attack-card.hbs @@ -3,4 +3,34 @@ + + {{#if preAttackMods.length}} +
+ +
+ {{#each preAttackMods}} + {{#if this.system.active}} + + + + + {{else}} + + + + + {{/if}} + {{/each}} +
+ + + {{this.system.description}} +
+ + + {{this.system.description}} +
+
+ {{/if}}