Balancing
Editing examples for base-game balance, recipes, items, resources, equipment, consumables, schematics, research, categories, belts, and bulk edits by tag, producer building, ingredient, product, name pattern, or property value.
Everything below works through type: cdo documents. Every reflected property of every
FactoryGame class is addressable this way. Creating new content has its own page per type:
Item, Recipe,
Schematic, and friends. Start at
End-to-End Pack for the tied-together version.
Building and machine tuning lives in Power & Machines.
ℹ️ Verify property names and asset paths against your game version. The examples on this page use real FactoryGame properties and content paths, but game updates occasionally rename or move them. If a
target:path orpath:doesn’t resolve, nothing breaks, the load report tells you exactly which patch failed (/kdf report, see Getting Started → Troubleshooting). Confirm the effect in-game after every patch.
Recipes (UFGRecipe)
Section titled “Recipes (UFGRecipe)”Every craftable recipe in the game, whether base, alternate, or buildable, is backed by a UFGRecipe
class default object. This means rebalancing crafting time, ingredient ratios, or output without
replacing the recipe outright is just a cdo patch against that class. The properties worth
knowing are mDisplayName, mDisplayNameOverride, mIngredients, mProduct,
mManufactoringDuration, mManualManufacturingMultiplier, mProducedIn, mRelevantEvents,
mVariablePowerConsumptionConstant, mVariablePowerConsumptionFactor, and mGameplayTags.
The patch below halves iron ingot smelting time, doubles the ore cost, adds a bonus iron plate to the output, and unlocks the recipe in the Manufacturer as well as the Smelter:
type: cdopatches: - target: /Game/FactoryGame/Recipes/Smelter/Recipe_IngotIron.Recipe_IngotIron_C properties: - path: mManufactoringDuration op: multiply value: 0.5 - path: mIngredients[0].Amount value: 2 - path: mProduct op: append value: ItemClass: /Game/FactoryGame/Resource/Parts/IronPlate/Desc_IronPlate.Desc_IronPlate_C Amount: 1 - path: mProducedIn op: append value: /Game/FactoryGame/Buildable/Factory/ManufacturerMk1/Build_ManufacturerMk1.Build_ManufacturerMk1_CItems (UFGItemDescriptor)
Section titled “Items (UFGItemDescriptor)”UFGItemDescriptor is the base class behind every stackable item in the game. It drives how
the item stacks, what it burns for, how much radiation it emits, where it sorts in the build
menu, and its display text. Balancing an item’s inventory footprint or fuel value means patching
one of mDisplayName, mDescription, mAbbreviatedDisplayName, mStackSize, mForm,
mEnergyValue, mRadioactiveDecay, mCanBeDiscarded, mRememberPickUp, mResourceSinkPoints,
mSmallIcon, mPersistentBigIcon, mCategory, mSubCategories, mMenuPriority,
mFluidColor, mGasColor, mIsAlienItem, or mGameplayTags.
The example below bumps iron ingots to the largest stack bucket, rewrites its tooltip description, and doubles the AWESOME Sink points earned for scrapping it:
type: cdopatches: - target: /Game/FactoryGame/Resource/Parts/IronIngot/Desc_IronIngot.Desc_IronIngot_C properties: - path: mStackSize value: SS_HUGE # EStackSize enum: SS_ONE/SS_SMALL/SS_MEDIUM/SS_BIG/SS_HUGE/SS_FLUID - path: mDescription value: key: IronIngot_Desc ns: MyPack source: Now stacks higher. - path: mResourceSinkPoints op: multiply value: 2⚠️
mStackSizeis an enum, not a count.EStackSizehas exactly six buckets (SS_ONE…SS_HUGE, plusSS_FLUID). It isn’t “items per stack” as a number, so always set it by name. It’s also cached by the engine: writing it on the CDO alone would change nothing in-game (KBFL ships a dedicatedUKBFLCDOItemStackSizehelper for exactly this reason). KDataForge handles the cache automatically. Any op that writesmStackSizeon anFGItemDescriptor-derived target also resets the descriptor’s cached stack size (mCachedStackSize), so the example above works out of the box. Two things to know: inventories that already contain the item may need the item re-added (or a game reload) before existing slots reflect the new limit. Also, if you use KBFL’s own CDO assets for stack sizes, don’t patch the same descriptor from both sides. See YAML Schema → Special-cased properties.
Resources (UFGResourceDescriptor) & fluids/gases
Section titled “Resources (UFGResourceDescriptor) & fluids/gases”Raw resources, fluids, and gases extend the same UFGItemDescriptor chain but add a handful of
fields that only make sense for non-solid items: mForm selects solid/liquid/gas, and
mFluidColor/mGasColor drive the color used for pipe contents, tank fill levels, and the
resource icon in the UI. Recoloring a fluid or changing its form uses those fields directly:
type: cdopatches: - target: /Game/FactoryGame/Resource/RawResources/Water/Desc_Water.Desc_Water_C properties: - path: mFluidColor value: R: 20 G: 120 B: 255 A: 255 - path: mForm value: RF_LIQUIDEquipment & consumables (UFGEquipmentDescriptor, UFGConsumableDescriptor)
Section titled “Equipment & consumables (UFGEquipmentDescriptor, UFGConsumableDescriptor)”Descriptor subclasses inherit every UFGItemDescriptor property. Equipment adds, for example,
mEquipmentClass (soft class), while consumables add health and consume properties.
type: cdopatches: - target: /Game/FactoryGame/Resource/Equipment/Chainsaw/Desc_Chainsaw.Desc_Chainsaw_C properties: - path: mEnergyValue value: 0Removing radioactivity
Section titled “Removing radioactivity”mRadioactiveDecay on the item descriptor drives the radiation emitted by held or stored items.
set it to 0 on the nuclear items:
type: cdopatches: - target: /Game/FactoryGame/Resource/RawResources/OreUranium/Desc_OreUranium.Desc_OreUranium_C properties: - path: mRadioactiveDecay value: 0 - target: /Game/FactoryGame/Resource/Parts/NuclearWaste/Desc_NuclearWaste.Desc_NuclearWaste_C properties: - path: mRadioactiveDecay value: 0Repeat the patch entry for every radioactive descriptor you want to neutralise (uranium fuel rods, plutonium parts, …).
Conveyor belt speed: ALL belts at once
Section titled “Conveyor belt speed: ALL belts at once”Every belt mark derives from the native AFGBuildableConveyorBelt, so one subclass-expanded patch
covers Mk.1 through Mk.6 (and modded belts!):
type: cdopatches: - target: /Script/FactoryGame.FGBuildableConveyorBelt applyToSubclasses: true # Mk.1–Mk.6 + every modded belt class properties: - path: mSpeed op: multiply value: 2ℹ️
mSpeedis not items/min. It is stored in the engine’s internal unit (cm/s scaled by 2), so a Mk.1 belt’s “60 items/min” is not60here. Prefer relative ops (multiply) over absolutesetvalues, and verify the resulting throughput in-game.
Schematics (UFGSchematic)
Section titled “Schematics (UFGSchematic)”UFGSchematic backs every unlockable in the game: HUB milestones, MAM research nodes, and
AWESOME Shop entries alike. It’s the single class to patch whether you’re rebalancing
progression cost, research time, or which tier something unlocks at. The properties in play are
mType, mDisplayName, mDescription, mSchematicCategory, mSubCategories,
mMenuPriority, mTechTier, mCost, mTimeToComplete, mUnlocks, mSchematicIcon,
mSmallSchematicIcon, mSchematicDependencies, mHiddenUntilDependenciesMet, and mRelevantEvents.
The patch below shrinks a tutorial schematic’s completion time to a tenth, clears its cost array
so it’s free, and clamps its tech tier into the valid range from 0 to 9:
type: cdopatches: - target: /Game/FactoryGame/Schematics/Tutorial/Schematic_Tutorial1.Schematic_Tutorial1_C properties: - path: mTimeToComplete op: multiply value: 0.1 - path: mCost op: clear # free! - path: mTechTier op: clamp min: 0 max: 9Making a milestone free
Section titled “Making a milestone free”Same mechanism, applied to a HUB milestone: clear the cost array and set the cooldown to
(nearly) nothing:
type: cdopatches: - target: /Game/FactoryGame/Schematics/Progression/Schematic_1-1.Schematic_1-1_C properties: - path: mCost op: clear # no item cost - path: mTimeToComplete value: 1 # 1-second freighter turnaroundResearch / M.A.M. (UFGResearchTree)
Section titled “Research / M.A.M. (UFGResearchTree)”M.A.M. research trees are their own class, UFGResearchTree, separate from the schematic nodes
they unlock. This class supplies the tree’s own display name and grouping in the M.A.M. menu.
Renaming a tree, for a total-conversion pack or just to fix a typo, is a one-line patch against
that display name:
type: cdopatches: - target: /Game/FactoryGame/Schematics/Research/Mycelia_RS/ResearchTree_Mycelia.ResearchTree_Mycelia_C properties: - path: mDisplayName value: 'Mycelia (Rebalanced)'Categories (UFGItemCategory, UFGSchematicCategory)
Section titled “Categories (UFGItemCategory, UFGSchematicCategory)”Categories are the group headers shown above rows of items in the inventory and build menus, and
above schematic groups in the M.A.M. and AWESOME Shop. They hold nothing but display metadata, so
renaming one is the same mDisplayName patch as any other descriptor. Just target the category
asset instead of an item or schematic:
type: cdopatches: - target: /Game/FactoryGame/Resource/ItemCategories/Cat_Ingots.Cat_Ingots_C properties: - path: mDisplayName value: 'Metals'Vehicles (AFGWheeledVehicle)
Section titled “Vehicles (AFGWheeledVehicle)”UFGVehicleDescriptor (the buildable-menu entry) only points at the actual vehicle class, the
tunable stats live on AFGWheeledVehicle itself, same as any other actor CDO:
type: cdopatches: - target: /Game/FactoryGame/Buildable/Vehicle/Tractor/BP_Tractor.BP_Tractor_C properties: - path: mManualFuelConsumption op: multiply value: 0.75 # 25% less fuel while driven manually - path: mAutopilotFuelConsumption op: multiply value: 0.75 - path: mInventorySize value: 12applyToSubclasses: true on /Script/FactoryGame.FGWheeledVehicle tunes every vehicle (Tractor,
Truck, Explorer, Cyber Wagon, and modded vehicles) in one patch, same as the belt-speed example
above.
Trains (AFGLocomotive)
Section titled “Trains (AFGLocomotive)”The locomotive’s power draw is an FFloatInterval (idle/full-throttle range), not a flat number:
type: cdopatches: - target: /Game/FactoryGame/Buildable/Vehicle/Train/Locomotive/BP_Locomotive.BP_Locomotive_C properties: - path: mPowerConsumption value: Min: 5 Max: 80 # MW; vanilla is roughly Min 15 and Max 110There’s no separate top-level “max speed” stat on the locomotive to patch, speed on a given track segment is governed by curvature/signals, not a flat per-locomotive cap.
Creatures (AFGCreature, AFGCreatureDescriptor)
Section titled “Creatures (AFGCreature, AFGCreatureDescriptor)”Creature identity/UI lives on the lightweight UFGCreatureDescriptor (mostly just a pointer at the
creature class plus arachnophobia-mode icons); combat/movement stats live on AFGCreature itself
and its movement data, reflectable the same way. Move speed is a lookup table
(mMoveSpeedData: TArray<FMoveSpeedPair>, one entry per EMoveSpeed, MS_Walk/MS_Run/
MS_Sprint), so target the entry by index rather than a single flat property:
type: cdopatches: - target: /Game/FactoryGame/Character/Creature/Stinger/Char_Stinger.Char_Stinger_C properties: - path: mMoveSpeedData[2].Speed # index 2 = MS_Sprint in the base game's authored order op: multiply value: 0.75 # 25% slower at a sprintℹ️ Array index isn’t guaranteed by the enum value, confirm which index holds which
MoveSpeedTypefor your target creature (export it from the in-game editor, or check/kdf reportafter a dry patch) rather than assuming index order matchesEMoveSpeed’s declaration order.
⚠️ Spawner density fields are per-instance, not a class default.
AFGCreatureSpawner(mNumberOfCreatures,mCanSpawnDuringDay/mCanSpawnDuringNight,mRespawnTimeIndays) controls how many creatures a given spawner produces, but every spawner is a pre-placed level actor, its property values are baked into the map/save data at placement time, the same way an already-built building keeps its saved values after a CDO edit (see Power & Machines → Runtime actor patches). Acdopatch toAFGCreatureSpawnerchanges the class default, which only affects spawners that don’t already have their own saved override, in practice, treat this as unverified until you’ve confirmed it in-game on your target save, and prefer patching the creature class itself (as above) for stats that are meant to apply uniformly.
Bulk edits with the tag matcher
Section titled “Bulk edits with the tag matcher”Patching one target at a time doesn’t scale once you want “every alternate recipe” or “every
Tier 3+ schematic” to shift together. matchTag (with a required ofClass scope) finds every
CDO or asset of that class, including subclasses, that carries the given gameplay tag, then applies
the same ops to all of them in one patch entry. The tag container property is auto-detected, but
it can be pinned explicitly with tagProperty when a class exposes more than one.
The example below speeds up every recipe carrying the KMods.AlternateRecipe tag by 20%:
type: cdopatches: - matchTag: KMods.AlternateRecipe # every recipe carrying this tag … ofClass: /Script/FactoryGame.FGRecipe tagProperty: mGameplayTags properties: - path: mManufactoringDuration # … gets 20% faster op: multiply value: 0.8Buffing all alternate recipes (KAPI tags)
Section titled “Buffing all alternate recipes (KAPI tags)”KMods.AlternateRecipe is a KAPI-registered native tag, so gate the document behind hasMod.
Without KAPI, the tag does not exist and the document is skipped with an Info diagnostic:
type: cdoconditions: hasMod: KAPIpatches: - matchTag: KMods.AlternateRecipe ofClass: /Script/FactoryGame.FGRecipe properties: - path: mManufactoringDuration op: multiply value: 0.75 # all alternates 25% faster - path: mVariablePowerConsumptionFactor op: multiply value: 0.9Registering and matching your own tags works the same way. See Game Tags.
Bulk edits by producer building (producedIn)
Section titled “Bulk edits by producer building (producedIn)”Not everything you want to select carries a tag. The most common recipe question is “everything
crafted in machine X”, and producedIn: answers it directly. It defaults its scope to FGRecipe,
takes one class or a list, and a recipe matches when its mProducedIn references that class or a
subclass of it. Full class paths, bare class names, and generated-content ids all work:
type: cdopatches: # every Constructor recipe: 25% faster - producedIn: Build_ConstructorMk1_C properties: - path: mManufactoringDuration op: multiply value: 0.75
# every Assembler OR Manufacturer recipe: 10% less power (variable-power recipes) - producedIn: - /Game/FactoryGame/Buildable/Factory/AssemblerMk1/Build_AssemblerMk1.Build_AssemblerMk1_C - /Game/FactoryGame/Buildable/Factory/ManufacturerMk1/Build_ManufacturerMk1.Build_ManufacturerMk1_C properties: - path: mVariablePowerConsumptionFactor op: multiply value: 0.9The subclass rule turns the native base classes into families. Three are worth memorising:
producedIn: value | Selects every recipe crafted … |
|---|---|
/Script/FactoryGame.FGBuildableManufacturer | in any manufacturing machine: Constructor, Assembler, Manufacturer, Refinery, Blender, Packager, … and modded machines built on the same base |
/Script/FactoryGame.FGBuildGun | with the build gun, that is every buildable’s build recipe |
/Script/FactoryGame.FGWorkBench | by hand at the Craft Bench / Equipment Workshop (BP_WorkBenchComponent_C and friends derive from it) |
type: cdopatches: # hand-crafting is twice as fast, everywhere - producedIn: /Script/FactoryGame.FGWorkBench properties: - path: mManualManufacturingMultiplier op: multiply value: 0.5
# every buildable costs 20% less (rounded down by the game per ingredient) - producedIn: /Script/FactoryGame.FGBuildGun where: path: mIngredients isEmpty: false properties: - path: mIngredients[0].Amount op: multiply value: 0.8Bulk edits by ingredient or product
Section titled “Bulk edits by ingredient or product”ingredient: and product: follow the same rules for mIngredients and mProduct: an item class
(or a base class, to catch every item deriving from it), one or many, any-of. They stack with each
other and with producedIn:; every filter on the entry has to hold:
type: cdopatches: # everything that consumes Plastic or Rubber crafts 10% faster - ingredient: [Desc_Plastic_C, Desc_Rubber_C] properties: - path: mManufactoringDuration op: multiply value: 0.9
# every recipe producing Screws in an Assembler yields double - product: Desc_IronScrew_C producedIn: Build_AssemblerMk1_C properties: - path: mProduct[0].Amount op: multiply value: 2
# every recipe that produces ANY equipment (subclass rule on an item base class) - product: /Script/FactoryGame.FGEquipmentDescriptor properties: - path: mManufactoringDuration op: multiply value: 0.5mProduct[0].Amount addresses the first product. Recipes with several outputs need one op per
index; a where: clause such as path: mProduct[*].ItemClass / equals: Desc_IronScrew_C does
not tell an op which index matched.
Bulk edits by name pattern (matchName)
Section titled “Bulk edits by name pattern (matchName)”Vanilla content follows naming conventions, and matchName: lets you select on them without tags.
Patterns are case-insensitive wildcards; the _C suffix is optional, and a pattern containing /
is matched against the full class path so you can select by content folder:
type: cdopatches: # every vanilla alternate recipe, no KAPI dependency needed - ofClass: /Script/FactoryGame.FGRecipe matchName: Recipe_Alternate_* properties: - path: mManufactoringDuration op: multiply value: 0.8
# every milestone (Progression folder) researches twice as fast - ofClass: /Script/FactoryGame.FGSchematic matchName: /Game/FactoryGame/Schematics/Progression/* properties: - path: mTimeToComplete op: multiply value: 0.5Bulk edits by property value (where)
Section titled “Bulk edits by property value (where)”where: compares any reflected property of every candidate in an ofClass: scope. One map, or a
list of maps that must all hold; each map has a path: and exactly one operator (equals,
notEquals, lessThan, lessOrEqual, greaterThan, greaterOrEqual, contains, notContains,
matches, isEmpty). [*] in a path means “any element”:
type: cdopatches: # slow recipes (longer than 8 s) that use Iron Ingot get a bit faster - ofClass: /Script/FactoryGame.FGRecipe where: - path: mManufactoringDuration greaterThan: 8 - path: mIngredients[*].ItemClass equals: Desc_IronIngot_C properties: - path: mManufactoringDuration op: multiply value: 0.8
# every schematic up to tier 3 unlocks instantly - ofClass: /Script/FactoryGame.FGSchematic where: path: mTechTier lessOrEqual: 3 properties: - path: mTimeToComplete value: 0
# every liquid or gas: no sink points (`in` = any of the listed values; enums compare by name) - ofClass: /Script/FactoryGame.FGItemDescriptor where: path: mForm in: [RF_LIQUID, RF_GAS] properties: - path: mResourceSinkPoints value: 0
# items whose display name mentions "Nuclear" cannot be discarded - ofClass: /Script/FactoryGame.FGItemDescriptor where: path: mDisplayName matches: "*Nuclear*" properties: - path: mCanBeDiscarded value: false
# recipes with no producer at all become hand-craftable - ofClass: /Script/FactoryGame.FGRecipe where: path: mProducedIn isEmpty: true properties: - path: mProducedIn op: append value: /Game/FactoryGame/Buildable/-Shared/WorkBench/BP_WorkBenchComponent.BP_WorkBenchComponent_CReference-typed comparisons (equals/contains/in on a class or soft-class property) match the
class or any subclass, the same way producedIn: does, so path: mProducedIn / contains: /Script/FactoryGame.FGBuildableManufacturer is the long-hand spelling of producedIn:, and
path: mProducedIn / in: [Build_ConstructorMk1_C, Build_AssemblerMk1_C] of a two-entry
producedIn: list. The full operator table lives on
Targeting.
Removing content in bulk
Section titled “Removing content in bulk”The same filters drive remove:. Taking every Particle Accelerator recipe and every tier-8
milestone out of the game is two short documents; the mechanics, save impact, and the
recipe-plus-schematic pairing are on Removing Content:
type: reciperemove: - producedIn: Build_HadronCollider_Ctype: schematicremove: - where: path: mTechTier greaterOrEqual: 8