Gates
A gate is a condition that decides whether a patch runs against a given asset. Where :Named filters by id, gates filter by the asset's actual contents or by some external condition. When a gate rejects an asset, the patch is skipped for that asset and the skip is recorded in the summary, so you can see why nothing happened instead of guessing.
There are three gates: :Requires, :Has, and :HasNo.
Requires: an arbitrary condition
:Requires(predicate, message) takes a function that receives the asset and returns true to allow the patch or false to skip it. The optional message is logged when it skips.
PM.Parts:Patch("BuffHeavyEngines")
:Requires(|part| part.mass > 5.0, "part is not heavy enough")
:Do(function(part) ... end)The predicate runs per asset, so this patch applies only to the parts that pass.
:Requires also accepts a plain boolean instead of a function, which gates the whole patch at once. PatchManager calculates the boolean one time, when your script registers. During registration, no feature set is active, so PM:Loaded counts each installed mod as on. Use the boolean for facts about the install, for example whether a different mod is installed:
PM.Parts:Patch("MatchEnginePackBalance")
:Requires(PM:Loaded("EnginePack"), "EnginePack is not installed")
:Do(function(part) ... end)When the boolean is false, the patch is skipped entirely and the reason shows up in the summary.
To turn a patch on or off for each campaign, use a feature with :Feature, not a boolean. A boolean from a config value does not work for this, because settings never cause a rebuild of the patch cache. To run a patch only in campaigns that have a different mod on, use :Needs, not PM:Loaded.
Has: the asset must expose a key
:Has(key, predicate, message) skips the asset unless it has the named key. This is how you guard a patch that reads a field that might not be present, so you do not error on the assets that lack it:
PM.Parts:Patch("RetuneEngines")
:Has("Module_Engine")
:Do(function(part) ... end)The predicate and message are optional. If you pass a predicate, it runs against the value at that key, not the whole asset, and the asset is allowed only if the predicate also passes:
:Has("maxTemp", |value| value < 2000, "already heat-tolerant")So this reads as "the asset must have maxTemp, and that maxTemp must be under 2000."
HasNo: the asset must not expose a key
:HasNo(key, message) is the inverse. It skips the asset if the key is present. Use it to avoid adding something that is already there, or to target only the assets missing a piece:
PM.Parts:Patch("AddDefaultDrag")
:HasNo("Module_Drag", "already has drag")
:Do(function(part) ... end)Gates versus doing it in the callback
You could write any of these as an if inside the Do callback and simply return early. The difference is reporting. A gate records a named skip in the summary, so when a patch does not run you can see exactly which gate rejected which asset and why. An early return inside the callback is invisible. Prefer gates for conditions that decide whether the patch should run at all, and save callback logic for shaping the change once it does.