Creating assets
Patching edits content that already exists. Sometimes you want to add content that does not: a new resource, a new moon, a new mission step. That is a separate operation. Instead of registering a patch against existing assets, you queue a brand new asset for creation.
Two ways to create
There are two different ways to create an asset from scratch, and which one you use depends on the kind of content. A third, copying an asset that already exists, is covered below.
A new top-level asset
For a standalone asset under a label, the typed modules give you a New method. The resource module, for instance, has NewResource and NewRecipe. The generic JSON API has PM.JSON:New. You give the new asset a name (its globally unique address) and a callback that fills it in:
PM.JSON:New("my_label", "MyNewThing", function(value)
value.someField = 1
value.anotherField = "hello"
end)The name must be unique across everything under that label, since it is the address the game and other patches use to find your asset. Once created, your new asset is a normal asset: other patches (yours or other mods') can target it by that name.
The creation helpers return the queued asset. The queued asset accepts :Feature and :NoFeature, the same as a patch. Thus, a new asset can belong to one of your features:
PM.JSON:New("my_label", "MyHardModeThing", function(value)
value.someField = 2
end):Feature("HardMode")A new entry inside an existing asset
Some content is not a top-level asset but a member of a container that already exists. A new celestial body is added to the galaxy, not dropped in as a loose asset. For these you patch the container and call an Add helper on it. This is how Redux adds the moon Drast to Dres:
PM.Planets:PatchDefaultGalaxy("AddDrast"):Do(function(galaxy)
galaxy:Add("Drast", function(body)
body.PrefabKey = "Celestial.Drast.Local"
body.referenceBodyGuid = "Dres"
body.OrbitProperties = {
referenceBodyGuid = "Dres",
inclination = 0,
eccentricity = 0,
semiMajorAxis = 434400,
longitudeOfAscendingNode = 0,
argumentOfPeriapsis = 0,
meanAnomalyAtEpoch = 0,
epoch = 0,
}
end)
end)This is a normal patch (PatchDefaultGalaxy against the galaxy), and inside the callback galaxy:Add("Drast", ...) adds a new body. The Add callback fills in the new body the same way a Do callback fills in an existing one.
Filling in a new asset
A new asset starts close to empty, so you usually set more fields than you would when editing. The callback receives the new value, and you build it up with plain assignments and tables, exactly as in the Do callback:
body.OrbiterProperties = {
orbitColor = { r = 0.86, g = 0.38, b = 0.18, a = 0.03 },
autoTextureOffset = false,
textureOffset = 0,
}The hard part of creating content is rarely the PatchManager call. It is knowing which fields the game needs for the content to work. The best reference is an existing asset of the same kind: find a stock body, resource, or mission and copy its form, then change what you need. The typed module pages call out the load-bearing fields for each kind of content.
The patch cache is the easiest place to read an existing asset's form. After a run, the cache holds one archive per label with the final JSON of every asset in it, so opening the archive for the kind of content you are creating (parts are under the parts_data label) and reading a similar asset's entry shows you exactly which fields it sets and how. Copy that as your starting point. See Inspecting the cache for the location of the archive of each label.
Copying an existing asset
Often the easiest new asset is a copy of an old one. Every typed module has a Duplicate method (PM.Parts:Duplicate, PM.Planets:Duplicate, PM.Missions:Duplicate, and so on), and PM.JSON:Duplicate(label, source, newName) covers any label. It copies each asset it matches into a new asset under the same label, and the chained :Do callback runs on the copy:
PM.Parts:Duplicate("fueltank_1v_inline_methalox_1x2", "fueltank_1v_inline_methalox_1x2_heavy")
:Do(function(part)
part.partName = "fueltank_1v_inline_methalox_1x2_heavy"
part.PrefabAddress = "fueltank_1v_inline_methalox_1x2.prefab"
part.IconAddress = "fueltank_1v_inline_methalox_1x2_icon.png"
part.mass = part.mass * 1.5
end)sourcesupports*and?wildcards, so one call can copy many assets.newNamenames each copy, with{name}standing in for the source's name.:Dois optional. Without one the copy is unchanged. Returning"remove"from it drops that copy.Each
Duplicatemethod also takes the callback as an optional last argument, as shorthand for:Do. Chaining:Dois the canonical form.A duplicate is an ordinary patch, so it chains like one. Gates such as
:Hasdecide which sources get copied at all,:Featuredecides which campaigns get the copy, and the pass and ordering methods decide when the copy is made. Patches ordered after the duplicate also run against the copy, unless they target the source by name.
The copy keeps the source's internal ids (partName, bodyName, a mission's id). If you do not give the copy a new id, it defines the same part or body as its source. The game then sees two definitions of one item. It keeps the first and logs an error. When you copy an asset to make a new asset, set the new id in the callback. The example above sets partName.
The game finds the model and icon of a part by its name: <partName>.prefab and <partName>_icon.png. A copied part with a new name has no model or icon at these addresses. Thus, set PrefabAddress and IconAddress to the model and icon of the original part, as in the example above. If your mod ships its own prefab and icon with the new name, do not set them.
To change an existing asset for some campaigns only, do not copy it. Patch it in place under a feature. Campaigns without the feature keep the original.
When the typed module has no helper
If there is no New, Add or Duplicate for what you want to create, drop to the generic JSON API and PM.JSON:New with the right label. You will have to spell the asset out as raw JSON, but it always works.
And also if you think a typed module would help, reach out to the Redux team or the author of the mod you are patching and ask them. :3