Material costs, recipes & unlock gating¶
How pieces cost materials, the three built-in material models, heterogeneous recipes, and gating pieces behind skills/magazines/research.
Per-piece cost: materialRequirement¶
The simplest cost is a single item type and count per piece:
{ spriteName = "walls_exterior_wooden_01_0", category = "wall", categoryGroup = "wall",
materialRequirement = { fullType = "Base.Plank", count = 2 } }
materialRequirement is { fullType?, tag?, count } - use tag to accept any item with a
build tag (e.g. { tag = "Plank", count = 2 }). The active material source consumes it.
Choosing a material source¶
Set materialSource on the def to pick how pieces are paid for. See
Concepts → Material sources for the full picture.
|
Consumes from… |
|---|---|
|
the player’s inventory, item by item. The everyday choice. |
|
a single container item holding per-piece counts (a “build kit”). |
|
a bag of variant containers (e.g. one pack per tent color). |
For anything custom, implement def.createMaterialSource(character, container, plan) and
return an object with canConsume / consume / refund / availableSummary / describe.
Heterogeneous recipes (materialRecipe)¶
When a piece costs several different things (and some are tools you keep), give it a
materialRecipe instead of a single materialRequirement. The
RecipeSource consumes it atomically - all or nothing:
{ spriteName = "walls_exterior_wooden_01_0", category = "wall", categoryGroup = "wall",
materialRecipe = {
{ fullType = "Base.Plank", count = 4 },
{ fullType = "Base.Nails", count = 8 },
{ tag = "Hammer", count = 1, keep = true }, -- needed, but not consumed
} }
keep = true means the item must be present but isn’t consumed (tools). You can drive a
recipe directly:
local ok, missing = RCSF.RecipeSource.hasAll(recipe, player)
if ok then RCSF.RecipeSource.consumeAtomic(recipe, player) end
Gate pieces behind skills, magazines, or research¶
Add unlockSources to a piece and the catalog hides it until the player qualifies. Any
matching source unlocks it (OR semantics within and across buckets):
{ spriteName = "walls_exterior_wooden_01_0", category = "wall", categoryGroup = "wall",
unlockSources = {
skill = { Woodwork = 4 }, -- Woodwork >= 4
magazines = { "Base.BookCarpentry3" }, -- OR has read this magazine
research = { itemFullType = "Base.WoodenWall" }, -- OR researched this
} }
Skill is checked directly against the player’s perk levels.
Magazine / research knowledge comes from a provider you register, so the framework doesn’t care how your mod tracks “known recipes”:
RCSF.PieceLibrary.addRecipeKnowledgeProvider("MyMod", function(player)
return player:getModData().MyMod_KnownRecipes -- table<string, boolean> | nil
end)
Multiple providers can be registered (their results OR together), so independent mods
contribute unlocks without clobbering each other. Build a stable research key with
RCSF.PieceLibrary.makeResearchKey(research) and write the same key into your KnownRecipes
table.
Important
Unlock checks must be re-run server-side on save/build - a client can lie about its perks/known recipes. The framework re-checks on the authoritative build path; if you build headlessly, do it server-side.
See also
Reference → API → PieceLibrary for isUnlockedFor,
addRecipeKnowledgeProvider, and makeResearchKey.