From 434e8bc2d99d9b0265df966cec04dd72840cee00 Mon Sep 17 00:00:00 2001 From: Eunomiac Date: Sun, 24 Sep 2023 05:20:51 -0400 Subject: [PATCH] Reducing code duplication --- module/blades-actor.js | 68 ++++++++--------- module/blades-item.js | 100 +++---------------------- module/blades-roll-collab.js | 89 ++++++++++++++++++++++ module/documents/actors/blades-crew.js | 91 +--------------------- module/documents/actors/blades-pc.js | 96 ++---------------------- ts/blades-actor.ts | 4 +- ts/blades-item.ts | 94 ++++------------------- ts/blades-roll-collab.ts | 70 +++++++++++++++++ ts/documents/actors/blades-crew.ts | 78 ++----------------- ts/documents/actors/blades-pc.ts | 79 ++----------------- 10 files changed, 241 insertions(+), 528 deletions(-) diff --git a/module/blades-actor.js b/module/blades-actor.js index 223ab67a..f5fe20f7 100644 --- a/module/blades-actor.js +++ b/module/blades-actor.js @@ -99,7 +99,7 @@ class BladesActor extends Actor { get archivedSubActors() { return this.subActors.filter((subActor) => subActor.hasTag(Tag.System.Archived)); } checkActorPrereqs(actor) { - return true; + return actor && true; } processEmbeddedActorMatches(globalActors) { return globalActors @@ -279,8 +279,8 @@ class BladesActor extends Actor { switch (pType) { case PrereqType.HasActiveItem: { const thisItem = this.activeSubItems - .filter((item) => !hitRecord[pType]?.includes(item.id)) - .find((item) => item.system.world_name === pString); + .filter((i) => !hitRecord[pType]?.includes(i.id)) + .find((i) => i.system.world_name === pString); if (thisItem) { hitRecord[pType].push(thisItem.id); } @@ -291,8 +291,8 @@ class BladesActor extends Actor { } case PrereqType.HasActiveItemsByTag: { const thisItem = this.activeSubItems - .filter((item) => !hitRecord[pType]?.includes(item.id)) - .find((item) => item.hasTag(pString)); + .filter((i) => !hitRecord[pType]?.includes(i.id)) + .find((i) => i.hasTag(pString)); if (thisItem) { hitRecord[pType].push(thisItem.id); } @@ -539,14 +539,24 @@ class BladesActor extends Actor { return dialogData; } getSubItem(itemRef, activeOnly = false) { + const activeCheck = (i) => !activeOnly || !i.hasTag(Tag.System.Archived); if (typeof itemRef === "string" && this.items.get(itemRef)) { - return this.items.get(itemRef); + const returnItem = this.items.get(itemRef); + if (returnItem && activeCheck(returnItem)) { + return returnItem; + } + else { + return undefined; + } } - const globalItem = BladesItem.Get(itemRef); - if (!globalItem) { - return undefined; + else { + const globalItem = BladesItem.Get(itemRef); + if (!globalItem) { + return undefined; + } + return this.items.find((item) => item.name === globalItem.name && activeCheck(item)) + ?? this.items.find((item) => item.system.world_name === globalItem.system.world_name && activeCheck(item)); } - return this.items.find((item) => item.name === globalItem.name) ?? this.items.find((item) => item.system.world_name === globalItem.system.world_name); } hasSubItemOf(itemRef) { const item = BladesItem.Get(itemRef); @@ -765,7 +775,7 @@ class BladesActor extends Actor { return this.activeSubItems.filter((item) => [BladesItemType.cohort_gang, BladesItemType.cohort_expert].includes(item.type)); } getTaggedItemBonuses(tags) { - return 0; + return tags.length; } prepareDerivedData() { @@ -775,12 +785,6 @@ class BladesActor extends Actor { if (BladesActor.IsType(this, BladesActorType.crew)) { this._prepareCrewData(this.system); } - if (BladesActor.IsType(this, BladesActorType.npc)) { - this._prepareNPCData(this.system); - } - if (BladesActor.IsType(this, BladesActorType.faction)) { - this._prepareFactionData(this.system); - } } _preparePCData(system) { if (!BladesActor.IsType(this, BladesActorType.pc)) { @@ -802,16 +806,6 @@ class BladesActor extends Actor { system.turfs = this.playbook.system.turfs; } } - _prepareNPCData(system) { - if (!BladesActor.IsType(this, BladesActorType.npc)) { - return; - } - } - _prepareFactionData(system) { - if (!BladesActor.IsType(this, BladesActorType.faction)) { - return; - } - } async _onCreateDescendantDocuments(parent, collection, docs, data, options, userId) { await Promise.all(docs.map(async (doc) => { @@ -861,14 +855,16 @@ class BladesActor extends Actor { } rollAttributePopup(attribute_name) { - const attribute_label = U.tCase(attribute_name); + const attribute_label = U.tCase(attribute_name); + const MIN_DICE_MOD = -3; + const MAX_DICE_MOD = 3; let content = `

${game.i18n.localize("BITD.Roll")} ${attribute_label}

`; if ([...Object.keys(Attribute), ...Object.keys(Action)].includes(attribute_name)) { @@ -913,7 +909,7 @@ class BladesActor extends Actor { if (html instanceof HTMLElement) { html = $(html); } - const modifier = parseInt(`${html.find('[name="mod"]').attr("value") ?? 0}`); + const modifier = parseInt(`${html.find('[name="mod"]').attr("value") ?? 0}`, 10); const position = `${html.find('[name="pos"]').attr("value") ?? Position.risky}`; const effect = `${html.find('[name="fx"]').attr("value") ?? Effect.standard}`; const note = `${html.find('[name="note"]').attr("value") ?? 0}`; @@ -968,14 +964,14 @@ class BladesActor extends Actor { return arr[Math.floor(Math.random() * arr.length)]; } const randomGen = { - name: (gender) => { + name: (gen) => { return [ Math.random() <= titleChance ? sampleArray(Randomizers.NPC.name_title) : "", sampleArray([ - ...((gender ?? "").charAt(0).toLowerCase() !== "m" ? Randomizers.NPC.name_first.female : []), - ...((gender ?? "").charAt(0).toLowerCase() !== "f" ? Randomizers.NPC.name_first.male : []) + ...((gen ?? "").charAt(0).toLowerCase() !== "m" ? Randomizers.NPC.name_first.female : []), + ...((gen ?? "").charAt(0).toLowerCase() !== "f" ? Randomizers.NPC.name_first.male : []) ]), `"${sampleArray(Randomizers.NPC.name_alias)}"`, sampleArray(Randomizers.NPC.name_surname), @@ -994,9 +990,9 @@ class BladesActor extends Actor { trait: () => sampleArray(Randomizers.NPC.trait, persona.trait1.value, persona.trait2.value, persona.trait3.value, secret.trait.value), interests: () => sampleArray(Randomizers.NPC.interests, persona.interests.value, secret.interests.value), quirk: () => sampleArray(Randomizers.NPC.quirk, persona.quirk.value), - style: (gender = "") => sampleArray([ - ...(gender.charAt(0).toLowerCase() !== "m" ? Randomizers.NPC.style.female : []), - ...(gender.charAt(0).toLowerCase() !== "f" ? Randomizers.NPC.style.male : []) + style: (gen = "") => sampleArray([ + ...(gen.charAt(0).toLowerCase() !== "m" ? Randomizers.NPC.style.female : []), + ...(gen.charAt(0).toLowerCase() !== "f" ? Randomizers.NPC.style.male : []) ], persona.style.value) }; const gender = persona.gender.isLocked ? persona.gender.value : randomGen.gender(); diff --git a/module/blades-item.js b/module/blades-item.js index 3bf7e960..154b5495 100644 --- a/module/blades-item.js +++ b/module/blades-item.js @@ -5,9 +5,10 @@ |* ▌██████████████████░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░███████████████████▐ *| \* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ -import C, { BladesActorType, BladesItemType, Tag, RollModCategory, Factor, RollModStatus } from "./core/constants.js"; +import C, { BladesActorType, BladesItemType, Tag, Factor } from "./core/constants.js"; import U from "./core/utilities.js"; -import BladesActor from "./blades-actor.js"; +import { BladesActor } from "./documents/blades-actor-proxy.js"; +import { BladesRollMod } from "./blades-roll-collab.js"; class BladesItem extends Item { static async create(data, options = {}) { @@ -100,7 +101,10 @@ class BladesItem extends Item { return this.getFactorTotal(Factor.tier) + (this.hasTag("Fine") ? 1 : 0) + (this.parent?.getTaggedItemBonuses(this.tags) ?? 0) - + (BladesActor.IsType(this.parent, BladesActorType.pc) && this.parent?.crew ? this.parent.crew.getTaggedItemBonuses(this.tags) : 0); + + (BladesActor.IsType(this.parent, BladesActorType.pc) + && BladesActor.IsType(this.parent.crew, BladesActorType.crew) + ? this.parent.crew.getTaggedItemBonuses(this.tags) + : 0); } if (BladesItem.IsType(this, BladesItemType.design)) { return this.system.min_quality; @@ -174,95 +178,9 @@ class BladesItem extends Item { get rollPrimaryType() { return this.type; } get rollPrimaryImg() { return this.img; } get rollModsData() { - const { roll_mods } = this.system; - if (!roll_mods) { - return []; - } - const rollModsData = roll_mods.map((modString) => { - const pStrings = modString.split(/@/); - const nameString = U.pullElement(pStrings, (v) => typeof v === "string" && /^na/i.test(v)); - const nameVal = (typeof nameString === "string" && nameString.replace(/^.*:/, "")); - if (!nameVal) { - throw new Error(`RollMod Missing Name: '${modString}'`); - } - const catString = U.pullElement(pStrings, (v) => typeof v === "string" && /^cat/i.test(v)); - const catVal = (typeof catString === "string" && catString.replace(/^.*:/, "")); - if (!catVal || !(catVal in RollModCategory)) { - throw new Error(`RollMod Missing Category: '${modString}'`); - } - const posNegString = (U.pullElement(pStrings, (v) => typeof v === "string" && /^p/i.test(v)) || "posNeg:positive"); - const posNegVal = posNegString.replace(/^.*:/, ""); - const rollModData = { - id: `${nameVal}-${posNegVal}-${catVal}`, - name: nameVal, - category: catVal, - base_status: RollModStatus.ToggledOff, - modType: "general", - value: 1, - posNeg: posNegVal, - tooltip: "" - }; - pStrings.forEach((pString) => { - const [keyString, valString] = pString.split(/:/); - let val = /\|/.test(valString) ? valString.split(/\|/) : valString; - let key; - if (/^stat/i.test(keyString)) { - key = "base_status"; - } - else if (/^val/i.test(keyString)) { - key = "value"; - } - else if (/^eff|^ekey/i.test(keyString)) { - key = "effectKeys"; - } - else if (/^side|^ss/i.test(keyString)) { - key = "sideString"; - } - else if (/^s.*ame/i.test(keyString)) { - key = "source_name"; - } - else if (/^tool|^tip/i.test(keyString)) { - key = "tooltip"; - } - else if (/^ty/i.test(keyString)) { - key = "modType"; - } - else if (/^c.{0,10}r?.{0,3}ty/i.test(keyString)) { - key = "conditionalRollTypes"; - } - else if (/^a.{0,3}r?.{0,3}y/i.test(keyString)) { - key = "autoRollTypes"; - } - else if (/^c.{0,10}r?.{0,3}tr/i.test(keyString)) { - key = "conditionalRollTraits"; - } - else if (/^a.{0,3}r?.{0,3}tr/i.test(keyString)) { - key = "autoRollTraits"; - } - else { - throw new Error(`Bad Roll Mod Key: ${keyString}`); - } - if (key === "base_status" && val === "Conditional") { - val = RollModStatus.Hidden; - } - function extractValue(key, val) { - if (["value"].includes(key)) { - return U.pInt(val); - } - else if (["effectKeys", "conditionalRollTypes", "autoRollTypes,", "conditionalRollTraits", "autoRollTraits"].includes(key)) { - return [val].flat(); - } - else { - return val.replace(/%COLON%/g, ":"); - } - } - Object.assign(rollModData, { [key]: extractValue(key, val) }); - - }); - return rollModData; - }); + const rollModData = BladesRollMod.ParseDocRollMods(this); - return rollModsData; + return rollModData; } get rollOppID() { return this.id; } diff --git a/module/blades-roll-collab.js b/module/blades-roll-collab.js index 98a7d5b2..04f366eb 100644 --- a/module/blades-roll-collab.js +++ b/module/blades-roll-collab.js @@ -1074,6 +1074,95 @@ function isFactor(trait) { } function isNumber(trait) { return U.isInt(trait); } export class BladesRollMod { + static ParseDocRollMods(doc) { + const { roll_mods } = doc.system; + if (!roll_mods || roll_mods.length === 0) { + return []; + } + return roll_mods + .filter((elem) => typeof elem === "string") + .map((modString) => { + const pStrings = modString.split(/@/); + const nameString = U.pullElement(pStrings, (v) => typeof v === "string" && /^na/i.test(v)); + const nameVal = (typeof nameString === "string" && nameString.replace(/^.*:/, "")); + if (!nameVal) { + throw new Error(`RollMod Missing Name: '${modString}'`); + } + const catString = U.pullElement(pStrings, (v) => typeof v === "string" && /^cat/i.test(v)); + const catVal = (typeof catString === "string" && catString.replace(/^.*:/, "")); + if (!catVal || !(catVal in RollModCategory)) { + throw new Error(`RollMod Missing Category: '${modString}'`); + } + const posNegString = (U.pullElement(pStrings, (v) => typeof v === "string" && /^p/i.test(v)) || "posNeg:positive"); + const posNegVal = posNegString.replace(/^.*:/, ""); + const rollModData = { + id: `${nameVal}-${posNegVal}-${catVal}`, + name: nameVal, + category: catVal, + base_status: RollModStatus.ToggledOff, + modType: "general", + value: 1, + posNeg: posNegVal, + tooltip: "" + }; + pStrings.forEach((pString) => { + const [keyString, valString] = pString.split(/:/); + let val = /\|/.test(valString) ? valString.split(/\|/) : valString; + let key; + if (/^stat/i.test(keyString)) { + key = "base_status"; + } + else if (/^val/i.test(keyString)) { + key = "value"; + } + else if (/^eff|^ekey/i.test(keyString)) { + key = "effectKeys"; + } + else if (/^side|^ss/i.test(keyString)) { + key = "sideString"; + } + else if (/^s.*ame/i.test(keyString)) { + key = "source_name"; + } + else if (/^tool|^tip/i.test(keyString)) { + key = "tooltip"; + } + else if (/^ty/i.test(keyString)) { + key = "modType"; + } + else if (/^c.{0,10}r?.{0,3}ty/i.test(keyString)) { + key = "conditionalRollTypes"; + } + else if (/^a.{0,3}r?.{0,3}y/i.test(keyString)) { + key = "autoRollTypes"; + } + else if (/^c.{0,10}r?.{0,3}tr/i.test(keyString)) { + key = "conditionalRollTraits"; + } + else if (/^a.{0,3}r?.{0,3}tr/i.test(keyString)) { + key = "autoRollTraits"; + } + else { + throw new Error(`Bad Roll Mod Key: ${keyString}`); + } + if (key === "base_status" && val === "Conditional") { + val = RollModStatus.Hidden; + } + let valProcessed; + if (["value"].includes(key)) { + valProcessed = U.pInt(val); + } + else if (["effectKeys", "conditionalRollTypes", "autoRollTypes,", "conditionalRollTraits", "autoRollTraits"].includes(key)) { + valProcessed = [val].flat(); + } + else { + valProcessed = val.replace(/%COLON%/g, ":"); + } + Object.assign(rollModData, { [key]: valProcessed }); + }); + return rollModData; + }); + } get status() { if (this.user_status && [RollModStatus.ForcedOn, RollModStatus.ForcedOff, RollModStatus.Hidden].includes(this.user_status)) { return this.user_status; diff --git a/module/documents/actors/blades-crew.js b/module/documents/actors/blades-crew.js index fcbb6d3f..ec72dd55 100644 --- a/module/documents/actors/blades-crew.js +++ b/module/documents/actors/blades-crew.js @@ -5,9 +5,9 @@ |* ▌██████████████████░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░███████████████████▐ *| \* ****▌███████████████████████████████████████████████████████████████████████████▐**** */ -import { BladesItemType, RollModCategory, Factor, RollModStatus } from "../../core/constants.js"; -import U from "../../core/utilities.js"; +import { BladesItemType, Factor } from "../../core/constants.js"; import BladesActor from "../../blades-actor.js"; +import { BladesRollMod } from "../../blades-roll-collab.js"; class BladesCrew extends BladesActor { static async create(data, options = {}) { @@ -27,92 +27,7 @@ class BladesCrew extends BladesActor { return super.create(data, options); } get rollModsData() { - const { roll_mods } = this.system; - if (roll_mods.length === 0) { - return []; - } - const rollModsData = roll_mods - .filter((elem) => elem !== undefined) - .map((modString) => { - const pStrings = modString.split(/@/); - const nameString = U.pullElement(pStrings, (v) => typeof v === "string" && /^na/i.test(v)); - const nameVal = (typeof nameString === "string" && nameString.replace(/^.*:/, "")); - if (!nameVal) { - throw new Error(`RollMod Missing Name: '${modString}'`); - } - const catString = U.pullElement(pStrings, (v) => typeof v === "string" && /^cat/i.test(v)); - const catVal = (typeof catString === "string" && catString.replace(/^.*:/, "")); - if (!catVal || !(catVal in RollModCategory)) { - throw new Error(`RollMod Missing Category: '${modString}'`); - } - const posNegString = (U.pullElement(pStrings, (v) => typeof v === "string" && /^p/i.test(v)) || "posNeg:positive"); - const posNegVal = posNegString.replace(/^.*:/, ""); - const rollModData = { - id: `${nameVal}-${posNegVal}-${catVal}`, - name: nameVal, - category: catVal, - base_status: RollModStatus.ToggledOff, - modType: "general", - value: 1, - posNeg: posNegVal, - tooltip: "" - }; - pStrings.forEach((pString) => { - const [keyString, valString] = pString.split(/:/); - let val = /\|/.test(valString) ? valString.split(/\|/) : valString; - let key; - if (/^stat/i.test(keyString)) { - key = "base_status"; - } - else if (/^val/i.test(keyString)) { - key = "value"; - } - else if (/^eff|^ekey/i.test(keyString)) { - key = "effectKeys"; - } - else if (/^side|^ss/i.test(keyString)) { - key = "sideString"; - } - else if (/^s.*ame/i.test(keyString)) { - key = "source_name"; - } - else if (/^tool|^tip/i.test(keyString)) { - key = "tooltip"; - } - else if (/^ty/i.test(keyString)) { - key = "modType"; - } - else if (/^c.{0,10}r?.{0,3}ty/i.test(keyString)) { - key = "conditionalRollTypes"; - } - else if (/^a.{0,3}r?.{0,3}y/i.test(keyString)) { - key = "autoRollTypes"; - } - else if (/^c.{0,10}r?.{0,3}tr/i.test(keyString)) { - key = "conditionalRollTraits"; - } - else if (/^a.{0,3}r?.{0,3}tr/i.test(keyString)) { - key = "autoRollTraits"; - } - else { - throw new Error(`Bad Roll Mod Key: ${keyString}`); - } - if (key === "base_status" && val === "Conditional") { - val = RollModStatus.Hidden; - } - let processedVal; - if (["effectKeys", "conditionalRollTypes", "autoRollTypes,", "conditionalRollTraits", "autoRollTraits"].includes(key)) { - processedVal = [val].flat(); - } - else { - processedVal = val.replace(/%COLON%/g, ":"); - } - const value = key === "value" ? U.pInt(val) : processedVal; - Object.assign(rollModData, { [key]: value }); - }); - return rollModData; - }); - return rollModsData; + return BladesRollMod.ParseDocRollMods(this); } get rollFactors() { const factorData = { diff --git a/module/documents/actors/blades-pc.js b/module/documents/actors/blades-pc.js index 0e7b6083..a0e5dfc1 100644 --- a/module/documents/actors/blades-pc.js +++ b/module/documents/actors/blades-pc.js @@ -9,6 +9,7 @@ import BladesItem from "../../blades-item.js"; import C, { Attribute, Harm, BladesActorType, BladesItemType, Tag, RollModCategory, Factor, RollModStatus } from "../../core/constants.js"; import U from "../../core/utilities.js"; import BladesActor from "../../blades-actor.js"; +import { BladesRollMod } from "../../blades-roll-collab.js"; class BladesPC extends BladesActor { static async create(data, options = {}) { @@ -146,9 +147,8 @@ class BladesPC extends BladesActor { return 0; } return Object.keys(this.system.trauma.checked) - .filter((traumaName) => { - return this.system.trauma.active[traumaName] && this.system.trauma.checked[traumaName]; - }) + .filter((traumaName) => + this.system.trauma.active[traumaName] && this.system.trauma.checked[traumaName]) .length; } get traumaList() { @@ -159,7 +159,7 @@ class BladesPC extends BladesActor { return {}; } return U.objFilter(this.system.trauma.checked, - (v, traumaName) => Boolean(traumaName in this.system.trauma.active && this.system.trauma.active[traumaName])); + (_v, traumaName) => Boolean(traumaName in this.system.trauma.active && this.system.trauma.active[traumaName])); } get currentLoad() { if (!BladesActor.IsType(this, BladesActorType.pc)) { @@ -215,93 +215,7 @@ class BladesPC extends BladesActor { get rollPrimaryType() { return this.type; } get rollPrimaryImg() { return this.img; } get rollModsData() { - const { roll_mods } = this.system; - if (roll_mods.length === 0) { - return []; - } - const rollModsData = roll_mods - .filter((elem) => elem !== undefined) - .map((modString) => { - const pStrings = modString.split(/@/); - const nameString = U.pullElement(pStrings, (v) => typeof v === "string" && /^na/i.test(v)); - const nameVal = (typeof nameString === "string" && nameString.replace(/^.*:/, "")); - if (!nameVal) { - throw new Error(`RollMod Missing Name: '${modString}'`); - } - const catString = U.pullElement(pStrings, (v) => typeof v === "string" && /^cat/i.test(v)); - const catVal = (typeof catString === "string" && catString.replace(/^.*:/, "")); - if (!catVal || !(catVal in RollModCategory)) { - throw new Error(`RollMod Missing Category: '${modString}'`); - } - const posNegString = (U.pullElement(pStrings, (v) => typeof v === "string" && /^p/i.test(v)) || "posNeg:positive"); - const posNegVal = posNegString.replace(/^.*:/, ""); - const rollModData = { - id: `${nameVal}-${posNegVal}-${catVal}`, - name: nameVal, - category: catVal, - base_status: RollModStatus.ToggledOff, - modType: "general", - value: 1, - posNeg: posNegVal, - tooltip: "" - }; - pStrings.forEach((pString) => { - const [keyString, valString] = pString.split(/:/); - let val = /\|/.test(valString) ? valString.split(/\|/) : valString; - let key; - if (/^stat/i.test(keyString)) { - key = "base_status"; - } - else if (/^val/i.test(keyString)) { - key = "value"; - } - else if (/^eff|^ekey/i.test(keyString)) { - key = "effectKeys"; - } - else if (/^side|^ss/i.test(keyString)) { - key = "sideString"; - } - else if (/^s.*ame/i.test(keyString)) { - key = "source_name"; - } - else if (/^tool|^tip/i.test(keyString)) { - key = "tooltip"; - } - else if (/^ty/i.test(keyString)) { - key = "modType"; - } - else if (/^c.{0,10}r?.{0,3}ty/i.test(keyString)) { - key = "conditionalRollTypes"; - } - else if (/^a.{0,3}r?.{0,3}y/i.test(keyString)) { - key = "autoRollTypes"; - } - else if (/^c.{0,10}r?.{0,3}tr/i.test(keyString)) { - key = "conditionalRollTraits"; - } - else if (/^a.{0,3}r?.{0,3}tr/i.test(keyString)) { - key = "autoRollTraits"; - } - else { - throw new Error(`Bad Roll Mod Key: ${keyString}`); - } - if (key === "base_status" && val === "Conditional") { - val = RollModStatus.Hidden; - } - let valProcessed; - if (["value"].includes(key)) { - valProcessed = U.pInt(val); - } - else if (["effectKeys", "conditionalRollTypes", "autoRollTypes,", "conditionalRollTraits", "autoRollTraits"].includes(key)) { - valProcessed = [val].flat(); - } - else { - valProcessed = val.replace(/%COLON%/g, ":"); - } - Object.assign(rollModData, { [key]: valProcessed }); - }); - return rollModData; - }); + const rollModsData = BladesRollMod.ParseDocRollMods(this); [[/1d/, RollModCategory.roll], [/Less Effect/, RollModCategory.effect]].forEach(([effectPat, effectCat]) => { const { one: harmConditionOne, two: harmConditionTwo } = Object.values(this.system.harm) .find((harmData) => effectPat.test(harmData.effect)) ?? {}; diff --git a/ts/blades-actor.ts b/ts/blades-actor.ts index 5a50b04a..ba21cce9 100644 --- a/ts/blades-actor.ts +++ b/ts/blades-actor.ts @@ -49,7 +49,7 @@ class BladesActor extends Actor implements BladesDocument { .filter((actor) => actor.hasTag(...tags)) as Array>; } - static IsType(doc: unknown, ...types: T[]): doc is BladesActor & BladesActorOfType { + static IsType(doc: unknown, ...types: T[]): doc is BladesActorOfType { const typeSet = new Set(types); return doc instanceof BladesActor && typeSet.has(doc.type); } @@ -1184,6 +1184,8 @@ class BladesActor extends Actor implements BladesDocument { declare interface BladesActor { get id(): string; + get name(): string; + get img(): string; get type(): BladesActorType; get items(): EmbeddedCollection; system: BladesActorSystem, diff --git a/ts/blades-item.ts b/ts/blades-item.ts index a94dd6af..c7f4760e 100644 --- a/ts/blades-item.ts +++ b/ts/blades-item.ts @@ -1,7 +1,7 @@ -import C, {BladesActorType, BladesItemType, Tag, RollModCategory, Factor, RollModStatus} from "./core/constants.js"; +import C, {BladesActorType, BladesItemType, Tag, Factor} from "./core/constants.js"; import U from "./core/utilities.js"; -import BladesActor from "./blades-actor.js"; -import BladesRollCollab from "./blades-roll-collab.js"; +import {BladesActor, BladesCrew} from "./documents/blades-actor-proxy.js"; +import {BladesRollMod} from "./blades-roll-collab.js"; import type {ItemDataConstructorData} from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/data/data.mjs/itemData.js"; class BladesItem extends Item implements BladesDocument, @@ -56,7 +56,7 @@ class BladesItem extends Item implements BladesDocument, .filter((item) => item.hasTag(...tags)); } - static IsType(doc: unknown, ...types: T[]): doc is BladesItem & BladesItemOfType { + static IsType(doc: unknown, ...types: T[]): doc is BladesItemOfType { const typeSet = new Set(types); return doc instanceof BladesItem && typeSet.has(doc.type); } @@ -114,7 +114,12 @@ class BladesItem extends Item implements BladesDocument, return this.getFactorTotal(Factor.tier) + (this.hasTag("Fine") ? 1 : 0) + (this.parent?.getTaggedItemBonuses(this.tags) ?? 0) - + (BladesActor.IsType(this.parent, BladesActorType.pc) && this.parent?.crew ? this.parent.crew.getTaggedItemBonuses(this.tags) : 0); + + ( + BladesActor.IsType(this.parent, BladesActorType.pc) + && BladesActor.IsType(this.parent.crew, BladesActorType.crew) + ? this.parent.crew.getTaggedItemBonuses(this.tags) + : 0 + ); } if (BladesItem.IsType(this, BladesItemType.design)) { return this.system.min_quality } return this.getFactorTotal(Factor.tier); @@ -190,85 +195,16 @@ class BladesItem extends Item implements BladesDocument, // #region BladesRollCollab.PrimaryDoc Implementation get rollPrimaryID() { return this.id } get rollPrimaryDoc() { return this } - get rollPrimaryName() { return this.name! } + get rollPrimaryName() { return this.name } get rollPrimaryType() { return this.type } - get rollPrimaryImg() { return this.img! } + get rollPrimaryImg() { return this.img } get rollModsData(): BladesRollCollab.RollModData[] { - const {roll_mods} = this.system; - if (!roll_mods) { return [] } - - const rollModsData: BladesRollCollab.RollModData[] = roll_mods.map((modString) => { - const pStrings = modString.split(/@/); - const nameString = U.pullElement(pStrings, (v) => typeof v === "string" && /^na/i.test(v)); - const nameVal = (typeof nameString === "string" && nameString.replace(/^.*:/, "")) as string|false; - if (!nameVal) { throw new Error(`RollMod Missing Name: '${modString}'`) } - const catString = U.pullElement(pStrings, (v) => typeof v === "string" && /^cat/i.test(v)); - const catVal = (typeof catString === "string" && catString.replace(/^.*:/, "")) as RollModCategory|false; - if (!catVal || !(catVal in RollModCategory)) { throw new Error(`RollMod Missing Category: '${modString}'`) } - const posNegString = (U.pullElement(pStrings, (v) => typeof v === "string" && /^p/i.test(v)) || "posNeg:positive"); - const posNegVal = posNegString.replace(/^.*:/, "") as "positive"|"negative"; - - const rollModData: BladesRollCollab.RollModData = { - id: `${nameVal}-${posNegVal}-${catVal}`, - name: nameVal, - category: catVal, - base_status: RollModStatus.ToggledOff, - modType: "general", - value: 1, - posNeg: posNegVal, - tooltip: "" - }; - - pStrings.forEach((pString) => { - const [keyString, valString] = pString.split(/:/) as [string, string]; - let val: string|string[] = /\|/.test(valString) ? valString.split(/\|/) : valString; - let key: KeyOf; - if (/^stat/i.test(keyString)) { key = "base_status" } else - if (/^val/i.test(keyString)) { key = "value" } else - if (/^eff|^ekey/i.test(keyString)) { key = "effectKeys" } else - if (/^side|^ss/i.test(keyString)) { key = "sideString" } else - if (/^s.*ame/i.test(keyString)) { key = "source_name" } else - if (/^tool|^tip/i.test(keyString)) { key = "tooltip" } else - if (/^ty/i.test(keyString)) { key = "modType" } else - if (/^c.{0,10}r?.{0,3}ty/i.test(keyString)) {key = "conditionalRollTypes"} else - if (/^a.{0,3}r?.{0,3}y/i.test(keyString)) {key = "autoRollTypes"} else - if (/^c.{0,10}r?.{0,3}tr/i.test(keyString)) {key = "conditionalRollTraits"} else - if (/^a.{0,3}r?.{0,3}tr/i.test(keyString)) {key = "autoRollTraits"} else { - throw new Error(`Bad Roll Mod Key: ${keyString}`); - } - - if (key === "base_status" && val === "Conditional") { - val = RollModStatus.Hidden; - } - - function extractValue(key: string, val: string | string[]): any { - if (["value"].includes(key)) { - return U.pInt(val); - } else if (["effectKeys", "conditionalRollTypes", "autoRollTypes,", "conditionalRollTraits", "autoRollTraits"].includes(key)) { - return [val].flat(); - } else { - return (val as string).replace(/%COLON%/g, ":"); - } - } - Object.assign(rollModData, {[key]: extractValue(key, val)}); - - // Object.assign( - // rollModData, - // {[key]: ["value"].includes(key) - // ? U.pInt(val) - // : (["effectKeys", "conditionalRollTypes", "autoRollTypes,", "conditionalRollTraits", "autoRollTraits"].includes(key) - // ? [val].flat() - // : (val as string).replace(/%COLON%/g, ":"))} - // ); - }); - - return rollModData; - }); + const rollModData = BladesRollMod.ParseDocRollMods(this); // Add roll mods from COHORT harm - return rollModsData; + return rollModData; } // #endregion @@ -390,6 +326,8 @@ class BladesItem extends Item implements BladesDocument, declare interface BladesItem { get id(): string; + get name(): string; + get img(): string; get type(): BladesItemType, parent: BladesActor | null, system: BladesItemSystem diff --git a/ts/blades-roll-collab.ts b/ts/blades-roll-collab.ts index 132eb928..709c931f 100644 --- a/ts/blades-roll-collab.ts +++ b/ts/blades-roll-collab.ts @@ -1186,6 +1186,76 @@ function isNumber(trait: string | number): trait is BladesRollCollab.RollTrait & // #region *** CLASS *** BladesRollMod ~ export class BladesRollMod { + static ParseDocRollMods(doc: BladesDoc): BladesRollCollab.RollModData[] { + + const {roll_mods} = doc.system; + if (!roll_mods || roll_mods.length === 0) { return [] } + + return (roll_mods + .filter((elem) => typeof elem === "string") as string[]) + .map((modString) => { + const pStrings = modString.split(/@/); + const nameString = U.pullElement(pStrings, (v) => typeof v === "string" && /^na/i.test(v)); + const nameVal = (typeof nameString === "string" && nameString.replace(/^.*:/, "")) as string|false; + if (!nameVal) { throw new Error(`RollMod Missing Name: '${modString}'`) } + const catString = U.pullElement(pStrings, (v) => typeof v === "string" && /^cat/i.test(v)); + const catVal = (typeof catString === "string" && catString.replace(/^.*:/, "")) as RollModCategory|false; + if (!catVal || !(catVal in RollModCategory)) { throw new Error(`RollMod Missing Category: '${modString}'`) } + const posNegString = (U.pullElement(pStrings, (v) => typeof v === "string" && /^p/i.test(v)) || "posNeg:positive"); + const posNegVal = posNegString.replace(/^.*:/, "") as "positive"|"negative"; + + const rollModData: BladesRollCollab.RollModData = { + id: `${nameVal}-${posNegVal}-${catVal}`, + name: nameVal, + category: catVal, + base_status: RollModStatus.ToggledOff, + modType: "general", + value: 1, + posNeg: posNegVal, + tooltip: "" + }; + + pStrings.forEach((pString) => { + const [keyString, valString] = pString.split(/:/) as [string, string]; + let val: string|string[] = /\|/.test(valString) ? valString.split(/\|/) : valString; + let key: KeyOf; + if (/^stat/i.test(keyString)) { key = "base_status" } else + if (/^val/i.test(keyString)) { key = "value" } else + if (/^eff|^ekey/i.test(keyString)) { key = "effectKeys" } else + if (/^side|^ss/i.test(keyString)) { key = "sideString" } else + if (/^s.*ame/i.test(keyString)) { key = "source_name" } else + if (/^tool|^tip/i.test(keyString)) { key = "tooltip" } else + if (/^ty/i.test(keyString)) { key = "modType" } else + if (/^c.{0,10}r?.{0,3}ty/i.test(keyString)) {key = "conditionalRollTypes"} else + if (/^a.{0,3}r?.{0,3}y/i.test(keyString)) {key = "autoRollTypes"} else + if (/^c.{0,10}r?.{0,3}tr/i.test(keyString)) {key = "conditionalRollTraits"} else + if (/^a.{0,3}r?.{0,3}tr/i.test(keyString)) {key = "autoRollTraits"} else { + throw new Error(`Bad Roll Mod Key: ${keyString}`); + } + + if (key === "base_status" && val === "Conditional") { + val = RollModStatus.Hidden; + } + + let valProcessed; + if (["value"].includes(key)) { + valProcessed = U.pInt(val); + } else if (["effectKeys", "conditionalRollTypes", "autoRollTypes,", "conditionalRollTraits", "autoRollTraits"].includes(key)) { + valProcessed = [val].flat(); + } else { + valProcessed = (val as string).replace(/%COLON%/g, ":"); + } + + Object.assign( + rollModData, + {[key]: valProcessed} + ); + }); + + return rollModData; + }); + } + get status() { if (this.user_status && [RollModStatus.ForcedOn, RollModStatus.ForcedOff, RollModStatus.Hidden].includes(this.user_status)) { return this.user_status; diff --git a/ts/documents/actors/blades-crew.ts b/ts/documents/actors/blades-crew.ts index c1772d00..bde89977 100644 --- a/ts/documents/actors/blades-crew.ts +++ b/ts/documents/actors/blades-crew.ts @@ -1,8 +1,7 @@ import BladesItem from "../../blades-item.js"; -import {BladesActorType, Playbook, BladesItemType, RollModCategory, Factor, RollModStatus} from "../../core/constants.js"; -import U from "../../core/utilities.js"; +import {BladesActorType, Playbook, BladesItemType, Factor} from "../../core/constants.js"; import BladesActor from "../../blades-actor.js"; -import BladesRollCollab from "../../blades-roll-collab.js"; +import BladesRollCollab, {BladesRollMod} from "../../blades-roll-collab.js"; import type {ActorDataConstructorData} from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/data/data.mjs/actorData.js"; class BladesCrew extends BladesActor implements BladesActorSubClass.Crew, @@ -38,70 +37,7 @@ class BladesCrew extends BladesActor implements BladesActorSubClass.Crew, // #region BladesRollCollab Implementation get rollModsData(): BladesRollCollab.RollModData[] { - const {roll_mods} = this.system; - if (roll_mods.length === 0) {return []} - - const rollModsData = roll_mods - .filter((elem): elem is string => elem !== undefined) - .map((modString) => { - const pStrings = modString.split(/@/); - const nameString = U.pullElement(pStrings, (v) => typeof v === "string" && /^na/i.test(v)); - const nameVal = (typeof nameString === "string" && nameString.replace(/^.*:/, "")) as string | false; - if (!nameVal) {throw new Error(`RollMod Missing Name: '${modString}'`)} - const catString = U.pullElement(pStrings, (v) => typeof v === "string" && /^cat/i.test(v)); - const catVal = (typeof catString === "string" && catString.replace(/^.*:/, "")) as RollModCategory | false; - if (!catVal || !(catVal in RollModCategory)) {throw new Error(`RollMod Missing Category: '${modString}'`)} - const posNegString = (U.pullElement(pStrings, (v) => typeof v === "string" && /^p/i.test(v)) || "posNeg:positive"); - const posNegVal = posNegString.replace(/^.*:/, "") as "positive" | "negative"; - - const rollModData: BladesRollCollab.RollModData = { - id: `${nameVal}-${posNegVal}-${catVal}`, - name: nameVal, - category: catVal, - base_status: RollModStatus.ToggledOff, - modType: "general", - value: 1, - posNeg: posNegVal, - tooltip: "" - }; - - pStrings.forEach((pString) => { - const [keyString, valString] = pString.split(/:/) as [string, string]; - let val: string | string[] = /\|/.test(valString) ? valString.split(/\|/) : valString; - let key: KeyOf; - if (/^stat/i.test(keyString)) {key = "base_status"} else - if (/^val/i.test(keyString)) {key = "value"} else - if (/^eff|^ekey/i.test(keyString)) {key = "effectKeys"} else - if (/^side|^ss/i.test(keyString)) {key = "sideString"} else - if (/^s.*ame/i.test(keyString)) {key = "source_name"} else - if (/^tool|^tip/i.test(keyString)) {key = "tooltip"} else - if (/^ty/i.test(keyString)) {key = "modType"} else - if (/^c.{0,10}r?.{0,3}ty/i.test(keyString)) {key = "conditionalRollTypes"} else - if (/^a.{0,3}r?.{0,3}y/i.test(keyString)) {key = "autoRollTypes"} else - if (/^c.{0,10}r?.{0,3}tr/i.test(keyString)) {key = "conditionalRollTraits"} else - if (/^a.{0,3}r?.{0,3}tr/i.test(keyString)) {key = "autoRollTraits"} else { - throw new Error(`Bad Roll Mod Key: ${keyString}`); - } - - if (key === "base_status" && val === "Conditional") { - val = RollModStatus.Hidden; - } - let processedVal: any; - if (["effectKeys", "conditionalRollTypes", "autoRollTypes,", "conditionalRollTraits", "autoRollTraits"].includes(key)) { - processedVal = [val].flat(); - } else { - processedVal = (val as string).replace(/%COLON%/g, ":"); - } - - const value = key === "value" ? U.pInt(val) : processedVal; - - Object.assign(rollModData, {[key]: value}); - }); - - return rollModData; - }); - - return rollModsData; + return BladesRollMod.ParseDocRollMods(this); } get rollFactors(): Partial> { @@ -133,16 +69,16 @@ class BladesCrew extends BladesActor implements BladesActorSubClass.Crew, // #region BladesRollCollab.PrimaryDoc Implementation get rollPrimaryID() {return this.id} get rollPrimaryDoc() {return this} - get rollPrimaryName() {return this.name!} + get rollPrimaryName() {return this.name} get rollPrimaryType() {return this.type} - get rollPrimaryImg() {return this.img!} + get rollPrimaryImg() {return this.img} // #endregion // #region BladesRollCollab.ParticipantDoc Implementation get rollParticipantID() {return this.id} get rollParticipantDoc() {return this} - get rollParticipantIcon() {return this.playbook?.img ?? this.img!} - get rollParticipantName() {return this.name!} + get rollParticipantIcon() {return this.playbook?.img ?? this.img} + get rollParticipantName() {return this.name} get rollParticipantType() {return this.type} get rollParticipantModsData(): BladesRollCollab.RollModData[] {return []} diff --git a/ts/documents/actors/blades-pc.ts b/ts/documents/actors/blades-pc.ts index edbf6037..53d23e9e 100644 --- a/ts/documents/actors/blades-pc.ts +++ b/ts/documents/actors/blades-pc.ts @@ -3,7 +3,7 @@ import C, {Playbook, Attribute, Action, Harm, BladesActorType, BladesItemType, T import U from "../../core/utilities.js"; import BladesActor from "../../blades-actor.js"; import BladesCrew from "./blades-crew.js"; -import BladesRollCollab from "../../blades-roll-collab.js"; +import BladesRollCollab, {BladesRollMod} from "../../blades-roll-collab.js"; import type {ActorDataConstructorData} from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/data/data.mjs/actorData.js"; @@ -147,10 +147,9 @@ class BladesPC extends BladesActor implements BladesActorSubClass.Scoundrel, get trauma(): number { if (!BladesActor.IsType(this, BladesActorType.pc)) { return 0 } return Object.keys(this.system.trauma.checked) - .filter((traumaName) => { + .filter((traumaName) => // @ts-ignore Compiler linter mismatch. - return this.system.trauma.active[traumaName] && this.system.trauma.checked[traumaName]; - }) + this.system.trauma.active[traumaName] && this.system.trauma.checked[traumaName]) .length; } get traumaList(): string[] { @@ -162,7 +161,7 @@ class BladesPC extends BladesActor implements BladesActorSubClass.Scoundrel, return U.objFilter( this.system.trauma.checked, // @ts-ignore Compiler linter mismatch. - (v: unknown, traumaName: string): boolean => Boolean(traumaName in this.system.trauma.active && this.system.trauma.active[traumaName]) + (_v: unknown, traumaName: string): boolean => Boolean(traumaName in this.system.trauma.active && this.system.trauma.active[traumaName]) ) as Record; } get currentLoad(): number { @@ -216,77 +215,13 @@ class BladesPC extends BladesActor implements BladesActorSubClass.Scoundrel, get rollPrimaryID() { return this.id } get rollPrimaryDoc() { return this } - get rollPrimaryName() { return this.name! } + get rollPrimaryName() { return this.name } get rollPrimaryType() { return this.type } - get rollPrimaryImg() { return this.img! } + get rollPrimaryImg() { return this.img } get rollModsData(): BladesRollCollab.RollModData[] { - const {roll_mods} = this.system; - if (roll_mods.length === 0) { return [] } - - const rollModsData = roll_mods - .filter((elem): elem is string => elem !== undefined) - .map((modString) => { - const pStrings = modString.split(/@/); - const nameString = U.pullElement(pStrings, (v) => typeof v === "string" && /^na/i.test(v)); - const nameVal = (typeof nameString === "string" && nameString.replace(/^.*:/, "")) as string|false; - if (!nameVal) { throw new Error(`RollMod Missing Name: '${modString}'`) } - const catString = U.pullElement(pStrings, (v) => typeof v === "string" && /^cat/i.test(v)); - const catVal = (typeof catString === "string" && catString.replace(/^.*:/, "")) as RollModCategory|false; - if (!catVal || !(catVal in RollModCategory)) { throw new Error(`RollMod Missing Category: '${modString}'`) } - const posNegString = (U.pullElement(pStrings, (v) => typeof v === "string" && /^p/i.test(v)) || "posNeg:positive"); - const posNegVal = posNegString.replace(/^.*:/, "") as "positive"|"negative"; - - const rollModData: BladesRollCollab.RollModData = { - id: `${nameVal}-${posNegVal}-${catVal}`, - name: nameVal, - category: catVal, - base_status: RollModStatus.ToggledOff, - modType: "general", - value: 1, - posNeg: posNegVal, - tooltip: "" - }; - - pStrings.forEach((pString) => { - const [keyString, valString] = pString.split(/:/) as [string, string]; - let val: string|string[] = /\|/.test(valString) ? valString.split(/\|/) : valString; - let key: KeyOf; - if (/^stat/i.test(keyString)) { key = "base_status" } else - if (/^val/i.test(keyString)) { key = "value" } else - if (/^eff|^ekey/i.test(keyString)) { key = "effectKeys" } else - if (/^side|^ss/i.test(keyString)) { key = "sideString" } else - if (/^s.*ame/i.test(keyString)) { key = "source_name" } else - if (/^tool|^tip/i.test(keyString)) { key = "tooltip" } else - if (/^ty/i.test(keyString)) { key = "modType" } else - if (/^c.{0,10}r?.{0,3}ty/i.test(keyString)) {key = "conditionalRollTypes"} else - if (/^a.{0,3}r?.{0,3}y/i.test(keyString)) {key = "autoRollTypes"} else - if (/^c.{0,10}r?.{0,3}tr/i.test(keyString)) {key = "conditionalRollTraits"} else - if (/^a.{0,3}r?.{0,3}tr/i.test(keyString)) {key = "autoRollTraits"} else { - throw new Error(`Bad Roll Mod Key: ${keyString}`); - } - - if (key === "base_status" && val === "Conditional") { - val = RollModStatus.Hidden; - } - - let valProcessed; - if (["value"].includes(key)) { - valProcessed = U.pInt(val); - } else if (["effectKeys", "conditionalRollTypes", "autoRollTypes,", "conditionalRollTraits", "autoRollTraits"].includes(key)) { - valProcessed = [val].flat(); - } else { - valProcessed = (val as string).replace(/%COLON%/g, ":"); - } - - Object.assign( - rollModData, - {[key]: valProcessed} - ); - }); - return rollModData; - }); + const rollModsData = BladesRollMod.ParseDocRollMods(this); // Add roll mods from harm [[/1d/, RollModCategory.roll] as const, [/Less Effect/, RollModCategory.effect] as const].forEach(([effectPat, effectCat]) => {