Type: schematic
Generating new schematics/milestones, registering existing ones, the instanced unlocks list that's the normal way to hook up a recipe, and gating a schematic on prerequisites.
A type: schematic document generates new schematic classes (milestones, MAM research, alt
recipes’ tech-tree entries, HUB upgrades) or registers an existing one. It works exactly like
Type: recipe does for recipes.
What makes schematics different is two sublists only they support: an inline unlocks: list and
an inline dependencies: list. unlocks: instances real FGUnlock subobjects directly onto the
schematic, and it’s how you actually wire a generated recipe to something the player can unlock.
dependencies: does the same for FGAvailabilityDependency subobjects, gating the schematic on
prerequisites.
This type runs in the Schematics stage, after Recipes, so a schematic can reference recipes
generated in the same pack. It does not support live-reload: changing it needs a restart. Its
entries key is schematics, its default parent is /Script/FactoryGame.FGSchematic, and its
required base is /Script/FactoryGame.FGSchematic or a subclass of it. Register-only mode
(class:) is supported, the same as for recipe. The unlocks: and dependencies: sublists are
supported only by schematic.
Worked example: generate + unlock a recipe
Section titled “Worked example: generate + unlock a recipe”A schematic entry uses the same generate-or-register duality as recipes: id: generates a brand
new class, and class: registers an existing one. properties: configures it with the usual op
grammar. Here a new milestone is generated with a display name, tech tier, cost, and a build time,
and its unlocks: list immediately hooks up the recipe generated elsewhere in the pack:
type: schematicschematics: - id: Schematic_MySuperIngot properties: - path: mDisplayName value: Super Ingot Smelting - path: mTechTier value: 2 - path: mTimeToComplete value: 60 - path: mCost value: - ItemClass: /Game/FactoryGame/Resource/Parts/IronPlate/Desc_IronPlate.Desc_IronPlate_C Amount: 50 unlocks: - class: /Game/FactoryGame/Unlocks/BP_UnlockRecipe.BP_UnlockRecipe_C properties: - path: mRecipes value: - /KDataForge/Gen/MyPack.Gen_MyPack_Recipe_MySuperIngotregister: true (the default) queues this into SML’s UModContentRegistry per-world before the
freeze. Importantly, it auto-propagates: registering the schematic also registers
Recipe_MySuperIngot and every item that recipe touches. You almost never need to register a
recipe or item separately once its owning schematic is registered.
Instanced unlocks:, the normal way to wire up a recipe
Section titled “Instanced unlocks:, the normal way to wire up a recipe”Under the hood, each entry in unlocks: resolves the schematic CDO’s mUnlocks array by
reflection. It instantiates the given class: as a subobject of that CDO, then applies
properties: to that new instance before appending it. This is the exact same mechanism vanilla
schematics use to hold their unlocks, just driven from YAML instead of a Blueprint graph. A
schematic can carry more than one unlock, for example a recipe unlock alongside an inventory-slot
unlock:
unlocks: - class: /Game/FactoryGame/Unlocks/BP_UnlockRecipe.BP_UnlockRecipe_C # any concrete FGUnlock subclass properties: - path: mRecipes value: - /KDataForge/Gen/MyPack.Gen_MyPack_Recipe_MySuperIngot - class: /Script/FactoryGame.FGUnlockInventorySlot properties: - path: mNumInventorySlotsToUnlock value: 1class: here must resolve to an FGUnlock subclass, either native, Blueprint, or a class you
generated with Unlocks. Known native FactoryGame/KMods
paths automatically remap to their Blueprint implementation before instancing, so
/Script/FactoryGame.FGUnlockRecipe and BP_UnlockRecipe_C are equivalent. properties: then configures
that specific instance. This is what you want for a one-off unlock tied to a single schematic.
Reach for a standalone type: unlock document only if you want to reuse the exact same
preconfigured unlock class across several schematics: see
Unlocks for that narrower case.
Only FGUnlockRecipe and FGUnlockInventorySlot are shown above, but the mechanism works with
any concrete FGUnlock subclass the base game ships (scanner unlocks, emotes, customizer
options, Central Storage upgrades, …). See the
Base-Game Unlock Classes for the full list with every
class’s settable properties.
For a repeatable schematic with a purchase cap, level-dependent rewards and costs, and generated
cost/reward curves, see the complete
UKLUnlockMaxRepeatPurchases example.
Shortcut unlocks
Section titled “Shortcut unlocks”For the common unlocks, schematic also accepts compact fields alongside (not instead of) the
fully generic unlocks: list. They still create normal engine FGUnlock instances in mUnlocks,
so they work with generated content, vanilla classes, and other mods exactly as a hand-written
unlock does:
schematics: - id: Schematic_Convenience recipes: - Recipe_MySuperIngot - /Game/FactoryGame/Recipes/Smelter/Recipe_IngotIron.Recipe_IngotIron_C schematics: - /Game/FactoryGame/Schematics/Progression/Schematic_2-3.Schematic_2-3_C handSlots: 1 inventorySlots: 3 scannableResources: - resource: /Game/FactoryGame/Resource/RawResources/Bauxite/Desc_OreBauxite.Desc_OreBauxite_C nodeType: Node # optional; Node is the defaultrecipes: accepts only UFGRecipe classes and produces one FGUnlockRecipe; schematics:
accepts only UFGSchematic classes and produces one FGUnlockSchematic. handSlots: and
inventorySlots: are positive integers and produce the matching arm-equipment/inventory unlock.
Each scannableResources: entry takes a resource descriptor class and optional nodeType: (Node,
FrackingSatellite, FrackingCore, Geyser, or Deposit) and produces an
FGUnlockScannableResource. Use unlocks: whenever a shortcut does not cover the unlock type or
when you need custom properties.
Unlocking resource scanner visibility
Section titled “Unlocking resource scanner visibility”FGUnlockScannableResource adds a resource descriptor and node-type pair to the Object Scanner.
It does not create or place a Resource Node:
unlocks: - class: /Game/FactoryGame/Unlocks/BP_UnlockScannableResource.BP_UnlockScannableResource_C properties: - path: mResourcePairsToAddToScanner value: - ResourceDescriptor: MyCustomOre ResourceNodeType: NodeResourceNodeType accepts Node, FrackingSatellite, FrackingCore, Geyser, or Deposit.
See Resources and Resource Nodes for descriptor
generation, node actor patches, and placement limits.
Gating a schematic on prerequisites
Section titled “Gating a schematic on prerequisites”dependencies: is the mirror image of unlocks:, using the exact same instanced-object mechanism.
Each entry creates a real instanced FGAvailabilityDependency subobject directly on the
schematic’s CDO, appended to mSchematicDependencies:
type: schematicschematics: - id: Schematic_AdvancedSuperIngot properties: - path: mDisplayName value: Advanced Super Ingot dependencies: - class: /Script/FactoryGame.FGSchematicPurchasedDependency properties: - path: mSchematics value: - /KDataForge/Gen/MyPack.Gen_MyPack_Schematic_MySuperIngot - path: mRequireAllSchematicsToBePurchased value: trueThat schematic won’t be purchasable until Schematic_MySuperIngot is. Dependency entries use the
same native-to-Blueprint remapping as unlock entries. See
Availability Dependency Classes for every
native and Blueprint class, abstract state, path, and field. Common choices:
FGSchematicNotPurchasedDependency: inverse ofFGSchematicPurchasedDependencyFGItemPickedUpDependency: requires specific items to have been picked upFGResearchTreeProgressionDependency: requires progress in a M.A.M. research treeFGGamePhaseReachedDependency: requires a minimum Space Elevator game phase
Two flags on the schematic itself control what happens while dependencies aren’t met:
mDependenciesBlocksSchematicAccess (block purchase, but don’t hide the entry) and
mHiddenUntilDependenciesMet (hide the schematic entirely until then). Set them via ordinary
properties: alongside dependencies:.
Making a milestone (schematic) free
Section titled “Making a milestone (schematic) free”Because properties: is ordinary op grammar, the usual tricks apply to an existing, registered
schematic too. clear wipes an array, and a plain numeric value: overrides a duration without
having to compute a relative offset:
type: schematicschematics: - class: /Game/FactoryGame/Schematics/Progression/Schematic_1-1.Schematic_1-1_C # register-only: editing a vanilla milestone properties: - path: mCost op: clear # no item cost - path: mTimeToComplete value: 1 # 1-second freighter turnaround(This particular example edits an existing vanilla schematic. For a genuinely simpler one-off
tweak like this, a plain cdo patch with target: works identically and doesn’t need the
register-only framing at all. See
Balancing → Making a milestone free.
Using class: here is only meaningful when you also want SML registration semantics applied, which
a vanilla schematic that’s already registered doesn’t need.)
Removing schematics
Section titled “Removing schematics”A type: schematic document accepts a top-level remove: sequence. Each entry is either a bare
class reference (a full Unreal class path, a bare loaded class name, or a generated content id from
this or another loaded KDataForge pack) or a filter map that removes every schematic passing the
scope filters
(matchName:, matchTag:, where:; the scope defaults to FGSchematic). Removal-only documents
are valid, and remove: can also be mixed with normal schematics: entries:
type: schematicremove: - /Game/FactoryGame/Schematics/Progression/Schematic_1-1.Schematic_1-1_C - Schematic_MyGeneratedMilestone - where: # every tier 8+ schematic path: mTechTier greaterOrEqual: 8
schematics: - id: Schematic_ReplacementMilestone properties: - path: mDisplayName value: Replacement MilestoneA bare reference names the exact schematic class to remove; it does not remove subclasses. If a
generated id is ambiguous across packs, KDataForge logs the same ambiguity diagnostic used for
other short-id class references. Removals run after registrations, so an explicit remove: wins
over a register: for the same class. The consolidated guide is
Removing Content.
Schematic removal is save-clean. KDataForge removes the class from SML’s content registry before the schematic manager builds its per-session list, so uninstalling the pack brings the schematic back on the next load.
Removal does not revoke purchases. An already-purchased schematic stays in the save and keeps
re-applying its unlocks on load, including recipes it granted. To actually take away craftable
content, pair schematic removal with
recipe remove:.
Troubleshooting
Section titled “Troubleshooting”- The schematic shows up but the recipe never unlocks: check that the
unlocks:entry’sclass:actually resolves (/kdf reportwill show a failed patch if it doesn’t) and thatmRecipespoints at the exact generated class path, with no_Csuffix. - Two mods both try to unlock the same recipe: that’s fine.
FGUnlockRecipeinstances are additive, and duplicate unlocks of an already-unlocked recipe are a no-op in the base game. - Want to unlock a recipe from a schematic you didn’t generate (vanilla, another mod’s, or your
own from an earlier pack):
unlocks:works in register-only (class:) mode too, it’s just easy to miss. See Adding a Recipe to an Existing Schematic. - A schematic with
dependencies:is purchasable even though its prerequisite isn’t met:dependencies:alone only registers the check; you also needmDependenciesBlocksSchematicAccess: true(block purchase until met) and/ormHiddenUntilDependenciesMet: true(hide the entry entirely) as ordinaryproperties:on the same schematic. - Full worked pack: End-to-End Pack ties an item + recipe + this schematic + sink points + localization together in one folder.
