From f85f2d4a074a08ab9c4be06bf4fa01a70923269a Mon Sep 17 00:00:00 2001 From: Matt Potts Date: Mon, 11 Sep 2023 09:01:30 +0100 Subject: [PATCH] defect: Fix Recipe Import (#135) --- docs/_config.yml | 2 +- package.json | 2 +- src/applications/common/LoadingModal.svelte | 18 -- src/applications/common/LoadingStore.ts | 33 --- .../CraftingSystemEditor.svelte | 5 - .../CraftingSystemNavbar.svelte | 5 +- .../componentManager/ComponentEditor.svelte | 2 +- .../essenceManager/EssenceEditor.ts | 5 + .../recipeManager/RecipeBrowser.svelte | 11 - src/applications/stores/EssenceStore.ts | 10 + src/public/module.json | 2 +- src/scripts/crafting/Tool.ts | 35 --- src/scripts/crafting/component/Component.ts | 45 --- src/scripts/error/ValidationFailures.ts | 35 --- src/scripts/foundry/DiceRoller.ts | 65 ---- src/scripts/interface/FabricateApplication.ts | 34 --- src/scripts/system/ComponentDictionary.ts | 268 ----------------- src/scripts/system/Dictionary.ts | 31 -- src/scripts/system/EssenceDictionary.ts | 164 ---------- src/scripts/system/GameSystem.ts | 7 - src/scripts/system/RecipeDictionary.ts | 279 ------------------ 21 files changed, 20 insertions(+), 1038 deletions(-) delete mode 100644 src/applications/common/LoadingModal.svelte delete mode 100644 src/applications/common/LoadingStore.ts delete mode 100644 src/scripts/crafting/Tool.ts delete mode 100644 src/scripts/error/ValidationFailures.ts delete mode 100644 src/scripts/foundry/DiceRoller.ts delete mode 100644 src/scripts/interface/FabricateApplication.ts delete mode 100644 src/scripts/system/ComponentDictionary.ts delete mode 100644 src/scripts/system/Dictionary.ts delete mode 100644 src/scripts/system/EssenceDictionary.ts delete mode 100644 src/scripts/system/GameSystem.ts delete mode 100644 src/scripts/system/RecipeDictionary.ts diff --git a/docs/_config.yml b/docs/_config.yml index 63b6396a..5683e884 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: Fabricate 0.9.3 +title: Fabricate 0.9.4 email: matt@misterpotts.uk description: >- End user documentation for the Foundry Virtual Tabletop (VTT) Module, "Fabricate". diff --git a/package.json b/package.json index 0a3df92f..341f3f67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fabricate", - "version": "0.9.3", + "version": "0.9.4", "description": "A system-agnostic, flexible crafting module for FoundryVT", "main": "index.js", "type": "module", diff --git a/src/applications/common/LoadingModal.svelte b/src/applications/common/LoadingModal.svelte deleted file mode 100644 index 8acc497e..00000000 --- a/src/applications/common/LoadingModal.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - -{#if loading} -
-
-
-
-
-
-
-
-
-
-{/if} \ No newline at end of file diff --git a/src/applications/common/LoadingStore.ts b/src/applications/common/LoadingStore.ts deleted file mode 100644 index c764fe11..00000000 --- a/src/applications/common/LoadingStore.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {get, Subscriber, Updater, writable, Writable} from "svelte/store"; - -class LoadingStore { - - private readonly _loading: Writable; - - constructor({ - loading = false - }: { - loading?: boolean; - }) { - this._loading = writable(loading); - } - - public subscribe(subscriber: Subscriber) { - return this._loading.subscribe(subscriber); - } - - public set(value: boolean) { - return this._loading.set(value); - } - - public update(updater: Updater) { - return this._loading.update(updater); - } - - public read(): boolean { - return get(this._loading); - } - -} - -export { LoadingStore } \ No newline at end of file diff --git a/src/applications/craftingSystemManagerApp/CraftingSystemEditor.svelte b/src/applications/craftingSystemManagerApp/CraftingSystemEditor.svelte index 3bccab3d..28616c31 100644 --- a/src/applications/craftingSystemManagerApp/CraftingSystemEditor.svelte +++ b/src/applications/craftingSystemManagerApp/CraftingSystemEditor.svelte @@ -12,8 +12,6 @@ import {ComponentsStore} from "../stores/ComponentsStore"; import {SelectedRecipeStore} from "../stores/SelectedRecipeStore"; import {SelectedCraftingComponentStore} from "../stores/SelectedCraftingComponentStore"; - import {LoadingStore} from "../common/LoadingStore"; - import LoadingModal from "../common/LoadingModal.svelte"; import eventBus from "../common/EventBus"; import {onMount, setContext} from "svelte"; @@ -29,7 +27,6 @@ export let fabricateAPI; const localizationPath = `${Properties.module.id}.CraftingSystemManagerApp` - const loading = new LoadingStore({}); const craftingSystems = new CraftingSystemsStore({}); const selectedCraftingSystem = new SelectedCraftingSystemStore({ craftingSystems }); @@ -77,8 +74,6 @@ handleItemDeleted(e)} use:eventBus='{"itemDeleted"}'> - - {#if $craftingSystems.length > 0} diff --git a/src/applications/craftingSystemManagerApp/CraftingSystemNavbar.svelte b/src/applications/craftingSystemManagerApp/CraftingSystemNavbar.svelte index 10630b60..69ffafcb 100644 --- a/src/applications/craftingSystemManagerApp/CraftingSystemNavbar.svelte +++ b/src/applications/craftingSystemManagerApp/CraftingSystemNavbar.svelte @@ -10,14 +10,11 @@ craftingSystems, craftingSystemEditor, selectedCraftingSystem, - localization, - loading + localization } = getContext(key); async function createCraftingSystem() { - $loading = true; $selectedCraftingSystem = await craftingSystemEditor.createNewCraftingSystem(); - $loading = false; } async function importCraftingSystem(targetCraftingSystemId) { diff --git a/src/applications/craftingSystemManagerApp/componentManager/ComponentEditor.svelte b/src/applications/craftingSystemManagerApp/componentManager/ComponentEditor.svelte index 244c8ca2..29a39486 100644 --- a/src/applications/craftingSystemManagerApp/componentManager/ComponentEditor.svelte +++ b/src/applications/craftingSystemManagerApp/componentManager/ComponentEditor.svelte @@ -136,7 +136,7 @@ } else { salvageOption.addResult(component.id); } - $selectedComponent.saveSalvageOption(salvageOption); + $selectedComponent.setSalvageOption(salvageOption); await componentEditor.saveComponent($selectedComponent); } diff --git a/src/applications/craftingSystemManagerApp/essenceManager/EssenceEditor.ts b/src/applications/craftingSystemManagerApp/essenceManager/EssenceEditor.ts index 79e5766b..2b5e8c51 100644 --- a/src/applications/craftingSystemManagerApp/essenceManager/EssenceEditor.ts +++ b/src/applications/craftingSystemManagerApp/essenceManager/EssenceEditor.ts @@ -91,6 +91,11 @@ class EssenceEditor { return essence; } + public async saveAll(essences: Essence[]) { + await this._fabricateAPI.essences.saveAll(essences); + this._essences.insertAll(essences); + } + } export { EssenceEditor } \ No newline at end of file diff --git a/src/applications/craftingSystemManagerApp/recipeManager/RecipeBrowser.svelte b/src/applications/craftingSystemManagerApp/recipeManager/RecipeBrowser.svelte index 789b8475..268716b2 100644 --- a/src/applications/craftingSystemManagerApp/recipeManager/RecipeBrowser.svelte +++ b/src/applications/craftingSystemManagerApp/recipeManager/RecipeBrowser.svelte @@ -11,7 +11,6 @@ recipes, selectedRecipe, selectedCraftingSystem, - loading, recipeEditor } = getContext(key); @@ -19,9 +18,7 @@ const searchTerms = recipeSearchResults.searchTerms; async function importRecipe(event) { - $loading = true; await recipeEditor.importRecipe(event, $selectedCraftingSystem); - $loading = false; } function clearSearch() { @@ -33,14 +30,11 @@ } async function deleteRecipe(event, recipe) { - $loading = true; await recipeEditor.deleteRecipe(event, recipe, $selectedCraftingSystem); - $loading = false; } async function disableRecipe(recipe) { recipe.isDisabled = true; - $loading = true; await recipeEditor.saveRecipe(recipe, $selectedCraftingSystem); const message = localization.format( `${localizationPath}.recipe.disabled`, @@ -49,12 +43,10 @@ } ); ui.notifications.info(message); - $loading = false; } async function enableRecipe(recipe) { recipe.isDisabled = false; - $loading = true; await recipeEditor.saveRecipe(recipe, $selectedCraftingSystem); const message = localization.format( `${localizationPath}.recipe.enabled`, @@ -63,7 +55,6 @@ } ); ui.notifications.info(message); - $loading = false; } function toggleRecipeDisabled(recipe) { @@ -71,9 +62,7 @@ } async function duplicateRecipe(recipe) { - $loading = true; await recipeEditor.duplicateRecipe(recipe, $selectedCraftingSystem); - $loading = false; } async function openItemSheet(recipe) { diff --git a/src/applications/stores/EssenceStore.ts b/src/applications/stores/EssenceStore.ts index 3a4e007f..44ee8fa9 100644 --- a/src/applications/stores/EssenceStore.ts +++ b/src/applications/stores/EssenceStore.ts @@ -63,6 +63,16 @@ class EssencesStore implements Writable { }); } + insertAll(essences: Essence[]) { + this._essences.update((existingEssences) => { + const essencesById = new Map(existingEssences.map((essence) => [essence.id, essence])); + essences.forEach((essence) => { + essencesById.set(essence.id, essence); + }); + return Array.from(essencesById.values()); + }); + } + remove(essence: Essence) { this._essences.update((components) => { const index = components.findIndex((candidate) => candidate.id === essence.id); diff --git a/src/public/module.json b/src/public/module.json index c6d51b6c..666408b3 100644 --- a/src/public/module.json +++ b/src/public/module.json @@ -1,7 +1,7 @@ { "id": "fabricate", "title": "Fabricate", - "version": "0.9.3", + "version": "0.9.4", "description": "A system-agnostic, flexible crafting module for FoundryVTT", "authors": [ { diff --git a/src/scripts/crafting/Tool.ts b/src/scripts/crafting/Tool.ts deleted file mode 100644 index 89bc0f04..00000000 --- a/src/scripts/crafting/Tool.ts +++ /dev/null @@ -1,35 +0,0 @@ -class Tool { - /** - * The value to use in the User Interface when displaying and describing the tool - * */ - private readonly _displayName: string; - - /** - * The value to look for when checking if an Actor for a matching tool proficiency - * */ - private readonly _proficiencyLabel: string; - - /** - * Creates a new Tool - * */ - constructor(name: string, proficiencyLabel: string) { - this._displayName = name; - this._proficiencyLabel = proficiencyLabel; - } - - /** - * The value to use in the User Interface when displaying and describing the tool - * */ - get displayName(): string { - return this._displayName; - } - - /** - * The value to look for when checking if an Actor for a matching tool proficiency - * */ - get proficiencyLabel(): string { - return this._proficiencyLabel; - } -} - -export {Tool}; \ No newline at end of file diff --git a/src/scripts/crafting/component/Component.ts b/src/scripts/crafting/component/Component.ts index d4be3231..a78f904c 100644 --- a/src/scripts/crafting/component/Component.ts +++ b/src/scripts/crafting/component/Component.ts @@ -27,15 +27,6 @@ interface ComponentJson { class Component implements Identifiable, Serializable { - private static readonly _NONE: Component = new Component({ - id: "NO_ID", - craftingSystemId: "NO_CRAFTING_SYSTEM_ID", - itemData: NoFabricateItemData.INSTANCE(), - disabled: true, - essences: Combination.EMPTY(), - salvageOptions: new SelectableOptions({}) - }); - private readonly _id: string; private readonly _embedded: boolean; private readonly _craftingSystemId: string; @@ -95,10 +86,6 @@ class Component implements Identifiable, Serializable { return this._itemData.uuid; } - public static NONE() { - return this._NONE; - } - get name(): string { return this._itemData.name; } @@ -111,26 +98,6 @@ class Component implements Identifiable, Serializable { return this._essences; } - get selectedSalvage(): Combination { - return this._salvageOptions.selectedOption.results; - } - - get selectedSalvageOptionName(): string { - return this._salvageOptions.selectedOptionId; - } - - public selectSalvageOption(combinationId: string) { - this._salvageOptions.select(combinationId); - } - - public selectNextSalvageOption(): void { - this._salvageOptions.selectNext(); - } - - public selectPreviousSalvageOption(): void { - this._salvageOptions.selectPrevious(); - } - get isDisabled(): boolean { return this._disabled; } @@ -212,10 +179,6 @@ class Component implements Identifiable, Serializable { return this._salvageOptions; } - public selectFirstOption(): void { - this._salvageOptions.selectFirst() - } - get salvageOptionsById(): Map { return this._salvageOptions.byId; } @@ -236,10 +199,6 @@ class Component implements Identifiable, Serializable { return this._itemData.errors.map(error => error.code); } - deselectSalvage() { - this._salvageOptions.deselect(); - } - removeEssence(essenceIdToDelete: string) { this._essences = this._essences.without(essenceIdToDelete); } @@ -277,10 +236,6 @@ class Component implements Identifiable, Serializable { this._salvageOptions.set(created); } - public saveSalvageOption(value: SalvageOption) { - this._salvageOptions.set(value); - } - addEssence(essenceId: string, quantity: number = 1) { this._essences = this._essences.addUnit(new Unit(new EssenceReference(essenceId), quantity)); } diff --git a/src/scripts/error/ValidationFailures.ts b/src/scripts/error/ValidationFailures.ts deleted file mode 100644 index 9960f202..00000000 --- a/src/scripts/error/ValidationFailures.ts +++ /dev/null @@ -1,35 +0,0 @@ -class ValidationFailures { - - private readonly _propertyValidationFailures: Map = new Map(); - - constructor() {} - - public recordError(propertyName: string, errorDescription: string): boolean { - if (this._propertyValidationFailures.has(propertyName)) { - this._propertyValidationFailures.get(propertyName).push(errorDescription); - return true; - } - this._propertyValidationFailures.set(propertyName, [errorDescription]); - return true; - } - - get propertyValidationFailures(): Map { - return new Map(this._propertyValidationFailures); - } - - get isEmpty(): boolean { - return this._propertyValidationFailures.size === 0; - } - - public toString(): string { - if (this._propertyValidationFailures.size === 0) { - return 'No validation errors. ' - } - const errorDescriptions: string[] = []; - this._propertyValidationFailures.forEach((value, key) => errorDescriptions.push(`[${key}]: ${value}`)); - return errorDescriptions.join('\n'); - } - -} - -export {ValidationFailures} \ No newline at end of file diff --git a/src/scripts/foundry/DiceRoller.ts b/src/scripts/foundry/DiceRoller.ts deleted file mode 100644 index fbc887fc..00000000 --- a/src/scripts/foundry/DiceRoller.ts +++ /dev/null @@ -1,65 +0,0 @@ -import {Evaluated} from "@league-of-foundry-developers/foundry-vtt-types/src/foundry/client/dice/roll"; - -class RollResult { - private readonly _value: number; - private readonly _expression: string; - - constructor(value: number, expression: string) { - this._value = value; - this._expression = expression; - } - - get value(): number { - return this._value; - } - - get expression(): string { - return this._expression; - } - -} - -class DiceRoller { - - private readonly _unmodifiedRollExpression: string; - - constructor(unmodifiedRollExpression: string) { - this._unmodifiedRollExpression = unmodifiedRollExpression; - } - - public createUnmodifiedRoll(): Roll { - return new Roll(this._unmodifiedRollExpression); - } - - public evaluate(roll: Roll, modifiers?: RollTerm[]): RollResult { - if (!modifiers || modifiers.length === 0) { - const rollResult: Evaluated = roll.roll({ async: false }); - return new RollResult(rollResult.total, rollResult.result); - } - const preparedModifiers = modifiers - .flatMap((rollTerm: RollTerm) => [rollTerm, new OperatorTerm({ operator: "+" })]); - const modifiedRoll = this.combine(roll, Roll.fromTerms(preparedModifiers)); - const rollResult: Evaluated = modifiedRoll.roll({ async: false }); - return new RollResult(rollResult.total, rollResult.result); - } - - public multiply(input: string | Roll, factor: number): Roll { - if (typeof input === "string") { - const roll: Roll = new Roll(input, {}); - return roll.alter(factor, 0); - } - return input.alter(factor, 0); - } - - fromExpression(expression: string): Roll { - return new Roll(expression); - } - - combine(left: Roll, right: Roll) { - const simplifiedTerms: RollTerm[] = Roll.simplifyTerms(left.terms.concat(right.terms)); - return Roll.fromTerms(simplifiedTerms); - } - -} - -export {DiceRoller, RollResult} \ No newline at end of file diff --git a/src/scripts/interface/FabricateApplication.ts b/src/scripts/interface/FabricateApplication.ts deleted file mode 100644 index 1e6b3717..00000000 --- a/src/scripts/interface/FabricateApplication.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {SvelteApplication} from "../../applications/SvelteApplication"; -import {ComponentSalvageAppCatalog} from "../../applications/componentSalvageApp/ComponentSalvageAppCatalog"; -import {RecipeCraftingAppCatalog} from "../../applications/recipeCraftingApp/RecipeCraftingAppCatalog"; -import {FabricateAPI} from "../api/FabricateAPI"; - -class FabricateApplication { - - private _fabricateAPI: FabricateAPI; - - private _craftingSystemManagerApp: SvelteApplication; - private _componentSalvageAppCatalog: ComponentSalvageAppCatalog; - private _recipeCraftingAppCatalog: RecipeCraftingAppCatalog; - - constructor() {} - - get fabricateAPI(): FabricateAPI { - return this._fabricateAPI; - } - - get craftingSystemManagerApp(): SvelteApplication { - return this._craftingSystemManagerApp; - } - - get componentSalvageAppCatalog(): ComponentSalvageAppCatalog { - return this._componentSalvageAppCatalog; - } - - get recipeCraftingAppCatalog(): RecipeCraftingAppCatalog { - return this._recipeCraftingAppCatalog; - } - -} - -export { FabricateApplication } \ No newline at end of file diff --git a/src/scripts/system/ComponentDictionary.ts b/src/scripts/system/ComponentDictionary.ts deleted file mode 100644 index cb3863d4..00000000 --- a/src/scripts/system/ComponentDictionary.ts +++ /dev/null @@ -1,268 +0,0 @@ -import {Dictionary} from "./Dictionary"; -import {Component, ComponentJson} from "../crafting/component/Component"; -import { - DocumentManager, - FabricateItemData, - NoFabricateItemData -} from "../foundry/DocumentManager"; -import {EssenceDictionary} from "./EssenceDictionary"; -import {SelectableOptions} from "../crafting/selection/SelectableOptions"; -import {Essence} from "../crafting/essence/Essence"; -import Properties from "../Properties"; -import {Combination} from "../common/Combination"; -import {SalvageOption, SalvageOptionJson} from "../crafting/component/SalvageOption"; - -export class ComponentDictionary implements Dictionary { - - private _sourceData: Record; - private readonly _documentManager: DocumentManager; - private readonly _essenceDictionary: EssenceDictionary; - private readonly _entriesById: Map; - private readonly _entriesByItemUuid: Map; - private _loaded: boolean; - - constructor({ - sourceData, - documentManager, - essenceDictionary, - entriesById = new Map(), - entriesByItemUuid = new Map(), - loaded = false - }: { - sourceData: Record; - documentManager: DocumentManager; - essenceDictionary: EssenceDictionary; - entriesById?: Map; - entriesByItemUuid?: Map; - loaded?: boolean; - }) { - this._sourceData = sourceData; - this._documentManager = documentManager; - this._essenceDictionary = essenceDictionary; - this._entriesById = entriesById; - this._entriesByItemUuid = entriesByItemUuid; - this._loaded = loaded; - } - - public clone(): ComponentDictionary { - return new ComponentDictionary({ - sourceData: this._sourceData, - documentManager: this._documentManager, - essenceDictionary: this._essenceDictionary - }) - } - - get entriesWithErrors(): Component[] { - return Array.from(this._entriesById.values()).filter(entry => entry.hasErrors); - } - - get hasErrors(): boolean { - return this.entriesWithErrors.length > 0; - } - - get isLoaded(): boolean { - return this._loaded && this._essenceDictionary.isLoaded; - } - - get size(): number { - return this._entriesById.size; - } - - contains(id: string): boolean { - return this._entriesById.has(id); - } - - getAll(): Map { - return new Map(this._entriesById); - } - - getById(id: string): Component { - if (!this._entriesById.has(id)) { - throw new Error(`No Component data was found for the id "${id}". Known Component IDs for this system are: ${Array.from(this._entriesById.keys()).join(", ")}`); - } - return this._entriesById.get(id); - } - - get allByItemUuid(): Map { - return new Map(this._entriesByItemUuid) - } - - get sourceData(): Record { - return this._sourceData; - } - - set sourceData(value: Record) { - this._sourceData = value; - } - - insert(craftingComponent: Component): void { - if (craftingComponent.itemUuid !== NoFabricateItemData.UUID()) { - this._entriesByItemUuid.set(craftingComponent.itemUuid, craftingComponent); - } - this._entriesById.set(craftingComponent.id, craftingComponent); - } - - deleteById(id: string): void { - if (!this._entriesById.has(id)) { - return; - } - const componentToDelete = this._entriesById.get(id); - this._entriesById.delete(id); - this._entriesByItemUuid.delete(id); - Array.from(this._entriesById.values()) - .forEach(component => { - if (!component.isSalvageable) { - return; - } - component.salvageOptions = component.salvageOptions - .map(option => { - if (!option.salvage.has(componentToDelete)) { - return option; - } - option.salvage = option.salvage.without(componentToDelete); - return option; - }); - }); - } - - async loadAll(): Promise { - if (!this._sourceData) { - throw new Error("Unable to load Crafting components. No source data was provided. "); - } - await this.loadDependencies(); - const itemUuids = Object.values(this._sourceData) - .map(data => data.itemUuid); - const cachedItemDataByUUid = await this._documentManager.loadItemDataForDocumentsByUuid(itemUuids); - this._entriesById.clear(); - this._entriesByItemUuid.clear(); - // Loads all components _without_ loading salvage data - Object.keys(this._sourceData) - .map(id => { - return {id, json: this._sourceData[id]} - }) - .map(data => new Component({ - id: data.id, - itemData: cachedItemDataByUUid.get(data.json.itemUuid), - essences: Combination.fromRecord(data.json.essences, this._essenceDictionary.getAll()), - disabled: data.json.disabled, - salvageOptions: new SelectableOptions({}) - })) - .forEach(component => this.insert(component)); - // Iterates over components again to load their salvage data using loaded component references - const craftingComponents = Array.from(this._entriesById.values()); - craftingComponents - .forEach(component => { - const salvageOptionsConfig = this._sourceData[component.id].salvageOptions; - if (salvageOptionsConfig && Object.keys(salvageOptionsConfig).length > 0) { - component.salvageOptions = this.buildSalvageOptions(salvageOptionsConfig, this._entriesById).options; - } - }); - this._loaded = true; - return craftingComponents; - } - - async loadById(id: string): Promise { - const sourceRecord = this._sourceData[id]; - if (!sourceRecord) { - throw new Error(`Unable to load Crafting Component with ID ${id}. No definition for the component was found in source data. - This can occur if a component is loaded before it is saved or an invalid ID is passed.`); - } - await this.loadDependencies(); - const itemUuid = sourceRecord.itemUuid; - const itemData = await this._documentManager.loadItemDataByDocumentUuid(itemUuid); - return this.buildComponent(id, sourceRecord, itemData); - } - - private buildComponent(id: string, sourceRecord: ComponentJson, itemData: FabricateItemData): Component { - return new Component({ - id, - itemData, - disabled: sourceRecord.disabled, - salvageOptions: this.buildSalvageOptions(sourceRecord.salvageOptions, this._entriesById), - essences: Combination.fromRecord(sourceRecord.essences, this._essenceDictionary.getAll()), - }); - } - - private buildSalvageOptions(salvageOptionsJson: Record, allComponents: Map): SelectableOptions { - const options = Object.keys(salvageOptionsJson) - .map(name => this.buildSalvageOption(name, salvageOptionsJson[name], allComponents)); - return new SelectableOptions({ - options - }); - } - - private buildSalvageOption(name: string, salvageOptionJson: SalvageOptionJson, allComponents: Map): SalvageOption { - return new SalvageOption({ - name, - results: Combination.fromRecord(salvageOptionJson, allComponents) - }); - } - - private async loadDependencies() { - if (!this._essenceDictionary.isLoaded) { - await this._essenceDictionary.loadAll(); - } - } - - toJson(): Record { - return Array.from(this._entriesById.entries()) - .map(entry => { - return {key: entry[0], value: entry[1].toJson()} - }) - .reduce((left, right) => { - left[right.key] = right.value; - return left; - }, >{}); - } - - get isEmpty(): boolean { - return this._entriesById.size === 0; - } - - dropEssenceReferences(essenceToDelete: Essence) { - Array.from(this._entriesById.values()) - .forEach(component => { - component.essences = component.essences.without(essenceToDelete); - }); - } - - containsItemByUuid(itemUuid: string) { - return this._entriesByItemUuid.has(itemUuid); - } - - getByItemUuid(uuid: string) { - return this._entriesByItemUuid.get(uuid); - } - - async create(craftingComponentJson: ComponentJson): Promise { - const itemData = await this._documentManager.loadItemDataByDocumentUuid(craftingComponentJson.itemUuid); - if (itemData.hasErrors) { - throw new Error(`Could not load document with UUID "${craftingComponentJson.itemUuid}". Errors ${itemData.errors.join(", ")} `); - } - if (!Properties.module.documents.supportedTypes.includes(itemData.sourceDocument.documentName)) { - throw new Error(`Document with UUID is a ${itemData.sourceDocument.documentName}. Fabricate only allows the following document types: ${Properties.module.documents.supportedTypes.join(", ")}`); - } - const componentId = randomID(); - const component = await this.buildComponent(componentId, craftingComponentJson, itemData); - this.insert(component); - return component; - } - - async mutate(id: string, mutation: ComponentJson): Promise { - if (!this._loaded) { - throw new Error("Fabricate doesn't currently support modifying components before the component dictionary has been loaded. "); - } - if (!this._entriesById.has(id)) { - throw new Error(`Unable to mutate component with ID ${id}. It doesn't exist.`); - } - const target = this._entriesById.get(id); - const itemData = target.itemUuid === mutation.itemUuid ? target.itemData : await this._documentManager.loadItemDataByDocumentUuid(mutation.itemUuid); - if (itemData.hasErrors) { - throw new Error(`Could not load document with UUID "${mutation.itemUuid}". Errors ${itemData.errors.join(", ")} `); - } - const result = this.buildComponent(target.id, mutation, itemData); - this.insert(result); - return result; - } - -} \ No newline at end of file diff --git a/src/scripts/system/Dictionary.ts b/src/scripts/system/Dictionary.ts deleted file mode 100644 index 6325d097..00000000 --- a/src/scripts/system/Dictionary.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {Identifiable} from "../common/Identifiable"; -import {Serializable} from "../common/Serializable"; - -export interface Dictionary> { - - loadAll(): Promise; - - loadById(id: string): Promise; - - sourceData: Record; - isLoaded: boolean; - - getById(id: string): I; - - getAll(): Map; - - toJson(): Record; - - contains(id: string): boolean; - - size: number; - isEmpty: boolean; - - insert(item: I): void; - - hasErrors: boolean; - entriesWithErrors: I[]; - - deleteById(id: string): void; - -} \ No newline at end of file diff --git a/src/scripts/system/EssenceDictionary.ts b/src/scripts/system/EssenceDictionary.ts deleted file mode 100644 index 998fa1d3..00000000 --- a/src/scripts/system/EssenceDictionary.ts +++ /dev/null @@ -1,164 +0,0 @@ -import {Dictionary} from "./Dictionary"; -import {Essence, EssenceJson} from "../crafting/essence/Essence"; -import {DocumentManager, FabricateItemData, PendingFabricateItemData} from "../foundry/DocumentManager"; -import Properties from "../Properties"; - -export class EssenceDictionary implements Dictionary { - - private _sourceData: Record; - private readonly _documentManager: DocumentManager; - private readonly _entries: Map; - private _loaded: boolean; - - constructor({ - sourceData, - documentManager, - entries = new Map(), - loaded = false - }: { - sourceData: Record; - documentManager: DocumentManager; - entries?: Map; - loaded?: boolean; - }) { - this._sourceData = sourceData; - this._documentManager = documentManager; - this._entries = entries; - this._loaded = loaded; - } - - public clone(): EssenceDictionary { - return new EssenceDictionary({ - sourceData: this._sourceData, - documentManager: this._documentManager - }) - } - - get entriesWithErrors(): Essence[] { - return Array.from(this._entries.values()).filter(entry => entry.hasErrors); - } - - get hasErrors(): boolean { - return this.entriesWithErrors.length > 0; - } - - get isLoaded(): boolean { - return this._loaded; - } - - get size(): number { - return this._entries.size; - } - - contains(id: string): boolean { - return this._entries.has(id); - } - - getAll(): Map { - return new Map(this._entries); - } - - getById(id: string): Essence { - if (!this._entries.has(id)) { - throw new Error(`No Essence data was found for the id "${id}". Known Essence IDs for this system are: ${Array.from(this._entries.keys()).join(", ")}`); - } - return this._entries.get(id); - } - - get sourceData(): Record { - return this._sourceData; - } - - set sourceData(value: Record) { - this._sourceData = value; - } - - insert(essence: Essence): void { - this._entries.set(essence.id, essence); - } - - deleteById(id: string): void { - this._entries.delete(id); - } - - async loadAll(): Promise { - if (!this._sourceData) { - throw new Error("Unable to load Essences. No source data was provided. "); - } - const itemUuids = Object.values(this._sourceData) - .filter(data => !!data.activeEffectSourceItemUuid) - .map(data => data.activeEffectSourceItemUuid); - const cachedItemDataByUUid = await this._documentManager.loadItemDataForDocumentsByUuid(itemUuids); - this._entries.clear(); - const essences = await Promise.all(Object.keys(this._sourceData).map(id => this.loadById(id, cachedItemDataByUUid))); - essences.forEach(essence => this._entries.set(essence.id, essence)); - this._loaded = true; - return essences; - } - - async loadById(id: string, itemDataCache?: Map): Promise { - const sourceRecord = this._sourceData[id]; - if (!sourceRecord) { - throw new Error(`Unable to load Essence with ID ${id}. No definition for the essence was found in source data. - This can occur if an Essence is loaded before it is saved or an invalid ID is passed.`); - } - const itemData = sourceRecord.activeEffectSourceItemUuid ? new PendingFabricateItemData(sourceRecord.activeEffectSourceItemUuid) : null; - const essence = this.buildEssence(id, sourceRecord, itemData); - if (essence.hasActiveEffectSource) { - let itemData: FabricateItemData; - if (itemDataCache.has(essence.activeEffectSource.uuid)) { - itemData = itemDataCache.get(essence.activeEffectSource.uuid); - } else { - itemData = await this._documentManager.loadItemDataByDocumentUuid(essence.activeEffectSource.uuid); - } - essence.activeEffectSource = itemData; - } - return essence; - } - - private buildEssence(id: string, sourceRecord:EssenceJson, itemData: FabricateItemData): Essence { - return new Essence({ - id: id, - name: sourceRecord.name, - description: sourceRecord.description, - iconCode: sourceRecord.iconCode, - tooltip: sourceRecord.tooltip, - activeEffectSource: itemData - }) - } - - toJson(): Record { - return Array.from(this._entries.entries()) - .map(entry => { - return {key: entry[0], value: entry[1].toJson()} - }) - .reduce((left, right) => { - left[right.key] = right.value; - return left; - }, >{}); - } - - get isEmpty(): boolean { - return this._entries.size === 0; - } - - async create(essenceJson: EssenceJson): Promise { - let essence: Essence; - const essenceId = randomID(); - if (essenceJson.activeEffectSourceItemUuid) { - const itemData = await this._documentManager.loadItemDataByDocumentUuid(essenceJson.activeEffectSourceItemUuid); - if (itemData.hasErrors) { - throw new Error(`Could not load document with UUID "${essenceJson.activeEffectSourceItemUuid}". Errors ${itemData.errors.join(", ")} `); - } - if (!Properties.module.documents.supportedTypes.includes(itemData.sourceDocument.documentName)) { - throw new Error(`Document with UUID is a ${itemData.sourceDocument.documentName}. Fabricate only allows the following document types: ${Properties.module.documents.supportedTypes.join(", ")}`); - } - essence = await this.buildEssence(essenceId, essenceJson, itemData); - this.insert(essence); - return essence; - } - essence = await this.buildEssence(essenceId, essenceJson, null); - this.insert(essence); - return essence; - } -} \ No newline at end of file diff --git a/src/scripts/system/GameSystem.ts b/src/scripts/system/GameSystem.ts deleted file mode 100644 index 3b50fa0a..00000000 --- a/src/scripts/system/GameSystem.ts +++ /dev/null @@ -1,7 +0,0 @@ -enum GameSystem { - DND5E = "dnd5e", - PF2E = "pf2e", - NONE = "NONE" -} - -export {GameSystem} \ No newline at end of file diff --git a/src/scripts/system/RecipeDictionary.ts b/src/scripts/system/RecipeDictionary.ts deleted file mode 100644 index 0c3f6abf..00000000 --- a/src/scripts/system/RecipeDictionary.ts +++ /dev/null @@ -1,279 +0,0 @@ -import {Dictionary} from "./Dictionary"; -import { - Recipe, - RecipeJson -} from "../crafting/recipe/Recipe"; -import {DocumentManager, FabricateItemData, NoFabricateItemData} from "../foundry/DocumentManager"; -import {EssenceDictionary} from "./EssenceDictionary"; -import {ComponentDictionary} from "./ComponentDictionary"; -import {Component} from "../crafting/component/Component"; -import {SelectableOptions} from "../crafting/selection/SelectableOptions"; -import {Essence} from "../crafting/essence/Essence"; -import Properties from "../Properties"; -import {Combination} from "../common/Combination"; -import {RequirementOption, RequirementOptionJson} from "../crafting/recipe/RequirementOption"; -import {ResultOption, ResultOptionJson} from "../crafting/recipe/ResultOption"; - -export class RecipeDictionary implements Dictionary { - private _sourceData: Record; - private readonly _documentManager: DocumentManager; - private readonly _essenceDictionary: EssenceDictionary; - private readonly _componentDictionary: ComponentDictionary; - private readonly _entriesById: Map; - private readonly _entriesByItemUuid: Map; - private _loaded: boolean; - - constructor({ - sourceData, - documentManager, - essenceDictionary, - componentDictionary, - entriesById = new Map(), - entriesByItemUuid = new Map(), - loaded = false - }: { - sourceData: Record; - documentManager: DocumentManager; - essenceDictionary: EssenceDictionary; - componentDictionary: ComponentDictionary; - entriesById?: Map; - entriesByItemUuid?: Map; - loaded?: boolean; - }) { - this._sourceData = sourceData; - this._documentManager = documentManager; - this._essenceDictionary = essenceDictionary; - this._componentDictionary = componentDictionary; - this._entriesById = entriesById; - this._entriesByItemUuid = entriesByItemUuid; - this._loaded = loaded; - } - - public clone(): RecipeDictionary { - return new RecipeDictionary({ - sourceData: this._sourceData, - documentManager: this._documentManager, - essenceDictionary: this._essenceDictionary, - componentDictionary: this._componentDictionary - }); - } - - get entriesWithErrors(): Recipe[] { - return Array.from(this._entriesById.values()).filter(entry => entry.hasErrors); - } - - get hasErrors(): boolean { - return this.entriesWithErrors.length > 0; - } - - get isLoaded(): boolean { - return this._loaded && this._essenceDictionary.isLoaded; - } - - get size(): number { - return this._entriesById.size; - } - - contains(id: string): boolean { - return this._entriesById.has(id); - } - - getAll(): Map { - return new Map(this._entriesById); - } - - getById(id: string): Recipe { - if (!this._entriesById.has(id)) { - throw new Error(`No Recipe data was found for the id "${id}". Known Recipe IDs for this system are: ${Array.from(this._entriesById.keys()).join(", ")}`); - } - return this._entriesById.get(id); - } - - get sourceData(): Record { - return this._sourceData; - } - - set sourceData(value: Record) { - this._sourceData = value; - } - - insert(recipe: Recipe): void { - if (recipe.itemUuid !== NoFabricateItemData.UUID()) { - this._entriesByItemUuid.set(recipe.itemUuid, recipe); - } - this._entriesById.set(recipe.id, recipe); - } - - deleteById(id: string): void { - if (!this._entriesById.has(id)) { - return; - } - this._entriesById.delete(id); - this._entriesByItemUuid.delete(id); - } - - async loadAll(): Promise { - if (!this._sourceData) { - throw new Error("Unable to load Recipes. No source data was provided. "); - } - await this.loadDependencies(); - const itemUuids = Object.values(this._sourceData) - .map(data => data.itemUuid); - const cachedItemDataByUUid = await this._documentManager.loadItemDataForDocumentsByUuid(itemUuids); - this._entriesById.clear(); - this._entriesByItemUuid.clear(); - const recipes = await Promise.all(Object.keys(this._sourceData) - .map(id => this.loadById(id, cachedItemDataByUUid))); - recipes.forEach(recipe => this.insert(recipe)); - this._loaded = true; - return Array.from(this._entriesById.values()); - } - - async loadById(id: string, itemDataCache: Map = new Map()): Promise { - const sourceRecord = this._sourceData[id]; - if (!sourceRecord) { - throw new Error(`Unable to load Recipe with ID ${id}. No definition for the recipe was found in source data. - This can occur if a recipe is loaded before it is saved or an invalid ID is passed.`); - } - const itemUuid = sourceRecord.itemUuid; - const itemData = itemDataCache.has(itemUuid) ? itemDataCache.get(itemUuid) : await this._documentManager.loadItemDataByDocumentUuid(itemUuid); - await this.loadDependencies(); - return this.buildRecipe(id, sourceRecord, itemData); - } - - private buildRecipe(id: string, sourceRecord: RecipeJson, itemData: FabricateItemData): Recipe { - return new Recipe({ - id, - itemData, - disabled: sourceRecord.disabled, - requirementOptions: this.buildIngredientOptions(sourceRecord.requirementOptions, this._componentDictionary.getAll()), - resultOptions: this.buildResultOptions(sourceRecord.resultOptions, this._componentDictionary.getAll()), - essences: Combination.fromRecord(sourceRecord.essences, this._essenceDictionary.getAll()) - }); - } - - private buildIngredientOptions(ingredientOptionsJson: Record, allComponents: Map): SelectableOptions { - const options = Object.keys(ingredientOptionsJson) - .map(name => this.buildIngredientOption(name, ingredientOptionsJson[name], allComponents)); - return new SelectableOptions({ - options - }); - } - - private buildResultOptions(resultOptionsJson: Record, allComponents: Map): SelectableOptions { - const options = Object.keys(resultOptionsJson) - .map(name => this.buildResultOption(name, resultOptionsJson[name], allComponents)); - return new SelectableOptions({ - options - }); - } - - private buildIngredientOption(name: string, ingredientOptionJson: RequirementOptionJson, allComponents: Map): RequirementOption { - return new RequirementOption({ - name, - catalysts: Combination.fromRecord(ingredientOptionJson.catalysts, allComponents), - ingredients: Combination.fromRecord(ingredientOptionJson.ingredients, allComponents) - }); - } - - private buildResultOption(name: string, resultOptionJson: ResultOptionJson, allComponents: Map): ResultOption { - return new ResultOption({ - name, - results: Combination.fromRecord(resultOptionJson, allComponents) - }); - } - - private async loadDependencies() { - if (!this._essenceDictionary.isLoaded) { - await this._essenceDictionary.loadAll(); - } - if (!this._componentDictionary.isLoaded) { - await this._componentDictionary.loadAll(); - } - } - - toJson(): Record { - return Array.from(this._entriesById.entries()) - .map(entry => { - return {key: entry[0], value: entry[1].toJson()} - }) - .reduce((left, right) => { - left[right.key] = right.value; - return left; - }, >{}); - } - - get isEmpty(): boolean { - return this._entriesById.size === 0; - } - - dropComponentReferences(componentToDelete: Component) { - Array.from(this._entriesById.values()) - .forEach(recipe => { - recipe.ingredientOptions = recipe.ingredientOptions - .map(ingredientOption => { - if (ingredientOption.ingredients.has(componentToDelete)) { - ingredientOption.ingredients = ingredientOption.ingredients.without(componentToDelete); - } - if (ingredientOption.catalysts.has(componentToDelete)) { - ingredientOption.catalysts = ingredientOption.catalysts.without(componentToDelete); - } - return ingredientOption; - }); - recipe.resultOptions = recipe.resultOptions - .map(resultOption => { - if (resultOption.results.has(componentToDelete)) { - resultOption.results = resultOption.results.without(componentToDelete); - } - return resultOption; - }); - }); - } - - dropEssenceReferences(essenceToDelete: Essence) { - Array.from(this._entriesById.values()) - .forEach(recipe => { - recipe.essences = recipe.essences.without(essenceToDelete); - }); - } - - containsItemByUuid(itemUuid: string) { - return this._entriesByItemUuid.has(itemUuid); - } - - getByItemUuid(itemUuid: string) { - return this._entriesByItemUuid.get(itemUuid); - } - - async create(recipeJson: RecipeJson): Promise { - const itemData = await this._documentManager.loadItemDataByDocumentUuid(recipeJson.itemUuid); - if (itemData.hasErrors) { - throw new Error(`Could not load document with UUID "${recipeJson.itemUuid}". Errors ${itemData.errors.join(", ")} `); - } - if (!Properties.module.documents.supportedTypes.includes(itemData.sourceDocument.documentName)) { - throw new Error(`Document with UUID is a ${itemData.sourceDocument.documentName}. Fabricate only allows the following document types: ${Properties.module.documents.supportedTypes.join(", ")}`); - } - const recipeId = randomID(); - const recipe = await this.buildRecipe(recipeId, recipeJson, itemData); - this.insert(recipe); - return recipe; - } - - async mutate(id: string, mutation: RecipeJson): Promise { - if (!this._loaded) { - throw new Error("Fabricate doesn't currently support modifying recipes before the recipe dictionary has been loaded. "); - } - if (!this._entriesById.has(id)) { - throw new Error(`Unable to mutate recipe with ID ${id}. It doesn't exist.`); - } - const target = this._entriesById.get(id); - const itemData = target.itemUuid === mutation.itemUuid ? target.itemData : await this._documentManager.loadItemDataByDocumentUuid(mutation.itemUuid); - if (itemData.hasErrors) { - throw new Error(`Could not load document with UUID "${mutation.itemUuid}". Errors ${itemData.errors.join(", ")} `); - } - const result = this.buildRecipe(target.id, mutation, itemData); - this.insert(result); - return result; - } - -} \ No newline at end of file