diff --git a/css/style.css b/css/style.css index f2938745..9ebf5177 100644 --- a/css/style.css +++ b/css/style.css @@ -14433,7 +14433,7 @@ template { --sheet-top-height: 100px; --sheet-mid-height: 0px; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.actor.faction .window-content form .sheet-root > section.sheet-top { - grid-template-areas: ". . title title title title title title title title tier" ". . subtitle subtitle subtitle subtitle subtitle subtitle subtitle subtitle tier"; + grid-template-areas: ". . title title title title title title title title tier" ". . subtitle subtitle subtitle subtitle subtitle subtitle subtitle subtitle tier"; grid-template-rows: 60px 40px; } :root body.vtt.game.system-eunos-blades .app.window-app.sheet.actor.faction .window-content form .sheet-root > section.sheet-top .sheet-title { --sheet-title-font-size: 3rem; } diff --git a/ts/@types/blades-item.d.ts b/ts/@types/blades-item.d.ts index a6b7a311..36a145c0 100644 --- a/ts/@types/blades-item.d.ts +++ b/ts/@types/blades-item.d.ts @@ -213,19 +213,6 @@ declare global { Partial, Partial { } - // Distinguishing schema types for BladesItem subtypes - // type BladesItemOfType = ( - // T extends BladesItemType.clock_keeper ? BladesClockKeeper - // : T extends BladesItemType.gm_tracker ? BladesGMTracker - // : T extends BladesItemType.score ? BladesScore - // : T extends BladesItemType.location ? BladesLocation : BladesItem - // ) & BladesItem & { - // system: ExtractBladesItemSystem - // }; - - - - type BladesItemOfType = BladesItem & { system: ExtractBladesItemSystem }; diff --git a/ts/@types/index.d.ts b/ts/@types/index.d.ts index bcd47c92..dbdc391c 100644 --- a/ts/@types/index.d.ts +++ b/ts/@types/index.d.ts @@ -11,7 +11,7 @@ import "./blades-general-types"; import "./blades-document"; import "./blades-actor"; -import "./blades-actor-sheet"; +import "./blades-pc-sheet"; import "./blades-item"; import "./blades-item-sheet"; diff --git a/ts/blades-actor.ts b/ts/blades-actor.ts index 7c94981b..f7a75ed8 100644 --- a/ts/blades-actor.ts +++ b/ts/blades-actor.ts @@ -875,31 +875,10 @@ class BladesActor extends Actor implements BladesDocument { eLog.checkLog("actorTrigger", "_onCreateDescendantDocuments", {parent, collection, docs, data, options, userId}); docs.forEach(async (doc) => { - if (doc instanceof BladesItem) { - switch (doc.type) { - case BladesItemType.vice: { - if (!BladesActor.IsType(this, BladesActorType.pc)) { return } - this.activeSubActors - .filter((subActor) => subActor.hasTag(Tag.NPC.VicePurveyor) && !subActor.hasTag(doc.name as Vice)) - .forEach((subActor) => this.remSubActor(subActor)); - break; - } - // case BladesItemType.playbook: { - // if (!BladesActor.IsType(this, BladesActorType.pc)) { return } - // await this.update({ - // "system.trauma.active": Object.assign( - // Object.fromEntries(Object.keys(this.system.trauma.active).map((tCond: string) => [tCond, false])), - // Object.fromEntries((doc.system.trauma_conditions ?? []).map((tCond: string) => [tCond, true])) - // ), - // "system.trauma.checked": Object.assign( - // Object.fromEntries(Object.keys(this.system.trauma.checked).map((tCond: string) => [tCond, false])), - // Object.fromEntries((doc.system.trauma_conditions ?? []).map((tCond: string) => [tCond, false])) - // ) - // }); - // break; - // } - // no default - } + if (BladesItem.IsType(doc, BladesItemType.vice) && BladesActor.IsType(this, BladesActorType.pc)) { + this.activeSubActors + .filter((subActor) => subActor.hasTag(Tag.NPC.VicePurveyor) && !subActor.hasTag(doc.name as Vice)) + .forEach((subActor) => this.remSubActor(subActor)); } }); } diff --git a/ts/blades-roll-collab.ts b/ts/blades-roll-collab.ts index ccf00b8f..f1d0129c 100644 --- a/ts/blades-roll-collab.ts +++ b/ts/blades-roll-collab.ts @@ -2656,23 +2656,41 @@ class BladesRollCollab extends DocumentSheet { }; _rollMods?: BladesRollMod[]; + /** + * Compare function for sorting roll mods. + * @param modA - First mod to compare. + * @param modB - Second mod to compare. + * @returns {number} - Comparison result. + */ + private compareMods(modA: BladesRollMod, modB: BladesRollMod): number { + // Define the order of mod names for sorting + const modOrder = ["Bargain", "Assist", "Setup"]; + + // Check for basic push + if (modA.isBasicPush) {return -1} + if (modB.isBasicPush) {return 1} + + // Check for active Bargain + if (modA.name === "Bargain" && modA.isActive) {return -1} + if (modB.name === "Bargain" && modB.isActive) {return 1} + + // Check for push + if (modA.isPush) {return -1} + if (modB.isPush) {return 1} + + // Check for mod name order + const modAIndex = modOrder.indexOf(modA.name); + const modBIndex = modOrder.indexOf(modB.name); + if (modAIndex !== -1 && modBIndex !== -1) { + return modAIndex - modBIndex; + } + + // Default to alphabetical order + return modA.name.localeCompare(modB.name); + } get rollMods(): BladesRollMod[] { if (!this._rollMods) { throw new Error("[get rollMods] No roll mods found!") } - return this._rollMods.sort((modA, modB) => { - if (modA.isBasicPush) { return -1 } - if (modB.isBasicPush) { return 1 } - if (modA.name === "Bargain" && modA.isActive) { return -1 } - if (modB.name === "Bargain" && modB.isActive) { return 1 } - if (modA.isPush) { return -1 } - if (modB.isPush) { return 1 } - if (modA.name === "Bargain") { return -1 } - if (modB.name === "Bargain") { return 1 } - if (modA.name === "Assist") { return -1 } - if (modB.name === "Assist") { return 1 } - if (modA.name === "Setup") { return -1 } - if (modB.name === "Setup") { return 1 } - return modA.name.localeCompare(modB.name); - }); + return this._rollMods.sort((modA, modB) => this.compareMods(modA, modB)); } set rollMods(val: BladesRollMod[]) { this._rollMods = val } @@ -3570,4 +3588,11 @@ interface BladesRollCollab { } // #endregion +export const BladesRollCollabComps = { + Mod: BladesRollMod, + Primary: BladesRollPrimary, + Opposition: BladesRollOpposition, + Participant: BladesRollParticipant +}; + export default BladesRollCollab; \ No newline at end of file diff --git a/ts/blades.ts b/ts/blades.ts index fd1bc7f2..fbef10df 100644 --- a/ts/blades.ts +++ b/ts/blades.ts @@ -1,6 +1,6 @@ // #region ▮▮▮▮▮▮▮ IMPORTS ▮▮▮▮▮▮▮ ~ -import C, {BladesActorType, BladesItemType, Tag, Playbook, RollType, Action} from "./core/constants.js"; -import registerSettings, {initTinyMCEStyles, initCanvasStyles, initFonts} from "./core/settings.js"; +import C from "./core/constants.js"; +import registerSettings, {initTinyMCEStyles, initCanvasStyles} from "./core/settings.js"; import {registerHandlebarHelpers, preloadHandlebarsTemplates} from "./core/helpers.js"; import BladesPushController from "./blades-push-notifications.js"; import U from "./core/utilities.js"; @@ -13,14 +13,14 @@ import BladesActorProxy from "./documents/blades-actor-proxy.js"; import BladesItemProxy, {BladesItem, BladesClockKeeper, BladesGMTracker, BladesLocation, BladesScore} from "./documents/blades-item-proxy.js"; import BladesItemSheet from "./sheets/item/blades-item-sheet.js"; -import BladesActorSheet from "./sheets/actor/blades-actor-sheet.js"; +import BladesPCSheet from "./sheets/actor/blades-pc-sheet.js"; import BladesCrewSheet from "./sheets/actor/blades-crew-sheet.js"; import BladesNPCSheet from "./sheets/actor/blades-npc-sheet.js"; import BladesFactionSheet from "./sheets/actor/blades-faction-sheet.js"; import BladesRollCollab, {ApplyRollEffects, ApplyDescriptions} from "./blades-roll-collab.js"; import {bladesRoll, simpleRollPopup} from "./blades-roll.js"; -import BladesSelectorDialog, {SelectionCategory} from "./blades-dialog.js"; +import BladesSelectorDialog from "./blades-dialog.js"; import BladesActiveEffect from "./blades-active-effect.js"; import BladesTrackerSheet from "./sheets/item/blades-tracker-sheet.js"; import BladesClockKeeperSheet from "./sheets/item/blades-clock-keeper-sheet.js"; @@ -41,7 +41,7 @@ registerDebugger(); UpdateContacts, UpdateOps, BladesActor, - BladesActorSheet, + BladesPCSheet, BladesCrewSheet, BladesFactionSheet, BladesNPCSheet, @@ -61,237 +61,7 @@ registerDebugger(); BladesLocation, BladesItemSheet, BladesClockKeeperSheet, - BladesTrackerSheet, - MutateItems: () => { - const patternParts = { - strong: [ - { - items: ["all"], - patterns: [ - "(?:[^ a-zA-Z]<>)?\\d+d", - ...[ - "effect", - "result level", - "faction status(?:es)?", - "dot", - "stash", - "(?:free )?load", - "drain", - "armor", - "scale", - "potency", - "coin", - "quality", - "quality", - "xp", - "stress(?: box)?", - "rep", - "trauma(?: box)?" - ].map((pat) => `!B(?:[^ a-zA-Z<>])?\\d+ ${pat}`), - ...["Hunt", "Study", "Survey", "Tinker", "Finesse", "Prowl", "Skirmish", "Wreck", "Attune", "Command", "Consort", "Sway", "Insight", "Prowess", "Resolve"], - ...[ - "crew xp", - "special armor", - "push yourself", - "gather info", - "cohorts?", - "xp trigger", - "downtime", - "one tick", - "two ticks", - "long-?term project", - "Tier(?:\\s*[^ a-zA-Z]\\d+)?", - "acquire an asset", - "!B(?:(?:[^ a-zA-Z<>])?\\d+ )?(?:reduce\\s*)?heat", - "incarcerated", - "gather information", - "engagement(?: roll)?", - "hull", - "hollow", - "half the coin", - "entanglements", - "group action", - "teamwork(?:\\s* maneuvers)?", - "rep", - "wanted level", - "resistance)(\\s*rolls?", - "healing)(\\s*(?:treatment\\s*)?rolls?" - ] - ] - }, - {patterns: ["Hunt", "Study", "Survey", "Tinker", "Finesse", "Prowl", "Skirmish", "Wreck", "Attune", "Command", "Consort", "Sway", "Insight", "Prowess", "Resolve"] - .map((str) => str.toLowerCase()), - items: ["Physicker", "Occultist", "Possess", "Interface"]}, - {patterns: ["protect"], - items: ["Bodyguard"]}, - {patterns: ["potency", "quality", "magnitude", "potent"], - items: ["The Good Stuff", "Ghost Fighter", "Ghost Hunter (Arrow-Swift)", "Ghost Hunter (Ghost Form)", "Ghost Hunter (Mind Link)", "Infiltrator", "Ghost Voice", "Electroplasmic Projectors", "Undead", "Like Part of the Family"]}, - {patterns: ["invent", "craft"], - items: ["Alchemist"]}, - {patterns: ["assist", "\\d*\\s*drain", "gloom", "compel"], - items: ["Foresight", "Ghost Form", "Manifest"]}, - {patterns: ["load", "armor", "functions", "drain"], - items: ["Automaton", "Reavers"]}, - {patterns: ["items"], - items: ["Compartments"]}, - {patterns: ["frame", "frames", "feature"], - items: ["Secondary Hull"]}, - {patterns: ["turf"], - items: ["Accord", "Fiends"]}, - {patterns: ["heat", "rep"], - items: ["Crow's Veil"]}, - {patterns: ["Vice", "assist"], - items: ["Conviction", "Vault"]}, - {patterns: ["workshop", "coin"], - items: ["Ritual Sanctum in Lair"]}, - {patterns: ["quality rating", "Documents", "Gear", "Arcane Implements", "Subterfuge Supplies", "Tools", "Weapons"], - items: ["Quality"]} - ], - em: [ - { - items: ["all"], - patterns: [ - ...[ - "Thugs", - "Skulks", - "Adepts", - "Rovers", - "Rooks", - "!BYou got payback against someone who harmed you or someone you care about.!B" - ], - ...[ - "alchemical)(\\s*features", - "spark-craft)(\\s*features", - "arcane)(\\s*features" - ], - ...[ - "This factors into effect\\." - ], - "!B\\([^)]+\\)!B" - ] - }, - {patterns: ["Whenever you would.+instead"], - items: ["Ghost Form", "Automaton"]}, - {patterns: ["feature"], - items: ["Frame Upgrade"]}, - {patterns: ["Worship"], - items: ["Conviction"]}, - {patterns: ["savage", "unreliable", "wild"], - items: ["Hooked"]} - ] - }; - - function getPattern(patStr: string): RegExp { - return new RegExp(`\\b(${patStr})\\b` - .replace(/^\\b\(!B/, "(") - .replace(/!B\)\\b$/, ")"), "g"); - } - - const patterns = { - strong: patternParts.strong.map(({items, patterns}) => ({ - items: items.map(getWorldName), - patterns: patterns.map((pat) => getPattern(pat)) - })), - em: patternParts.em.map(({items, patterns}) => ({ - items: items.map(getWorldName), - patterns: patterns.map((pat) => getPattern(pat)) - })) - }; - - function getWorldName(name: string) { - return name - .replace(/[^A-Za-z_0-9 ]/g, "") - .trim() - .replace(/ /g, "_"); - } - function getPrimaryName(name: string) { - return name - .replace(/^\s*\d+\s*|\s*\d+\s*$/g, "") - .replace(/:.*$/, "") - .replace(/\(.*?\)/g, "") - .trim(); - } - - game.items - .forEach((i: BladesItem) => { - const updateData: Record = {}; - let ruleString = i.system.rules; - if (ruleString) { - // ruleString = `

${ruleString - // .replace(/<\/?[^ >]+>/g, "") - // .replace(/[<>]/g, "")}

`; - - ruleString = ruleString - .replace(/|<\/strong>||<\/em>/g, ""); - updateData["system.description"] = ruleString; - for (const [wrapper, patParts] of Object.entries(patterns)) { - for (const {items, patterns} of patParts) { - if (items.includes("all") || items.includes(i.system.world_name) || items.includes(getPrimaryName(i.name!)) || items.includes(getWorldName(getPrimaryName(i.name!)))) { - patterns.forEach((patr) => { - ruleString = ruleString - .trim() - .replace(/-(\d+)/g, "−$1") - .replace(patr, `<${wrapper}>$1$2`) - .replace(/\$2/g, "") - .replace(new RegExp(`(<${wrapper}>)+`, "g"), `<${wrapper}>`) - .replace(new RegExp(`()+`, "g"), ``) - .replace(/<(strong|em)>( +)/g, "$2<$1>" ) - .replace(/( +)<\/(strong|em)>/g, "$1" ) - .replace(/ +/g, " ") - .trim(); - }); - } - } - } - updateData["system.rules"] = ruleString; - } - - i.update(updateData); - }); - }, - DebugPC: async () => { - // const {clientTop, clientLeft, clientHeight, clientWidth} = document.documentElement; - // const positions = { - // pc: () => ({top: clientTop, left: clientLeft}), - // crew: ({pcSheetElem}: {pcSheetElem?: JQuery}) => ({top: clientTop, left: (pcSheetElem?.position()?.left ?? 0) + (pcSheetElem?.width() ?? 0)}), - // npc: ({height, width}: {height: number, width: number}) => ({top: (clientTop + clientHeight) - height, left: (clientLeft + clientWidth) - width}) - // }; - // const pc = BladesActor.GetTypeWithTags(BladesActorType.pc).shift(); - // const crew = BladesActor.GetTypeWithTags(BladesActorType.crew).shift(); - // const npc = BladesActor.GetTypeWithTags(BladesActorType.npc).shift(); - - // if (pc) { - // Object.assign(globalThis, {pc}); - // if (pc.sheet) { - // pc.sheet.render(true); - // } - // } - // if (crew) { - // Object.assign(globalThis, {crew}); - // if (crew.sheet) { - // crew.sheet.render(true); - // } - // } - // if (npc) { - // Object.assign(globalThis, {npc}); - // if (npc.sheet) { - // npc.sheet.render(true); - // } - // } - // setTimeout(() => { - // if (pc?.sheet) { - // pc.sheet.setPosition(positions.pc()); - // } - // if (npc?.sheet) { - // const height = $(npc.sheet.element).height()!; - // const width = $(npc.sheet.element).width()!; - // npc.sheet.setPosition(positions.npc({height, width})); - // } - // if (crew?.sheet) { - // crew.sheet.setPosition(positions.crew({pcSheetElem: pc?.sheet?.element})); - // } - // }, 2000); - } + BladesTrackerSheet } );/*!DEVCODE*/ // #endregion Globals @@ -304,7 +74,6 @@ Hooks.once("init", async () => { // Initialize Fonts & Gsap Animations GsapInitialize(); - initFonts(); CONFIG.Item.documentClass = BladesItemProxy as any; CONFIG.Actor.documentClass = BladesActorProxy as any; @@ -320,7 +89,7 @@ Hooks.once("init", async () => { // Initialize subclasses await Promise.all([ - BladesActorSheet.Initialize(), + BladesPCSheet.Initialize(), BladesActiveEffect.Initialize(), BladesTrackerSheet.Initialize(), BladesScore.Initialize(), @@ -392,7 +161,7 @@ Hooks.once("diceSoNiceReady", (dice3d: Record) => { labels: [1, 2, 3, 4, 5, 6].map((num) => `systems/eunos-blades/assets/dice/faces/${num}.webp`), system: "eunos-blades", bumpMaps: [1, 2, 3, 4, 5, 6].map((num) => `systems/eunos-blades/assets/dice/bump-maps/${num}.webp`), - emissiveMaps: [, , , , , "systems/eunos-blades/assets/dice/emission-maps/6.webp"], // eslint-disable-line no-sparse-arrays + emissiveMaps: [undefined, undefined, undefined, undefined, undefined, "systems/eunos-blades/assets/dice/emission-maps/6.webp"], emissive: "#d89300" }); }); diff --git a/ts/core/settings.ts b/ts/core/settings.ts index e3c19ab8..169bf342 100644 --- a/ts/core/settings.ts +++ b/ts/core/settings.ts @@ -8,12 +8,11 @@ const registerSettings = function() { "scope": "client", // This specifies a world-level setting "config": true, // This specifies that the setting appears in the configuration view "type": Number, - // @ts-expect-error For some reason, they don't let me assign to range. "range": { // If range is specified, the resulting setting will be a range slider min: 0, max: 5, step: 1 - }, + } as { min: number; max: number; step: number }, "default": 3 // The default value for the setting }); game.settings.register("eunos-blades", "blacklist", { @@ -158,32 +157,4 @@ export function initCanvasStyles() { }); } -export function initFonts() { - // CONFIG.fontDefinitions["Roboto"] = { - // editor: true, - // fonts: [ - // {urls: ["assets/fonts/Roboto.woff2"]}, - // {urls: ["assets/fonts/RobotoBold.woff2"], weight: 700}, - // {urls: ["assets/fonts/RobotoItalic.woff2"], style: "italic"} - // ] - // }; - - // CONFIG.fontFamilies = [ - // "Historical FellType", - // "Historical FellType SC", - // "IM FELL Double Pica", - // "IM FELL Double Pica SC", - // "Kirsty", - // "Lekton", - // "Minion Pro", - // "Minion Pro Caption", - // "Minion Pro Cond", - // "Minion Pro Caption Cond", - // "PWSignaturetwo", - // "Ravenscroft", - // "UglyQua" - // ]; - // CONFIG.defaultFontFamily = "Minion Pro"; -} - export default registerSettings; \ No newline at end of file diff --git a/ts/core/tags.ts b/ts/core/tags.ts index e32aa477..617d3cf7 100644 --- a/ts/core/tags.ts +++ b/ts/core/tags.ts @@ -1,5 +1,5 @@ import Tagify from "../../lib/tagify/tagify.esm.js"; -import {Tag, District, MainDistrict, OtherDistrict, Vice, Playbook, BladesActorType} from "./constants.js"; +import {Tag, MainDistrict, OtherDistrict, Vice, Playbook, BladesActorType} from "./constants.js"; import U from "./utilities.js"; async function _onTagifyChange(event: Event, doc: BladesDoc, targetKey: keyof BladesDoc) { @@ -85,7 +85,6 @@ const Tags = { // Check if element specifies an alternate schema target from doc.tags const targetKey = $(elem).data("tagTarget") ?? "system.tags"; const curTags = [getProperty(doc, targetKey) ?? []].flat().filter(Boolean); - // eLog.checkLog("tags", "Current Tags", curTags); tagify.addTags( curTags .filter(findDataGroup) diff --git a/ts/core/utilities.ts b/ts/core/utilities.ts index 8d1978b8..922afbb8 100644 --- a/ts/core/utilities.ts +++ b/ts/core/utilities.ts @@ -268,7 +268,7 @@ const FILTERS = { // #region ████████ STRINGS: String Parsing, Manipulation, Conversion, Regular Expressions ████████ // #region ░░░░░░░[Case Conversion]░░░░ Upper, Lower, Sentence & Title Case ░░░░░░░ ~ -const uCase = (str: T): Uppercase => String(str).toUpperCase() as Uppercase; +const uCase = (str: T): Uppercase => String(str).toUpperCase() as Uppercase; const lCase = (str: T): Lowercase => String(str).toLowerCase() as Lowercase; const sCase = (str: T): Capitalize => { let [first, ...rest] = `${str ?? ""}`.split(/\s+/); @@ -459,7 +459,7 @@ const verbalizeNum = (num: number | string) => { }; const ordinalizeNum = (num: string | number, isReturningWords = false) => { if (isReturningWords) { - const [numText, suffix]: RegExpMatchArray = lCase(verbalizeNum(num)).match(/.*?[-|\s]?(\w*?)$/) ?? ["", ""]; + const [numText, suffix]: RegExpMatchArray = lCase(verbalizeNum(num)).match(/.*?[-\s]?(\w*)$/i) ?? ["", ""]; return numText.replace( new RegExp(`${suffix}$`), suffix in _ordinals ? _ordinals[suffix] : `${suffix}th` @@ -930,8 +930,8 @@ function objMerge(target: Tx, source: Ty, {isMutatingOk = false, isStric function objDiff(obj1: any, obj2: any): any { const diff: any = {}; for (const key in obj2) { - if (Object.prototype.hasOwnProperty.call(obj2, key)) { - if (Object.prototype.hasOwnProperty.call(obj1, key)) { + if (Object.hasOwn(obj2, key)) { + if (Object.hasOwn(obj1, key)) { if (typeof obj1[key] === "object" && typeof obj2[key] === "object" && !Array.isArray(obj1[key]) && !Array.isArray(obj2[key])) { const nestedDiff = objDiff(obj1[key], obj2[key]); if (Object.keys(nestedDiff).length > 0) { diff --git a/ts/documents/actors/blades-crew.ts b/ts/documents/actors/blades-crew.ts index 61dd7816..c1772d00 100644 --- a/ts/documents/actors/blades-crew.ts +++ b/ts/documents/actors/blades-crew.ts @@ -1,17 +1,16 @@ import BladesItem from "../../blades-item.js"; -import C, {SVGDATA, BladesActorType, Playbook, BladesItemType, Tag, BladesPhase, RollModCategory, PrereqType, Factor, RollModStatus} from "../../core/constants.js"; +import {BladesActorType, Playbook, BladesItemType, RollModCategory, Factor, RollModStatus} from "../../core/constants.js"; import U from "../../core/utilities.js"; import BladesActor from "../../blades-actor.js"; import BladesRollCollab from "../../blades-roll-collab.js"; -import type {ItemDataConstructorData} from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/data/data.mjs/itemData.js"; -import type {ActorData, ActorDataConstructorData} from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/data/data.mjs/actorData.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, - BladesRollCollab.PrimaryDocData, - BladesRollCollab.ParticipantDocData { + BladesRollCollab.PrimaryDocData, + BladesRollCollab.ParticipantDocData { // #region Static Overrides: Create ~ - static override async create(data: ActorDataConstructorData & { system?: Partial }, options = {}) { + static override async create(data: ActorDataConstructorData & {system?: Partial}, options = {}) { data.token = data.token || {}; data.system = data.system ?? {}; @@ -40,20 +39,20 @@ class BladesCrew extends BladesActor implements BladesActorSubClass.Crew, get rollModsData(): BladesRollCollab.RollModData[] { const {roll_mods} = this.system; - if (roll_mods.length === 0) { return [] } + 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 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 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 posNegVal = posNegString.replace(/^.*:/, "") as "positive" | "negative"; const rollModData: BladesRollCollab.RollModData = { id: `${nameVal}-${posNegVal}-${catVal}`, @@ -68,34 +67,35 @@ class BladesCrew extends BladesActor implements BladesActorSubClass.Crew, pStrings.forEach((pString) => { const [keyString, valString] = pString.split(/:/) as [string, string]; - let val: string|string[] = /\|/.test(valString) ? valString.split(/\|/) : valString; + 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.*r?.*ty/i.test(keyString)) { key = "conditionalRollTypes" } else - if (/^a.*r?.*y/i.test(keyString)) { key = "autoRollTypes" } else - if (/^c.*r?.*tr/i.test(keyString)) { key = "conditionalRollTraits" } else - if (/^a.*r?.*tr/i.test(keyString)) { key = "autoRollTraits" } else { + 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"].includes(key) - ? U.pInt(val) - : (["effectKeys", "conditionalRollTypes", "autoRollTypes,", "conditionalRollTraits", "autoRollTraits"].includes(key) - ? [val].flat() - : (val as string).replace(/%COLON%/g, ":"))} - ); + Object.assign(rollModData, {[key]: value}); }); return rollModData; @@ -104,8 +104,8 @@ class BladesCrew extends BladesActor implements BladesActorSubClass.Crew, return rollModsData; } - get rollFactors(): Partial> { - const factorData: Partial> = { + get rollFactors(): Partial> { + const factorData: Partial> = { [Factor.tier]: { name: Factor.tier, value: this.getFactorTotal(Factor.tier), @@ -131,21 +131,21 @@ class BladesCrew extends BladesActor implements BladesActorSubClass.Crew, return factorData; } // #region BladesRollCollab.PrimaryDoc Implementation - get rollPrimaryID() { return this.id } - get rollPrimaryDoc() { return this } - get rollPrimaryName() { return this.name! } - get rollPrimaryType() { return this.type } - get rollPrimaryImg() { return this.img! } + get rollPrimaryID() {return this.id} + get rollPrimaryDoc() {return this} + get rollPrimaryName() {return this.name!} + get rollPrimaryType() {return this.type} + 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 rollParticipantType() { return this.type } + get rollParticipantID() {return this.id} + get rollParticipantDoc() {return this} + get rollParticipantIcon() {return this.playbook?.img ?? this.img!} + get rollParticipantName() {return this.name!} + get rollParticipantType() {return this.type} - get rollParticipantModsData(): BladesRollCollab.RollModData[] { return [] } + get rollParticipantModsData(): BladesRollCollab.RollModData[] {return []} // #endregion @@ -153,14 +153,14 @@ class BladesCrew extends BladesActor implements BladesActorSubClass.Crew, get abilities(): BladesItem[] { - if (!this.playbook) { return [] } + if (!this.playbook) {return []} return this.activeSubItems.filter((item) => [BladesItemType.ability, BladesItemType.crew_ability].includes(item.type)); } get playbookName() { return this.playbook?.name as (BladesTag & Playbook) | undefined; } - get playbook(): BladesItemOfType|undefined { + get playbook(): BladesItemOfType | undefined { return this.activeSubItems.find((item): item is BladesItemOfType => item.type === BladesItemType.crew_playbook); } diff --git a/ts/sheets/actor/blades-actor-sheet.ts b/ts/sheets/actor/blades-pc-sheet.ts similarity index 94% rename from ts/sheets/actor/blades-actor-sheet.ts rename to ts/sheets/actor/blades-pc-sheet.ts index 29341f05..3437d9ae 100644 --- a/ts/sheets/actor/blades-actor-sheet.ts +++ b/ts/sheets/actor/blades-pc-sheet.ts @@ -3,19 +3,13 @@ import C, {BladesActorType, BladesItemType, Attribute, Tag, Action, BladesPhase} import U from "../../core/utilities.js"; import BladesSheet from "./blades-sheet.js"; import BladesActor from "../../blades-actor.js"; -import BladesPC from "../../documents/actors/blades-pc.js"; -import BladesNPC from "../../documents/actors/blades-npc.js"; -import BladesFaction from "../../documents/actors/blades-faction.js"; -import BladesCrew from "../../documents/actors/blades-crew.js"; -import BladesItem from "../../blades-item.js"; import BladesTrackerSheet from "../item/blades-tracker-sheet.js"; // import ConstructorDataType from "@league-of-foundry-developers/foundry-vtt-types/src/types/helperTypes.js"; // import type {ItemDataConstructorData} from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/common/data/data.mjs/itemData.js"; -class BladesActorSheet extends BladesSheet { +class BladesPCSheet extends BladesSheet { - declare actor: BladesActorOfType; static override get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { @@ -28,7 +22,7 @@ class BladesActorSheet extends BladesSheet { } static Initialize() { - Actors.registerSheet("blades", BladesActorSheet, {types: ["pc"], makeDefault: true}); + Actors.registerSheet("blades", BladesPCSheet, {types: ["pc"], makeDefault: true}); Hooks.on("dropActorSheetData", async (parentActor: BladesActor, _, {uuid}: {uuid: string}) => { const doc = await fromUuid(uuid) as BladesDoc|null; @@ -253,9 +247,9 @@ class BladesActorSheet extends BladesSheet { "" ].join(""))).toString(); - eLog.checkLog("Attribute", "[BladesActorSheet] attributeData", {attributeData: sheetData.attributeData}); + eLog.checkLog("Attribute", "[BladesPCSheet] attributeData", {attributeData: sheetData.attributeData}); - eLog.checkLog("actor", "[BladesActorSheet] getData()", {...context, ...sheetData}); + eLog.checkLog("actor", "[BladesPCSheet] getData()", {...context, ...sheetData}); return {...context, ...sheetData} as BladesActorSheetData; } @@ -364,8 +358,6 @@ class BladesActorSheet extends BladesSheet { } } -// declare interface BladesActorSheet { -// get actor(): BladesActor -// } +// declare interface BladesPCSheet { -export default BladesActorSheet; \ No newline at end of file +export default BladesPCSheet; \ No newline at end of file diff --git a/ts/sheets/actor/blades-sheet.ts b/ts/sheets/actor/blades-sheet.ts index 97028a52..cf0acaa6 100644 --- a/ts/sheets/actor/blades-sheet.ts +++ b/ts/sheets/actor/blades-sheet.ts @@ -2,13 +2,9 @@ import U from "../../core/utilities.js"; import G, {ApplyTooltipListeners} from "../../core/gsap.js"; -import C, {Tag, BladesActorType, BladesItemType, BladesPermissions, RollType, Action, Attribute, Factor} from "../../core/constants.js"; +import C, {Tag, BladesActorType, BladesItemType, RollType, Action, Attribute, Factor} from "../../core/constants.js"; import Tags from "../../core/tags.js"; import BladesActor from "../../blades-actor.js"; -import BladesPC from "../../documents/actors/blades-pc.js"; -import BladesNPC from "../../documents/actors/blades-npc.js"; -import BladesFaction from "../../documents/actors/blades-faction.js"; -import BladesCrew from "../../documents/actors/blades-crew.js"; import BladesItem from "../../blades-item.js"; import BladesSelectorDialog, {SelectionCategory} from "../../blades-dialog.js"; import BladesActiveEffect from "../../blades-active-effect.js"; @@ -30,16 +26,24 @@ type BladesCompData = { class BladesSheet extends ActorSheet { + /** + * Override the default getData method to provide additional data for the actor sheet. + * This includes cssClass, editable, isGM, actor, system, tierTotal, rollData, activeEffects, hasFullVision, hasLimitedVision, hasControl, preparedItems. + * @returns {BladesActorSheetData} The data object for the actor sheet. + */ override getData() { + // Get the base data context from the parent class. const context = super.getData(); + // Prepare additional data specific to this actor's sheet. const sheetData: DeepPartial & BladesActorDataOfType & BladesActorDataOfType & BladesActorDataOfType > = { + // Basic actor data. cssClass: this.actor.type, editable: this.options.editable, isGM: game.eunoblades.Tracker!.system.is_spoofing_player ? false : game.user.isGM, @@ -51,11 +55,14 @@ class BladesSheet extends ActorSheet { hasFullVision: game.user.isGM || this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OBSERVER), hasLimitedVision: game.user.isGM || this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.LIMITED), hasControl: game.user.isGM || this.actor.testUserPermission(game.user, CONST.DOCUMENT_PERMISSION_LEVELS.OWNER), + + // Prepare items for display on the actor sheet. preparedItems: { cohorts: { gang: this.actor.activeSubItems .filter((item): item is BladesItemOfType => item.type === BladesItemType.cohort_gang) .map((item) => { + // Prepare gang cohort items. const subtypes = U.unique(Object.values(item.system.subtypes) .map((subtype) => subtype.trim()) .filter((subtype) => /[A-Za-z]/.test(subtype))) as Tag.GangType[]; @@ -67,6 +74,7 @@ class BladesSheet extends ActorSheet { .map((subtype) => subtype.trim()) .filter((subtype) => /[A-Za-z]/.test(subtype) && subtypes.includes(subtype as Tag.GangType))) as Tag.GangType[]; + // Prepare images for gang cohort items. const imgTypes = [...elite_subtypes]; if (imgTypes.length < 2) { imgTypes.push(...subtypes.filter((subtype) => !imgTypes.includes(subtype))); @@ -79,6 +87,7 @@ class BladesSheet extends ActorSheet { item.system.imageRight = Object.values(item.system.elite_subtypes).includes(rightType) ? `elite-${U.lCase(rightType)}.svg` : `${U.lCase(rightType)}.svg`; } + // Prepare additional data for gang cohort items. Object.assign( item.system, { @@ -99,6 +108,7 @@ class BladesSheet extends ActorSheet { expert: this.actor.activeSubItems .filter((item): item is BladesItemOfType => item.type === BladesItemType.cohort_expert) .map((item) => { + // Prepare expert cohort items. Object.assign( item.system, { @@ -120,6 +130,7 @@ class BladesSheet extends ActorSheet { } }; + // Prepare additional data for PC and Crew actors. if (BladesActor.IsType(this.actor, BladesActorType.pc) || BladesActor.IsType(this.actor, BladesActorType.crew)) { sheetData.playbookData = { dotline: { @@ -151,6 +162,7 @@ class BladesSheet extends ActorSheet { }; } + // Return the combined data context for the actor sheet. return { ...context, ...sheetData @@ -282,7 +294,6 @@ class BladesSheet extends ActorSheet { eLog.checkLog("actorSheetTrigger", "User does not have permission to edit this actor", {user: game.user, actor: this.actor}); return {}; } - // eLog.checkLog("actorSheetTrigger", "Submitting Form Data", {parentActor: this.actor.parentActor, systemTags: this.actor.system.tags, sourceTags: this.actor._source.system.tags, params}); return super._onSubmit(event, params); } @@ -381,7 +392,6 @@ class BladesSheet extends ActorSheet { return; } const {docCat, docType, dialogDocs, docTags} = this._getCompData(event); - // eLog.checkLog("_onItemAddClick", {docCat, dialogDocs}); if (!dialogDocs || !docCat || !docType) { return; }