From 0ecf80932b3e7979acb09475cb206e745ad4b242 Mon Sep 17 00:00:00 2001 From: marvin9257 <72580196+marvin9257@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:58:21 -0600 Subject: [PATCH 1/6] feat: preliminary roll table display in Journals --- src/module/hooks/tableRolls.ts | 10 ++ src/module/utils/enrichers.ts | 163 +++++++++++++++++++++++++++++++++ src/twodsix.ts | 5 + static/styles/twodsix.css | 6 ++ 4 files changed, 184 insertions(+) create mode 100644 src/module/hooks/tableRolls.ts create mode 100644 src/module/utils/enrichers.ts diff --git a/src/module/hooks/tableRolls.ts b/src/module/hooks/tableRolls.ts new file mode 100644 index 000000000..a545cc013 --- /dev/null +++ b/src/module/hooks/tableRolls.ts @@ -0,0 +1,10 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck This turns off *all* typechecking, make sure to remove this once foundry-vtt-types are updated to cover v10. + +import { handleTableRoll } from "../utils/enrichers"; + +for (const sheet of ["JournalPageSheet"]) { + Hooks.on(`render${sheet}`, (_app, html, _options) => { + html.on('click contextmenu', '.table-roll', handleTableRoll.bind()); + }); +} diff --git a/src/module/utils/enrichers.ts b/src/module/utils/enrichers.ts new file mode 100644 index 000000000..8983d4135 --- /dev/null +++ b/src/module/utils/enrichers.ts @@ -0,0 +1,163 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-nocheck This turns off *all* typechecking, make sure to remove this once foundry-vtt-types are updated to cover v10. + +//Adapted from https://github.com/pafvel/dragonbane/blob/master/modules/journal.js + +export function addCustomEnrichers() { + CONFIG.TextEditor.enrichers.push( + { + pattern: /@DisplayTable\[(.+?)\](?:{(.+?)})?/gm, + enricher: enrichDisplayTable + }, + { + pattern: /@Table\[(.+?)\](?:{(.+?)})?/gm, + enricher: rollTable + } + ); +} + +async function enrichDisplayTable (match, options) { + const table = findTable(match[1], options); + const tableName = match[2] ?? table?.name; + const a = document.createElement("div"); + if (table) { + a.classList.add("display-table"); + const html = displayTable(match[1], table, tableName); + a.innerHTML = await TextEditor.enrichHTML(html, {async: true}); + } else { + a.dataset.tableId = match[1]; + if (match[2]) { + a.dataset.tableName = match[2]; + } + a.classList.add("content-link"); + a.classList.add("broken"); + a.innerHTML = ` ${tableName}`; + } + return a; +} + +async function rollTable (match, options) { + const table = findTable(match[1], options); + const tableName = match[2] ?? table?.name; + const a = document.createElement("a"); + if (table) { + a.classList.add("inline-roll"); + a.classList.add("table-roll"); + a.dataset.tableId = table.uuid; + a.dataset.tableName = table.name; + a.innerHTML = ` ${tableName}`; + } else { + a.dataset.tableId = match[1]; + if (match[2]) { + a.dataset.tableName = match[2]; + } + a.classList.add("content-link"); + a.classList.add("broken"); + a.innerHTML = ` ${tableName}`; + } + return a; +} + +function displayTable(uuid, table, tableName) { + if (!table) { + return ""; + } + + /* + // Rollable table in caption + let html = ` + + + + + + `; + */ + + // Rollable table in roll column header + let html = ` +
@Table[${uuid}]{${tableName}}
[[/roll ${table.formula}]]${game.i18n.localize("DoD.journal.tableResult")}
+ + + + + `; + + for (const result of table.results) { + html += ` + + + + `; + } else { + html += ` + + `; + } + } else if (result.documentCollection == "Item") { + html += ` + + `; + } else { + html += ` + + `; + } + } + html += `
${tableName}
@Table[${uuid}]{${table.formula}}${game.i18n.localize("Table Result")}
${result.range[0]}`; + if (result.range[1] != result.range[0]) { + html += ` - ${result.range[1]}`; + } + if (result.documentCollection == "RollTable") { + const subTable = findTable(result.text); + if (subTable?.uuid != table.uuid) { + let subTableName = result.text; + if(subTableName.startsWith(table.name)) { + subTableName = subTableName.slice(table.name.length); + if (subTableName.startsWith(" - ")) { + subTableName = subTableName.slice(3); + } + } + html += `${subTable?.description} @DisplayTable[RollTable.${result.documentId}]{${subTableName}}
${result.text}
@UUID[Item.${result.documentId}]{${result.text}}
${result.text}
`; + return html; +} + +function findTable(tableName:string, options?:any) { + const table = game.tables.find(i => i.name.toLowerCase() == tableName.toLowerCase()) || fromUuidSync(tableName); + if (!table) { + if (!options?.noWarnings){ + sendWarning("WARNING.tableNotFound", {id: tableName}); + } + return null; + } + if (!(table instanceof RollTable)) { + if (!options?.noWarning){ + sendWarning("WARNING.typeMismatch", {id: tableName}); + } + return null; + } + return table; +} + +function sendWarning(msg, params) { + if (!params) { + return ui.notifications.warn(game.i18n.localize(msg)); + } else { + return ui.notifications.warn(game.i18n.format(game.i18n.localize(msg), params)); + } +} + +export async function handleTableRoll(event) { + const tableId = event.currentTarget.dataset.tableId; + const tableName = event.currentTarget.dataset.tableName; + const table = fromUuidSync(tableId) || this.findTable(tableName); + if (table) { + if (event.type == "click") { // left click + table.draw(); + } else { // right click + table.sheet.render(true); + } + } + event.preventDefault(); + event.stopPropagation(); +} diff --git a/src/twodsix.ts b/src/twodsix.ts index 09702408e..894a4b0f1 100644 --- a/src/twodsix.ts +++ b/src/twodsix.ts @@ -28,6 +28,7 @@ import { TwodsixRobotSheet } from "./module/sheets/TwodsixRobotSheet"; import { TwodsixSpaceObjectSheet } from "./module/sheets/TwodsixSpaceObjectSheet"; import { TwodsixDiceRoll } from "./module/utils/TwodsixDiceRoll"; import { TwodsixRollSettings } from "./module/utils/TwodsixRollSettings"; +import { addCustomEnrichers } from "./module/utils/enrichers"; // @ts-ignore hookScriptFiles.forEach((hookFile:string) => import(`./module/hooks/${hookFile}.ts`)); @@ -112,9 +113,13 @@ Hooks.once('init', async function () { registerHandlebarsHelpers(); registerSettings(); + //Dice Rolls CONFIG.Dice.rolls.push(TwodsixDiceRoll); + //Add custom Enrichers + addCustomEnrichers(); + /* add fonts */ // @ts-ignore CONFIG.fontDefinitions["Asap"] = { diff --git a/static/styles/twodsix.css b/static/styles/twodsix.css index c17c09f88..6777a47c8 100644 --- a/static/styles/twodsix.css +++ b/static/styles/twodsix.css @@ -3071,6 +3071,12 @@ select.form-input, input.form-input, span.form-input { color: var(--s2d6-stat-input); } +.table-caption { + color: var(--s2d6-stat-input); + font-size: 1.5em; + font-style: italic; +} + .chat-message .table-draw .table-results .table-result .result-text { color: var(--s2d6-dialog-color); } From 3fb2f0e06ca86b9f0c8402eb8bbbd5caf047db5b Mon Sep 17 00:00:00 2001 From: marvin9257 <72580196+marvin9257@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:15:37 -0600 Subject: [PATCH 2/6] feat: improvements add SkillRoll enrichment add JSDoc add localizations --- src/module/hooks/tableRolls.ts | 5 +- src/module/utils/enrichers.ts | 122 ++++++++++++++++++++++++++------ static/lang/en.json | 7 +- static/styles/twodsix_basic.css | 6 ++ 4 files changed, 115 insertions(+), 25 deletions(-) diff --git a/src/module/hooks/tableRolls.ts b/src/module/hooks/tableRolls.ts index a545cc013..30fc7d003 100644 --- a/src/module/hooks/tableRolls.ts +++ b/src/module/hooks/tableRolls.ts @@ -1,10 +1,11 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck This turns off *all* typechecking, make sure to remove this once foundry-vtt-types are updated to cover v10. -import { handleTableRoll } from "../utils/enrichers"; - +import { handleTableRoll, handleSkillRoll } from "../utils/enrichers"; +// Add hooks when clicking on rollable table link for (const sheet of ["JournalPageSheet"]) { Hooks.on(`render${sheet}`, (_app, html, _options) => { html.on('click contextmenu', '.table-roll', handleTableRoll.bind()); + html.on('click contextmenu', '.skill-roll', handleSkillRoll.bind()); }); } diff --git a/src/module/utils/enrichers.ts b/src/module/utils/enrichers.ts index 8983d4135..e66b1aa00 100644 --- a/src/module/utils/enrichers.ts +++ b/src/module/utils/enrichers.ts @@ -1,8 +1,12 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck This turns off *all* typechecking, make sure to remove this once foundry-vtt-types are updated to cover v10. -//Adapted from https://github.com/pafvel/dragonbane/blob/master/modules/journal.js +import { getControlledTraveller } from "../sheets/TwodsixVehicleSheet"; +import TwodsixItem from "../entities/TwodsixItem"; +// Adapted from https://github.com/pafvel/dragonbane/blob/master/modules/journal.js + +// Add custom enrichers during init phase export function addCustomEnrichers() { CONFIG.TextEditor.enrichers.push( { @@ -12,11 +16,21 @@ export function addCustomEnrichers() { { pattern: /@Table\[(.+?)\](?:{(.+?)})?/gm, enricher: rollTable + }, + { + pattern: /@SkillRoll(?:{(.+?)})?/gm, + enricher: rollSkill } ); } -async function enrichDisplayTable (match, options) { +/** + * Convert a rollTable link in a journal entry with a nice format of the table based on roll table name or uuid. + * @param {any} match An array matching the RegEx expression. Match[0] is the unernriched JE string. Match[1] is the table name or UUID. Match[2] is the user entered table label. + * @param {any} options Options to the roll action + * @returns {Promise} The displayed html element for the enriched RollTable reference + */ +async function enrichDisplayTable (match: any, options: any): Promise { const table = findTable(match[1], options); const tableName = match[2] ?? table?.name; const a = document.createElement("div"); @@ -36,7 +50,13 @@ async function enrichDisplayTable (match, options) { return a; } -async function rollTable (match, options) { +/** + * A rollable link in a journal entry to a RollTable. + * @param {string} match An array matching the RegEx expression. Match[0] is the unernriched JE string. Match[1] is the table name or UUID. Match[2] is the user entered table label. + * @param {string} options Options to the roll action + * @returns {HTMLAnchorElement} The rolltable in an html format + */ +async function rollTable (match: any, options: any): Promise { const table = findTable(match[1], options); const tableName = match[2] ?? table?.name; const a = document.createElement("a"); @@ -58,29 +78,40 @@ async function rollTable (match, options) { return a; } -function displayTable(uuid, table, tableName) { +/** + * A rollable link in a skill. + * @param {string} match An array matching the RegEx expression. Match[0] is the unenriched JE string. Match[1] is the skill name. Match[2] is the user entered skill label. + * @param {string} options Options to the roll action + * @returns {HTMLAnchorElement} The rolltable in an html format + */ +async function rollSkill (match: any, _options: any): Promise { + const skillName = match[1] ?? match[2]; + const a = document.createElement("a"); + a.classList.add("inline-roll"); + a.classList.add("skill-roll"); + a.dataset.skillName = skillName; + a.innerHTML = ` ${skillName}`; + return a; +} + +/** + * Convert a rollTable link in a journal entry with a nice format of the table based on roll table name or uuid. + * @param {string} uuid The rolltable UUID. + * @param {string} tableName The string name of the table + * @returns {string} The rolltable in an html format + */ +function displayTable(uuid: string, table:any, tableName: string): string { if (!table) { return ""; } - /* - // Rollable table in caption - let html = ` - - - - - - `; - */ - // Rollable table in roll column header let html = `
@Table[${uuid}]{${tableName}}
[[/roll ${table.formula}]]${game.i18n.localize("DoD.journal.tableResult")}
- - + + `; for (const result of table.results) { @@ -122,17 +153,23 @@ function displayTable(uuid, table, tableName) { return html; } -function findTable(tableName:string, options?:any) { +/** + * Finds a RollTable document based on either the RollTable name or uuid. + * @param {string} tableName The rolltable UUID or name. + * @param {any} options The optional find strings + * @returns {RollTable} The RollTable document + */ +function findTable(tableName:string, options?:any): RollTable { const table = game.tables.find(i => i.name.toLowerCase() == tableName.toLowerCase()) || fromUuidSync(tableName); if (!table) { if (!options?.noWarnings){ - sendWarning("WARNING.tableNotFound", {id: tableName}); + sendWarning("TWODSIX.Warnings.tableNotFound", {id: tableName}); } return null; } if (!(table instanceof RollTable)) { if (!options?.noWarning){ - sendWarning("WARNING.typeMismatch", {id: tableName}); + sendWarning("TWODSIX.Warnings.typeMismatch", {id: tableName}); } return null; } @@ -147,10 +184,16 @@ function sendWarning(msg, params) { } } -export async function handleTableRoll(event) { +/** + * Make a roll from a RollTable from a clickable link in JournalEntry. + * @param {Event} event The click event. + */ +export async function handleTableRoll(event: Event): Promise { + event.preventDefault(); + event.stopPropagation(); const tableId = event.currentTarget.dataset.tableId; const tableName = event.currentTarget.dataset.tableName; - const table = fromUuidSync(tableId) || this.findTable(tableName); + const table = fromUuidSync(tableId) || findTable(tableName); if (table) { if (event.type == "click") { // left click table.draw(); @@ -158,6 +201,41 @@ export async function handleTableRoll(event) { table.sheet.render(true); } } +} + +/** + * Make a roll from a RollTable from a clickable link in JournalEntry. + * @param {Event} event The click event. + */ +export async function handleSkillRoll(event: Event): Promise { event.preventDefault(); event.stopPropagation(); + const skillName:string = event.currentTarget.dataset.skillName; + const skill:TwodsixItem = findSkill(skillName); + if (skill) { + if (event.type == "click") { // left click + await skill.skillRoll(true); + } else { // right click + skill.sheet.render(true); + } + } +} + +/** + * Finds a Skill Item document based on either the name or uuid. + * @param {string} skillName The skill name or UUID. + * @param {any} options The optional find strings + * @returns {TwodsixItem} The RollTable document + */ +function findSkill(skillName:string, _options?:any): TwodsixItem { + const actorToUse = getControlledTraveller(); + if (actorToUse) { + let skill = actorToUse.itemTypes.skills?.find(i => i.name.toLowerCase() == skillName.toLowerCase()) || fromUuidSync(skillName); + if (!skill) { + skill = actorToUse.getUntrainedSkill(); + } + return skill; + } else { + ui.notifications.warn(game.i18n.localize("TWODSIX.Warnings.NoActorSelected")); + } } diff --git a/static/lang/en.json b/static/lang/en.json index 66bd9efcb..5a53b84fc 100644 --- a/static/lang/en.json +++ b/static/lang/en.json @@ -380,6 +380,9 @@ } } }, + "Table": { + "TableResults": "Table Results" + }, "Create": "Create", "Handlebars": { "CantShowCharacteristic": "You need to reselect the characteristic for all skills marked with XXX. Sorry..." @@ -1406,7 +1409,9 @@ "MissingUntrainedSkill": "Actor is missing untrained skill. Reload application to fix.", "TooManyTargets": "More targets than the number of attacks, targets will be chosen in order selected.", "WearingMultipleLayers": "Multiple armor layers equipped while wearing non-stackable armor.", - "ResetEffectForManualDamage": "Effects not included in damage rolls. Resetting effects in manual rolls to false." + "ResetEffectForManualDamage": "Effects not included in damage rolls. Resetting effects in manual rolls to false.", + "tableNotFound": "Cannot find RollTable in Journal Entry Link.", + "typeMismatch": "RollTable link/UUID in Journal Entry is not a RollTable." } }, "TYPES": { diff --git a/static/styles/twodsix_basic.css b/static/styles/twodsix_basic.css index df703b695..e094fc57d 100644 --- a/static/styles/twodsix_basic.css +++ b/static/styles/twodsix_basic.css @@ -2543,6 +2543,12 @@ background: var(--sidebar-background); word-break: break-all; font-size: inherit; }*/ + +.table-caption { + font-size: 1.5em; + font-style: italic; +} + .dice-total { white-space: pre-wrap; } From 6a6179515067a8f1034ba424857c82b7028d1e74 Mon Sep 17 00:00:00 2001 From: marvin9257 <72580196+marvin9257@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:17:13 -0600 Subject: [PATCH 3/6] fix: deepscan issue --- src/module/utils/enrichers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/module/utils/enrichers.ts b/src/module/utils/enrichers.ts index e66b1aa00..9a1a9c642 100644 --- a/src/module/utils/enrichers.ts +++ b/src/module/utils/enrichers.ts @@ -227,7 +227,7 @@ export async function handleSkillRoll(event: Event): Promise { * @param {any} options The optional find strings * @returns {TwodsixItem} The RollTable document */ -function findSkill(skillName:string, _options?:any): TwodsixItem { +function findSkill(skillName:string): TwodsixItem { const actorToUse = getControlledTraveller(); if (actorToUse) { let skill = actorToUse.itemTypes.skills?.find(i => i.name.toLowerCase() == skillName.toLowerCase()) || fromUuidSync(skillName); From 3f7f601b5abe463fa213f5d476dc9ff2f669fdea Mon Sep 17 00:00:00 2001 From: marvin9257 <72580196+marvin9257@users.noreply.github.com> Date: Wed, 6 Dec 2023 14:59:26 -0600 Subject: [PATCH 4/6] fix: make SkillRoll regex more robust to inputs --- src/module/utils/enrichers.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/module/utils/enrichers.ts b/src/module/utils/enrichers.ts index 9a1a9c642..c27e26009 100644 --- a/src/module/utils/enrichers.ts +++ b/src/module/utils/enrichers.ts @@ -18,7 +18,7 @@ export function addCustomEnrichers() { enricher: rollTable }, { - pattern: /@SkillRoll(?:{(.+?)})?/gm, + pattern: /@SkillRoll(?:\[(.*?)\])?(?:{(.*?)})?/gm, enricher: rollSkill } ); @@ -85,12 +85,13 @@ async function rollTable (match: any, options: any): Promise * @returns {HTMLAnchorElement} The rolltable in an html format */ async function rollSkill (match: any, _options: any): Promise { - const skillName = match[1] ?? match[2]; + const skillName = match[1] || match[2]; + const descrip = match[2] || match[1]; const a = document.createElement("a"); a.classList.add("inline-roll"); a.classList.add("skill-roll"); a.dataset.skillName = skillName; - a.innerHTML = ` ${skillName}`; + a.innerHTML = ` ${descrip}`; return a; } From d67e2b61f70727bd8fad7fe811d796f2557964f2 Mon Sep 17 00:00:00 2001 From: marvin9257 <72580196+marvin9257@users.noreply.github.com> Date: Fri, 8 Dec 2023 07:52:10 -0600 Subject: [PATCH 5/6] refactor: allow enhancers to use formula skill roll from ship positions --- src/module/entities/TwodsixItem.ts | 2 +- src/module/utils/TwodsixRollSettings.ts | 69 +++++++++++++++- src/module/utils/TwodsixShipActions.ts | 101 ++++-------------------- src/module/utils/enrichers.ts | 33 +++++--- src/module/utils/utils.ts | 21 +++++ 5 files changed, 127 insertions(+), 99 deletions(-) diff --git a/src/module/entities/TwodsixItem.ts b/src/module/entities/TwodsixItem.ts index 89aa789bd..933adc1c1 100644 --- a/src/module/entities/TwodsixItem.ts +++ b/src/module/entities/TwodsixItem.ts @@ -11,7 +11,7 @@ import TwodsixActor from "./TwodsixActor"; import {DICE_ROLL_MODES} from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/constants.mjs"; import {Component, Consumable, Gear, Skills, UsesConsumables, Weapon} from "../../types/template"; import { confirmRollFormula } from "../utils/sheetUtils"; -import { getCharacteristicFromDisplayLabel } from "../utils/TwodsixShipActions"; +import { getCharacteristicFromDisplayLabel } from "../utils/utils"; import ItemTemplate from "../utils/ItemTemplate"; import { getDamageTypes } from "../sheets/TwodsixItemSheet"; import { TWODSIX } from "../config"; diff --git a/src/module/utils/TwodsixRollSettings.ts b/src/module/utils/TwodsixRollSettings.ts index 7aa6b9916..761417340 100644 --- a/src/module/utils/TwodsixRollSettings.ts +++ b/src/module/utils/TwodsixRollSettings.ts @@ -9,7 +9,7 @@ import {Gear, Skills} from "../../types/template"; import TwodsixActor from "../entities/TwodsixActor"; import { simplifySkillName } from "./utils"; import { effectType } from "../hooks/showStatusIcons"; -import { addSign } from "./utils"; +import { addSign, getCharacteristicFromDisplayLabel } from "./utils"; export class TwodsixRollSettings { difficulty:{ mod:number, target:number }; @@ -361,3 +361,70 @@ export async function getCustomModifiers(selectedActor:TwodsixActor, characteris } return returnObject; } + +export function getInitialSettingsFromFormula(parseString: string, actor: TwodsixActor): any { + const difficulties = TWODSIX.DIFFICULTIES[(game.settings.get('twodsix', 'difficultyListUsed'))]; + // eslint-disable-next-line no-useless-escape + const re = new RegExp(/^(.[^\/\+=]*?) ?(?:\/([\S]+))? ?(?:(\d{0,2})\+)? ?(?:=(\w*))? ?$/); + const parsedResult: RegExpMatchArray | null = re.exec(parseString); + + if (parsedResult !== null) { + const [, parsedSkills, char, diff] = parsedResult; + const skillOptions = parsedSkills.split("|"); + let skill:TwodsixItem|undefined = undefined; + /* add qualified skill objects to an array*/ + const skillObjects = actor?.itemTypes.skills?.filter((itm: TwodsixItem) => skillOptions.includes(itm.name)); + + // find the most advantageous skill to use from the collection + if(skillObjects?.length > 0){ + skill = skillObjects.reduce((prev, current) => (prev.system.value > current.system.value) ? prev : current); + } + + // If skill missing, try to use Untrained + if (!skill) { + skill = actor?.itemTypes.skills.find((itm: TwodsixItem) => itm.name === game.i18n.localize("TWODSIX.Actor.Skills.Untrained")) as TwodsixItem; + if (!skill) { + ui.notifications.error(game.i18n.localize("TWODSIX.Ship.ActorLacksSkill").replace("_ACTOR_NAME_", actor?.name ?? "").replace("_SKILL_", parsedSkills)); + return false; + } + } + + // get characteristic key, default to skill key if none specificed in formula + let characteristicKey = ""; + const charObject = actor?.system["characteristics"] ?? {}; + //we need an array + const charObjectArray = Object.values(charObject); + if(!char) { + characteristicKey = getKeyByValue(TWODSIX.CHARACTERISTICS, (skill.system).characteristic); + } else { + //find the most advantageous characteristic to use based on the displayed (custom) short label + const charOptions = char.split("|"); + let candidateCharObject = undefined; + const candidateCharObjects = charObjectArray.filter(ch => charOptions.includes(ch.displayShortLabel)); + if(candidateCharObjects.length > 0){ + candidateCharObject = candidateCharObjects.reduce((prev, current) =>(prev.mod > current.mod) ? prev: current); + } + characteristicKey = candidateCharObject?.key ?? getCharacteristicFromDisplayLabel(char, actor); + } + + let shortLabel = "NONE"; + let displayLabel = "NONE"; + if (charObject && characteristicKey) { + shortLabel = charObject[characteristicKey].shortLabel; + displayLabel = charObject[characteristicKey].displayShortLabel; + } + const returnValues = { + skill: skill, + skillRoll: true, + displayLabel: displayLabel, + rollModifiers: {characteristic: shortLabel} + }; + if (diff) { + returnValues["difficulty"] = Object.values(difficulties).filter((difficulty: Record) => difficulty.target === parseInt(diff, 10))[0]; + } + return returnValues; + } else { + ui.notifications.error(game.i18n.localize("TWODSIX.Ship.CannotParseArgument")); + return false; + } +} diff --git a/src/module/utils/TwodsixShipActions.ts b/src/module/utils/TwodsixShipActions.ts index 60c7fa238..25ee49739 100644 --- a/src/module/utils/TwodsixShipActions.ts +++ b/src/module/utils/TwodsixShipActions.ts @@ -1,13 +1,13 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck This turns off *all* typechecking, make sure to remove this once foundry-vtt-types are updated to cover v10. -import { Component, Skills } from "src/types/template"; +import { Component} from "src/types/template"; import { AvailableShipActionData, AvailableShipActions, ExtraData } from "../../types/twodsix"; import { TWODSIX } from "../config"; import TwodsixItem from "../entities/TwodsixItem"; import TwodsixActor from "../entities/TwodsixActor"; -import { confirmRollFormula, getKeyByValue } from "./sheetUtils"; -import { TwodsixRollSettings } from "./TwodsixRollSettings"; +import { confirmRollFormula} from "./sheetUtils"; +import { TwodsixRollSettings, getInitialSettingsFromFormula } from "./TwodsixRollSettings"; import { DICE_ROLL_MODES } from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/constants.mjs"; export class TwodsixShipActions { @@ -59,78 +59,26 @@ export class TwodsixShipActions { public static async skillRoll(text: string, extra: ExtraData) { const useInvertedShiftClick: boolean = (game.settings.get('twodsix', 'invertSkillRollShiftClick')); - const showTrowDiag = useInvertedShiftClick ? extra.event["shiftKey"] : !extra.event["shiftKey"]; - const difficulties = TWODSIX.DIFFICULTIES[(game.settings.get('twodsix', 'difficultyListUsed'))]; - // eslint-disable-next-line no-useless-escape - const re = new RegExp(/^(.[^\/\+=]*?) ?(?:\/([\S]+))? ?(?:(\d{0,2})\+)? ?(?:=(\w*))? ?$/); - const parsedResult: RegExpMatchArray | null = re.exec(text); - const selectedActor = extra.actor; + const showTrowDiag:boolean = useInvertedShiftClick ? extra.event["shiftKey"] : !extra.event["shiftKey"]; - if (parsedResult !== null) { - const [, parsedSkills, char, diff] = parsedResult; - const skillOptions = parsedSkills.split("|"); - let skill = undefined; - /* add qualified skill objects to an array*/ - const skillObjects = selectedActor?.itemTypes.skills.filter((itm: TwodsixItem) => skillOptions.includes(itm.name)); - - // find the most advantageous sill to use from the collection - if(skillObjects?.length > 0){ - skill = skillObjects.reduce((prev, current) => (prev.system.value > current.system.value) ? prev : current); - } - - - /*if skill missing, try to use Untrained*/ - if (!skill) { - skill = selectedActor?.itemTypes.skills.find((itm: TwodsixItem) => itm.name === game.i18n.localize("TWODSIX.Actor.Skills.Untrained")) as TwodsixItem; - if (!skill) { - ui.notifications.error(game.i18n.localize("TWODSIX.Ship.ActorLacksSkill").replace("_ACTOR_NAME_", selectedActor?.name ?? "").replace("_SKILL_", parsedSkills)); - return false; - } - } - - /*get characteristic key, default to skill key if none specificed in formula */ - let characteristicKey = ""; - const charObject = selectedActor?.system["characteristics"] ?? {}; - //we need an array - const charObjectArray = Object.values(charObject); - if(!char) { - characteristicKey = getKeyByValue(TWODSIX.CHARACTERISTICS, (skill.system).characteristic); - } else { - //find the most advantageous characteristic to use - const charOptions = char.split("|"); - let candidateCharObject = undefined; - const candidateCharObjects = charObjectArray.filter(ch => charOptions.includes(ch.displayShortLabel)); - if(candidateCharObjects.length > 0){ - candidateCharObject = candidateCharObjects.reduce((prev, current) =>(prev.mod > current.mod) ? prev: current); - } - characteristicKey = candidateCharObject?.key ?? getCharacteristicFromDisplayLabel(char, selectedActor);; - } - - - let shortLabel = "NONE"; - let displayLabel = "NONE"; - if (charObject && characteristicKey) { - shortLabel = charObject[characteristicKey].shortLabel; - displayLabel = charObject[characteristicKey].displayShortLabel; - } - const settings = { - displayLabel: displayLabel, + const settings = getInitialSettingsFromFormula(text, extra.actor); + if (settings) { + Object.assign (settings, { extraFlavor: game.i18n.localize("TWODSIX.Ship.MakesChatRollAction").replace( "_ACTION_NAME_", extra.actionName || game.i18n.localize("TWODSIX.Ship.Unknown")).replace("_POSITION_NAME_", (extra.positionName || game.i18n.localize("TWODSIX.Ship.Unknown"))), - rollModifiers: {characteristic: shortLabel, item: extra.diceModifier ? parseInt(extra.diceModifier) : 0}, flags: {tokenUUID: extra.ship?.uuid} - }; - if (diff) { - settings["difficulty"] = Object.values(difficulties).filter((difficulty: Record) => difficulty.target === parseInt(diff, 10))[0]; - } - const options = await TwodsixRollSettings.create(showTrowDiag, settings, skill, extra.component, selectedActor); + }); + Object.assign(settings.rollModifiers, {item: extra.diceModifier ? parseInt(extra.diceModifier) : 0}); + const skill:TwodsixItem = settings.skill; + delete settings.skill; + const options = await TwodsixRollSettings.create(showTrowDiag, settings, skill, extra.component, extra.actor); if (!options.shouldRoll) { return false; } if (extra.component) { - return extra.component.skillRoll(showTrowDiag, options); + return extra.component.skillRoll(false, options); } else { - return skill.skillRoll(showTrowDiag, options); + return skill.skillRoll(false, options); } } else { @@ -177,24 +125,3 @@ export class TwodsixShipActions { } } } - -/** - * A function for getting the full characteristic label from the displayed short label. - * - * @param {string} char The displayed characteristic short label. - * @param {TwodsixActor} actor The Actor in question. - * @returns {string} Full logical name of the characteristic. - */ -export function getCharacteristicFromDisplayLabel(char:string, actor?:TwodsixActor):string { - let tempObject = {}; - let charObject= {}; - if (actor) { - charObject = actor.system["characteristics"]; - for (const key in charObject) { - tempObject[key] = charObject[key].displayShortLabel; - } - } else { - tempObject = TWODSIX.CHARACTERISTICS; - } - return getKeyByValue(tempObject, char); -} diff --git a/src/module/utils/enrichers.ts b/src/module/utils/enrichers.ts index c27e26009..af6d8885c 100644 --- a/src/module/utils/enrichers.ts +++ b/src/module/utils/enrichers.ts @@ -3,6 +3,8 @@ import { getControlledTraveller } from "../sheets/TwodsixVehicleSheet"; import TwodsixItem from "../entities/TwodsixItem"; +import { getInitialSettingsFromFormula } from "./TwodsixRollSettings"; +import { TwodsixRollSettings } from "./TwodsixRollSettings"; // Adapted from https://github.com/pafvel/dragonbane/blob/master/modules/journal.js @@ -85,12 +87,12 @@ async function rollTable (match: any, options: any): Promise * @returns {HTMLAnchorElement} The rolltable in an html format */ async function rollSkill (match: any, _options: any): Promise { - const skillName = match[1] || match[2]; + const skillName = match[1] || ""; const descrip = match[2] || match[1]; const a = document.createElement("a"); a.classList.add("inline-roll"); a.classList.add("skill-roll"); - a.dataset.skillName = skillName; + a.dataset.parseString = skillName; a.innerHTML = ` ${descrip}`; return a; } @@ -205,20 +207,31 @@ export async function handleTableRoll(event: Event): Promise { } /** - * Make a roll from a RollTable from a clickable link in JournalEntry. + * Make a roll from a skill formula using a clickable link in JournalEntry. * @param {Event} event The click event. */ export async function handleSkillRoll(event: Event): Promise { event.preventDefault(); event.stopPropagation(); - const skillName:string = event.currentTarget.dataset.skillName; - const skill:TwodsixItem = findSkill(skillName); - if (skill) { - if (event.type == "click") { // left click - await skill.skillRoll(true); - } else { // right click - skill.sheet.render(true); + const parseString:string = event.currentTarget.dataset.parseString; + const actorToUse = getControlledTraveller(); + if (actorToUse) { + const parsedValues:any = getInitialSettingsFromFormula(parseString, actorToUse); + if (parsedValues) { + const skill:TwodsixItem = parsedValues.skill; + if (event.type == "click") { // left click + delete parsedValues.skill; + const settings:TwodsixRollSettings = await TwodsixRollSettings.create(true, parsedValues, skill, undefined, actorToUse); + if (!settings.shouldRoll) { + return; + } + await skill.skillRoll(false, settings); + } else { // right click + skill.sheet.render(true); + } } + } else { + ui.notifications.warn(game.i18n.localize("TWODSIX.Warnings.NoActorSelected")); } } diff --git a/src/module/utils/utils.ts b/src/module/utils/utils.ts index fe9db254a..c6d79a407 100644 --- a/src/module/utils/utils.ts +++ b/src/module/utils/utils.ts @@ -1,5 +1,6 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck This turns off *all* typechecking, make sure to remove this once foundry-vtt-types are updated to cover v10. +import { getKeyByValue } from "./sheetUtils"; // https://stackoverflow.com/a/34749873 /** @@ -121,3 +122,23 @@ export function capitalizeFirstLetter(inputString:string):string { return ""; } } + +/** + * A function for getting the full characteristic key from the displayed short label. + * @param {string} char The displayed characteristic short label. + * @param {TwodsixActor} actor The Actor in question. + * @returns {string} Full logical name (key) of the characteristic. + */ +export function getCharacteristicFromDisplayLabel(char:string, actor?:TwodsixActor):string { + let tempObject = {}; + let charObject= {}; + if (actor) { + charObject = actor.system["characteristics"]; + for (const key in charObject) { + tempObject[key] = charObject[key].displayShortLabel; + } + } else { + tempObject = TWODSIX.CHARACTERISTICS; + } + return getKeyByValue(tempObject, char); +} From f8077421c6ae8c0f91558b172a446408260c549c Mon Sep 17 00:00:00 2001 From: marvin9257 <72580196+marvin9257@users.noreply.github.com> Date: Fri, 8 Dec 2023 08:10:04 -0600 Subject: [PATCH 6/6] fix: deepscan issue --- src/module/utils/enrichers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/module/utils/enrichers.ts b/src/module/utils/enrichers.ts index af6d8885c..5e192aa1d 100644 --- a/src/module/utils/enrichers.ts +++ b/src/module/utils/enrichers.ts @@ -241,7 +241,7 @@ export async function handleSkillRoll(event: Event): Promise { * @param {any} options The optional find strings * @returns {TwodsixItem} The RollTable document */ -function findSkill(skillName:string): TwodsixItem { +/*function findSkill(skillName:string): TwodsixItem { const actorToUse = getControlledTraveller(); if (actorToUse) { let skill = actorToUse.itemTypes.skills?.find(i => i.name.toLowerCase() == skillName.toLowerCase()) || fromUuidSync(skillName); @@ -252,4 +252,4 @@ function findSkill(skillName:string): TwodsixItem { } else { ui.notifications.warn(game.i18n.localize("TWODSIX.Warnings.NoActorSelected")); } -} +}*/
${tableName}
@Table[${uuid}]{${table.formula}}${game.i18n.localize("Table Result")}@Table[${uuid}]{${table.formula}}${game.i18n.localize("TWODSIX.Table.TableResults")}