Skip to content

Commit

Permalink
docs: Latest release (#109)
Browse files Browse the repository at this point in the history
  • Loading branch information
misterpotts authored Mar 22, 2023
1 parent 4650228 commit 5235be2
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 15 deletions.
122 changes: 114 additions & 8 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ type SalvageOptionJson = Record<string, number>;
To create one, get the crafting system you want to add it to, and call `CraftingSystem#createComponent`.
Then, save the crafting system.

{: .highlight }
> If your crafting system has components and recipes already defined, make sure that you **load the part dictionary**
> before you save the crafting system. Attempts to save a crafting system that has not had its part dictionary loaded
> will produce an error. This area of the API is _likely to change_ in future.
<details open markdown="block">
<summary>
Add a component to a crafting system
Expand All @@ -271,23 +276,124 @@ const myComponent = {

const myCraftingSystemId = "8Bn1VBc6noSUeKlG"; // replace this with your own crafting system ID
const craftingSystem = await game.fabricate.SystemRegistry.getCraftingSystemById(myCraftingSystemId);
await craftingSystem.loadPartDictionary() // <-- Do this before modifying systems that already have components
const createdComponent = await craftingSystem.createComponent(myComponent);
await game.fabricate.SystemRegistry.saveCraftingSystem(craftingSystem);
console.log(`Created component with ID "${createdComponent.id}"`); // <-- You'll need this to edit the component
```

</details>

## Accessing a component

To access a component, you will need to:

1. Get the crafting system it belongs to
2. Load the part dictionary for that system
3. Get the component by ID from the crafting system

<details open markdown="block">
<summary>
Accessing a component
</summary>

```js
const myCraftingSystemId = "8Bn1VBc6noSUeKlG"; // replace this with your own crafting system ID
const craftingSystem = await game.fabricate.SystemRegistry.getCraftingSystemById(myCraftingSystemId);
// Fabricate currently requires you to load essences, components and recipes into memory before you can act on them
// **This area of the API is likely to change** in future as I move towards loading individual parts on demand
await craftingSystem.loadPartDictionary();
const myComponentId = "T3zD0zrcd0otXXwB"; // replace this with the ID of the component you want to get
const myComponent = craftingSystem.getComponentById(myComponentId);
```

</details>

## Editing a component

{: .highlight }
> The docs haven't caught up with the code here just yet.
> The API exists, if you want to [dig around in the sources](http://github.com/misterpotts/fabricate) and experiment.
> Alternatively, you can wait for these docs to be updated.
To edit a component, you will need to:

1. [Access the component](#accessing-a-component)
2. Serialize it
3. Modify the serialized data
4. Call `CraftingSystem#mutateComponent` with the component ID and the modified data
5. Save the crafting system

<details open markdown="block">
<summary>
CraftingSystem#mutateComponent
</summary>

```typescript
/**
* Modifies an existing component by applying the mutations defined in the supplied `CraftingComponentJson`
*
* @param id The ID of the component to modify
* @param the complete target state of the component to apply. Anything you omit is deleted
* @return a Promise that resolves to the updated crafting component
*
* @throws an Error if the ID is not for an existing component
* @throws an Error if the the component dictionary has not been loaded
* @throws an Error if the mutation contains an invalid item UUID
* @throws an Error if the mutation references essences orcomponents that do not exist
* */
mutateComponent(id: string, mutation: CraftingComponentJson): Promise<CraftingComponent>;
```

</details>

<details open markdown="block">
<summary>
Modifying a component
</summary>

```js
// Access the component as described above
const myComponent = craftingSystem.getComponentById(myComponentId);
console.log(`Before modification: ${myComponent}`);
const componentData = myComponent.toJson();
componentData.salvageOptions["My new Salvage Option"] = { "rLP3cTCTnQsxddDt": 1 } // Adds a new salvage option
// perform any other modifications you want here. You can alter any of the fields in the `CraftingComponentJson`,
// including the item UUID. The new values meet the requirements specified in the interface.
const updatedComponent = await craftingSystem.mutateComponent(myComponent.id, componentData);
await game.fabricate.SystemRegistry.saveCraftingSystem(craftingSystem);
console.log(`After modification: ${updatedComponent}`);
```

</details>

## Deleting a component

{: .highlight }
> The docs haven't caught up with the code here just yet.
> The API exists, if you want to [dig around in the sources](http://github.com/misterpotts/fabricate) and experiment.
> Alternatively, you can wait for these docs to be updated.
Deleting a component requires only that you load the crafting system, delete the component using its ID by calling
`CraftingSystem#deleteComponentById` then save the crafting system.

<details open markdown="block">
<summary>
CraftingSystem#deleteComponentById
</summary>

```typescript
/**
* Deletes a crafting component by ID
*
* @param the ID of the component to delete
* */
deleteComponentById(id: string): void;
```

</details>

<details open markdown="block">
<summary>
Modifying a component
</summary>

```js
// Obtain the component ID and load the crafting system part dictionary beforehand
console.log(`Before deletion, CraftingSystem#hasComponent is ${craftingSystem.hasComponent(myComponentId)}`);
craftingSystem.deleteComponentById(myComponentId);
await game.fabricate.SystemRegistry.saveCraftingSystem(craftingSystem);
console.log(`After deletion, CraftingSystem#hasComponent is ${craftingSystem.hasComponent(myComponentId)}`);
```

</details>
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
component.isDisabled = true;
$loading = true;
await craftingComponentEditor.saveComponent(component, $selectedCraftingSystem);
const message = this._localizationService.format(
`${this._localizationPath}.component.disabled`,
const message = localization.format(
`${localizationPath}.component.disabled`,
{
componentName: component.name
}
Expand Down
3 changes: 2 additions & 1 deletion src/scripts/foundry/DocumentManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ class PendingFabricateItemData implements FabricateItemData {
}

toJson(): FabricateItemDataJson {
throw new Error("Pending item data should never be serialised. ");
console.warn("Serialising pending item data. The item UUID has not been checked and may not be valid. ");
return { uuid: this._itemUuid };
}

}
Expand Down
24 changes: 23 additions & 1 deletion src/scripts/system/ComponentDictionary.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import {Dictionary} from "./Dictionary";
import {CraftingComponent, CraftingComponentJson, SalvageOption, SalvageOptionJson} from "../common/CraftingComponent";
import {DocumentManager, FabricateItemData, NoFabricateItemData} from "../foundry/DocumentManager";
import {
DocumentManager,
FabricateItemData,
NoFabricateItemData
} from "../foundry/DocumentManager";
import {EssenceDictionary} from "./EssenceDictionary";
import {combinationFromRecord} from "./DictionaryUtils";
import {SelectableOptions} from "../common/SelectableOptions";
Expand Down Expand Up @@ -245,4 +249,22 @@ export class ComponentDictionary implements Dictionary<CraftingComponentJson, Cr
this.insert(component);
return component;
}

async mutate(id: string, mutation: CraftingComponentJson): Promise<CraftingComponent> {
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.getDocumentByUuid(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;
}

}
4 changes: 4 additions & 0 deletions src/scripts/system/CraftingSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ class CraftingSystem {
return this._partDictionary.insertComponent(component);
}

public async mutateComponent(id: string, mutation: CraftingComponentJson): Promise<CraftingComponent> {
return await this._partDictionary.mutateComponent(id, mutation);
}

public getRecipes(): Recipe[] {
return this._partDictionary.getRecipes();
}
Expand Down
15 changes: 13 additions & 2 deletions src/scripts/system/PartDictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ class PartDictionary {
this._componentDictionary.insert(craftingComponent);
}

async mutateComponent(id: string, mutation: CraftingComponentJson): Promise<CraftingComponent> {
return this._componentDictionary.mutate(id, mutation);
}

public insertRecipe(recipe: Recipe): void {
this._recipeDictionary.insert(recipe);
}
Expand Down Expand Up @@ -125,7 +129,7 @@ class PartDictionary {
await this._componentDictionary.loadAll();
await this._recipeDictionary.loadAll();
}

async loadEssences(updatedSource?: Record<string, EssenceJson>): Promise<void> {
if (updatedSource) {
this._essenceDictionary.sourceData = updatedSource;
Expand All @@ -148,6 +152,9 @@ class PartDictionary {
}

public toJson(): PartDictionaryJson {
if (!this.isLoaded) {
throw new Error("Fabricate currently requires that a part dictionary is loaded before it is serialized and saved. ");
}
const essences = this._essenceDictionary.toJson();
const components = this._componentDictionary.toJson();
const recipes = this._recipeDictionary.toJson();
Expand Down Expand Up @@ -193,6 +200,7 @@ class PartDictionary {
async createEssence(essenceJson: EssenceJson): Promise<Essence> {
return this._essenceDictionary.create(essenceJson);
}

}

class PartDictionaryFactory {
Expand All @@ -208,7 +216,10 @@ class PartDictionaryFactory {

make(sourceData: PartDictionaryJson): PartDictionary {
const documentManager = this._documentManager;
const essenceDictionary = new EssenceDictionary({sourceData: sourceData.essences, documentManager});
const essenceDictionary = new EssenceDictionary({
sourceData: sourceData.essences,
documentManager
});
const componentDictionary = new ComponentDictionary({
sourceData: sourceData.components,
documentManager,
Expand Down
3 changes: 2 additions & 1 deletion test/PartDictionary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {LoadedFabricateItemData} from "../src/scripts/foundry/DocumentManager";

describe("Create", () => {

test("Should construct empty Part Dictionary", () => {
test("Should construct empty Part Dictionary", async () => {
const underTest: PartDictionary = new PartDictionaryFactory({})
.make({
essences: {},
Expand All @@ -41,6 +41,7 @@ describe("Create", () => {
expect(underTest.hasComponent(id)).toBe(false);
expect(underTest.hasRecipe(id)).toBe(false);

await underTest.loadAll();
const asJson = underTest.toJson();
expect(Object.keys(asJson.essences).length).toEqual(0);
expect(Object.keys(asJson.recipes).length).toEqual(0);
Expand Down

0 comments on commit 5235be2

Please sign in to comment.