Skip to content
k-mods.com

Type: class

Generating classes of any kind from YAML, any parent (C++ or Blueprint), optional content registration via registerAs, and where the preset types stop and this escape hatch begins.

This type runs first in the Items stage at priority 50, ahead of unlock, item, resource, and building. Later generation types can therefore use a custom generated class as their parent, and recipe, schematic, and research can reference it. Changes load during session initialization, so restart after editing YAML. Its entries key is classes.

There is no default parent, so every entry must set parent: explicitly. There is no required base class either: every parent works, whether native C++ or Blueprint. Register-only mode (class:) is not supported; use id: instead, and register existing classes via Type: recipe, schematic, or research.

The preset types (item, resource, building, unlock, recipe, schematic, research) remain the recommended, ergonomic forms. They pick the right parent, stage priority, and registration for you. class is the escape hatch for everything else: equipment descriptors, vehicle descriptors, item and schematic categories, FGUnlock subclasses, custom classes from other mods gated behind hasMod, reparented buildables, and any other class kind KDataForge has no preset for.

type: class
debug: true # optional log every applied op (see Getting Started → Debugging)
classes:
- id: MyCustomThing
parent: /Script/FactoryGame.FGEquipmentDescriptor # ANY class: native C++ or /Game/...Thing_C
registerAs: recipe
# OPTIONAL: recipe | schematic | research queues SML content
# registration for kinds that need it; omit for everything else
properties:
- path: mDisplayName
value: My Equipment
# cdo-style op entries applied to the new class's CDO

Everything that holds for the preset generation types holds here too:

  • Same save-stable identity: package /KDataForge/Gen/<PackRef>, class Gen_<PackRef>_<Id>. Reference it elsewhere as /KDataForge/Gen/<PackRef>.Gen_<PackRef>_<Id>. The global manifest, tombstone reconstruction, and redirects: in pack.yml all apply.
  • Layout-copy constraint: the generated class copies the parent’s layout exactly, with no new reflected properties and no new components. All variation lives in the CDO defaults that properties: sets.
  • registerAs: is only for the three kinds SML’s UModContentRegistry tracks: recipe, schematic, and research. If you generate one of those through class instead of the preset type, set it so registration is queued. Everything else, such as descriptors, categories, and unlocks, just needs to exist and be referenced.

Example 1: an equipment descriptor from a C++ parent

Section titled “Example 1: an equipment descriptor from a C++ parent”
my-equipment.class.yml
type: class
classes:
- id: Desc_MyGadget
parent: /Script/FactoryGame.FGEquipmentDescriptor
properties:
- path: mDisplayName
value:
key: MyGadget_Name
ns: MyPack
source: Portable Gadget
- path: mStackSize
value: SS_ONE # equipment is conventionally non-stacking
- path: mEquipmentClass
value: /Game/FactoryGame/Equipment/JetPack/Equip_JetPack.Equip_JetPack_C

The result is a real UFGEquipmentDescriptor subclass, usable in recipes, schematics, and sinkpoints like any other descriptor. Because of the layout-copy constraint, mEquipmentClass has to point at an existing equipment actor, here the jetpack. You are re-parametrising existing equipment behaviour, not writing new equipment logic.

There is no preset type for categories, which makes them the textbook class use case:

my-category.class.yml
type: class
classes:
- id: Cat_MyPackParts
parent: /Script/FactoryGame.FGItemCategory
properties:
- path: mDisplayName
value:
key: Cat_MyPackParts_Name
ns: MyPack
source: MyPack Parts
- path: mMenuPriority
value: 999

Point generated items at it from the same pack. The Items stage guarantees the category class exists before any later stage references it. Within the stage, include: orders documents when one Items-stage document needs another:

type: item
include:
- my-category.class.yml # the category must generate before this item references it
items:
- id: MySuperGadgetPart
properties:
- path: mCategory
value: /KDataForge/Gen/MyPack.Gen_MyPack_Cat_MyPackParts

Example 3: a modded parent class, gated behind hasMod

Section titled “Example 3: a modded parent class, gated behind hasMod”

Any loaded mod’s classes work as parents. Gate the document so it is skipped cleanly (Info diagnostic) when the mod is missing:

ff-crop.class.yml
type: class
conditions:
hasMod: FicsitFarming
classes:
- id: Desc_MyCustomCrop
parent: /Game/FicsitFarming/Descriptors/Crops/Desc_Berry.Desc_Berry_C # Blueprint parent
properties:
- path: mDisplayName
value: My Custom Crop

registerAs: (generating a registered kind through class)

Section titled “registerAs: (generating a registered kind through class)”

If you generate a recipe, schematic, or research class via class, for example because you want a specific modded subclass as the parent, queue its registration with registerAs:. It does exactly what the preset type’s default register: true does:

type: class
classes:
- id: Recipe_MySpecial
parent: /Game/SomeMod/Recipes/Recipe_SpecialBase.Recipe_SpecialBase_C
registerAs: recipe # queued into UModContentRegistry before the freeze
properties:
- path: mDisplayName
value: My Special Recipe
  • “parent is required”: unlike the preset types, class has no default parent. Every entry needs an explicit parent:.
  • “My generated recipe/schematic never shows up”: you generated it via class without registerAs:. Only the preset types register automatically. class registers nothing unless you ask.
  • Wanting new components, meshes, or logic: not possible. See the layout-copy constraint above and the honest scope callout in A Complete Building.
  • Preset-type pages for the ergonomic forms: Type: item, Type: recipe, Type: schematic, Type: building.