diff --git a/.eslintignore b/.eslintignore index 03caea5e..5ff9ec06 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,3 +6,5 @@ /.pnp.cjs /.pnp.loader.mjs /.yarn/ +client +common diff --git a/.gitignore b/.gitignore index a45e3fd1..f8959419 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,7 @@ junit.xml !.yarn/sdks !.yarn/versions .pnp.* + +# foundry +client +common diff --git a/.prettierignore b/.prettierignore index 2080674e..2a9469b0 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,3 +8,5 @@ /.pnp.loader.mjs /.yarn/ /.vscode/ +client +common diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 00000000..1e137104 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "module": "es2022", + "target": "ES2022" + }, + "exclude": ["node_modules", "dist"], + "include": ["src", "client", "common"] +} diff --git a/jsconfig.json.license b/jsconfig.json.license new file mode 100644 index 00000000..467ee146 --- /dev/null +++ b/jsconfig.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2022 Johannes Loher + +SPDX-License-Identifier: MIT diff --git a/package.json b/package.json index 2937aaf8..482d0ffe 100644 --- a/package.json +++ b/package.json @@ -64,18 +64,9 @@ "@commitlint/cli": "17.3.0", "@commitlint/config-conventional": "17.3.0", "@guanghechen/rollup-plugin-copy": "2.1.4", - "@league-of-foundry-developers/foundry-vtt-types": "9.280.0", - "@pixi/constants": "6.2.1", - "@pixi/core": "6.2.1", - "@pixi/display": "6.2.1", - "@pixi/graphics": "6.2.1", - "@pixi/math": "6.2.1", - "@pixi/runner": "6.2.1", - "@pixi/settings": "6.2.1", - "@pixi/utils": "6.2.1", - "@rollup/plugin-typescript": "10.0.0", "@swc/core": "1.3.20", "@types/fs-extra": "9.0.13", + "@types/jquery": "3.5.14", "@types/node": "18.11.9", "@typescript-eslint/eslint-plugin": "5.44.0", "@typescript-eslint/parser": "5.44.0", @@ -85,6 +76,7 @@ "eslint-config-prettier": "8.5.0", "eslint-plugin-prettier": "4.2.1", "fs-extra": "10.1.0", + "handlebars": "4.7.7", "npm-run-all": "4.1.5", "prettier": "2.8.0", "rimraf": "3.0.2", diff --git a/spec/tsconfig.json b/spec/tsconfig.json index a5662b44..23db97a9 100644 --- a/spec/tsconfig.json +++ b/spec/tsconfig.json @@ -1,7 +1,4 @@ { "extends": "../tsconfig.json", - "compilerOptions": { - "types": ["@league-of-foundry-developers/foundry-vtt-types"] - }, "include": ["../src", "./"] } diff --git a/src/apps/active-effect-config.ts b/src/apps/active-effect-config.js similarity index 58% rename from src/apps/active-effect-config.ts rename to src/apps/active-effect-config.js index f26494e8..10ede23b 100644 --- a/src/apps/active-effect-config.ts +++ b/src/apps/active-effect-config.js @@ -3,21 +3,28 @@ // SPDX-License-Identifier: MIT export class DS4ActiveEffectConfig extends ActiveEffectConfig { - static override get defaultOptions(): DocumentSheetOptions { + /** @override */ + static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { template: "systems/ds4/templates/sheets/active-effect/active-effect-config.hbs", }); } - override activateListeners(html: JQuery): void { + /** + * @override + * @param {JQuery} html + */ + activateListeners(html) { super.activateListeners(html); - const checkbox = html[0]?.querySelector( - 'input[name="flags.ds4.itemEffectConfig.applyToItems"]', - ); - checkbox?.addEventListener("change", () => this.toggleItemEffectConfig(checkbox.checked)); + const checkbox = html[0]?.querySelector('input[name="flags.ds4.itemEffectConfig.applyToItems"]'); + checkbox?.addEventListener("change", () => this.#toggleItemEffectConfig(checkbox.checked)); } - private toggleItemEffectConfig(active: boolean) { + /** + * Toggle the visibility of the item effect config section + * @param {boolean} active The target state + */ + #toggleItemEffectConfig(active) { const elements = this.element[0]?.querySelectorAll(".ds4-item-effect-config"); elements?.forEach((element) => { if (active) { diff --git a/src/apps/actor/base-sheet.ts b/src/apps/actor/base-sheet.js similarity index 74% rename from src/apps/actor/base-sheet.ts rename to src/apps/actor/base-sheet.js index bb2e5fa4..e29a3e79 100644 --- a/src/apps/actor/base-sheet.ts +++ b/src/apps/actor/base-sheet.js @@ -13,15 +13,12 @@ import { notifications } from "../../ui/notifications"; import { enforce, getCanvas, getGame } from "../../utils/utils"; import { disableOverriddenFields } from "../sheet-helpers"; -import type { ModifiableDataBaseTotal } from "../../documents/common/common-data"; -import type { DS4Settings } from "../../settings"; -import type { DS4Item } from "../../documents/item/item"; - /** * The base sheet class for all {@link DS4Actor}s. */ -export class DS4ActorSheet extends ActorSheet { - static override get defaultOptions(): ActorSheet.Options { +export class DS4ActorSheet extends ActorSheet { + /** @override */ + static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["sheet", "ds4-actor-sheet"], height: 635, @@ -36,13 +33,15 @@ export class DS4ActorSheet extends ActorSheet { + /** @override */ + async getData(options) { const itemsByType = Object.fromEntries( Object.entries(this.actor.itemTypes).map(([itemType, items]) => { return [itemType, items.map((item) => item.data).sort((a, b) => (a.sort || 0) - (b.sort || 0))]; @@ -60,7 +59,7 @@ export class DS4ActorSheet extends ActorSheet { - Object.values(valueGroup).forEach((attribute: ModifiableDataBaseTotal & { tooltip?: string }) => { + Object.values(valueGroup).forEach((attribute) => { attribute.tooltip = this.getTooltipForValue(attribute); }); }); @@ -85,8 +86,11 @@ export class DS4ActorSheet extends ActorSheet} value The value to get a tooltip for + * @returns {string} The tooltip + * @protected */ - protected getTooltipForValue(value: ModifiableDataBaseTotal): string { + getTooltipForValue(value) { return `${value.base} (${getGame().i18n.localize("DS4.TooltipBaseValue")}) + ${ value.mod } (${getGame().i18n.localize("DS4.TooltipModifier")}) ➞ ${getGame().i18n.localize("DS4.TooltipEffects")} ➞ ${ @@ -94,7 +98,11 @@ export class DS4ActorSheet extends ActorSheet this.render(false)); @@ -184,9 +196,10 @@ export class DS4ActorSheet extends ActorSheet notifications.error(e, { log: true })); } - override _onDragStart(event: DragEvent): void { - const target = event.currentTarget as HTMLElement; + /** + * @param {DragEvent} event + * @override + */ + _onDragStart(event) { + const target = event.currentTarget; if (!(target instanceof HTMLElement)) return super._onDragStart(event); const check = target.dataset.check; @@ -356,9 +383,10 @@ export class DS4ActorSheet extends ActorSheet): void { + onSortItems(event) { event.preventDefault(); const target = event.currentTarget; const type = target.parentElement?.dataset["type"]; @@ -369,26 +397,28 @@ export class DS4ActorSheet extends ActorSheet item.type === type); items.sort((a, b) => a.data.sort - b.data.sort); - const sortFunction = - (invert: boolean) => - (a: DS4Item, b: DS4Item): number => { + /** + * @param {boolean} invert Whether or not to inverse the sort order + * @returns {(a: import("../../documents/item/item").DS4Item, b: import("../../documents/item/item").DS4Item) => number} A function for sorting items + */ + const sortFunction = (invert) => (a, b) => { + const propertyA = getProperty(a.data, dataPath); + const propertyB = getProperty(b.data, dataPath); + const comparison = + typeof propertyA === "string" || typeof propertyB === "string" + ? compareAsStrings(propertyA, propertyB, invert) + : compareAsNumbers(propertyA, propertyB, invert); + + if (comparison === 0 && dataPath2 !== undefined) { const propertyA = getProperty(a.data, dataPath); const propertyB = getProperty(b.data, dataPath); - const comparison = - typeof propertyA === "string" || typeof propertyB === "string" - ? compareAsStrings(propertyA, propertyB, invert) - : compareAsNumbers(propertyA, propertyB, invert); + return typeof propertyA === "string" || typeof propertyB === "string" + ? compareAsStrings(propertyA, propertyB, invert) + : compareAsNumbers(propertyA, propertyB, invert); + } - if (comparison === 0 && dataPath2 !== undefined) { - const propertyA = getProperty(a.data, dataPath); - const propertyB = getProperty(b.data, dataPath); - return typeof propertyA === "string" || typeof propertyB === "string" - ? compareAsStrings(propertyA, propertyB, invert) - : compareAsNumbers(propertyA, propertyB, invert); - } - - return comparison; - }; + return comparison; + }; const sortedItems = [...items].sort(sortFunction(false)); const wasSortedAlready = !sortedItems.find((item, index) => item !== items[index]); @@ -405,7 +435,12 @@ export class DS4ActorSheet extends ActorSheet { + /** + * @param {DragEvent} event + * @param {object} data + * @override + */ + async _onDropItem(event, data) { const item = await Item.fromDropData(data); if (item && !this.actor.canOwnItemType(item.data.type)) { notifications.warn( @@ -422,19 +457,6 @@ export class DS4ActorSheet extends ActorSheet; - enrichedEffects: EnrichedActiveEffectDataSource[]; - settings: DS4Settings; -} - -type ActiveEffectDataSource = foundry.data.ActiveEffectData["_source"]; - -interface EnrichedActiveEffectDataSource extends ActiveEffectDataSource { - sourceName: string; -} - /** * This object contains information about specific properties embedded document list entries for each different type. */ @@ -449,10 +471,24 @@ const embeddedDocumentListEntryProperties = Object.freeze({ }, }); -const compareAsStrings = (a: { toString(): string }, b: { toString(): string }, invert: boolean): number => { +/** + * Compare two stringifiables as strings. + * @param {{ toString(): string }} a The thing to compare with + * @param {{ toString(): string }} b The thing to compare + * @param {boolean} invert Should the comparison be inverted? + * @return {number} A number that indicates the result of the comparison + */ +const compareAsStrings = (a, b, invert) => { return invert ? b.toString().localeCompare(a.toString()) : a.toString().localeCompare(b.toString()); }; -const compareAsNumbers = (a: number, b: number, invert: boolean): number => { +/** + * Compare two number. + * @param {number} a The number to compare with + * @param {number} b The number to compare + * @param {boolean} invert Should the comparison be inverted? + * @return {number} A number that indicates the result of the comparison + */ +const compareAsNumbers = (a, b, invert) => { return invert ? b - a : a - b; }; diff --git a/src/apps/actor/character-sheet.ts b/src/apps/actor/character-sheet.js similarity index 86% rename from src/apps/actor/character-sheet.ts rename to src/apps/actor/character-sheet.js index f6ec0649..16bff856 100644 --- a/src/apps/actor/character-sheet.ts +++ b/src/apps/actor/character-sheet.js @@ -8,7 +8,7 @@ import { DS4ActorSheet } from "./base-sheet"; * The Sheet class for DS4 Character Actors */ export class DS4CharacterActorSheet extends DS4ActorSheet { - static override get defaultOptions(): ActorSheet.Options { + static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["sheet", "ds4-actor-sheet", "ds4-character-sheet"], }); diff --git a/src/apps/actor/creature-sheet.ts b/src/apps/actor/creature-sheet.js similarity index 86% rename from src/apps/actor/creature-sheet.ts rename to src/apps/actor/creature-sheet.js index 53b75523..2cd109f2 100644 --- a/src/apps/actor/creature-sheet.ts +++ b/src/apps/actor/creature-sheet.js @@ -8,7 +8,7 @@ import { DS4ActorSheet } from "./base-sheet"; * The Sheet class for DS4 Creature Actors */ export class DS4CreatureActorSheet extends DS4ActorSheet { - static override get defaultOptions(): ActorSheet.Options { + static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["sheet", "ds4-actor-sheet", "ds4-creature-sheet"], }); diff --git a/src/apps/dialog-with-listeners.ts b/src/apps/dialog-with-listeners.js similarity index 53% rename from src/apps/dialog-with-listeners.ts rename to src/apps/dialog-with-listeners.js index b2b053bc..f2412575 100644 --- a/src/apps/dialog-with-listeners.ts +++ b/src/apps/dialog-with-listeners.js @@ -2,18 +2,20 @@ // // SPDX-License-Identifier: MIT +/** + * @typedef {DialogOptions} DialogWithListenersOptions + * @property {(html: JQuery, app: DialogWithListeners) => void} [activateAdditionalListeners] An optional function to attach additional listeners to the dialog + */ + /** * A simple extension to the {@link Dialog} class that allows attaching additional listeners. */ -export class DialogWithListeners extends Dialog { - override activateListeners(html: JQuery): void { +export class DialogWithListeners extends Dialog { + /** @override */ + activateListeners(html) { super.activateListeners(html); if (this.options.activateAdditionalListeners !== undefined) { this.options.activateAdditionalListeners(html, this); } } } - -interface DialogWithListenersOptions extends DialogOptions { - activateAdditionalListeners?: ((html: JQuery, app: DialogWithListeners) => void) | undefined; -} diff --git a/src/apps/item-sheet.ts b/src/apps/item-sheet.js similarity index 79% rename from src/apps/item-sheet.ts rename to src/apps/item-sheet.js index 3062419b..7d151168 100644 --- a/src/apps/item-sheet.ts +++ b/src/apps/item-sheet.js @@ -14,8 +14,9 @@ import { disableOverriddenFields } from "./sheet-helpers"; /** * The Sheet class for DS4 Items */ -export class DS4ItemSheet extends ItemSheet { - static override get defaultOptions(): ItemSheet.Options { +export class DS4ItemSheet extends ItemSheet { + /** @override */ + static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["sheet", "ds4-item-sheet"], height: 400, @@ -25,12 +26,14 @@ export class DS4ItemSheet extends ItemSheet }); } - override get template(): string { + /** @override */ + get template() { const basePath = "systems/ds4/templates/sheets/item"; return `${basePath}/${this.item.data.type}-sheet.hbs`; } - override async getData(): Promise { + /** @override */ + async getData() { const data = { ...(await super.getData()), config: DS4, @@ -41,7 +44,8 @@ export class DS4ItemSheet extends ItemSheet return data; } - override _getSubmitData(updateData = {}) { + /** @override */ + _getSubmitData(updateData = {}) { const data = super._getSubmitData(updateData); // Prevent submitting overridden values const overrides = foundry.utils.flattenObject(this.item.overrides); @@ -51,9 +55,8 @@ export class DS4ItemSheet extends ItemSheet return data; } - override setPosition( - options: Partial = {}, - ): (Application.Position & { height: number }) | void { + /** @override */ + setPosition(options = {}) { const position = super.setPosition(options); if (position) { const sheetBody = this.element.find(".sheet-body"); @@ -64,7 +67,11 @@ export class DS4ItemSheet extends ItemSheet return position; } - override activateListeners(html: JQuery): void { + /** + * @override + * @param {JQuery} html + */ + activateListeners(html) { super.activateListeners(html); if (!this.options.editable) return; @@ -78,9 +85,10 @@ export class DS4ItemSheet extends ItemSheet * Handles a click on an element of this sheet to control an embedded effect of the item corresponding to this * sheet. * - * @param event - The originating click event + * @param {JQuery.ClickEvent} event The originating click event + * @protected */ - protected onControlEffect(event: JQuery.ClickEvent): void { + onControlEffect(event) { event.preventDefault(); if (this.item.isOwned) { return notifications.warn(getGame().i18n.localize("DS4.WarningManageActiveEffectOnOwnedItem")); @@ -98,17 +106,19 @@ export class DS4ItemSheet extends ItemSheet /** * Creates a new embedded effect. + * @protected */ - protected onCreateEffect(): void { + onCreateEffect() { DS4ActiveEffect.createDefault(this.item); } /** * Opens the sheet of the embedded effect corresponding to the clicked element. * - * @param event - The originating click event + * @param {JQuery.ClickEvent} event The originating click event + * @porotected */ - protected onEditEffect(event: JQuery.ClickEvent): void { + onEditEffect(event) { const id = $(event.currentTarget) .parents(embeddedDocumentListEntryProperties.ActiveEffect.selector) .data(embeddedDocumentListEntryProperties.ActiveEffect.idDataAttribute); @@ -120,9 +130,10 @@ export class DS4ItemSheet extends ItemSheet /** * Deletes the embedded item corresponding to the clicked element. * - * @param event - The originating click event + * @param {JQuery.ClickEvent} event The originating click event + * @protected */ - protected onDeleteEffect(event: JQuery.ClickEvent): void { + onDeleteEffect(event) { const li = $(event.currentTarget).parents(embeddedDocumentListEntryProperties.ActiveEffect.selector); const id = li.data(embeddedDocumentListEntryProperties.ActiveEffect.idDataAttribute); this.item.deleteEmbeddedDocuments("ActiveEffect", [id]); @@ -130,13 +141,6 @@ export class DS4ItemSheet extends ItemSheet } } -interface DS4ItemSheetData extends ItemSheet.Data { - config: typeof DS4; - isOwned: boolean; - actor: DS4ItemSheet["item"]["actor"]; - isPhysical: boolean; -} - /** * This object contains information about specific properties embedded document list entries for each different type. */ diff --git a/src/apps/sheet-helpers.ts b/src/apps/sheet-helpers.js similarity index 65% rename from src/apps/sheet-helpers.ts rename to src/apps/sheet-helpers.js index dad3c75a..a098e250 100644 --- a/src/apps/sheet-helpers.ts +++ b/src/apps/sheet-helpers.js @@ -4,11 +4,13 @@ import { getGame } from "../utils/utils"; -export function disableOverriddenFields( - form: HTMLElement | null, - overrides: Record, - selector: (key: string) => string, -): void { +/** + * Disable elements in the given form that match the selector returned for overridden properties. + * @param {HTMLElement | null} form The form in which to disable fields + * @param {Record} overrides The set of overrides of the underlying document + * @param {(key: string) => string} selector A function that generates a selector, based on a property key + */ +export function disableOverriddenFields(form, overrides, selector) { const inputs = ["INPUT", "SELECT", "TEXTAREA", "BUTTON"]; const titleAddition = `(${getGame().i18n.localize("DS4.TooltipNotEditableDueToEffects")})`; diff --git a/src/dice/check-evaluation.ts b/src/dice/check-evaluation.ts index 753c7d8f..608c503e 100644 --- a/src/dice/check-evaluation.ts +++ b/src/dice/check-evaluation.ts @@ -99,7 +99,13 @@ function shouldUseCoupForLastSubCheck( ); } -interface SubCheckResult extends DieWithSubCheck, DiceTerm.Result {} +interface SubCheckResult extends DieWithSubCheck { + active?: boolean; + discarded?: boolean; + success?: boolean; + failure?: boolean; + count?: number; +} function evaluateDiceWithSubChecks( results: DieWithSubCheck[], diff --git a/src/dice/check-factory.ts b/src/dice/check-factory.js similarity index 59% rename from src/dice/check-factory.ts rename to src/dice/check-factory.js index 0952bc93..30836836 100644 --- a/src/dice/check-factory.ts +++ b/src/dice/check-factory.js @@ -7,70 +7,96 @@ import { DialogWithListeners } from "../apps/dialog-with-listeners"; import { DS4 } from "../config"; import { getGame } from "../utils/utils"; -/** - * Provides default values for all arguments the `CheckFactory` expects. - */ -class DefaultCheckOptions implements DS4CheckFactoryOptions { - readonly maximumCoupResult = 1; - readonly minimumFumbleResult = 20; - readonly useSlayingDice = false; - readonly rollMode: keyof CONFIG.Dice.RollModes = "publicroll"; - readonly flavor: undefined; - - mergeWith(other: Partial): DS4CheckFactoryOptions { - return { ...this, ...other }; - } -} - -/** - * Singleton reference for default value extraction. - */ -const defaultCheckOptions = new DefaultCheckOptions(); +/** @typedef {"publicroll" | "gmroll" | "gmroll" | "selfroll"} RollModes */ /** * Most basic class responsible for generating the chat formula and passing it to the chat as roll. */ class CheckFactory { - constructor( - private checkTargetNumber: number, - private checkModifier: number, - options: Partial = {}, - ) { - this.options = defaultCheckOptions.mergeWith(options); + /** + * @param {number} checkTargetNumber The check target number for this check factory + * @param {number} checkModifier The check modifier for this check factory + * @param {Partial} [options] Options for this check factory + */ + constructor(checkTargetNumber, checkModifier, options = {}) { + this.#checkTargetNumber = checkTargetNumber; + this.#checkModifier = checkModifier; + this.#options = foundry.utils.mergeObject(this.constructor.defaultOptions, options); } - private options: DS4CheckFactoryOptions; + /** + * The check target number for this check factory. + * @type {number} + */ + #checkTargetNumber; - async execute(): Promise { + /** + * The check modifier for this check factory. + * @type {number} + */ + #checkModifier; + + /** + * The options for this check factory. + * @type {DS4CheckFactoryOptions} + */ + #options; + + /** + * The default options of thos CheckFactory class. Upon instantiation, they are merged with the explicitly provided options. + * @type {DS4CheckFactoryOptions} + */ + static get defaultOptions() { + return { + maximumCoupResult: 1, + minimumFumbleResult: 20, + useSlayingDice: false, + rollMode: "publicroll", + }; + } + + /** + * Execute this check factory. + * @returns {Promise} A promise that resolves to the created chat message for the roll */ + async execute() { const innerFormula = ["ds", this.createCheckTargetNumberModifier(), this.createCoupFumbleModifier()].filterJoin( "", ); - const formula = this.options.useSlayingDice ? `{${innerFormula}}x` : innerFormula; + const formula = this.#options.useSlayingDice ? `{${innerFormula}}x` : innerFormula; const roll = Roll.create(formula); - const speaker = this.options.speaker ?? ChatMessage.getSpeaker(); + const speaker = this.#options.speaker ?? ChatMessage.getSpeaker(); return roll.toMessage( { speaker, - flavor: this.options.flavor, - flags: this.options.flavorData ? { ds4: { flavorData: this.options.flavorData } } : undefined, + flavor: this.#options.flavor, + flags: this.#options.flavorData ? { ds4: { flavorData: this.#options.flavorData } } : undefined, }, - { rollMode: this.options.rollMode, create: true }, + { rollMode: this.#options.rollMode, create: true }, ); } - createCheckTargetNumberModifier(): string { - const totalCheckTargetNumber = this.checkTargetNumber + this.checkModifier; - return totalCheckTargetNumber >= 0 ? `v${this.checkTargetNumber + this.checkModifier}` : "v0"; + /** + * Create the check target number modifier for this roll. + * @returns string + */ + createCheckTargetNumberModifier() { + const totalCheckTargetNumber = this.#checkTargetNumber + this.#checkModifier; + return totalCheckTargetNumber >= 0 ? `v${this.#checkTargetNumber + this.#checkModifier}` : "v0"; } - createCoupFumbleModifier(): string | null { + /** + * Create the coup fumble modifier for this roll. + * @returns {string | null} + */ + createCoupFumbleModifier() { const isMinimumFumbleResultRequired = - this.options.minimumFumbleResult !== defaultCheckOptions.minimumFumbleResult; - const isMaximumCoupResultRequired = this.options.maximumCoupResult !== defaultCheckOptions.maximumCoupResult; + this.#options.minimumFumbleResult !== this.constructor.defaultOptions.minimumFumbleResult; + const isMaximumCoupResultRequired = + this.#options.maximumCoupResult !== this.constructor.defaultOptions.maximumCoupResult; if (isMinimumFumbleResultRequired || isMaximumCoupResultRequired) { - return `c${this.options.maximumCoupResult ?? ""}:${this.options.minimumFumbleResult ?? ""}`; + return `c${this.#options.maximumCoupResult ?? ""}:${this.#options.minimumFumbleResult ?? ""}`; } else { return null; } @@ -79,19 +105,18 @@ class CheckFactory { /** * Asks the user for all unknown/necessary information and passes them on to perform a roll. - * @param checkTargetNumber - The Check Target Number ("CTN") - * @param options - Options changing the behavior of the roll and message. + * @param {number} checkTargetNumber The Check Target Number ("CTN") + * @param {Partial} [options={}] Options changing the behavior of the roll and message. + * @returns {Promise} A promise that resolves to the chat message created by the roll */ -export async function createCheckRoll( - checkTargetNumber: number, - options: Partial = {}, -): Promise { +export async function createCheckRoll(checkTargetNumber, options = {}) { // Ask for additional required data; const interactiveRollData = await askForInteractiveRollData(checkTargetNumber, options); const newTargetValue = interactiveRollData.checkTargetNumber ?? checkTargetNumber; const checkModifier = interactiveRollData.checkModifier ?? 0; - const newOptions: Partial = { + /** @type {Partial} */ + const newOptions = { maximumCoupResult: interactiveRollData.maximumCoupResult ?? options.maximumCoupResult, minimumFumbleResult: interactiveRollData.minimumFumbleResult ?? options.minimumFumbleResult, useSlayingDice: getGame().settings.get("ds4", "useSlayingDiceForAutomatedChecks"), @@ -117,26 +142,25 @@ export async function createCheckRoll( * @notes * At the moment, this asks for more data than it will do after some iterations. * - * @returns The data given by the user. + * @param {number} checkTargetNumber The check target number + * @param {Partial} [options={}] Predefined roll options + * @param {template?: string | undefined; title?: string | undefined} [additionalOptions={}] Additional options to use for the dialog + * @returns {Promise>} A promise that resolves to the data given by the user. */ -async function askForInteractiveRollData( - checkTargetNumber: number, - options: Partial = {}, - { template, title }: { template?: string; title?: string } = {}, -): Promise> { +async function askForInteractiveRollData(checkTargetNumber, options = {}, { template, title } = {}) { const usedTemplate = template ?? "systems/ds4/templates/dialogs/roll-options.hbs"; const usedTitle = title ?? getGame().i18n.localize("DS4.DialogRollOptionsDefaultTitle"); const id = foundry.utils.randomID(); const templateData = { title: usedTitle, checkTargetNumber: checkTargetNumber, - maximumCoupResult: options.maximumCoupResult ?? defaultCheckOptions.maximumCoupResult, - minimumFumbleResult: options.minimumFumbleResult ?? defaultCheckOptions.minimumFumbleResult, + maximumCoupResult: options.maximumCoupResult ?? this.constructor.defaultOptions.maximumCoupResult, + minimumFumbleResult: options.minimumFumbleResult ?? this.constructor.defaultOptions.minimumFumbleResult, rollMode: options.rollMode ?? getGame().settings.get("core", "rollMode"), rollModes: CONFIG.Dice.rollModes, checkModifiers: Object.entries(DS4.i18n.checkModifiers).map(([key, translation]) => { if (key in DS4.checkModifiers) { - const value = DS4.checkModifiers[key as keyof typeof DS4.checkModifiers]; + const value = DS4.checkModifiers[key]; const label = `${translation} (${value >= 0 ? `+${value}` : value})`; return { value, label }; } @@ -146,7 +170,7 @@ async function askForInteractiveRollData( }; const renderedHtml = await renderTemplate(usedTemplate, templateData); - const dialogPromise = new Promise((resolve) => { + const dialogPromise = new Promise((resolve) => { new DialogWithListeners( { title: usedTitle, @@ -190,7 +214,7 @@ async function askForInteractiveRollData( .parent(".form-group"); html.find(`#check-modifier-${id}`).on("change", (event) => { if ( - (event.currentTarget as HTMLSelectElement).value === "custom" && + event.currentTarget.value === "custom" && checkModifierCustomFormGroup.hasClass("ds4-hidden") ) { checkModifierCustomFormGroup.removeClass("ds4-hidden"); @@ -211,9 +235,10 @@ async function askForInteractiveRollData( /** * Extracts Dialog data from the returned DOM element. - * @param formData - The filed dialog + * @param {HTMLFormElement} formData The filed dialog + * @returns {Partial} */ -function parseDialogFormData(formData: HTMLFormElement): Partial { +function parseDialogFormData(formData) { const chosenCheckTargetNumber = parseInt(formData["check-target-number"]?.value); const chosenCheckModifier = formData["check-modifier"]?.value; @@ -237,11 +262,10 @@ function parseDialogFormData(formData: HTMLFormElement): Partial} [flavorData] + * @property {ChatSpeakerData} [speaker] + */ + +/** + * @typedef {object} ChatSpeakerData + * @property {string | null} [scene] + * @property {string | null} [actor] + * @property {string | null} [token] + * @property {string | null} [alias] */ -export interface DS4CheckFactoryOptions { - maximumCoupResult: number; - minimumFumbleResult: number; - useSlayingDice: boolean; - rollMode: keyof CONFIG.Dice.RollModes; - flavor?: string; - flavorData?: Record; - speaker?: ReturnType; -} diff --git a/src/dice/check.ts b/src/dice/check.js similarity index 78% rename from src/dice/check.ts rename to src/dice/check.js index ce9e983a..ff389693 100644 --- a/src/dice/check.ts +++ b/src/dice/check.js @@ -16,7 +16,7 @@ import { evaluateCheck, getRequiredNumberOfDice } from "./check-evaluation"; * - Roll a check with a racial ability that makes `5` a coup and default fumble: `/r dsv19c5` */ export class DS4Check extends DiceTerm { - constructor({ modifiers = [], results = [], options }: Partial = {}) { + constructor({ modifiers = [], results = [], options } = {}) { super({ faces: 20, results, @@ -65,34 +65,44 @@ export class DS4Check extends DiceTerm { } } - coup: boolean | null = null; - fumble: boolean | null = null; + /** @type {boolean | null} */ + coup = null; + /** @type {boolean | null} */ + fumble = null; canFumble = true; checkTargetNumber = DS4Check.DEFAULT_CHECK_TARGET_NUMBER; minimumFumbleResult = DS4Check.DEFAULT_MINIMUM_FUMBLE_RESULT; maximumCoupResult = DS4Check.DEFAULT_MAXIMUM_COUP_RESULT; - override get expression(): string { + /** @override */ + get expression() { return `ds${this.modifiers.join("")}`; } - override get total(): string | number | null | undefined { + /** @override */ + get total() { if (this.fumble) return 0; return super.total; } - override _evaluateSync({ minimize = false, maximize = false } = {}): this { + /** @override */ + _evaluateSync({ minimize = false, maximize = false } = {}) { super._evaluateSync({ minimize, maximize }); this.evaluateResults(); return this; } - override roll({ minimize = false, maximize = false } = {}): DiceTerm.Result { + /** @override */ + roll({ minimize = false, maximize = false } = {}) { // Swap minimize / maximize because in DS4, the best possible roll is a 1 and the worst possible roll is a 20 return super.roll({ minimize: maximize, maximize: minimize }); } - evaluateResults(): void { + /** + * Perform additional evaluation after the individual results have been evaluated. + * @protected + */ + evaluateResults() { const dice = this.results.map((die) => die.result); const results = evaluateCheck(dice, this.checkTargetNumber, { maximumCoupResult: this.maximumCoupResult, @@ -108,18 +118,19 @@ export class DS4Check extends DiceTerm { * @remarks "min" and "max" are filtered out because they are irrelevant for * {@link DS4Check}s and only result in some dice rolls being highlighted * incorrectly. + * @override */ - override getResultCSS(result: DiceTerm.Result): (string | null)[] { + getResultCSS(result) { return super.getResultCSS(result).filter((cssClass) => cssClass !== "min" && cssClass !== "max"); } - static readonly DEFAULT_CHECK_TARGET_NUMBER = 10; - static readonly DEFAULT_MAXIMUM_COUP_RESULT = 1; - static readonly DEFAULT_MINIMUM_FUMBLE_RESULT = 20; - static override DENOMINATION = "s"; - static override MODIFIERS = { - c: (): void => undefined, // Modifier is consumed in constructor for maximumCoupResult / minimumFumbleResult - v: (): void => undefined, // Modifier is consumed in constructor for checkTargetNumber - n: (): void => undefined, // Modifier is consumed in constructor for canFumble + static DEFAULT_CHECK_TARGET_NUMBER = 10; + static DEFAULT_MAXIMUM_COUP_RESULT = 1; + static DEFAULT_MINIMUM_FUMBLE_RESULT = 20; + static DENOMINATION = "s"; + static MODIFIERS = { + c: () => undefined, // Modifier is consumed in constructor for maximumCoupResult / minimumFumbleResult + v: () => undefined, // Modifier is consumed in constructor for checkTargetNumber + n: () => undefined, // Modifier is consumed in constructor for canFumble }; } diff --git a/src/dice/roll.ts b/src/dice/roll.js similarity index 74% rename from src/dice/roll.ts rename to src/dice/roll.js index fa86f212..bbb557ed 100644 --- a/src/dice/roll.ts +++ b/src/dice/roll.js @@ -5,19 +5,17 @@ import { getGame } from "../utils/utils"; import { DS4Check } from "./check"; -export class DS4Roll = Record> extends Roll { - static override CHAT_TEMPLATE = "systems/ds4/templates/dice/roll.hbs"; +export class DS4Roll extends Roll { + /** @override */ + static CHAT_TEMPLATE = "systems/ds4/templates/dice/roll.hbs"; /** + * @override * @remarks * This only differs from {@link Roll#render} in that it provides `isCoup` and `isFumble` properties to the roll * template if the first dice term is a ds4 check. */ - override async render({ - flavor, - template = (this.constructor as typeof DS4Roll).CHAT_TEMPLATE, - isPrivate = false, - }: Parameters[0] = {}): Promise { + async render({ flavor, template = this.constructor.CHAT_TEMPLATE, isPrivate = false } = {}) { if (!this._evaluated) await this.evaluate({ async: true }); const firstDiceTerm = this.dice[0]; const isCoup = firstDiceTerm instanceof DS4Check && firstDiceTerm.coup; diff --git a/src/dice/slaying-dice-modifier.ts b/src/dice/slaying-dice-modifier.js similarity index 89% rename from src/dice/slaying-dice-modifier.ts rename to src/dice/slaying-dice-modifier.js index 7f091e07..5eb879bf 100644 --- a/src/dice/slaying-dice-modifier.ts +++ b/src/dice/slaying-dice-modifier.js @@ -6,11 +6,15 @@ import { getGame } from "../utils/utils"; import { DS4Check } from "./check"; -export function registerSlayingDiceModifier(): void { +export function registerSlayingDiceModifier() { PoolTerm.MODIFIERS.x = slay; } -function slay(this: PoolTerm, modifier: string): void { +/** + * @this {PoolTerm} + * @param {string} modifier + */ +function slay(modifier) { const rgx = /[xX]/; const match = modifier.match(rgx); if (!match || !this.rolls) return; diff --git a/src/documents/active-effect.ts b/src/documents/active-effect.js similarity index 57% rename from src/documents/active-effect.ts rename to src/documents/active-effect.js index cbc816b9..b29d6c49 100644 --- a/src/documents/active-effect.ts +++ b/src/documents/active-effect.js @@ -5,27 +5,22 @@ import { mathEvaluator } from "../expression-evaluation/evaluator"; import { getGame } from "../utils/utils"; -import type { DS4Actor } from "./actor/actor"; -import type { DS4Item } from "./item/item"; +/** + * @typedef {object} ItemEffectConfig + * @property {boolean} [applyToItems] Whether or not to apply this effect to owned items instead of the actor + * @property {string} [itemName] Only apply this effect to items with this name + * @property {string} [condition] Only apply this effect to items where this condition is fullfilled + */ -declare global { - interface DocumentClassConfig { - ActiveEffect: typeof DS4ActiveEffect; - } - interface FlagConfig { - ActiveEffect: { - ds4?: { - itemEffectConfig?: { - applyToItems?: boolean; - itemName?: string; - condition?: string; - }; - }; - }; - } -} +/** + * @typedef {object} DS4ActiveEffectFlags + * @property {ItemEffectConfig} [itemEffectConfig] Configuration for applying this effect to owned items + */ -type PromisedType = T extends Promise ? U : T; +/** + * @typedef {Record} ActiveEffectFlags + * @property {DS4ActiveEffectFlags} [ds4] Flags for DS4 + */ export class DS4ActiveEffect extends ActiveEffect { /** @@ -35,13 +30,16 @@ export class DS4ActiveEffect extends ActiveEffect { /** * A cached reference to the source document to avoid recurring database lookups + * @type {foundry.abstract.Document | undefined | null} + * @protected */ - protected source: PromisedType> | undefined = undefined; + source = undefined; /** * Whether or not this effect is currently surpressed. + * @type {boolean} */ - get isSurpressed(): boolean { + get isSurpressed() { const originatingItem = this.originatingItem; if (!originatingItem) { return false; @@ -51,8 +49,9 @@ export class DS4ActiveEffect extends ActiveEffect { /** * The item which this effect originates from if it has been transferred from an item to an actor. + * @return {import("./item/item").DS4Item | undefined} */ - get originatingItem(): DS4Item | undefined { + get originatingItem() { if (!(this.parent instanceof Actor)) { return; } @@ -66,27 +65,28 @@ export class DS4ActiveEffect extends ActiveEffect { /** * The number of times this effect should be applied. + * @type {number} */ - get factor(): number { + get factor() { return this.originatingItem?.activeEffectFactor ?? 1; } - override apply(document: DS4Actor | DS4Item, change: EffectChangeData): unknown { + /** @override */ + apply(document, change) { change.value = Roll.replaceFormulaData(change.value, document.data); try { change.value = DS4ActiveEffect.safeEval(change.value).toString(); } catch (e) { // this is a valid case, e.g., if the effect change simply is a string } - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error In the types and foundry's documentation, only actors are allowed, but the implementation actually works for all kinds of documents return super.apply(document, change); } /** * Gets the current source name based on the cached source object. + * @returns {Promise} The current source name */ - async getCurrentSourceName(): Promise { + async getCurrentSourceName() { const game = getGame(); const origin = await this.getSource(); if (origin === null) return game.i18n.localize("None"); @@ -96,8 +96,10 @@ export class DS4ActiveEffect extends ActiveEffect { /** * Gets the source document for this effect. Uses the cached {@link DS4ActiveEffect#source} if it has already been * set. + * @protected + * @returns {Promise} */ - protected async getSource(): ReturnType { + async getSource() { if (this.source === undefined) { this.source = this.data.origin !== undefined ? await fromUuid(this.data.origin) : null; } @@ -107,10 +109,10 @@ export class DS4ActiveEffect extends ActiveEffect { /** * Create a new {@link DS4ActiveEffect} using default data. * - * @param parent The parent {@link DS4Actor} or {@link DS4Item} of the effect. - * @returns A promise that resolved to the created effect or udifined of the creation was prevented. + * @param {import("./item/item").DS4Item | import("./actor/actor").DS4Actor} parent The parent of the effect. + * @returns {Promise}A promise that resolved to the created effect or udifined of the creation was prevented. */ - static async createDefault(parent: DS4Actor | DS4Item): Promise { + static async createDefault(parent) { const createData = { label: getGame().i18n.localize(`DS4.NewEffectLabel`), icon: this.FALLBACK_ICON, @@ -119,26 +121,29 @@ export class DS4ActiveEffect extends ActiveEffect { return this.create(createData, { parent, pack: parent.pack ?? undefined }); } - static safeEval(expression: string): number | `${number | boolean}` { + /** + * Safely evaluate a mathematical expression. + * @param {string} expression The expression to evaluate + * @returns {number | `${number | boolean}`} The numeric result of the expression + * @throws If the expression could not be evaluated or did not produce a numeric resilt + */ + static safeEval(expression) { const result = mathEvaluator.evaluate(expression); if (!Number.isNumeric(result)) { throw new Error(`mathEvaluator.evaluate produced a non-numeric result from expression "${expression}"`); } - return result as number | `${number | boolean}`; + return result; } /** * Apply the given effects to the gicen Actor or item. - * @param document The Actor or Item to which to apply the effects - * @param effetcs The effects to apply - * @param predicate Apply only changes that fullfill this predicate + * @param {import("./item/item").DS4Item | import("./actor/actor").DS4Actor} document The Actor or Item to which to apply the effects + * @param {DS4ActiveEffect[]} effetcs The effects to apply + * @param {(change: EffectChangeData) => boolean} [predicate=() => true] Apply only changes that fullfill this predicate */ - static applyEffetcs( - document: DS4Actor | DS4Item, - effetcs: DS4ActiveEffect[], - predicate: (change: EffectChangeData) => boolean = () => true, - ): void { - const overrides: Record = {}; + static applyEffetcs(document, effetcs, predicate = () => true) { + /** @type {Record} */ + const overrides = {}; // Organize non-disabled and -surpressed effects by their application priority const changesWithEffect = effetcs.flatMap((e) => e.getFactoredChangesWithEffect(predicate)); @@ -159,22 +164,28 @@ export class DS4ActiveEffect extends ActiveEffect { /** * Get the array of changes for this effect, considering the {@link DS4ActiveEffect#factor}. - * @param predicate An optional predicate to filter which changes should be considered - * @returns The array of changes from this effect, considering the factor. + * @param {(change: EffectChangeData) => boolean} [predicate=() => true] An optional predicate to filter which changes should be considered + * @returns {EffectChangeDataWithEffect[]} The array of changes from this effect, considering the factor. + * @protected */ - protected getFactoredChangesWithEffect( - predicate: (change: EffectChangeData) => boolean = () => true, - ): EffectChangeDataWithEffect[] { + getFactoredChangesWithEffect(predicate = () => true) { if (this.data.disabled || this.isSurpressed) { return []; } return this.data.changes.filter(predicate).flatMap((change) => { change.priority = change.priority ?? change.mode * 10; - return Array(this.factor).fill({ effect: this, change }); + return Array(this.factor).fill({ effect: this, change }); }); } } -type EffectChangeData = foundry.data.ActiveEffectData["changes"][number]; -type EffectChangeDataWithEffect = { effect: DS4ActiveEffect; change: EffectChangeData }; +/** + * @typedef {foundry.data.ActiveEffectData["changes"][number]} EffectChangeData + */ + +/** + * @typedef {object} EffectChangeDataWithEffect + * @property {DS4ActiveEffect} effect + * @property {EffectChangeData} change + */ diff --git a/src/documents/actor/actor.ts b/src/documents/actor/actor.js similarity index 77% rename from src/documents/actor/actor.ts rename to src/documents/actor/actor.js index 16b6506a..c31145e6 100644 --- a/src/documents/actor/actor.ts +++ b/src/documents/actor/actor.js @@ -11,24 +11,12 @@ import { getGame } from "../../utils/utils"; import { DS4ActiveEffect } from "../active-effect"; import { isAttribute, isTrait } from "./actor-data-source-base"; -import type { ModifiableDataBaseTotal } from "../common/common-data"; -import type { DS4ArmorDataProperties } from "../item/armor/armor-data-properties"; -import type { DS4Item } from "../item/item"; -import type { ItemType } from "../item/item-data-source"; -import type { DS4ShieldDataProperties } from "../item/shield/shield-data-properties"; -import type { Check } from "./actor-data-properties-base"; - -declare global { - interface DocumentClassConfig { - Actor: typeof DS4Actor; - } -} - /** * The Actor class for DS4 */ export class DS4Actor extends Actor { - override prepareData(): void { + /** @override */ + prepareData() { this.data.reset(); this.prepareBaseData(); this.prepareEmbeddedDocuments(); @@ -39,7 +27,8 @@ export class DS4Actor extends Actor { this.prepareFinalDerivedData(); } - override prepareBaseData(): void { + /** @override */ + prepareBaseData() { const data = this.data; data.data.rolling = { @@ -48,17 +37,14 @@ export class DS4Actor extends Actor { }; const attributes = data.data.attributes; - Object.values(attributes).forEach( - (attribute: ModifiableDataBaseTotal) => (attribute.total = attribute.base + attribute.mod), - ); + Object.values(attributes).forEach((attribute) => (attribute.total = attribute.base + attribute.mod)); const traits = data.data.traits; - Object.values(traits).forEach( - (trait: ModifiableDataBaseTotal) => (trait.total = trait.base + trait.mod), - ); + Object.values(traits).forEach((trait) => (trait.total = trait.base + trait.mod)); } - override prepareEmbeddedDocuments() { + /** @override */ + prepareEmbeddedDocuments() { super.prepareEmbeddedDocuments(); this.applyActiveEffectsToItems(); } @@ -71,16 +57,21 @@ export class DS4Actor extends Actor { this.data.data.armorValueSpellMalus = this.armorValueSpellMalusOfEquippedItems; } - protected get actorEffects() { + /** + * The effects that should be applioed to this actor. + * @type {this["effects"]} + * @protected + */ + get actorEffects() { return this.effects.filter((effect) => !effect.data.flags.ds4?.itemEffectConfig?.applyToItems); } /** * Get the effects of this actor that should be applied to the given item. - * @param item The item for which to get effects + * @param {import("../item/item").DS4Item} item The item for which to get effects * @returns The array of effects that are candidates to be applied to the item */ - itemEffects(item: DS4Item) { + itemEffects(item) { return this.effects.filter((effect) => { const { applyToItems, itemName, condition } = effect.data.flags.ds4?.itemEffectConfig ?? {}; @@ -106,7 +97,14 @@ export class DS4Actor extends Actor { }); } - protected static replaceFormulaData(formula: string, data: object): string | undefined { + /** + * Replace placholders in a formula with data. + * @param {string} formula The formular to enricht with data + * @param {object} data The data to use for enriching + * @returns {string | undefined} The Enriched formula or undefined, if it contains placeholders that cannot be resolved + * @protected + */ + static replaceFormulaData(formula, data) { const dataRgx = new RegExp(/@([a-z.0-9_\-]+)/gi); try { return formula.replace(dataRgx, (_, term) => { @@ -124,8 +122,9 @@ export class DS4Actor extends Actor { /** * We override this with an empty implementation because we have our own custom way of applying * {@link ActiveEffect}s and {@link Actor#prepareEmbeddedDocuments} calls this. + * @override */ - override applyActiveEffects(): void { + applyActiveEffects() { return; } @@ -138,7 +137,7 @@ export class DS4Actor extends Actor { * no special ordering among talents. This means that having a talents that provide effects that adjust the total * rank of talents can lead to nondeterministic behavior and thus must be avoided. */ - applyActiveEffectsToItems(): void { + applyActiveEffectsToItems() { /* Handle talents before all other item types, because their rank might be affected, which in turn affects how many times effects provided by that talent are applied */ for (const item of this.itemTypes.talent) { @@ -150,12 +149,21 @@ export class DS4Actor extends Actor { } } - protected applyActiveEffectsToItem(item: DS4Item) { + /** + * Apply effects to the given item. + * @param {import("../item/item").DS4Item} item The item to which to apply effects + * @protected + */ + applyActiveEffectsToItem(item) { item.overrides = {}; DS4ActiveEffect.applyEffetcs(item, this.itemEffects(item)); } - applyActiveEffectsToBaseData(): void { + /** + * Apply effects to base data + * @protected + */ + applyActiveEffectsToBaseData() { this.overrides = {}; DS4ActiveEffect.applyEffetcs( this, @@ -166,7 +174,11 @@ export class DS4Actor extends Actor { ); } - applyActiveEffectsToDerivedData(): void { + /** + * Apply effects to derived data + * @protected + */ + applyActiveEffectsToDerivedData() { DS4ActiveEffect.applyEffetcs(this, this.actorEffects, (change) => this.derivedDataProperties.includes(change.key), ); @@ -174,8 +186,9 @@ export class DS4Actor extends Actor { /** * Apply transformations to the Actor data after effects have been applied to the base data. + * @override */ - override prepareDerivedData(): void { + prepareDerivedData() { this.data.data.armorValueSpellMalus = Math.max(this.data.data.armorValueSpellMalus, 0); this.prepareCombatValues(); this.prepareChecks(); @@ -183,8 +196,9 @@ export class DS4Actor extends Actor { /** * The list of properties that are derived from others, given in dot notation. + * @returns {string[]} The list of derived propertie */ - get derivedDataProperties(): Array { + get derivedDataProperties() { const combatValueProperties = Object.keys(DS4.i18n.combatValues).map( (combatValue) => `data.combatValues.${combatValue}.total`, ); @@ -197,20 +211,14 @@ export class DS4Actor extends Actor { /** * Apply final transformations to the Actor data after all effects have been applied. */ - prepareFinalDerivedData(): void { - Object.values(this.data.data.attributes).forEach( - (attribute: ModifiableDataBaseTotal) => (attribute.total = Math.ceil(attribute.total)), - ); - Object.values(this.data.data.traits).forEach( - (trait: ModifiableDataBaseTotal) => (trait.total = Math.ceil(trait.total)), - ); + prepareFinalDerivedData() { + Object.values(this.data.data.attributes).forEach((attribute) => (attribute.total = Math.ceil(attribute.total))); + Object.values(this.data.data.traits).forEach((trait) => (trait.total = Math.ceil(trait.total))); Object.entries(this.data.data.combatValues) .filter(([key]) => key !== "movement") .map(([, value]) => value) - .forEach( - (combatValue: ModifiableDataBaseTotal) => (combatValue.total = Math.ceil(combatValue.total)), - ); - (Object.keys(this.data.data.checks) as (keyof typeof this.data.data.checks)[]).forEach((key) => { + .forEach((combatValue) => (combatValue.total = Math.ceil(combatValue.total))); + Object.keys(this.data.data.checks).forEach((key) => { this.data.data.checks[key] = Math.ceil(this.data.data.checks[key]); }); @@ -221,30 +229,34 @@ export class DS4Actor extends Actor { /** * The list of properties that are completely derived (i.e. {@link ActiveEffect}s cannot be applied to them), * given in dot notation. + * @type {string[]} */ - get finalDerivedDataProperties(): string[] { + get finalDerivedDataProperties() { return ["data.combatValues.hitPoints.max", "data.checks.defend"]; } /** * The list of item types that can be owned by this actor. + * @type {import("../item/item-data-source").ItemType[]} */ - get ownableItemTypes(): Array { + get ownableItemTypes() { return ["weapon", "armor", "shield", "equipment", "loot", "spell"]; } /** * Checks whether or not the given item type can be owned by the actor. - * @param itemType - The item type to check + * @param {import("../item/item-data-source").ItemType} itemType The item type to check + * @returns {boolean} Whether or not this actor can own the given item type */ - canOwnItemType(itemType: ItemType): boolean { + canOwnItemType(itemType) { return this.ownableItemTypes.includes(itemType); } /** - * Prepares the combat values of the actor. + * Prepares the combat values of the actor. + * @protected */ - protected prepareCombatValues(): void { + prepareCombatValues() { const data = this.data.data; data.combatValues.hitPoints.base = data.attributes.body.total + data.traits.constitution.total + 10; @@ -260,21 +272,25 @@ export class DS4Actor extends Actor { data.attributes.mind.total + data.traits.dexterity.total - data.armorValueSpellMalus; Object.values(data.combatValues).forEach( - (combatValue: ModifiableDataBaseTotal) => (combatValue.total = combatValue.base + combatValue.mod), + (combatValue) => (combatValue.total = combatValue.base + combatValue.mod), ); } /** * The total armor value of the equipped items. + * @type {number} + * @protected */ - protected get armorValueOfEquippedItems(): number { + get armorValueOfEquippedItems() { return this.equippedItemsWithArmor.map((item) => item.data.data.armorValue).reduce((a, b) => a + b, 0); } /** * The armor value spell malus from equipped items. + * @type {number} + * @protected */ - protected get armorValueSpellMalusOfEquippedItems(): number { + get armorValueSpellMalusOfEquippedItems() { return this.equippedItemsWithArmor .filter( (item) => @@ -284,19 +300,22 @@ export class DS4Actor extends Actor { .reduce((a, b) => a + b, 0); } - protected get equippedItemsWithArmor() { + /** + * The equipped items of this actor that provide armor. + * @type {(import("../item/armor/armor").DS4Armor | import("../item/shield/shield").DS4Shield)[]} + * @protected + */ + get equippedItemsWithArmor() { return this.items - .filter( - (item): item is DS4Item & { data: DS4ArmorDataProperties | DS4ShieldDataProperties } => - item.data.type === "armor" || item.data.type === "shield", - ) + .filter((item) => item.data.type === "armor" || item.data.type === "shield") .filter((item) => item.data.data.equipped); } /** * Prepares the check target numbers of checks for the actor. + * @protected */ - protected prepareChecks(): void { + prepareChecks() { const data = this.data.data; data.checks = { appraise: data.attributes.mind.total + data.traits.intellect.total, @@ -334,17 +353,14 @@ export class DS4Actor extends Actor { /** * Handle how changes to a Token attribute bar are applied to the Actor. * This only differs from the base implementation by also allowing negative values. + * @override */ - override async modifyTokenAttribute( - attribute: string, - value: number, - isDelta = false, - isBar = true, - ): Promise { + async modifyTokenAttribute(attribute, value, isDelta = false, isBar = true) { const current = foundry.utils.getProperty(this.data.data, attribute); // Determine the updates to make to the actor data - let updates: Record; + /** @type {Record} */ + let updates; if (isBar) { if (isDelta) value = Math.min(Number(current.value) + value, current.max); updates = { [`data.${attribute}.value`]: value }; @@ -360,13 +376,11 @@ export class DS4Actor extends Actor { /** * Roll for a given check. - * @param check - The check to perform - * @param options - Additional options to customize the roll + * @param {import("./actor-data-properties-base").Check} check The check to perform + * @param {import("../common/roll-options").RollOptions} [options={}] Additional options to customize the roll + * @returns {Promise} A promise that resolves once the roll has been performed */ - async rollCheck( - check: Check, - options: { speaker?: { token?: TokenDocument; alias?: string } } = {}, - ): Promise { + async rollCheck(check, options = {}) { const speaker = ChatMessage.getSpeaker({ actor: this, ...options.speaker }); await createCheckRoll(this.data.data.checks[check], { rollMode: getGame().settings.get("core", "rollMode"), @@ -381,9 +395,10 @@ export class DS4Actor extends Actor { /** * Roll a generic check. A dialog is presented to select the combination of * Attribute and Trait to perform the check against. - * @param options - Additional options to customize the roll + * @param {import("../common/roll-options").RollOptions} [options={}] Additional options to customize the roll + * @returns {Promise} A promise that resolves once the roll has been performed */ - async rollGenericCheck(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { + async rollGenericCheck(options = {}) { const attributeAndTrait = await this.selectAttributeAndTrait(); if (!attributeAndTrait) { return; @@ -405,10 +420,12 @@ export class DS4Actor extends Actor { }); } - protected async selectAttributeAndTrait(): Promise<{ - attribute: keyof typeof DS4.i18n.attributes; - trait: keyof typeof DS4.i18n.traits; - } | null> { + /** + * Prompt the use to select an attribute and a trait. + * @returns {Promise} + * @protected + */ + async selectAttributeAndTrait() { const attributeIdentifier = "attribute-trait-selection-attribute"; const traitIdentifier = "attribute-trait-selection-trait"; return Dialog.prompt({ @@ -419,24 +436,20 @@ export class DS4Actor extends Actor { label: getGame().i18n.localize("DS4.Attribute"), identifier: attributeIdentifier, options: Object.fromEntries( - (Object.entries(DS4.i18n.attributes) as [keyof typeof DS4.i18n.attributes, string][]).map( - ([attribute, translation]) => [ - attribute, - `${translation} (${this.data.data.attributes[attribute].total})`, - ], - ), + Object.entries(DS4.i18n.attributes).map(([attribute, translation]) => [ + attribute, + `${translation} (${this.data.data.attributes[attribute].total})`, + ]), ), }, { label: getGame().i18n.localize("DS4.Trait"), identifier: traitIdentifier, options: Object.fromEntries( - (Object.entries(DS4.i18n.traits) as [keyof typeof DS4.i18n.traits, string][]).map( - ([trait, translation]) => [ - trait, - `${translation} (${this.data.data.traits[trait].total})`, - ], - ), + Object.entries(DS4.i18n.traits).map(([trait, translation]) => [ + trait, + `${translation} (${this.data.data.traits[trait].total})`, + ]), ), }, ], @@ -474,3 +487,9 @@ export class DS4Actor extends Actor { }); } } + +/** + * @typedef {object} AttributeAndTrait + * @property {keyof typeof DS4.i18n.attributes} attribute + * @property {keyof typeof DS4.i18n.traits} trait + */ diff --git a/src/documents/actor/character/character.ts b/src/documents/actor/character/character.js similarity index 56% rename from src/documents/actor/character/character.ts rename to src/documents/actor/character/character.js index bda2cb65..5a11d910 100644 --- a/src/documents/actor/character/character.ts +++ b/src/documents/actor/character/character.js @@ -4,23 +4,20 @@ import { DS4Actor } from "../actor"; -import type { ItemType } from "../../item/item-data-source"; - export class DS4Character extends DS4Actor { - override prepareFinalDerivedData(): void { + /** @override */ + prepareFinalDerivedData() { super.prepareFinalDerivedData(); this.data.data.slayerPoints.max = 3; } - override get finalDerivedDataProperties(): string[] { + /** @override */ + get finalDerivedDataProperties() { return [...super.finalDerivedDataProperties, "data.slayerPoints.max"]; } - override get ownableItemTypes(): Array { + /** @override */ + get ownableItemTypes() { return [...super.ownableItemTypes, "talent", "racialAbility", "language", "alphabet"]; } } - -export interface DS4Character { - data: foundry.data.ActorData & { type: "character"; _source: { type: "character" } }; -} diff --git a/src/documents/actor/creature/creature.ts b/src/documents/actor/creature/creature.ts index 1690195a..594812f3 100644 --- a/src/documents/actor/creature/creature.ts +++ b/src/documents/actor/creature/creature.ts @@ -11,7 +11,3 @@ export class DS4Creature extends DS4Actor { return [...super.ownableItemTypes, "specialCreatureAbility"]; } } - -export interface DS4Creature { - data: foundry.data.ActorData & { type: "creature"; _source: { type: "creature" } }; -} diff --git a/src/documents/actor/proxy.ts b/src/documents/actor/proxy.js similarity index 73% rename from src/documents/actor/proxy.ts rename to src/documents/actor/proxy.js index e5eb2f44..5a2434a8 100644 --- a/src/documents/actor/proxy.ts +++ b/src/documents/actor/proxy.js @@ -8,7 +8,11 @@ import { DS4Character } from "./character/character"; import { DS4Creature } from "./creature/creature"; const handler = { - construct(_: typeof DS4Actor, args: ConstructorParameters) { + /** + * @param {typeof import("./actor").DS4Actor} + * @param {unknown[]} args + */ + construct(_, args) { switch (args[0]?.type) { case "character": return new DS4Character(...args); @@ -20,4 +24,5 @@ const handler = { }, }; -export const DS4ActorProxy: typeof DS4Actor = new Proxy(DS4Actor, handler); +/** @type {typeof import("./actor").DS4Actor} */ +export const DS4ActorProxy = new Proxy(DS4Actor, handler); diff --git a/src/documents/chat-message.ts b/src/documents/chat-message.js similarity index 67% rename from src/documents/chat-message.ts rename to src/documents/chat-message.js index ac99b9d5..9392da66 100644 --- a/src/documents/chat-message.ts +++ b/src/documents/chat-message.js @@ -4,18 +4,18 @@ import { getGame } from "../utils/utils"; -declare global { - interface FlagConfig { - ChatMessage: { - ds4?: { - flavorData?: Record; - }; - }; - } -} +/** + * @typedef {object} DS4ChatMessageFlags + * @property {Record} [flavorData] Data to use for localizing the flavor of the chat message + */ + +/** + * @typedef {Record} ChatMessageFlags + * @property {DS4ChatMessageFlags} [ds4] Flags for DS4 + */ export class DS4ChatMessage extends ChatMessage { - override prepareData(): void { + prepareData() { super.prepareData(); if (this.data.flavor) { const game = getGame(); diff --git a/src/documents/item/alphabet/alphabet.ts b/src/documents/item/alphabet/alphabet.ts index d710b896..be6e5301 100644 --- a/src/documents/item/alphabet/alphabet.ts +++ b/src/documents/item/alphabet/alphabet.ts @@ -5,7 +5,3 @@ import { DS4Item } from "../item"; export class DS4Alphabet extends DS4Item {} - -export interface DS4Alphabet { - data: foundry.data.ItemData & { type: "alphabet"; _source: { type: "alphabet" } }; -} diff --git a/src/documents/item/armor/armor.ts b/src/documents/item/armor/armor.ts index 9c18fb6e..531d0d7c 100644 --- a/src/documents/item/armor/armor.ts +++ b/src/documents/item/armor/armor.ts @@ -5,7 +5,3 @@ import { DS4Item } from "../item"; export class DS4Armor extends DS4Item {} - -export interface DS4Armor { - data: foundry.data.ItemData & { type: "armor"; _source: { type: "armor" } }; -} diff --git a/src/documents/item/equipment/equipment.ts b/src/documents/item/equipment/equipment.ts index f5e7d455..748177d5 100644 --- a/src/documents/item/equipment/equipment.ts +++ b/src/documents/item/equipment/equipment.ts @@ -5,7 +5,3 @@ import { DS4Item } from "../item"; export class DS4Equipment extends DS4Item {} - -export interface DS4Equipment { - data: foundry.data.ItemData & { type: "equipment"; _source: { type: "equipment" } }; -} diff --git a/src/documents/item/item-data-source-base.ts b/src/documents/item/item-data-source-base.ts index dbd1a06b..7ef38be4 100644 --- a/src/documents/item/item-data-source-base.ts +++ b/src/documents/item/item-data-source-base.ts @@ -17,7 +17,7 @@ export interface DS4ItemDataSourceDataPhysical { storageLocation: string; } -export function isDS4ItemDataTypePhysical(input: foundry.data.ItemData["data"]): boolean { +export function isDS4ItemDataTypePhysical(input: object): boolean { return "quantity" in input && "price" in input && "availability" in input && "storageLocation" in input; } diff --git a/src/documents/item/item.js b/src/documents/item/item.js new file mode 100644 index 00000000..c54288ef --- /dev/null +++ b/src/documents/item/item.js @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// SPDX-FileCopyrightText: 2021 Gesina Schwalbe +// +// SPDX-License-Identifier: MIT + +import { getGame } from "../../utils/utils"; + +/** + * The Item class for DS4 + */ +export class DS4Item extends Item { + /** + * An object that tracks the changes to the data model which were applied by active effects + * @type {Record} + */ + overrides = {}; + + /** @override */ + prepareDerivedData() { + this.data.data.rollable = false; + } + + /** + * Is this item a non-equipped equipable? + * @returns {boolean} Whether the item is a non-equpped equibale or not + */ + isNonEquippedEuipable() { + return "equipped" in this.data.data && !this.data.data.equipped; + } + + /** + * The number of times that active effect changes originating from this item should be applied. + * @returns {number | undefined} The number of times the effect should be applied + */ + get activeEffectFactor() { + return 1; + } + + /** + * The list of item types that are rollable. + * @returns {import("../item/item-data-source").ItemType[]} The rollable item types + */ + static get rollableItemTypes() { + return ["weapon", "spell"]; + } + + /** + * Roll a check for an action with this item. + * @param {import("../common/roll-options").RollOptions} [options={}] Additional options to customize the roll + * @returns {Promise} A promise that resolves once the roll has been performed + * @abstract + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async roll(options = {}) { + throw new Error(getGame().i18n.format("DS4.ErrorRollingForItemTypeNotPossible", { type: this.data.type })); + } +} diff --git a/src/documents/item/item.ts b/src/documents/item/item.ts deleted file mode 100644 index 50015e37..00000000 --- a/src/documents/item/item.ts +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Johannes Loher -// SPDX-FileCopyrightText: 2021 Gesina Schwalbe -// -// SPDX-License-Identifier: MIT - -import { getGame } from "../../utils/utils"; - -import type { ItemType } from "./item-data-source"; - -declare global { - interface DocumentClassConfig { - Item: typeof DS4Item; - } - - // eslint-disable-next-line @typescript-eslint/no-namespace - namespace Hooks { - interface StaticCallbacks { - "ds4.rollItem": (item: DS4Item) => void; - } - } -} - -/** - * The Item class for DS4 - */ -export class DS4Item extends Item { - /** An object that tracks the changes to the data model which were applied by active effects */ - overrides: Record = {}; - - override prepareDerivedData(): void { - this.data.data.rollable = false; - } - - isNonEquippedEuipable(): boolean { - return "equipped" in this.data.data && !this.data.data.equipped; - } - - /** - * The number of times that active effect changes originating from this item should be applied. - */ - get activeEffectFactor(): number | undefined { - return 1; - } - - /** - * The list of item types that are rollable. - */ - static get rollableItemTypes(): ItemType[] { - return ["weapon", "spell"]; - } - - /** - * Roll a check for an action with this item. - * @param options - Additional options to customize the roll - */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async roll(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { - throw new Error(getGame().i18n.format("DS4.ErrorRollingForItemTypeNotPossible", { type: this.data.type })); - } -} diff --git a/src/documents/item/language/language.ts b/src/documents/item/language/language.ts index 0faf53da..2d8d45ec 100644 --- a/src/documents/item/language/language.ts +++ b/src/documents/item/language/language.ts @@ -5,7 +5,3 @@ import { DS4Item } from "../item"; export class DS4Language extends DS4Item {} - -export interface DS4Language { - data: foundry.data.ItemData & { type: "language"; _source: { type: "language" } }; -} diff --git a/src/documents/item/loot/loot.ts b/src/documents/item/loot/loot.ts index 34528207..ba90a2fd 100644 --- a/src/documents/item/loot/loot.ts +++ b/src/documents/item/loot/loot.ts @@ -5,7 +5,3 @@ import { DS4Item } from "../item"; export class DS4Loot extends DS4Item {} - -export interface DS4Loot { - data: foundry.data.ItemData & { type: "loot"; _source: { type: "loot" } }; -} diff --git a/src/documents/item/proxy.ts b/src/documents/item/proxy.js similarity index 88% rename from src/documents/item/proxy.ts rename to src/documents/item/proxy.js index b978bc30..ff8c1a8a 100644 --- a/src/documents/item/proxy.ts +++ b/src/documents/item/proxy.js @@ -17,7 +17,11 @@ import { DS4Talent } from "./talent/talent"; import { DS4Weapon } from "./weapon/weapon"; const handler = { - construct(_: typeof DS4Item, args: ConstructorParameters) { + /** + * @param {typeof import("./item").DS4Item} + * @param {unknown[]} args + */ + construct(_, args) { switch (args[0]?.type) { case "alphabet": return new DS4Alphabet(...args); @@ -47,4 +51,5 @@ const handler = { }, }; -export const DS4ItemProxy: typeof DS4Item = new Proxy(DS4Item, handler); +/** @type {typeof import("./item").DS4Item} */ +export const DS4ItemProxy = new Proxy(DS4Item, handler); diff --git a/src/documents/item/racial-ability/racial-ability.ts b/src/documents/item/racial-ability/racial-ability.ts index 526f2533..b3b360d9 100644 --- a/src/documents/item/racial-ability/racial-ability.ts +++ b/src/documents/item/racial-ability/racial-ability.ts @@ -5,7 +5,3 @@ import { DS4Item } from "../item"; export class DS4RacialAbility extends DS4Item {} - -export interface DS4RacialAbility { - data: foundry.data.ItemData & { type: "racialAbility"; _source: { type: "racialAbility" } }; -} diff --git a/src/documents/item/shield/shield.ts b/src/documents/item/shield/shield.ts index 16a6e683..0da6a219 100644 --- a/src/documents/item/shield/shield.ts +++ b/src/documents/item/shield/shield.ts @@ -5,7 +5,3 @@ import { DS4Item } from "../item"; export class DS4Shield extends DS4Item {} - -export interface DS4Shield { - data: foundry.data.ItemData & { type: "shield"; _source: { type: "shield" } }; -} diff --git a/src/documents/item/special-creature-ability/special-creature-ability.ts b/src/documents/item/special-creature-ability/special-creature-ability.ts index 1919ae88..57e90988 100644 --- a/src/documents/item/special-creature-ability/special-creature-ability.ts +++ b/src/documents/item/special-creature-ability/special-creature-ability.ts @@ -5,7 +5,3 @@ import { DS4Item } from "../item"; export class DS4SpecialCreatureAbility extends DS4Item {} - -export interface DS4SpecialCreatureAbility { - data: foundry.data.ItemData & { type: "specialCreatureAbility"; _source: { type: "specialCreatureAbility" } }; -} diff --git a/src/documents/item/spell/spell.ts b/src/documents/item/spell/spell.js similarity index 84% rename from src/documents/item/spell/spell.ts rename to src/documents/item/spell/spell.js index 6fe74b9a..79eaf871 100644 --- a/src/documents/item/spell/spell.ts +++ b/src/documents/item/spell/spell.js @@ -2,14 +2,15 @@ // // SPDX-License-Identifier: MIT -import { createCheckRoll, DS4CheckFactoryOptions } from "../../../dice/check-factory"; +import { createCheckRoll } from "../../../dice/check-factory"; import { notifications } from "../../../ui/notifications"; import { getGame } from "../../../utils/utils"; import { DS4Item } from "../item"; import { calculateSpellPrice } from "./calculate-spell-price"; export class DS4Spell extends DS4Item { - override prepareDerivedData(): void { + /** @override */ + prepareDerivedData() { this.data.data.rollable = this.data.data.equipped; this.data.data.price = calculateSpellPrice(this.data.data); if (this.data.data.allowsDefense) { @@ -17,7 +18,8 @@ export class DS4Spell extends DS4Item { } } - override async roll(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { + /** @override */ + async roll(options = {}) { const game = getGame(); if (!this.data.data.equipped) { @@ -54,7 +56,8 @@ export class DS4Spell extends DS4Item { opponentDefense !== undefined && opponentDefense !== 0 ? "DS4.ItemSpellCheckFlavorWithOpponentDefense" : "DS4.ItemSpellCheckFlavor"; - const flavorData: DS4CheckFactoryOptions["flavorData"] = { + /** @type {import("../../../dice/check-factory").DS4CheckFactoryOptions["flavorData"]} */ + const flavorData = { actor: speaker.alias ?? this.actor.name, spell: this.name, }; @@ -71,10 +74,12 @@ export class DS4Spell extends DS4Item { speaker, }); + /** + * A hook event that fires after an item is rolled. + * @function ds4.rollItem + * @memberof hookEvents + * @param {DS4Item} item Item being rolled. + */ Hooks.callAll("ds4.rollItem", this); } } - -export interface DS4Spell { - data: foundry.data.ItemData & { type: "spell"; _source: { type: "spell" } }; -} diff --git a/src/documents/item/talent/talent.ts b/src/documents/item/talent/talent.js similarity index 61% rename from src/documents/item/talent/talent.ts rename to src/documents/item/talent/talent.js index 9d4875c9..c440f08d 100644 --- a/src/documents/item/talent/talent.ts +++ b/src/documents/item/talent/talent.js @@ -5,17 +5,15 @@ import { DS4Item } from "../item"; export class DS4Talent extends DS4Item { - override prepareDerivedData(): void { + /** @override */ + prepareDerivedData() { super.prepareDerivedData(); const data = this.data.data; data.rank.total = data.rank.base + data.rank.mod; } - override get activeEffectFactor(): number | undefined { + /** @override */ + get activeEffectFactor() { return this.data.data.rank.total; } } - -export interface DS4Talent { - data: foundry.data.ItemData & { type: "talent"; _source: { type: "talent" } }; -} diff --git a/src/documents/item/weapon/weapon.ts b/src/documents/item/weapon/weapon.js similarity index 86% rename from src/documents/item/weapon/weapon.ts rename to src/documents/item/weapon/weapon.js index 3eaae806..b0d75afb 100644 --- a/src/documents/item/weapon/weapon.ts +++ b/src/documents/item/weapon/weapon.js @@ -3,13 +3,14 @@ // SPDX-License-Identifier: MIT import { DS4 } from "../../../config"; -import { createCheckRoll, DS4CheckFactoryOptions } from "../../../dice/check-factory"; +import { createCheckRoll } from "../../../dice/check-factory"; import { notifications } from "../../../ui/notifications"; import { getGame } from "../../../utils/utils"; import { DS4Item } from "../item"; export class DS4Weapon extends DS4Item { - override prepareDerivedData(): void { + /** @override */ + prepareDerivedData() { const data = this.data.data; data.rollable = data.equipped; data.opponentDefenseForAttackType = {}; @@ -21,7 +22,8 @@ export class DS4Weapon extends DS4Item { } } - override async roll(options: { speaker?: { token?: TokenDocument; alias?: string } } = {}): Promise { + /** @override */ + async roll(options = {}) { const game = getGame(); if (!this.data.data.equipped) { return notifications.warn( @@ -41,14 +43,15 @@ export class DS4Weapon extends DS4Item { const weaponBonus = this.data.data.weaponBonus; const attackType = await this.getPerformedAttackType(); const opponentDefense = this.data.data.opponentDefenseForAttackType[attackType]; - const combatValue = `${attackType}Attack` as const; + const combatValue = `${attackType}Attack`; const checkTargetNumber = ownerDataData.combatValues[combatValue].total + weaponBonus; const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker }); const flavor = opponentDefense !== undefined && opponentDefense !== 0 ? "DS4.ItemWeaponCheckFlavorWithOpponentDefense" : "DS4.ItemWeaponCheckFlavor"; - const flavorData: DS4CheckFactoryOptions["flavorData"] = { + /** @type {import("../../../dice/check-factory").DS4CheckFactoryOptions["flavorData"]} */ + const flavorData = { actor: speaker.alias ?? this.actor.name, weapon: this.name, }; @@ -68,7 +71,12 @@ export class DS4Weapon extends DS4Item { Hooks.callAll("ds4.rollItem", this); } - private async getPerformedAttackType(): Promise<"melee" | "ranged"> { + /** + * Get the attack type to perform with this weapon. If there are multiple options prompt the user for a choice. + * @returns {Promise<"melee" | "ranged">} The attack type to perform + * @protected + */ + async getPerformedAttackType() { if (this.data.data.attackType !== "meleeRanged") { return this.data.data.attackType; } @@ -102,7 +110,3 @@ export class DS4Weapon extends DS4Item { }); } } - -export interface DS4Weapon { - data: foundry.data.ItemData & { type: "weapon"; _source: { type: "weapon" } }; -} diff --git a/src/documents/token-document.ts b/src/documents/token-document.js similarity index 54% rename from src/documents/token-document.ts rename to src/documents/token-document.js index 4aa855e6..9212899e 100644 --- a/src/documents/token-document.ts +++ b/src/documents/token-document.js @@ -5,23 +5,21 @@ import { getGame } from "../utils/utils"; import { DS4ActorProxy } from "./actor/proxy"; -let fallbackData: foundry.data.ActorData["data"] | undefined = undefined; +/** @type {object | undefined} */ +let fallbackData = undefined; function getFallbackData() { if (!fallbackData) { - fallbackData = {} as foundry.data.ActorData["data"]; + fallbackData = {}; for (const type of getGame().system.template.Actor?.types ?? []) { - foundry.utils.mergeObject( - fallbackData, - new DS4ActorProxy({ type: type as foundry.data.ActorData["type"], name: "temporary" }).data.data, - ); + foundry.utils.mergeObject(fallbackData, new DS4ActorProxy({ type, name: "temporary" }).data.data); } } return fallbackData; } export class DS4TokenDocument extends TokenDocument { - static override getTrackedAttributes(data?: foundry.data.ActorData["data"], _path: string[] = []) { + static getTrackedAttributes(data, _path = []) { if (!data) { data = getFallbackData(); } diff --git a/src/global.d.ts b/src/global.d.ts deleted file mode 100644 index 91fc6c12..00000000 --- a/src/global.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Johannes Loher -// -// SPDX-License-Identifier: MIT - -declare global { - namespace ClientSettings { - interface Values { - "ds4.systemMigrationVersion": number; - "ds4.useSlayingDiceForAutomatedChecks": boolean; - "ds4.showSlayerPoints": boolean; - } - } - - namespace PoolTerm { - interface Modifiers { - x: (this: PoolTerm, modifier: string) => void; - } - } -} - -export {}; diff --git a/src/handlebars/handlebars-partials.ts b/src/handlebars/handlebars-partials.js similarity index 94% rename from src/handlebars/handlebars-partials.ts rename to src/handlebars/handlebars-partials.js index 1992b551..eb2c1b14 100644 --- a/src/handlebars/handlebars-partials.ts +++ b/src/handlebars/handlebars-partials.js @@ -4,7 +4,11 @@ // // SPDX-License-Identifier: MIT -export async function registerHandlebarsPartials(): Promise { +/** + * Register the Handlebars partials for DS4. + * @returns {Promise} A promise that resolves once all partials have been registered + */ +export async function registerHandlebarsPartials() { const templatePaths = [ "systems/ds4/templates/sheets/actor/components/actor-header.hbs", "systems/ds4/templates/sheets/actor/components/actor-progression.hbs", diff --git a/src/hooks/hotbar-drop.js b/src/hooks/hotbar-drop.js new file mode 100644 index 00000000..eed324cc --- /dev/null +++ b/src/hooks/hotbar-drop.js @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { isCheck } from "../documents/actor/actor-data-properties-base"; +import { DS4Item } from "../documents/item/item"; +import { createRollCheckMacro } from "../macros/roll-check"; +import { createRollItemMacro } from "../macros/roll-item"; +import { notifications } from "../ui/notifications"; +import { getGame } from "../utils/utils"; + +export function registerForHotbarDropHook() { + Hooks.on("hotbarDrop", onHotbarDrop); +} + +/** + * @typedef {Record} DropData + * @property {string} type + */ + +/** + * Handle a drop event on the hotbar + * @param {Hotbar} hotbar The hotbar on which something wqas + * @param {DropData} data The drop data associated to the drop event + * @param {string} slot The slot on the hotbar that somethingwas dropped on + * @returns {Promise} + */ +async function onHotbarDrop(hotbar, data, slot) { + switch (data.type) { + case "Item": { + if (!("data" in data)) { + return notifications.warn(getGame().i18n.localize("DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems")); + } + const itemData = data.data; + + if (!DS4Item.rollableItemTypes.includes(itemData.type)) { + return notifications.warn( + getGame().i18n.format("DS4.WarningItemIsNotRollable", { + name: itemData.name, + id: itemData._id, + type: itemData.type, + }), + ); + } + return createRollItemMacro(itemData, slot); + } + case "Check": { + if (!("data" in data) || typeof data.data !== "string" || !isCheck(data.data)) { + return notifications.warn(getGame().i18n.localize("DS4.WarningInvalidCheckDropped")); + } + return createRollCheckMacro(data.data, slot); + } + } +} diff --git a/src/hooks/hotbar-drop.ts b/src/hooks/hotbar-drop.ts deleted file mode 100644 index 438cb630..00000000 --- a/src/hooks/hotbar-drop.ts +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Johannes Loher -// -// SPDX-License-Identifier: MIT - -import { isCheck } from "../documents/actor/actor-data-properties-base"; -import { DS4Item } from "../documents/item/item"; -import { createRollCheckMacro } from "../macros/roll-check"; -import { createRollItemMacro } from "../macros/roll-item"; -import { notifications } from "../ui/notifications"; -import { getGame } from "../utils/utils"; - -export function registerForHotbarDropHook(): void { - Hooks.on("hotbarDrop", async (hotbar: Hotbar, data: HotbarDropData, slot: string) => { - switch (data.type) { - case "Item": { - if (!isItemDropData(data) || !("data" in data)) { - return notifications.warn( - getGame().i18n.localize("DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems"), - ); - } - const itemData = data.data; - - if (!DS4Item.rollableItemTypes.includes(itemData.type)) { - return notifications.warn( - getGame().i18n.format("DS4.WarningItemIsNotRollable", { - name: itemData.name, - id: itemData._id, - type: itemData.type, - }), - ); - } - return createRollItemMacro(itemData, slot); - } - case "Check": { - if (!("data" in data) || typeof data.data !== "string" || !isCheck(data.data)) { - return notifications.warn(getGame().i18n.localize("DS4.WarningInvalidCheckDropped")); - } - return createRollCheckMacro(data.data, slot); - } - } - }); -} - -type HotbarDropData = ActorSheet.DropData.Item | ({ type: string } & Partial>); - -function isItemDropData(dropData: HotbarDropData): dropData is ActorSheet.DropData.Item { - return dropData.type === "Item"; -} diff --git a/src/hooks/init.ts b/src/hooks/init.js similarity index 85% rename from src/hooks/init.ts rename to src/hooks/init.js index 19de9032..804f60b4 100644 --- a/src/hooks/init.ts +++ b/src/hooks/init.js @@ -27,10 +27,7 @@ import { preloadFonts } from "../ui/fonts"; import { logger } from "../utils/logger"; import { getGame } from "../utils/utils"; -import type { DS4Actor } from "../documents/actor/actor"; -import type { DS4Item } from "../documents/item/item"; - -export function registerForInitHook(): void { +export function registerForInitHook() { Hooks.once("init", init); } @@ -81,20 +78,3 @@ async function init() { await registerHandlebarsPartials(); registerHandlebarsHelpers(); } - -declare global { - interface Game { - ds4: { - DS4Actor: typeof DS4Actor; - DS4Item: typeof DS4Item; - DS4: typeof DS4; - createCheckRoll: typeof createCheckRoll; - migration: typeof migration; - macros: typeof macros; - }; - } - - interface CONFIG { - DS4: typeof DS4; - } -} diff --git a/src/hooks/ready.ts b/src/hooks/ready.js similarity index 81% rename from src/hooks/ready.ts rename to src/hooks/ready.js index 91c9c0cb..a04a0c0a 100644 --- a/src/hooks/ready.ts +++ b/src/hooks/ready.js @@ -4,7 +4,7 @@ import { migration } from "../migration/migration"; -export function registerForReadyHook(): void { +export function registerForReadyHook() { Hooks.once("ready", () => { migration.migrate(); }); diff --git a/src/hooks/render.ts b/src/hooks/render.js similarity index 64% rename from src/hooks/render.ts rename to src/hooks/render.js index b12c9ebe..bb59bcb8 100644 --- a/src/hooks/render.ts +++ b/src/hooks/render.js @@ -7,7 +7,7 @@ * @remarks The render hooks of all classes in the class hierarchy are called, so e.g. for a {@link Dialog}, both the * "renderDialog" hook and the "renderApplication" hook are called (in this order). */ -export function registerForRenderHooks(): void { +export function registerForRenderHooks() { ["renderApplication", "renderActorSheet", "renderItemSheet"].forEach((hook) => { Hooks.on(hook, selectTargetInputOnFocus); }); @@ -16,11 +16,11 @@ export function registerForRenderHooks(): void { /** * Select the text of input elements in given application when focused via an on focus listener. * - * @param app - The application in which to activate the listener. - * @param html - The {@link JQuery} representing the HTML of the application. + * @param {Application} app The application in which to activate the listener. + * @param {JQuery} html The {@link JQuery} representing the HTML of the application. */ -function selectTargetInputOnFocus(app: Application, html: JQuery) { - html.find("input").on("focus", (ev: JQuery.FocusEvent) => { +function selectTargetInputOnFocus(app, html) { + html.find("input").on("focus", (ev) => { ev.currentTarget.select(); }); } diff --git a/src/hooks/setup.ts b/src/hooks/setup.js similarity index 74% rename from src/hooks/setup.ts rename to src/hooks/setup.js index 6c339f51..50527e28 100644 --- a/src/hooks/setup.ts +++ b/src/hooks/setup.js @@ -8,7 +8,7 @@ import { DS4 } from "../config"; import { getGame } from "../utils/utils"; -export function registerForSetupHook(): void { +export function registerForSetupHook() { Hooks.once("setup", () => { localizeAndSortConfigObjects(); }); @@ -28,17 +28,23 @@ function localizeAndSortConfigObjects() { "checkModifiers", ]; - const localizeObject = (obj: T, sort = true): T => { - const localized = Object.entries(obj).map(([key, value]): [string, string] => { + /** + * @template {Record} T + * @param {T} obj The object to localize + * @param {boolean} [sort=true] whether or not to sort the object + * @returns {T} the localized object + */ + const localizeObject = (obj, sort = true) => { + const localized = Object.entries(obj).map(([key, value]) => { return [key, getGame().i18n.localize(value)]; }); if (sort) localized.sort((a, b) => a[1].localeCompare(b[1])); - return Object.fromEntries(localized) as T; + return Object.fromEntries(localized); }; DS4.i18n = Object.fromEntries( Object.entries(DS4.i18n).map(([key, value]) => { return [key, localizeObject(value, !noSort.includes(key))]; }), - ) as typeof DS4.i18n; + ); } diff --git a/src/macros/helpers.ts b/src/macros/helpers.js similarity index 74% rename from src/macros/helpers.ts rename to src/macros/helpers.js index 3542c7b8..8484a07e 100644 --- a/src/macros/helpers.ts +++ b/src/macros/helpers.js @@ -4,14 +4,12 @@ import { getCanvas, getGame } from "../utils/utils"; -import type { DS4Actor } from "../documents/actor/actor"; - /** * Gets the currently active actor and token based on how {@link ChatMessage} * determines the current speaker. - * @returns The currently active {@link DS4Actor} and {@link TokenDocument}. + * @returns {{actor?: import("../documents/actor/actor").DS4Actor, token?: TokenDocument}} The currently active actor and token. */ -export function getActiveActorAndToken(): { actor?: DS4Actor; token?: TokenDocument } { +export function getActiveActorAndToken() { const speaker = ChatMessage.getSpeaker(); const speakerToken = speaker.token ? getCanvas().tokens?.get(speaker.token)?.document : undefined; diff --git a/src/macros/roll-check.ts b/src/macros/roll-check.js similarity index 59% rename from src/macros/roll-check.ts rename to src/macros/roll-check.js index 45fa601e..9e061038 100644 --- a/src/macros/roll-check.ts +++ b/src/macros/roll-check.js @@ -7,19 +7,23 @@ import { notifications } from "../ui/notifications"; import { getGame } from "../utils/utils"; import { getActiveActorAndToken } from "./helpers"; -import type { Check } from "../documents/actor/actor-data-properties-base"; /** * Creates a macro from a check drop. * Get an existing roll check macro if one exists, otherwise create a new one. - * @param check - The name of the check to perform. - * @param slot - The hotbar slot to use. + * @param {import("../documents/actor/actor-data-properties-base").Check} check The name of the check to perform + * @param {string} slot The hotbar slot to use + * @returns {Promise} A promise that resoolves when the macro has been created. */ -export async function createRollCheckMacro(check: Check, slot: string): Promise { +export async function createRollCheckMacro(check, slot) { const macro = await getOrCreateRollCheckMacro(check); - getGame().user?.assignHotbarMacro(macro ?? null, slot); + await getGame().user?.assignHotbarMacro(macro ?? null, slot); } -async function getOrCreateRollCheckMacro(check: Check): Promise { +/** + * @param {import("../documents/actor/actor-data-properties-base").Check} check The name of the check to perform + * @returns {Promise} A promise that resolves to the created macro + */ +async function getOrCreateRollCheckMacro(check) { const command = `game.ds4.macros.rollCheck("${check}");`; const existingMacro = getGame().macros?.find( @@ -43,8 +47,10 @@ async function getOrCreateRollCheckMacro(check: Check): Promise} A promise that resolves once the check has been performed. */ -export async function rollCheck(check: Check): Promise { +export async function rollCheck(check) { const { actor, token } = getActiveActorAndToken(); if (!actor) { return notifications.warn(getGame().i18n.localize("DS4.WarningMustControlActorToUseRollCheckMacro")); diff --git a/src/macros/roll-item.ts b/src/macros/roll-item.js similarity index 70% rename from src/macros/roll-item.ts rename to src/macros/roll-item.js index a30d02cd..7e2fce2d 100644 --- a/src/macros/roll-item.ts +++ b/src/macros/roll-item.js @@ -9,15 +9,20 @@ import { getActiveActorAndToken } from "./helpers"; /** * Creates a macro from an item drop. * Get an existing roll item macro if one exists, otherwise create a new one. - * @param itemData - The item data - * @param slot - The hotbar slot to use + * @param {object} itemData The item data + * @param {string} slot The hotbar slot to use + * @returns {Promise} A promise that resolves once the macro has been created. */ -export async function createRollItemMacro(itemData: foundry.data.ItemData["_source"], slot: string): Promise { +export async function createRollItemMacro(itemData, slot) { const macro = await getOrCreateRollItemMacro(itemData); - getGame().user?.assignHotbarMacro(macro ?? null, slot); + await getGame().user?.assignHotbarMacro(macro ?? null, slot); } -async function getOrCreateRollItemMacro(itemData: foundry.data.ItemData["_source"]): Promise { +/** + * @param {object} itemData The item data + * @returns {Promise} A promise that resolves to the created macro + */ +async function getOrCreateRollItemMacro(itemData) { const command = `game.ds4.macros.rollItem("${itemData._id}");`; const existingMacro = getGame().macros?.find((m) => m.name === itemData.name && m.data.command === command); @@ -39,8 +44,10 @@ async function getOrCreateRollItemMacro(itemData: foundry.data.ItemData["_source /** * Executes the roll item macro for the item associated to the given `itemId`. + * @param {string} itemId The id of the item to roll + * @returns {Promise} A promise that resolves once the item has been rolled. */ -export async function rollItem(itemId: string): Promise { +export async function rollItem(itemId) { const { actor, token } = getActiveActorAndToken(); if (!actor) { return notifications.warn(getGame().i18n.localize("DS4.WarningMustControlActorToUseRollItemMacro")); diff --git a/src/migration/001.ts b/src/migration/001.js similarity index 80% rename from src/migration/001.ts rename to src/migration/001.js index c01252fa..55b5004e 100644 --- a/src/migration/001.ts +++ b/src/migration/001.js @@ -10,13 +10,15 @@ import { migrateScenes, } from "./migrationHelpers"; -async function migrate(): Promise { +/** @type {import("./migration").Migration["migrate"]} */ +async function migrate() { await migrateActors(getActorUpdateData); await migrateScenes(getSceneUpdateData); await migrateCompendiums(migrateCompendium); } -function getActorUpdateData(): Record { +/** @type {import("./migrationHelpers").ActorUpdateDataGetter} */ +function getActorUpdateData() { const updateData = { data: { combatValues: [ @@ -28,7 +30,7 @@ function getActorUpdateData(): Record { "rangedAttack", "spellcasting", "targetedSpellcasting", - ].reduce((acc: Partial>, curr) => { + ].reduce((acc, curr) => { acc[curr] = { "-=base": null }; return acc; }, {}), @@ -40,6 +42,7 @@ function getActorUpdateData(): Record { const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateCompendium = getCompendiumMigrator({ getActorUpdateData, getSceneUpdateData }); +/** @type {import("./migration").Migration} */ export const migration = { migrate, migrateCompendium, diff --git a/src/migration/002.ts b/src/migration/002.js similarity index 74% rename from src/migration/002.ts rename to src/migration/002.js index 0c1ccb53..17d9fe90 100644 --- a/src/migration/002.ts +++ b/src/migration/002.js @@ -12,18 +12,18 @@ import { migrateScenes, } from "./migrationHelpers"; -async function migrate(): Promise { +/** @type {import("./migration").Migration["migrate"]} */ +async function migrate() { await migrateItems(getItemUpdateData); await migrateActors(getActorUpdateData); await migrateScenes(getSceneUpdateData); await migrateCompendiums(migrateCompendium); } -function getItemUpdateData( - itemData: Partial, -): DeepPartial | undefined { +/** @type {import("./migrationHelpers").ItemUpdateDataGetter} */ +function getItemUpdateData(itemData) { if (!["equipment", "trinket"].includes(itemData.type ?? "")) return undefined; - return { type: itemData.type === "equipment" ? ("loot" as const) : ("equipment" as const) }; + return { type: itemData.type === "equipment" ? "loot" : "equipment" }; } const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); @@ -33,6 +33,7 @@ const migrateCompendium = getCompendiumMigrator( { migrateToTemplateEarly: false }, ); +/** @type {import("./migration").Migration} */ export const migration = { migrate, migrateCompendium, diff --git a/src/migration/003.ts b/src/migration/003.js similarity index 80% rename from src/migration/003.ts rename to src/migration/003.js index a2870ad5..f18da278 100644 --- a/src/migration/003.ts +++ b/src/migration/003.js @@ -12,14 +12,16 @@ import { migrateScenes, } from "./migrationHelpers"; -async function migrate(): Promise { +/** @type {import("./migration").Migration["migrate"]} */ +async function migrate() { await migrateItems(getItemUpdateData); await migrateActors(getActorUpdateData); await migrateScenes(getSceneUpdateData); await migrateCompendiums(migrateCompendium); } -function getItemUpdateData(itemData: Partial) { +/** @type {import("./migrationHelpers").ItemUpdateDataGetter} */ +function getItemUpdateData(itemData) { if (!["loot"].includes(itemData.type ?? "")) return undefined; return { data: { @@ -35,6 +37,7 @@ const migrateCompendium = getCompendiumMigrator( { migrateToTemplateEarly: false }, ); +/** @type {import("./migration").Migration} */ export const migration = { migrate, migrateCompendium, diff --git a/src/migration/004.ts b/src/migration/004.js similarity index 74% rename from src/migration/004.ts rename to src/migration/004.js index e0483376..435b1c97 100644 --- a/src/migration/004.ts +++ b/src/migration/004.js @@ -12,19 +12,20 @@ import { migrateScenes, } from "./migrationHelpers"; -async function migrate(): Promise { +/** @type {import("./migration").Migration["migrate"]} */ +async function migrate() { await migrateItems(getItemUpdateData); await migrateActors(getActorUpdateData); await migrateScenes(getSceneUpdateData); await migrateCompendiums(migrateCompendium); } -function getItemUpdateData(itemData: Partial) { +/** @type {import("./migrationHelpers").ItemUpdateDataGetter} */ +function getItemUpdateData(itemData) { if (itemData.type !== "spell") return; - // @ts-expect-error the type of cooldownDuration was UnitData at the point for this migration, but it changed later on - const cooldownDurationUnit: string | undefined = itemData.data?.cooldownDuration.unit; + const cooldownDurationUnit = itemData.data?.cooldownDuration.unit; - const updateData: Record = { + const updateData = { data: { "-=scrollPrice": null, minimumLevels: { healer: null, wizard: null, sorcerer: null }, @@ -40,6 +41,7 @@ const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateCompendium = getCompendiumMigrator({ getItemUpdateData, getActorUpdateData, getSceneUpdateData }); +/** @type {import("./migration").Migration} */ export const migration = { migrate, migrateCompendium, diff --git a/src/migration/005.ts b/src/migration/005.js similarity index 81% rename from src/migration/005.ts rename to src/migration/005.js index 16919ce6..528f2463 100644 --- a/src/migration/005.ts +++ b/src/migration/005.js @@ -21,22 +21,22 @@ const hoursPerDay = 24; const roundsPerDay = hoursPerDay / roundsPerHour; const secondsPerDay = secondsPerMinute * minutesPerHour * hoursPerDay; -async function migrate(): Promise { +/** @type {import("./migration").Migration["migrate"]} */ +async function migrate() { await migrateItems(getItemUpdateData); await migrateActors(getActorUpdateData); await migrateScenes(getSceneUpdateData); await migrateCompendiums(migrateCompendium); } -function getItemUpdateData(itemData: Partial) { +/** @type {import("./migrationHelpers").ItemUpdateDataGetter} */ +function getItemUpdateData(itemData) { if (itemData.type !== "spell") return; - // @ts-expect-error the type of cooldownDuration is changed from UnitData to CooldownDuation with this migration - const cooldownDurationUnit: string | undefined = itemData.data?.cooldownDuration.unit; - // @ts-expect-error the type of cooldownDuration is changed from UnitData to CooldownDuation with this migration - const cooldownDurationValue: string | undefined = itemData.data?.cooldownDuration.value; + const cooldownDurationUnit = itemData.data?.cooldownDuration.unit; + const cooldownDurationValue = itemData.data?.cooldownDuration.value; const cooldownDuration = migrateCooldownDuration(cooldownDurationValue, cooldownDurationUnit); - const updateData: Record = { + const updateData = { data: { cooldownDuration, }, @@ -88,7 +88,13 @@ function migrateCooldownDuration(cooldownDurationValue = "", cooldownDurationUni } } -function getRounds(unit: string, value: number): number { +/** + * Given a unit and a value, return the correct number of rounds + * @param {string} unit The unit + * @param {number} value The value + * @returns {number} The number of rounds + */ +function getRounds(unit, value) { switch (unit) { case "rounds": { return value; @@ -112,6 +118,7 @@ const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateCompendium = getCompendiumMigrator({ getItemUpdateData, getActorUpdateData, getSceneUpdateData }); +/** @type {import("./migration").Migration} */ export const migration = { migrate, migrateCompendium, diff --git a/src/migration/006.ts b/src/migration/006.js similarity index 71% rename from src/migration/006.ts rename to src/migration/006.js index 6ce4a9c0..8d87892f 100644 --- a/src/migration/006.ts +++ b/src/migration/006.js @@ -12,26 +12,25 @@ import { migrateScenes, } from "./migrationHelpers"; -import type { DS4SpellDataSourceData } from "../documents/item/spell/spell-data-source"; - -async function migrate(): Promise { +/** @type {import("./migration").Migration["migrate"]} */ +async function migrate() { await migrateItems(getItemUpdateData); await migrateActors(getActorUpdateData); await migrateScenes(getSceneUpdateData); await migrateCompendiums(migrateCompendium); } -function getItemUpdateData(itemData: Partial) { +/** @type {import("./migrationHelpers").ItemUpdateDataGetter} */ +function getItemUpdateData(itemData) { if (itemData.type !== "spell") return; - // @ts-expect-error spellCategory is removed with this migration - const spellCategory: string | undefined = itemData.data?.spellCategory; + const spellCategory = itemData.data?.spellCategory; const spellGroups = migrateSpellCategory(spellCategory); // @ts-expect-error bonus is removed with this migration - const bonus: string | undefined = itemData.data?.bonus; + const bonus = itemData.data?.bonus; const spellModifier = migrateBonus(bonus); - const updateData: Record = { + const updateData = { data: { spellGroups, "-=spellCategory": null, @@ -42,7 +41,12 @@ function getItemUpdateData(itemData: Partial) return updateData; } -function migrateSpellCategory(spellCategory: string | undefined): DS4SpellDataSourceData["spellGroups"] { +/** + * Migrate a spell category to spell groups. + * @param {string | undefined} spellCategory The spell category + * @returns {import("../documents/item/spell/spell-data-source").DS4SpellDataSourceData["spellGroups"]} The spell groups for the given category + */ +function migrateSpellCategory(spellCategory) { const spellGroups = { lightning: false, earth: false, @@ -95,7 +99,12 @@ function migrateSpellCategory(spellCategory: string | undefined): DS4SpellDataSo return spellGroups; } -function migrateBonus(bonus: string | undefined): DS4SpellDataSourceData["spellModifier"] { +/** + * Migrate a spell bonus to a spell modifier. + * @param {string | undefined} bonus The spell bonus + * @returns {import("../documents/item/spell/spell-data-source").DS4SpellDataSourceData["spellModifier"]} The spell modifier + */ +function migrateBonus(bonus) { const spellModifier = { numerical: 0, complex: "" }; if (bonus) { if (Number.isNumeric(bonus)) { @@ -111,6 +120,7 @@ const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateCompendium = getCompendiumMigrator({ getItemUpdateData, getActorUpdateData, getSceneUpdateData }); +/** @type {import("./migration").Migration} */ export const migration = { migrate, migrateCompendium, diff --git a/src/migration/007.ts b/src/migration/007.js similarity index 79% rename from src/migration/007.ts rename to src/migration/007.js index 4ff6a2ea..d06d65b9 100644 --- a/src/migration/007.ts +++ b/src/migration/007.js @@ -12,14 +12,16 @@ import { migrateScenes, } from "./migrationHelpers"; -async function migrate(): Promise { +/** @type {import("./migration").Migration["migrate"]} */ +async function migrate() { await migrateItems(getItemUpdateData); await migrateActors(getActorUpdateData); await migrateScenes(getSceneUpdateData); await migrateCompendiums(migrateCompendium); } -function getItemUpdateData(itemData: Partial) { +/** @type {import("./migrationHelpers").ItemUpdateDataGetter} */ +function getItemUpdateData(itemData) { if (itemData.type !== "spell") return; return { @@ -33,6 +35,7 @@ const getActorUpdateData = getActorUpdateDataGetter(getItemUpdateData); const getSceneUpdateData = getSceneUpdateDataGetter(getActorUpdateData); const migrateCompendium = getCompendiumMigrator({ getItemUpdateData, getActorUpdateData, getSceneUpdateData }); +/** @type {import("./migration").Migration} */ export const migration = { migrate, migrateCompendium, diff --git a/src/migration/migration.ts b/src/migration/migration.js similarity index 71% rename from src/migration/migration.ts rename to src/migration/migration.js index b110ac86..2841cbd8 100644 --- a/src/migration/migration.ts +++ b/src/migration/migration.js @@ -13,7 +13,11 @@ import { migration as migration005 } from "./005"; import { migration as migration006 } from "./006"; import { migration as migration007 } from "./007"; -async function migrate(): Promise { +/** + * Perform migrations. + * @returns {Promise} A promise that resolves once all migrations have completed + */ +async function migrate() { if (!getGame().user?.isGM) { return; } @@ -30,7 +34,13 @@ async function migrate(): Promise { return migrateFromTo(oldMigrationVersion, targetMigrationVersion); } -async function migrateFromTo(oldMigrationVersion: number, targetMigrationVersion: number): Promise { +/** + * Migrate from a given version to another version. + * @param {number} oldMigrationVersion The old migration version + * @param {number} targetMigrationVersion The migration version to migrate to + * @returns {Promise} A promise the resolves once the migration is complete + */ +async function migrateFromTo(oldMigrationVersion, targetMigrationVersion) { if (!getGame().user?.isGM) { return; } @@ -76,11 +86,14 @@ async function migrateFromTo(oldMigrationVersion: number, targetMigrationVersion } } -async function migrateCompendiumFromTo( - pack: CompendiumCollection, - oldMigrationVersion: number, - targetMigrationVersion: number, -): Promise { +/** + * Migrate a compendium pack from a given version to another version. + * @param {CompendiumCollection} pack The compendium pack to migrate + * @param {number} oldMigrationVersion The old version number + * @param {number} targetMigrationVersion The target version number + * @returns {Promise} A promise that resolves once the migration is complete + */ +async function migrateCompendiumFromTo(pack, oldMigrationVersion, targetMigrationVersion) { if (!getGame().user?.isGM) { return; } @@ -128,30 +141,39 @@ async function migrateCompendiumFromTo( } } -function getCurrentMigrationVersion(): number { +/** + * Get the current migration version. + * @returns {number} The current migration version + */ +function getCurrentMigrationVersion() { return getGame().settings.get("ds4", "systemMigrationVersion"); } -function getTargetMigrationVersion(): number { +/** + * Get the target migration version. + * @returns {number} The target migration version + */ +function getTargetMigrationVersion() { return migrations.length; } -interface Migration { - migrate: () => Promise; - migrateCompendium: (pack: CompendiumCollection) => Promise; -} +/** + * @typedef {object} Migration + * @property {() => Promise} migrate + * @property {import("./migrationHelpers").CompendiumMigrator} migrateCompendium + */ -const migrations: Migration[] = [ - migration001, - migration002, - migration003, - migration004, - migration005, - migration006, - migration007, -]; +/** + * @type {Migration[]} + */ +const migrations = [migration001, migration002, migration003, migration004, migration005, migration006, migration007]; -function isFirstWorldStart(migrationVersion: number): boolean { +/** + * DOes the migration version indicate the world is being started for the first time? + * @param {number} migrationVersion A migration version + * @returns {boolean} Whether the migration version indicates it is the first start of the world + */ +function isFirstWorldStart(migrationVersion) { return migrationVersion < 0; } diff --git a/src/migration/migrationHelpers.ts b/src/migration/migrationHelpers.js similarity index 56% rename from src/migration/migrationHelpers.ts rename to src/migration/migrationHelpers.js index 1e25b6f0..3c8193d5 100644 --- a/src/migration/migrationHelpers.ts +++ b/src/migration/migrationHelpers.js @@ -7,11 +7,14 @@ import { DS4Item } from "../documents/item/item"; import { logger } from "../utils/logger"; import { getGame } from "../utils/utils"; -type ItemUpdateDataGetter = ( - itemData: Partial, -) => DeepPartial | Record | undefined; +/** @typedef {(itemData: Partial) => DeepPartial | Record | undefined} ItemUpdateDataGetter */ -export async function migrateItems(getItemUpdateData: ItemUpdateDataGetter): Promise { +/** + * Migrate world items. + * @param {ItemUpdateDataGetter} getItemUpdateData A function for getting the update data for a given item data object + * @returns {Promise} A promise that resolves once the migration is complete + */ +export async function migrateItems(getItemUpdateData) { for (const item of getGame().items ?? []) { try { const updateData = getItemUpdateData(item.toObject()); @@ -25,11 +28,14 @@ export async function migrateItems(getItemUpdateData: ItemUpdateDataGetter): Pro } } -type ActorUpdateDataGetter = ( - itemData: Partial, -) => DeepPartial | undefined; +/** @typedef {(actorData: Partial) => DeepPartial | undefined} ActorUpdateDataGetter */ -export async function migrateActors(getActorUpdateData: ActorUpdateDataGetter): Promise { +/** + * Migrate world actors. + * @param {ActorUpdateDataGetter} getActorUpdateData A function for getting the update data for a given actor data object + * @returns {Promise} A promise that resolves once the migration is complete + */ +export async function migrateActors(getActorUpdateData) { for (const actor of getGame().actors ?? []) { try { const updateData = getActorUpdateData(actor.toObject()); @@ -46,17 +52,20 @@ export async function migrateActors(getActorUpdateData: ActorUpdateDataGetter): } } -type SceneUpdateDataGetter = (sceneData: foundry.data.SceneData) => DeepPartial; +/** @typedef {(aceneData: foundry.data.SceneData) => DeepPartial | undefined} SceneUpdateDataGetter */ -export async function migrateScenes(getSceneUpdateData: SceneUpdateDataGetter): Promise { +/** + * Migrate world scenes. + * @param {SceneUpdateDataGetter} getSceneUpdateData A function for getting the update data for a given scene data object + * @returns {Promise} A promise that resolves once the migration is complete + */ +export async function migrateScenes(getSceneUpdateData) { for (const scene of getGame().scenes ?? []) { try { const updateData = getSceneUpdateData(scene.data); if (updateData) { logger.info(`Migrating Scene document ${scene.name} (${scene.id})`); - await scene.update( - updateData as DeepPartial[0]>, - ); + await scene.update(updateData); } } catch (err) { logger.error( @@ -67,9 +76,14 @@ export async function migrateScenes(getSceneUpdateData: SceneUpdateDataGetter): } } -type CompendiumMigrator = (compendium: CompendiumCollection) => Promise; +/** @typedef {(pack: CompendiumCollection) => Promise} CompendiumMigrator*/ -export async function migrateCompendiums(migrateCompendium: CompendiumMigrator): Promise { +/** + * Migrate world compendium packs. + * @param {CompendiumMigrator} migrateCompendium A function for migrating a single compendium pack + * @returns {Promise} A promise that resolves once the migration is complete + */ +export async function migrateCompendiums(migrateCompendium) { for (const compendium of getGame().packs ?? []) { if (compendium.metadata.package !== "world") continue; if (!["Actor", "Item", "Scene"].includes(compendium.metadata.type)) continue; @@ -77,10 +91,13 @@ export async function migrateCompendiums(migrateCompendium: CompendiumMigrator): } } -export function getActorUpdateDataGetter(getItemUpdateData: ItemUpdateDataGetter): ActorUpdateDataGetter { - return ( - actorData: Partial, - ): DeepPartial | undefined => { +/** + * Get a function to create actor update data that adjusts the owned items of the actor according to the given function. + * @param {ItemUpdateDataGetter} getItemUpdateData The function to generate item update data + * @returns {ActorUpdateDataGetter} A function to get actor update data + */ +export function getActorUpdateDataGetter(getItemUpdateData) { + return (actorData) => { let hasItemUpdates = false; const items = actorData.items?.map((itemData) => { const update = getItemUpdateData(itemData); @@ -95,9 +112,14 @@ export function getActorUpdateDataGetter(getItemUpdateData: ItemUpdateDataGetter }; } -export function getSceneUpdateDataGetter(getActorUpdateData: ActorUpdateDataGetter): SceneUpdateDataGetter { - return (sceneData: foundry.data.SceneData) => { - const tokens = sceneData.tokens.map((token: TokenDocument) => { +/** + * Get a function to create scene update data that adjusts the actors of the tokens of the scene according to the given function. + * @param {ActorUpdateDataGetter} getItemUpdateData The function to generate actor update data + * @returns {SceneUpdateDataGetter} A function to get scene update data + */ +export function getSceneUpdateDataGetter(getActorUpdateData) { + return (sceneData) => { + const tokens = sceneData.tokens.map((token) => { const t = token.toObject(); if (!t.actorId || t.actorLink) { t.actorData = {}; @@ -109,7 +131,7 @@ export function getSceneUpdateDataGetter(getActorUpdateData: ActorUpdateDataGett actorData.type = token.actor?.type; const update = getActorUpdateData(actorData); if (update !== undefined) { - ["items" as const, "effects" as const].forEach((embeddedName) => { + ["items", "effects"].forEach((embeddedName) => { const embeddedUpdates = update[embeddedName]; if (embeddedUpdates === undefined || !embeddedUpdates.length) return; const updates = new Map(embeddedUpdates.flatMap((u) => (u && u._id ? [[u._id, u]] : []))); @@ -131,32 +153,37 @@ export function getSceneUpdateDataGetter(getActorUpdateData: ActorUpdateDataGett }; } +/** + * @typedef {object} UpdateDataGetters + * @property {ItemUpdateDataGetter} [getItemUpdateData] + * @property {ActorUpdateDataGetter} [getActorUpdateData] + * @property {SceneUpdateDataGetter} [getSceneUpdateData] + */ + +/** + * Get a compendium migrator for the given update data getters. + * @param {UpdateDataGetters} [updateDataGetters={}] The functions to use for getting update data + * @param {{migrateToTemplateEarly?: boolean}} [options={}] Additional options for the compendium migrator + * @returns {CompendiumMigrator} The resulting compendium migrator + */ export function getCompendiumMigrator( - { - getItemUpdateData, - getActorUpdateData, - getSceneUpdateData, - }: { - getItemUpdateData?: ItemUpdateDataGetter; - getActorUpdateData?: ActorUpdateDataGetter; - getSceneUpdateData?: SceneUpdateDataGetter; - } = {}, + { getItemUpdateData, getActorUpdateData, getSceneUpdateData } = {}, { migrateToTemplateEarly = true } = {}, ) { - return async (compendium: CompendiumCollection): Promise => { - const type = compendium.metadata.type; + return async (pack) => { + const type = pack.metadata.type; if (!["Actor", "Item", "Scene"].includes(type)) return; - const wasLocked = compendium.locked; - await compendium.configure({ locked: false }); + const wasLocked = pack.locked; + await pack.configure({ locked: false }); if (migrateToTemplateEarly) { - await compendium.migrate(); + await pack.migrate(); } - const documents = await compendium.getDocuments(); + const documents = await pack.getDocuments(); for (const doc of documents) { try { - logger.info(`Migrating document ${doc.name} (${doc.id}) in compendium ${compendium.collection}`); + logger.info(`Migrating document ${doc.name} (${doc.id}) in compendium ${pack.collection}`); if (doc instanceof DS4Item && getItemUpdateData) { const updateData = getItemUpdateData(doc.toObject()); updateData && (await doc.update(updateData)); @@ -164,23 +191,20 @@ export function getCompendiumMigrator( const updateData = getActorUpdateData(doc.toObject()); updateData && (await doc.update(updateData)); } else if (doc instanceof Scene && getSceneUpdateData) { - const updateData = getSceneUpdateData(doc.data as foundry.data.SceneData); - updateData && - (await doc.update( - updateData as DeepPartial[0]>, - )); + const updateData = getSceneUpdateData(doc.data); + updateData && (await doc.update(updateData)); } } catch (err) { logger.error( - `Error during migration of document ${doc.name} (${doc.id}) in compendium ${compendium.collection}, continuing anyways.`, + `Error during migration of document ${doc.name} (${doc.id}) in compendium ${pack.collection}, continuing anyways.`, err, ); } } if (!migrateToTemplateEarly) { - await compendium.migrate(); + await pack.migrate(); } - await compendium.configure({ locked: wasLocked }); + await pack.configure({ locked: wasLocked }); }; } diff --git a/src/settings.ts b/src/settings.js similarity index 75% rename from src/settings.ts rename to src/settings.js index 516d34f6..a7798c71 100644 --- a/src/settings.ts +++ b/src/settings.js @@ -4,11 +4,11 @@ import { getGame } from "./utils/utils"; -export function registerSystemSettings(): void { +export function registerSystemSettings() { const game = getGame(); /** - * Track the migrations version of the latest migration that has been applied + * Track the migration version of the latest migration that has been applied. */ game.settings.register("ds4", "systemMigrationVersion", { name: "System Migration Version", @@ -37,13 +37,18 @@ export function registerSystemSettings(): void { }); } -export interface DS4Settings { - systemMigrationVersion: number; - useSlayingDiceForAutomatedChecks: boolean; - showSlayerPoints: boolean; -} +/** + * @typedef DS4Settings + * @property {number} systemMigrationVersion + * @property {boolean} useSlayingDiceForAutomatedChecks + * @property {boolean} showSlayerPoints + */ -export function getDS4Settings(): DS4Settings { +/** + * Get the current values for DS4 settings. + * @returns {DS4Settings} + */ +export function getDS4Settings() { const game = getGame(); return { systemMigrationVersion: game.settings.get("ds4", "systemMigrationVersion"), diff --git a/src/ui/notifications.js b/src/ui/notifications.js new file mode 100644 index 00000000..fd5a55f7 --- /dev/null +++ b/src/ui/notifications.js @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { logger } from "../utils/logger"; +import { getNotificationsSafe } from "../utils/utils"; + +/** + * @typedef {Object} NotificationOptions + * @property {boolean} [permanent=false] + * @property {boolean} [log=false] + */ + +/** + * @typedef {(message: string, options?: NotificationOptions) => void} NotificationFunction + */ + +/** + * @typedef {"info" | "warn" | "error"} NotificationType + */ + +/** + * @param {NotificationType} type The type of the notification + * @returns {NotificationFunction} + */ +function getNotificationFunction(type) { + return (message, { permanent = false, log = false } = {}) => { + if (ui.notifications) { + ui.notifications[type](message, { permanent }); + if (log) { + logger[type](message); + } + } else { + logger[type](message); + } + }; +} + +/** + * @param {string} message + * @param {NotificationType} type + * @param {NotificationOptions} [options={}] + */ +function notify(message, type, { permanent = false, log = false } = {}) { + const notifications = getNotificationsSafe(); + if (notifications) { + notifications.notify(message, type, { permanent }); + if (log) { + logger.getLoggingFunction(type)(message); + } + } else { + logger.getLoggingFunction(type)(message); + } +} + +export const notifications = Object.freeze({ + info: getNotificationFunction("info"), + warn: getNotificationFunction("warn"), + error: getNotificationFunction("error"), + notify, +}); diff --git a/src/ui/notifications.ts b/src/ui/notifications.ts deleted file mode 100644 index 7e830beb..00000000 --- a/src/ui/notifications.ts +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Johannes Loher -// -// SPDX-License-Identifier: MIT - -import { logger } from "../utils/logger"; - -function getNotificationFunction(type: "info" | "warn" | "error") { - return (message: string, { permanent = false, log = false }: { permanent?: boolean; log?: boolean } = {}): void => { - if (ui.notifications) { - ui.notifications[type](message, { permanent }); - if (log) { - logger[type](message); - } - } else { - logger[type](message); - } - }; -} - -export const notifications = Object.freeze({ - info: getNotificationFunction("info"), - warn: getNotificationFunction("warn"), - error: getNotificationFunction("error"), - notify: ( - message: string, - type: "info" | "warning" | "error" = "info", - { permanent = false, log = false }: { permanent?: boolean; log?: boolean } = {}, - ): void => { - if (ui.notifications) { - ui.notifications.notify(message, type, { permanent }); - if (log) { - logger.getLoggingFunction(type)(message); - } - } else { - logger.getLoggingFunction(type)(message); - } - }, -}); diff --git a/src/utils/utils.js b/src/utils/utils.js new file mode 100644 index 00000000..dcb8cdf4 --- /dev/null +++ b/src/utils/utils.js @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: 2021 Johannes Loher +// +// SPDX-License-Identifier: MIT + +/** + * Tests if the given `value` is truthy. + * + * If it is not truthy, an {@link Error} is thrown, which depends on the given `message` parameter: + * - If `message` is a string`, it is used to construct a new {@link Error} which then is thrown. + * - If `message` is an instance of {@link Error}, it is thrown. + * - If `message` is `undefined`, an {@link Error} with a default message is thrown. + * @param {unknown} value The value to check for truthyness + * @param {string | Error} [message] An error message to use when the check fails + * @returns {asserts value} + */ +export function enforce(value, message) { + if (!value) { + if (!message) { + message = + getGameSafe()?.i18n.localize("DS4.ErrorUnexpectedError") ?? + "There was an unexpected error in the Dungeonslayers 4 system. For more details, please take a look at the console (F12)."; + } + throw message instanceof Error ? message : new Error(message); + } +} + +/** + * A wrapper that returns the canvas, if it is ready. + * @throws if the canvas is not ready yet + * @returns {Canvas} + */ +export function getCanvas() { + enforce(canvas instanceof Canvas && canvas.ready, getGame().i18n.localize("DS4.ErrorCanvasIsNotInitialized")); + return canvas; +} + +/** + * A wrapper that returns the game, if it already exists. + * @throws {Error} if the game is not ready yet + * @returns {Game} + */ +export function getGame() { + enforce(game instanceof Game, "Game is not initialized yet."); + return game; +} + +/** + * A wrapper that returns the game, or `undefined` if it doesn't exist yet + * @returns {Game | undefined} + */ +export function getGameSafe() { + return game instanceof Game ? game : undefined; +} + +/** + * A wrapper that returns `ui.notifications`, or `undefined` if it doesn't exist yet + * @returns {Notifications | undefined} + */ +export function getNotificationsSafe() { + return ui.notifications instanceof Notifications ? ui.notifications : undefined; +} diff --git a/src/utils/utils.ts b/src/utils/utils.ts deleted file mode 100644 index 44cce905..00000000 --- a/src/utils/utils.ts +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Johannes Loher -// -// SPDX-License-Identifier: MIT - -/** - * Tests if the given `value` is truthy. - * - * If it is not truthy, an {@link Error} is thrown, which depends on the given `message` parameter: - * - If `message` is a string`, it is used to construct a new {@link Error} which then is thrown. - * - If `message` is an instance of {@link Error}, it is thrown. - * - If `message` is `undefined`, an {@link Error} with a default message is thrown. - */ -export function enforce(value: unknown, message?: string | Error): asserts value { - if (!value) { - if (!message) { - message = - getGameSafe()?.i18n.localize("DS4.ErrorUnexpectedError") ?? - "There was an unexpected error in the Dungeonslayers 4 system. For more details, please take a look at the console (F12)."; - } - throw message instanceof Error ? message : new Error(message); - } -} - -export function getCanvas(): Canvas { - if (!(canvas instanceof Canvas) || !canvas.ready) { - throw new Error(getGame().i18n.localize("DS4.ErrorCanvasIsNotInitialized")); - } - return canvas; -} - -export function getGame(): Game { - if (!(game instanceof Game)) { - throw new Error("Game is not initialized yet."); - } - return game; -} - -export function getGameSafe(): Game | undefined { - return game instanceof Game ? game : undefined; -} diff --git a/system.json b/system.json index b8284abe..8cec60c0 100644 --- a/system.json +++ b/system.json @@ -36,12 +36,8 @@ "version": "1.18.2", "minimumCoreVersion": "9.238", "compatibleCoreVersion": "9", - "esmodules": [ - "ds4.js" - ], - "styles": [ - "css/ds4.css" - ], + "esmodules": ["ds4.js"], + "styles": ["css/ds4.css"], "languages": [ { "lang": "en", diff --git a/tsconfig.json b/tsconfig.json index cfb0eda8..e1c0203f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,9 @@ { "compilerOptions": { + "outDir": "dist", "target": "ES2021", "lib": ["ES2021", "DOM"], - "types": ["@league-of-foundry-developers/foundry-vtt-types"], + "types": ["@types/jquery", "handlebars"], "esModuleInterop": true, "moduleResolution": "node", "forceConsistentCasingInFileNames": true, @@ -10,7 +11,9 @@ "noUncheckedIndexedAccess": true, "noImplicitOverride": true, "resolveJsonModule": true, - "importsNotUsedAsValues": "error" + "importsNotUsedAsValues": "error", + "checkJs": false, + "allowJs": true }, - "include": ["src"] + "include": ["src", "client", "common"] } diff --git a/yarn.lock b/yarn.lock index 13dbaeac..34cf353e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -356,22 +356,6 @@ __metadata: languageName: node linkType: hard -"@league-of-foundry-developers/foundry-vtt-types@npm:9.280.0": - version: 9.280.0 - resolution: "@league-of-foundry-developers/foundry-vtt-types@npm:9.280.0" - dependencies: - "@pixi/graphics-smooth": 0.0.22 - "@types/jquery": ~3.5.9 - "@types/simple-peer": ~9.11.1 - handlebars: 4.7.7 - pixi-particles: 4.3.1 - pixi.js: 5.3.11 - socket.io-client: 4.3.2 - tinymce: 5.10.1 - checksum: 18fd05ff3fbdd849842f207e043e1924384bcd82ab65546067aa9b50d717e43dc4626822c8b96749cd687c2138cd7a7b39ac0b5fd453228876cbfa2f660f88f3 - languageName: node - linkType: hard - "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -419,503 +403,6 @@ __metadata: languageName: node linkType: hard -"@pixi/accessibility@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/accessibility@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/display": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: e64407bd08d472ccee9896aac21768cf5bad6fe52b75af4e7758168a153bd767c0e3e9959e646c86901acae77e9dfe4c05059075b5841000b7a2de50dd2d7083 - languageName: node - linkType: hard - -"@pixi/app@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/app@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/display": 5.3.11 - checksum: c4fce95715380fe8fcea99c92e1f0a2e98a172b3e6ac691d658b31d5a950bafd23753e982106e8be08c5db4b7872df156ea779f47f15b7fb2afdbef40dc3fa56 - languageName: node - linkType: hard - -"@pixi/constants@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/constants@npm:5.3.11" - checksum: ed23c735858a3a506ece0bea1afb3fac2ba261cb9722f17b15e9a4dcc8f70f3906a7cb90c94c255e7e7ece62d3447e68462975c3a30d2cb908f3632b4f97a891 - languageName: node - linkType: hard - -"@pixi/constants@npm:6.2.1": - version: 6.2.1 - resolution: "@pixi/constants@npm:6.2.1" - checksum: 16ce671884932afec41258891f301b9b725a4ae6285a39940b83ebd026a0dd077c78962b7f75eb6afba14a38de2420b0cfeaeae7986bbea4991506a4a83076b7 - languageName: node - linkType: hard - -"@pixi/core@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/core@npm:5.3.11" - dependencies: - "@pixi/constants": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/runner": 5.3.11 - "@pixi/settings": 5.3.11 - "@pixi/ticker": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: 865f26fa07397f67c0b418b7844576adff49aea47e4548b6122541c92da15744d128c681eb0c11c584906ba269485f816a8c0e0a4d6cb5521a2a488dfe4cfb40 - languageName: node - linkType: hard - -"@pixi/core@npm:6.2.1": - version: 6.2.1 - resolution: "@pixi/core@npm:6.2.1" - peerDependencies: - "@pixi/constants": 6.2.1 - "@pixi/math": 6.2.1 - "@pixi/runner": 6.2.1 - "@pixi/settings": 6.2.1 - "@pixi/ticker": 6.2.1 - "@pixi/utils": 6.2.1 - checksum: 946b39bd4a1e1414e2f6d60f9df1d56625312f482cf5a095ddb276554a351ebb0ebb8a0e98c7f98100eedecef89d1579b02e79b77626f1fe47e619aaffc3d469 - languageName: node - linkType: hard - -"@pixi/display@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/display@npm:5.3.11" - dependencies: - "@pixi/math": 5.3.11 - "@pixi/settings": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: 83db7f10fe610104bca5b71031abe3dfdd8946fd4e1a3957f2f9c1ca61ebbd3b7ab04915e9ced78997b88817063da42ae7c5aea527ad70959eb320966f70f92b - languageName: node - linkType: hard - -"@pixi/display@npm:6.2.1": - version: 6.2.1 - resolution: "@pixi/display@npm:6.2.1" - peerDependencies: - "@pixi/math": 6.2.1 - "@pixi/settings": 6.2.1 - "@pixi/utils": 6.2.1 - checksum: b72d4f16fd4c27e03d34c5cd8490cbd2c5d507584cef3abbab329fc1575ed5bcc53e104fb063c35fb6e85a6f8e16e1c114f1ff599ad43b7d1e4797fd644639fe - languageName: node - linkType: hard - -"@pixi/extract@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/extract@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: 15d68bed15e29c4d49f39bc33c9097b05eeb7b374a6a1f54177eaf0079e9882e4d96e3cbf61951ab6474b458fcdc36010de776ef7eeaaf9505464bfff89498e4 - languageName: node - linkType: hard - -"@pixi/filter-alpha@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/filter-alpha@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - checksum: c5348cc46125f443effd41ed46ccd8312b8a797fad5898ca3460d1c2f2146287881f8b18d1953b444cca6969ad1cbf60792c9d84434cc396e147bcc57f7a9d01 - languageName: node - linkType: hard - -"@pixi/filter-blur@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/filter-blur@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/settings": 5.3.11 - checksum: ff28b39f63c63cc9cb16d566ffb1543272830f3f566b677da22f47ad211ad594359fb07504280d80f9a7bfd1818409cf3cd9563c689057731d7e0fd105a3f988 - languageName: node - linkType: hard - -"@pixi/filter-color-matrix@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/filter-color-matrix@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - checksum: 5f76a7de0f9935fa16cbb66a18b430fa73a71d787f55334c2ba538bc58b11339ee0bfed0ccbe53c7693d6110fad5e88cb569af973849328b307e5314717130e7 - languageName: node - linkType: hard - -"@pixi/filter-displacement@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/filter-displacement@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/math": 5.3.11 - checksum: 03436312e1dd860accd6c801feea8d0a868e5b90de8c49edc28d40cf71ba4da293b4c027d0a71897c9aec884db132cb4c7d22fd18a05da5248e4ab35fccd0df9 - languageName: node - linkType: hard - -"@pixi/filter-fxaa@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/filter-fxaa@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - checksum: 02a16119e4796e9d1bf01768358cc244c24d71d22e229b5eec9b31968771a07d0fdf11196de7b59d34b9c075245d16b3c794b0eb02fc7e5e27b22e703ea0ed27 - languageName: node - linkType: hard - -"@pixi/filter-noise@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/filter-noise@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - checksum: 7d27c5af18a096418ced42f9f668577db39e4b30c7fad540d7ba31fd8a59c16d8926680ff6c3523c3e4b2a9fc4a7b46694dc0d547863cdf2763b28dac4c06455 - languageName: node - linkType: hard - -"@pixi/graphics-smooth@npm:0.0.22": - version: 0.0.22 - resolution: "@pixi/graphics-smooth@npm:0.0.22" - peerDependencies: - "@pixi/constants": ^6.0.4 - "@pixi/core": ^6.0.4 - "@pixi/display": ^6.0.4 - "@pixi/graphics": ^6.0.4 - "@pixi/math": ^6.0.4 - "@pixi/utils": ^6.0.4 - checksum: 81dea3626180b4c7997b20ba6611d41e7a79d6278f71f65867e440d73577e13569c574a31623f5732d08d60beef867eb99f4ca79e34ca8ffb6755fe00a4d8508 - languageName: node - linkType: hard - -"@pixi/graphics@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/graphics@npm:5.3.11" - dependencies: - "@pixi/constants": 5.3.11 - "@pixi/core": 5.3.11 - "@pixi/display": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/sprite": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: 31014dd5b9b05dd6d048a6a74c261f5e368b0e558aceaf21a90df38b321495b75f5caf2fe7e148fbad25b5db2998a76c66f816a4247d4e8d6c222a8b740b14b4 - languageName: node - linkType: hard - -"@pixi/graphics@npm:6.2.1": - version: 6.2.1 - resolution: "@pixi/graphics@npm:6.2.1" - peerDependencies: - "@pixi/constants": 6.2.1 - "@pixi/core": 6.2.1 - "@pixi/display": 6.2.1 - "@pixi/math": 6.2.1 - "@pixi/sprite": 6.2.1 - "@pixi/utils": 6.2.1 - checksum: 08681899d3177784126bb91b4e0c7966ad92e28d90f626c7c9c4a1973f4dc70cf43a9843d49133431db60ea9fbea4cfa86afab8391a57612a3bdee164aedcc31 - languageName: node - linkType: hard - -"@pixi/interaction@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/interaction@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/display": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/ticker": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: 2dc86690b85a614d062fe33dd1607ecb920e491bbb8822e28afef48ff745de1aa796b144225f887f6c391a96ab98c760e4b2081f621d9ccebdf59b4d63b9ded5 - languageName: node - linkType: hard - -"@pixi/loaders@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/loaders@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/utils": 5.3.11 - resource-loader: ^3.0.1 - checksum: 032e111c2dee5fdd26388ab95695f1800f9892ceb286248c4543d3b94b412390c6aa77d5f7b3118ae93a99b97b27db6b41e5459c67229cb5c22fcf3ef5f798c9 - languageName: node - linkType: hard - -"@pixi/math@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/math@npm:5.3.11" - checksum: 594557c1620ccee3e643b780cd66c0e0a01f4e2513d01e1f97fc9297cc8cdd9f0371360d578a6e16d8c4ee3d852a7770bbfcd26cc12f8c38f7d8a82b81dd76ec - languageName: node - linkType: hard - -"@pixi/math@npm:6.2.1": - version: 6.2.1 - resolution: "@pixi/math@npm:6.2.1" - checksum: 20ed1d294bb49c7a6f16dcd73e7befee97c15a5cc0127c120ec868bd5116039f9c5e13f05c19cdfedfffdf91625e10f27ccc51f96d54ad002d0a15102549f8ab - languageName: node - linkType: hard - -"@pixi/mesh-extras@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/mesh-extras@npm:5.3.11" - dependencies: - "@pixi/constants": 5.3.11 - "@pixi/core": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/mesh": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: 8c8188be1c978670f7c06b6de85d1d18b93eca321f17611830e88157958b40ac888490cf20a33e2fe3a85e57f0b654e7208dea3a4aa340330f1eb4a4c6029c7e - languageName: node - linkType: hard - -"@pixi/mesh@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/mesh@npm:5.3.11" - dependencies: - "@pixi/constants": 5.3.11 - "@pixi/core": 5.3.11 - "@pixi/display": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/settings": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: 161748cd29cd8522254577f556d0dde09198f17ff0fd5abc78aeea173f281c73cd4816eed134cab69a6ec41b0909cbdeb7c513aae696815605cdb67b8cc2e367 - languageName: node - linkType: hard - -"@pixi/mixin-cache-as-bitmap@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/mixin-cache-as-bitmap@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/display": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/settings": 5.3.11 - "@pixi/sprite": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: ea0999df8da2373f1617ad0d1f72e6f95d3d7501aad2f4808adf781d9df4d2b31540ad43d599b709872c0e6af9489d8531274153eaca5fa105173b6666057580 - languageName: node - linkType: hard - -"@pixi/mixin-get-child-by-name@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/mixin-get-child-by-name@npm:5.3.11" - dependencies: - "@pixi/display": 5.3.11 - checksum: 3c8d852728d0fb311923ac641cabefb31dcb31d2184e3ff6d6a152b7b662fce477c4f209f1a1947a086e46f59f44089072ef5c6d9798de016bd116b7502640f9 - languageName: node - linkType: hard - -"@pixi/mixin-get-global-position@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/mixin-get-global-position@npm:5.3.11" - dependencies: - "@pixi/display": 5.3.11 - "@pixi/math": 5.3.11 - checksum: 2be4698114f7061758efe3e2dad93a0216dfacf5cba4d20dc11105b55a4e69f26ef0d84c11d9670f157da11e235eb501d51018188176fd10c42955e7bf95bae0 - languageName: node - linkType: hard - -"@pixi/particles@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/particles@npm:5.3.11" - dependencies: - "@pixi/constants": 5.3.11 - "@pixi/core": 5.3.11 - "@pixi/display": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: 2b866a2998f6295f88b21451a1088a9d32f75bd5c3cdae10bff5a47bc6846ff69ac7e07db87a3346b6dbfc1c49fb5e4ea0df69aebd815f2fa5a4f43e49c89a17 - languageName: node - linkType: hard - -"@pixi/polyfill@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/polyfill@npm:5.3.11" - dependencies: - es6-promise-polyfill: ^1.2.0 - object-assign: ^4.1.1 - checksum: b9e69c974eab3ecba755c0b10c776aa6aac40eafec459fd205a786d1abbcf77c5ae05c6cde9381bc5d99830c284bfb746c56b09aedd4bed367063d3183b4722c - languageName: node - linkType: hard - -"@pixi/prepare@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/prepare@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/display": 5.3.11 - "@pixi/graphics": 5.3.11 - "@pixi/settings": 5.3.11 - "@pixi/text": 5.3.11 - "@pixi/ticker": 5.3.11 - checksum: eeb13f4c2eaff00f990e5f9d55593334a04a72ee1edf120b2b626e429e8e260514c371fc69dc87ee1c61355d9a6310d3daf56ec772ca1cd5976c2d94047e0552 - languageName: node - linkType: hard - -"@pixi/runner@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/runner@npm:5.3.11" - checksum: decbf8853a5634b3de2260321b813e5165ac23ff1eb1431ad50e69a0531d04fadd4693f80c0c1e0673380b15376b7840407ebafe404b95247c8d9187978f1de2 - languageName: node - linkType: hard - -"@pixi/runner@npm:6.2.1": - version: 6.2.1 - resolution: "@pixi/runner@npm:6.2.1" - checksum: 786cc632c5cf0001a74eb0621225048b7bec2fd37c65cd783e0dcff87a418a271f5b83a349c1171d546f78c41b91f05f89db3300a6ce78bee18ce403cdbb9ee5 - languageName: node - linkType: hard - -"@pixi/settings@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/settings@npm:5.3.11" - dependencies: - ismobilejs: ^1.1.0 - checksum: b45a10c0a556b3737f9b0442364635d36c8dfd351337f55829e4049e6f120ad5c201b242eb49c17a817c3348b085b7506e00438c0169c2951a9b7dba94ddc497 - languageName: node - linkType: hard - -"@pixi/settings@npm:6.2.1": - version: 6.2.1 - resolution: "@pixi/settings@npm:6.2.1" - dependencies: - ismobilejs: ^1.1.0 - checksum: f4db5bd74fca1f6c78153d4e86ae2a7961e929086543190cc8d923abfd35fb973eac61c3d722c2c753d30028a0d6c49a96fccd74f510636aa5a05afa3fda107d - languageName: node - linkType: hard - -"@pixi/sprite-animated@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/sprite-animated@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/sprite": 5.3.11 - "@pixi/ticker": 5.3.11 - checksum: e821e5882961e5e65abe950f7d3600622a76b5f188b83280d1dbb2d9434847fe6a8f204cc63e1f87805ded804f3e9992f6b779d1561868df530f33c5d71da3ef - languageName: node - linkType: hard - -"@pixi/sprite-tiling@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/sprite-tiling@npm:5.3.11" - dependencies: - "@pixi/constants": 5.3.11 - "@pixi/core": 5.3.11 - "@pixi/display": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/sprite": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: 2a3b82fb89a92be9413573ebe507a416485735de47d99ae4f9037cfec8619947b86fb53c4ba0d5c007d5b4e0148d8d83ffd9338a1ef2691b8c8d8dae05a77ebe - languageName: node - linkType: hard - -"@pixi/sprite@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/sprite@npm:5.3.11" - dependencies: - "@pixi/constants": 5.3.11 - "@pixi/core": 5.3.11 - "@pixi/display": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/settings": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: 6e4c5f108db8d49f005e61801bf94bb12173bfefd2e0c803851530c27d31f8c0a78e60bd9f07a469e06154bf22f5f6a1da5aabf7119c9e1448534df0e42e4398 - languageName: node - linkType: hard - -"@pixi/spritesheet@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/spritesheet@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/loaders": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: a1782b7b25ed355eba78850f764189f5cc433a8cf737b9ca54bb896afb47fb66cb329dab91072e2914b3d0f981f65e4788657e5f5ac8995c3ecdfde870f29890 - languageName: node - linkType: hard - -"@pixi/text-bitmap@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/text-bitmap@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/display": 5.3.11 - "@pixi/loaders": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/mesh": 5.3.11 - "@pixi/settings": 5.3.11 - "@pixi/text": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: c901ebaf47720369e080c85065b879a1a80e725d782c43d7d4873569a73d7d5e9543cd5a8a71550c4a7dd13cf5497361f7ecd527edb5208aaf7cdc8d8d7310dd - languageName: node - linkType: hard - -"@pixi/text@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/text@npm:5.3.11" - dependencies: - "@pixi/core": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/settings": 5.3.11 - "@pixi/sprite": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: 583cd3534ac8682e96e55f511325ed01e817c812db92262afab03581ad82e8d7b3444ee858673236c8069af2c6c25efc166a67ef0bf3f2ef8ecedf7c1c910347 - languageName: node - linkType: hard - -"@pixi/ticker@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/ticker@npm:5.3.11" - dependencies: - "@pixi/settings": 5.3.11 - checksum: 45a4b6f1b43c70f09f9b050bf28afbeda155c30d33d9666a1698f6b884a376505c37b508408068f4fb74abaff61529a395d73dd64e437a376cada95f647832fe - languageName: node - linkType: hard - -"@pixi/utils@npm:5.3.11": - version: 5.3.11 - resolution: "@pixi/utils@npm:5.3.11" - dependencies: - "@pixi/constants": 5.3.11 - "@pixi/settings": 5.3.11 - earcut: ^2.1.5 - eventemitter3: ^3.1.0 - url: ^0.11.0 - checksum: b107c782b9c27a25a2e04e4ce12f78022a65ffb71133f4e9e7840f9cf653852d382d940635f27f284b0081692eaea39a310f1ce14d1d6ed1216e2738ac3b3f61 - languageName: node - linkType: hard - -"@pixi/utils@npm:6.2.1": - version: 6.2.1 - resolution: "@pixi/utils@npm:6.2.1" - dependencies: - "@types/earcut": ^2.1.0 - earcut: ^2.2.2 - eventemitter3: ^3.1.0 - url: ^0.11.0 - peerDependencies: - "@pixi/constants": 6.2.1 - "@pixi/settings": 6.2.1 - checksum: e324cafe80cd2ec3bcf0763cd1e6a7193b15384819af9031d7425a4d6ff8520acb143fc63f5bbc12fe0af17de458fc2eb89d3b2ddd4de5657155e35e164e0108 - languageName: node - linkType: hard - -"@rollup/plugin-typescript@npm:10.0.0": - version: 10.0.0 - resolution: "@rollup/plugin-typescript@npm:10.0.0" - dependencies: - "@rollup/pluginutils": ^5.0.1 - resolve: ^1.22.1 - peerDependencies: - rollup: ^2.14.0||^3.0.0 - tslib: "*" - typescript: ">=3.7.0" - peerDependenciesMeta: - rollup: - optional: true - tslib: - optional: true - checksum: 232506215eb8e61bca195fc47e355e37d994795946d2b627466470f78c9d2be534464d0151e64aeef14b696356ca774419175625a8ac7d1239ed39ed098ff677 - languageName: node - linkType: hard - "@rollup/pluginutils@npm:^4.1.2": version: 4.2.0 resolution: "@rollup/pluginutils@npm:4.2.0" @@ -936,36 +423,6 @@ __metadata: languageName: node linkType: hard -"@rollup/pluginutils@npm:^5.0.1": - version: 5.0.2 - resolution: "@rollup/pluginutils@npm:5.0.2" - dependencies: - "@types/estree": ^1.0.0 - estree-walker: ^2.0.2 - picomatch: ^2.3.1 - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 - peerDependenciesMeta: - rollup: - optional: true - checksum: edea15e543bebc7dcac3b0ac8bc7b8e8e6dbd46e2864dbe5dd28072de1fbd5b0e10d545a610c0edaa178e8a7ac432e2a2a52e547ece1308471412caba47db8ce - languageName: node - linkType: hard - -"@socket.io/base64-arraybuffer@npm:~1.0.2": - version: 1.0.2 - resolution: "@socket.io/base64-arraybuffer@npm:1.0.2" - checksum: fa3e58c7581643d0557969cd3bece20e198596df77968ff29ede6be329d488e65104bef900e68a67f39d8855abfa59baa2b08d96fb856504bd01cbdd8f52249c - languageName: node - linkType: hard - -"@socket.io/component-emitter@npm:~3.0.0": - version: 3.0.0 - resolution: "@socket.io/component-emitter@npm:3.0.0" - checksum: b5e909dbb16bcf27958d1bfb8319f3255f3a50f62fde78ecf9a584f39f916b928fdc5661519892eea912da082c6413d671c1e67bde70725c75ee62956aa67c26 - languageName: node - linkType: hard - "@swc/core-darwin-arm64@npm:1.3.20": version: 1.3.20 resolution: "@swc/core-darwin-arm64@npm:1.3.20" @@ -1144,20 +601,6 @@ __metadata: languageName: node linkType: hard -"@types/earcut@npm:^2.1.0": - version: 2.1.1 - resolution: "@types/earcut@npm:2.1.1" - checksum: 7845dab97ba2bf379caeb4fb91bb732e6710b0ce018a8e357f023cb96ac2fa5c64352461e93ecac2504966dcf6b0b882d117c2258cf15a679bd0063b3cee0e14 - languageName: node - linkType: hard - -"@types/estree@npm:^1.0.0": - version: 1.0.0 - resolution: "@types/estree@npm:1.0.0" - checksum: 910d97fb7092c6738d30a7430ae4786a38542023c6302b95d46f49420b797f21619cdde11fa92b338366268795884111c2eb10356e4bd2c8ad5b92941e9e6443 - languageName: node - linkType: hard - "@types/fs-extra@npm:9.0.13": version: 9.0.13 resolution: "@types/fs-extra@npm:9.0.13" @@ -1167,7 +610,7 @@ __metadata: languageName: node linkType: hard -"@types/jquery@npm:~3.5.9": +"@types/jquery@npm:3.5.14": version: 3.5.14 resolution: "@types/jquery@npm:3.5.14" dependencies: @@ -1232,15 +675,6 @@ __metadata: languageName: node linkType: hard -"@types/simple-peer@npm:~9.11.1": - version: 9.11.4 - resolution: "@types/simple-peer@npm:9.11.4" - dependencies: - "@types/node": "*" - checksum: 13cea3c319b2f02b80a3ae5b051714840e8c75e4884b22242991ba0116f2ef10ccdc588853299b1320608ac191ea575544c00e173988ead53c5de96f3131b2fc - languageName: node - linkType: hard - "@types/sizzle@npm:*": version: 2.3.3 resolution: "@types/sizzle@npm:2.3.3" @@ -1567,13 +1001,6 @@ __metadata: languageName: node linkType: hard -"backo2@npm:~1.0.2": - version: 1.0.2 - resolution: "backo2@npm:1.0.2" - checksum: fda8d0a0f4810068d23715f2f45153146d6ee8f62dd827ce1e0b6cc3c8328e84ad61e11399a83931705cef702fe7cbb457856bf99b9bd10c4ed57b0786252385 - languageName: node - linkType: hard - "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -2287,7 +1714,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:~4.3.1, debug@npm:~4.3.2": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -2441,18 +1868,9 @@ __metadata: "@commitlint/cli": 17.3.0 "@commitlint/config-conventional": 17.3.0 "@guanghechen/rollup-plugin-copy": 2.1.4 - "@league-of-foundry-developers/foundry-vtt-types": 9.280.0 - "@pixi/constants": 6.2.1 - "@pixi/core": 6.2.1 - "@pixi/display": 6.2.1 - "@pixi/graphics": 6.2.1 - "@pixi/math": 6.2.1 - "@pixi/runner": 6.2.1 - "@pixi/settings": 6.2.1 - "@pixi/utils": 6.2.1 - "@rollup/plugin-typescript": 10.0.0 "@swc/core": 1.3.20 "@types/fs-extra": 9.0.13 + "@types/jquery": 3.5.14 "@types/node": 18.11.9 "@typescript-eslint/eslint-plugin": 5.44.0 "@typescript-eslint/parser": 5.44.0 @@ -2462,6 +1880,7 @@ __metadata: eslint-config-prettier: 8.5.0 eslint-plugin-prettier: 4.2.1 fs-extra: 10.1.0 + handlebars: 4.7.7 npm-run-all: 4.1.5 prettier: 2.8.0 rimraf: 3.0.2 @@ -2479,13 +1898,6 @@ __metadata: languageName: unknown linkType: soft -"earcut@npm:^2.1.5, earcut@npm:^2.2.2": - version: 2.2.3 - resolution: "earcut@npm:2.2.3" - checksum: 4e3bab80366c49e0332a0bb061649a3b9354dcbfa636155c7eee250be0670d7139d53f2aebbcf90788507c71b76ec1713c20e36527f202b9abb79c9e5858ec35 - languageName: node - linkType: hard - "electron-to-chromium@npm:^1.4.84": version: 1.4.88 resolution: "electron-to-chromium@npm:1.4.88" @@ -2509,32 +1921,6 @@ __metadata: languageName: node linkType: hard -"engine.io-client@npm:~6.0.1": - version: 6.0.3 - resolution: "engine.io-client@npm:6.0.3" - dependencies: - "@socket.io/component-emitter": ~3.0.0 - debug: ~4.3.1 - engine.io-parser: ~5.0.0 - has-cors: 1.1.0 - parseqs: 0.0.6 - parseuri: 0.0.6 - ws: ~8.2.3 - xmlhttprequest-ssl: ~2.0.0 - yeast: 0.1.2 - checksum: 11a70dba629e47981966f4b43e839327adc2444ce77d3476dcb93281712e92f972e3fdf97f2c298b3887f17d38ee609f022a470b6d2d255beccd471fa366603d - languageName: node - linkType: hard - -"engine.io-parser@npm:~5.0.0": - version: 5.0.3 - resolution: "engine.io-parser@npm:5.0.3" - dependencies: - "@socket.io/base64-arraybuffer": ~1.0.2 - checksum: 88d664420a441dd02db17d110f7bbbd9efe971747918150bf666b82ee138df596a2f5038f461c8a01864c83af67cb202548364e4174543f8c0bf5f4776ca6e0d - languageName: node - linkType: hard - "entities@npm:^2.0.0": version: 2.2.0 resolution: "entities@npm:2.2.0" @@ -2604,13 +1990,6 @@ __metadata: languageName: node linkType: hard -"es6-promise-polyfill@npm:^1.2.0": - version: 1.2.0 - resolution: "es6-promise-polyfill@npm:1.2.0" - checksum: b6022782ffdfa9c75d08e8b77580ea18908baa3c43a63f86a792d9c5cfa0c1e851de28743c44d6b5df11a8fc25f1be28bf3494bc1ff3bf9336204696897ee772 - languageName: node - linkType: hard - "esbuild-android-64@npm:0.15.10": version: 0.15.10 resolution: "esbuild-android-64@npm:0.15.10" @@ -3012,7 +2391,7 @@ __metadata: languageName: node linkType: hard -"estree-walker@npm:^2.0.1, estree-walker@npm:^2.0.2": +"estree-walker@npm:^2.0.1": version: 2.0.2 resolution: "estree-walker@npm:2.0.2" checksum: 6151e6f9828abe2259e57f5fd3761335bb0d2ebd76dc1a01048ccee22fabcfef3c0859300f6d83ff0d1927849368775ec5a6d265dde2f6de5a1be1721cd94efc @@ -3026,13 +2405,6 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^3.1.0": - version: 3.1.2 - resolution: "eventemitter3@npm:3.1.2" - checksum: 81e4e82b8418f5cfd986d2b4a2fa5397ac4eb8134e09bcb47005545e22fdf8e9e61d5c053d34651112245aae411bdfe6d0ad5511da0400743fef5fc38bfcfbe3 - languageName: node - linkType: hard - "eventemitter3@npm:^4.0.4": version: 4.0.7 resolution: "eventemitter3@npm:4.0.7" @@ -3477,13 +2849,6 @@ __metadata: languageName: node linkType: hard -"has-cors@npm:1.1.0": - version: 1.1.0 - resolution: "has-cors@npm:1.1.0" - checksum: 549ce94113fd23895b22d71ade9809918577b8558cd4d701fe79045d8b1d58d87eba870260b28f6a3229be933a691c55653afd496d0fc52e98fd2ff577f01197 - languageName: node - linkType: hard - "has-flag@npm:^3.0.0": version: 3.0.0 resolution: "has-flag@npm:3.0.0" @@ -3919,13 +3284,6 @@ __metadata: languageName: node linkType: hard -"ismobilejs@npm:^1.1.0": - version: 1.1.1 - resolution: "ismobilejs@npm:1.1.1" - checksum: 937479c4ae13306f41391e0f57611b0835bc57325750b446cb12ed844d697407f66f050ebf7446538cc3aae9575df6382243224be3032c1574b61e2f955d8417 - languageName: node - linkType: hard - "js-sdsl@npm:^4.1.4": version: 4.1.4 resolution: "js-sdsl@npm:4.1.4" @@ -4370,13 +3728,6 @@ __metadata: languageName: node linkType: hard -"mini-signals@npm:^1.2.0": - version: 1.2.0 - resolution: "mini-signals@npm:1.2.0" - checksum: fe28285d6ecc6c8035339fb909748e110ebf31cbaa4e8d849261017327d9a47ad43815f013ac1fc9b8b16c4d302dfd19b6dd952c9657293b70f8d1e95926545c - languageName: node - linkType: hard - "minimatch@npm:^3.0.4, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -4691,13 +4042,6 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.1.1": - version: 4.1.1 - resolution: "object-assign@npm:4.1.1" - checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f - languageName: node - linkType: hard - "object-inspect@npm:^1.11.0, object-inspect@npm:^1.9.0": version: 1.12.0 resolution: "object-inspect@npm:1.12.0" @@ -4897,27 +4241,6 @@ __metadata: languageName: node linkType: hard -"parse-uri@npm:^1.0.0": - version: 1.0.7 - resolution: "parse-uri@npm:1.0.7" - checksum: 0d4386a586bda98bcdd041f9b1a7e9a6c16bc2ab198c90531f2d169eb2eb520477cc059a75c5cf0695eb3c9e69ff6b90793d07781ab83e2de1cbb255ec66e37f - languageName: node - linkType: hard - -"parseqs@npm:0.0.6": - version: 0.0.6 - resolution: "parseqs@npm:0.0.6" - checksum: 7fc4ff4ba59764060bb8529875f6d4313056ea6939ff579b22dd7bd6f6033035e1fd2d6a559ab48ef0a7fa29a9d7731c982bfd1594e9115141fe1c328485ce9e - languageName: node - linkType: hard - -"parseuri@npm:0.0.6": - version: 0.0.6 - resolution: "parseuri@npm:0.0.6" - checksum: fa430e40f0c75293a28e5f1023da5f51a5038d5e34c48c517b0d5187143f6bcc67d3091a062b68765db4a22757e488c7d15854f9d1921f2c2b9afa5ca0629a84 - languageName: node - linkType: hard - "path-exists@npm:^3.0.0": version: 3.0.0 resolution: "path-exists@npm:3.0.0" @@ -5020,57 +4343,6 @@ __metadata: languageName: node linkType: hard -"pixi-particles@npm:4.3.1": - version: 4.3.1 - resolution: "pixi-particles@npm:4.3.1" - peerDependencies: - pixi.js: ">=4.0.0" - checksum: c7e6314921e6a5a935fe9874d5e1389b8a234f1a9d4774aec181faecd211aa26c7442b58435517212a737c9db24ff83383b9cf83efb5143cc078fa211259ab9a - languageName: node - linkType: hard - -"pixi.js@npm:5.3.11": - version: 5.3.11 - resolution: "pixi.js@npm:5.3.11" - dependencies: - "@pixi/accessibility": 5.3.11 - "@pixi/app": 5.3.11 - "@pixi/constants": 5.3.11 - "@pixi/core": 5.3.11 - "@pixi/display": 5.3.11 - "@pixi/extract": 5.3.11 - "@pixi/filter-alpha": 5.3.11 - "@pixi/filter-blur": 5.3.11 - "@pixi/filter-color-matrix": 5.3.11 - "@pixi/filter-displacement": 5.3.11 - "@pixi/filter-fxaa": 5.3.11 - "@pixi/filter-noise": 5.3.11 - "@pixi/graphics": 5.3.11 - "@pixi/interaction": 5.3.11 - "@pixi/loaders": 5.3.11 - "@pixi/math": 5.3.11 - "@pixi/mesh": 5.3.11 - "@pixi/mesh-extras": 5.3.11 - "@pixi/mixin-cache-as-bitmap": 5.3.11 - "@pixi/mixin-get-child-by-name": 5.3.11 - "@pixi/mixin-get-global-position": 5.3.11 - "@pixi/particles": 5.3.11 - "@pixi/polyfill": 5.3.11 - "@pixi/prepare": 5.3.11 - "@pixi/runner": 5.3.11 - "@pixi/settings": 5.3.11 - "@pixi/sprite": 5.3.11 - "@pixi/sprite-animated": 5.3.11 - "@pixi/sprite-tiling": 5.3.11 - "@pixi/spritesheet": 5.3.11 - "@pixi/text": 5.3.11 - "@pixi/text-bitmap": 5.3.11 - "@pixi/ticker": 5.3.11 - "@pixi/utils": 5.3.11 - checksum: ff33b02d78ef2266b8e3e1159142d2cc71605b846935760dcef36c8774dd87d8f804003f375ece259cbce1e9f64487ed5b2f1fca620677fc08a374a1203c60ce - languageName: node - linkType: hard - "postcss-calc@npm:^8.2.3": version: 8.2.4 resolution: "postcss-calc@npm:8.2.4" @@ -5518,13 +4790,6 @@ __metadata: languageName: node linkType: hard -"punycode@npm:1.3.2": - version: 1.3.2 - resolution: "punycode@npm:1.3.2" - checksum: b8807fd594b1db33335692d1f03e8beeddde6fda7fbb4a2e32925d88d20a3aa4cd8dcc0c109ccaccbd2ba761c208dfaaada83007087ea8bfb0129c9ef1b99ed6 - languageName: node - linkType: hard - "punycode@npm:^2.1.0": version: 2.1.1 resolution: "punycode@npm:2.1.1" @@ -5551,13 +4816,6 @@ __metadata: languageName: node linkType: hard -"querystring@npm:0.2.0": - version: 0.2.0 - resolution: "querystring@npm:0.2.0" - checksum: 8258d6734f19be27e93f601758858c299bdebe71147909e367101ba459b95446fbe5b975bf9beb76390156a592b6f4ac3a68b6087cea165c259705b8b4e56a69 - languageName: node - linkType: hard - "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -5757,16 +5015,6 @@ __metadata: languageName: node linkType: hard -"resource-loader@npm:^3.0.1": - version: 3.0.1 - resolution: "resource-loader@npm:3.0.1" - dependencies: - mini-signals: ^1.2.0 - parse-uri: ^1.0.0 - checksum: f7d35f589db48d0bde92a66a6dc8b884ddcadec3ae43df07c3e0cd99b215cf3002347633e7516baebef868f1edd5a433fe2260fbbc5f16f84d4e37d8923de451 - languageName: node - linkType: hard - "retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -6057,30 +5305,6 @@ __metadata: languageName: node linkType: hard -"socket.io-client@npm:4.3.2": - version: 4.3.2 - resolution: "socket.io-client@npm:4.3.2" - dependencies: - "@socket.io/component-emitter": ~3.0.0 - backo2: ~1.0.2 - debug: ~4.3.2 - engine.io-client: ~6.0.1 - parseuri: 0.0.6 - socket.io-parser: ~4.1.1 - checksum: e8c1c76848020f976958eed0a7630e1ed4a3108de75aa2cf7cb2c6babfcacff6d8fe1b70910b8b201a29e4965e238d520ac1868846c2872a948c9dd0e0761288 - languageName: node - linkType: hard - -"socket.io-parser@npm:~4.1.1": - version: 4.1.2 - resolution: "socket.io-parser@npm:4.1.2" - dependencies: - "@socket.io/component-emitter": ~3.0.0 - debug: ~4.3.1 - checksum: cd13cdbda929cce610b39fbf7f2c6aa59e55cfc58f13b38c592d7eb45b19d5110bcb81150607a88f8644959f5d0a384467a2083d29c12e224c010a406377649b - languageName: node - linkType: hard - "socks-proxy-agent@npm:^6.1.1": version: 6.1.1 resolution: "socks-proxy-agent@npm:6.1.1" @@ -6445,13 +5669,6 @@ __metadata: languageName: node linkType: hard -"tinymce@npm:5.10.1": - version: 5.10.1 - resolution: "tinymce@npm:5.10.1" - checksum: 6a518c0e9c8f7d9ca22deb73d777dece4aeabe9361b409c5ca7dbb62607a284550c9d0bf77dffa5d4b21a831b56c2f08c365f8c35d958e374d5beacc1a717753 - languageName: node - linkType: hard - "tinypool@npm:^0.3.0": version: 0.3.0 resolution: "tinypool@npm:0.3.0" @@ -6691,16 +5908,6 @@ __metadata: languageName: node linkType: hard -"url@npm:^0.11.0": - version: 0.11.0 - resolution: "url@npm:0.11.0" - dependencies: - punycode: 1.3.2 - querystring: 0.2.0 - checksum: 50d100d3dd2d98b9fe3ada48cadb0b08aa6be6d3ac64112b867b56b19be4bfcba03c2a9a0d7922bfd7ac17d4834e88537749fe182430dfd9b68e520175900d90 - languageName: node - linkType: hard - "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" @@ -6936,28 +6143,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:~8.2.3": - version: 8.2.3 - resolution: "ws@npm:8.2.3" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: c869296ccb45f218ac6d32f8f614cd85b50a21fd434caf11646008eef92173be53490810c5c23aea31bc527902261fbfd7b062197eea341b26128d4be56a85e4 - languageName: node - linkType: hard - -"xmlhttprequest-ssl@npm:~2.0.0": - version: 2.0.0 - resolution: "xmlhttprequest-ssl@npm:2.0.0" - checksum: 1e98df67f004fec15754392a131343ea92e6ab5ac4d77e842378c5c4e4fd5b6a9134b169d96842cc19422d77b1606b8df84a5685562b3b698cb68441636f827e - languageName: node - linkType: hard - "xtend@npm:~4.0.1": version: 4.0.2 resolution: "xtend@npm:4.0.2" @@ -7052,13 +6237,6 @@ __metadata: languageName: node linkType: hard -"yeast@npm:0.1.2": - version: 0.1.2 - resolution: "yeast@npm:0.1.2" - checksum: 81a250b69f601fed541e9518eb2972e75631dd81231689503d7f288612d4eec793b29c208d6807fd6bfc4c2a43614d0c6db233739a4ae6223e244aaed6a885c0 - languageName: node - linkType: hard - "yn@npm:3.1.1": version: 3.1.1 resolution: "yn@npm:3.1.1"