CraftingEngine
Executes the crafting pipeline. It validates ingredients, runs checks, consumes items, and creates results.
Access: game.fabricate.getCraftingEngine()
Methods
craft(craftingActor, componentSourceActors, recipe, ingredientSetId, options)
The main crafting method. Runs the full pipeline for a single step.
| Parameter | Type | Description |
|---|---|---|
craftingActor | Actor | The actor receiving crafted items |
componentSourceActors | Actor[] | Actors supplying ingredients |
recipe | Recipe | The recipe to craft |
ingredientSetId | string \| null | Which ingredient set to use. null auto-selects the first satisfiable set |
options | object | Optional: { runId, resultGroupId } |
Returns: Promise<{ success: boolean, results: Item[], message: string }>
Pipeline Steps
When craft() is called, the engine:
- Validates ingredients. Checks all groups in the selected ingredient set are satisfiable.
- Validates tools. Checks all required tools (resolved from
toolIds) are present in the source actors’ inventories and pass their requirement. - Validates essences. If essences are enabled, checks essence requirements.
- Runs crafting check. If enabled, executes the check (built-in or macro) and interprets the result per the resolution mode. See Crafting Checks for both modes.
- Applies failure consumption policy. If the check fails, consumes ingredients and/or applies tool breakage according to
craftingCheck.consumptionsettings. By default, ingredients are consumed (consumeIngredientsOnFail: true) and tools are not broken (consumeCatalystsOnFail: false, field name retained for backward compatibility). See Consumption on Failure. - Runs failure macro. If the check failed (step 4) or check-result validation failed (step 5), calls
system.craftingCheck.failureMacroUuidwith the failure context. See Crafting Checks. Macro errors are caught and logged. They do not affect the returned result. - Resolves result groups. Determines which result group(s) to create based on mode and check result.
- Consumes ingredients. Removes consumed items from source actors.
- Applies tool breakage. Runs each tool’s breakage mechanic (
limitedUses/breakageChance/diceExpression) and its on-break action, recordingusedToolsevidence. - Creates results. Creates new items on the crafting actor.
- Applies property macros. If enabled, runs property macros on created items.
- Transfers effects. If
system.features.essences,system.features.effectTransfer, andrecipe.transferEffectsare alltrue, collects active effects from thesourceItemUuidof each contributing essence definition and copies them to the result item. See Effect Transfer. - Runs success macro. Calls
system.craftingCheck.successMacroUuidwith the full success context. See Crafting Checks. Macro errors are caught and logged. The crafting result is still returned as a success.
Steps 5 and 6 only execute when the crafting check returns a failure result or check-result validation fails. Pre-check failures (missing ingredients, missing or unsatisfied tools, invalid recipe, missing actor) return immediately without consuming anything and without calling either macro.
Crafting Check Execution
Step 4 of the pipeline dispatches to one of two execution paths based on system.craftingCheck.checkSource.
Built-In Check Branch (checkSource: "builtIn")
When checkSource is "builtIn", the engine looks up a CraftingCheckAdapter for the current game system via CraftingCheckAdapterRegistry. The adapter calls the game system’s native dice API (actor.rollSkill() or actor.rollAbilityCheck() for D&D 5e) using the fields in system.craftingCheck.builtIn.
If no adapter is registered for the current game system, the engine returns immediately with success: false and the message:
No system adapter available for built-in checks. Switch to macro mode or install a compatible game system.
Nothing is consumed and no macros are called in that case.
If the adapter throws, the engine catches the error, logs it with Fabricate | Built-in crafting check failed, and returns success: false. The failure consumption policy (step 5) then applies.
The builtIn configuration object:
| Field | Type | Default | Description |
|---|---|---|---|
ability | string | "" | Ability key (e.g. "int", "wis") |
skill | string | "" | Skill key (e.g. "arc", "nat"). Takes precedence over ability when set. |
dc | number | 15 | Difficulty class. Roll total must meet or exceed this value to succeed. |
advantage | string | "normal" | "advantage", "disadvantage", or "normal" |
Macro Check Branch (checkSource: "macro")
When checkSource is "macro" (the default), the engine executes the macro at system.craftingCheck.macroUuid. If no macroUuid is configured and the resolution mode requires a check (routed macroOutcome provider or progressive), the engine returns success: false.
See Macros for the full macro contract and context shape.
CraftingCheckAdapter Interface
The adapter layer allows any game system to support built-in checks. A built-in D&D 5e adapter (Dnd5eCraftingCheckAdapter) is registered automatically when the dnd5e game system is active.
Custom adapters must implement three methods:
class MyCraftingCheckAdapter extends CraftingCheckAdapter {
constructor() { super('my-system-id'); }
/** @returns {Array<{key: string, label: string}>} */
getAbilities() { /* ... */ }
/** @returns {Array<{key: string, label: string}>} */
getSkills() { /* ... */ }
/**
* @param {Actor} actor
* @param config
* @returns {Promise<{ success: boolean, outcome: string|null, value: number|null, data: object }>}
*/
async executeCheck(actor, config) { /* ... */ }
}
Register a custom adapter during module initialisation:
Hooks.once('fabricate.ready', () => {
CraftingCheckAdapterRegistry.register('my-system-id', MyCraftingCheckAdapter);
});
See Registering a Custom Adapter for a worked example.
Success and Failure Macros
Configure macros to react to crafting outcomes without modifying the pipeline itself. Both macros are configured on the crafting system, not on individual recipes.
| Setting | Path | Description |
|---|---|---|
| Success macro | system.craftingCheck.successMacroUuid | UUID of the macro to run after a successful step |
| Failure macro | system.craftingCheck.failureMacroUuid | UUID of the macro to run after a failed step |
Both fields are optional. When absent or null, no macro is executed for that outcome. If a macro throws, Fabricate catches the error, logs it to the browser console with Fabricate |, and continues. The craft() return value is not changed.
See the full context shapes in Macros & Examples.
Effect Transfer Gating
Effect transfer in step 12 is controlled by an essence-based pipeline and requires all three of the following flags to be set:
system.features.essences === true. The essences system must be enabled. Effect transfer is built on top of essence definitions and requires this feature to resolve source items.system.features.effectTransfer === true. The GM opts the system in via the Features card in the Crafting Admin panel.recipe.transferEffects === true. The recipe author opts this specific recipe in.
If any of these flags is false, _transferEffects is not called and no effects are copied.
How effects are collected. When all three flags are met, the engine determines which essence IDs were contributed by the resolved ingredients, then for each contributing essence looks up its EssenceDefinition in system.essenceDefinitions. If the definition has a sourceItemUuid, that item is fetched via fromUuid() and its active effects are collected. All collected effects are applied to the created result item in a single createEmbeddedDocuments('ActiveEffect', ...) call. Essence definitions with no sourceItemUuid, or whose UUID no longer resolves, are silently skipped.
The old ingredient-level
extractEffects/effectFilterapproach has been removed. SettingextractEffects: trueon an ingredient has no effect. Configure effect transfer through essence definitions and theirsourceItemUuidfield instead.
// This recipe will transfer effects when the system has
// features.essences: true AND features.effectTransfer: true.
const recipe = {
transferEffects: true,
// ...other recipe fields
};
// This recipe will NOT transfer effects regardless of system settings.
const nonTransferRecipe = {
transferEffects: false,
// ...other recipe fields
};
Essence definitions drive which effects are transferred. If the resolved ingredients contribute a “fire” essence, and the system has a Fire essence definition with a sourceItemUuid pointing to a “Flame Shard” item, all active effects on that Flame Shard are copied to the result. See Essences for how to configure essence definitions with source items.
Example
const engine = game.fabricate.getCraftingEngine();
const rm = game.fabricate.getRecipeManager();
const recipe = rm.getRecipe('my-recipe-id');
const actor = game.user.character;
const result = await engine.craft(
actor, // crafting actor
[actor], // ingredient sources
recipe, // recipe
null, // auto-select ingredient set
{} // no special options
);
if (result.success) {
console.log(`Crafted: ${result.results.map(i => i.name).join(', ')}`);
} else {
console.log(`Failed: ${result.message}`);
}
Using the Quick Helper
For simpler cases, use the top-level craft() method.
const result = await game.fabricate.craft(actor, 'recipe-id', {
componentSourceActors: [actor, partyChest]
});
Or from a macro:
await fabricate.craft(game.user.character, 'recipe-id');