Conditions

A condition decides when a mission advances: when a branch transitions to a new stage, or when a stage counts as complete. Conditions can be simple (a watched value crossed a threshold, an event fired) or compound (this and that, either of these, not that). The missions module gives you builders for the compound case so the logic stays readable.

The builders

Three methods compose conditions:

  • PM.Missions:And(...) is true when every condition passed is true.

  • PM.Missions:Or(...) is true when at least one is true.

  • PM.Missions:Not(condition) inverts a single condition.

Each returns a condition you can pass to another builder or assign onto a branch or stage. They nest, so you can build a tree:

local condition = PM.Missions:And(
    reachedOrbit,
    PM.Missions:Or(hasCrew, isProbe)
)

This reads as "reached orbit, and either has crew or is a probe."

The leaf conditions

The leaves of the tree (the actual reachedOrbit, hasCrew values above) are the game's concrete condition types: a property condition that compares a watched value, an event condition that waits for a game event, or a script condition. These are JSON objects with a ConditionType discriminator. The builders combine them. You usually obtain a leaf by copying the form from an existing mission and adjusting it, since the exact fields depend on the condition type. The patch cache holds those missions' final JSON, which is the easiest place to read a real condition's form to copy.

The two you will use most are property conditions and event conditions. The watcher and message names they reference come from the game. The stub pm-missions.d.lua lists them, and an existing mission shows real ones in use.

Property conditions

A property condition watches a single game value and is true when that value compares a certain way against a target you set. The value comes from a property watcher: a small game object that knows how to read one thing, like an apoapsis, a crew count, or whether a vessel is landed. You name the watcher, the comparison, and the value to test against.

You do not write the watcher's full type name by hand. PM.Missions:GetPropertyWatcher(name) takes the watcher's short name and returns the fully-qualified type string the condition's PropertyTypeAQN field expects. (The AQN suffix on these field names means assembly-qualified name, the long precise form of an internal type name. You never type one yourself, the helpers produce them.)

A property condition is a plain JSON table with ConditionType = "PropertyCondition":

local reachedHighOrbit = {
    ConditionType = "PropertyCondition",
    -- watchers are named by their class, like this real apoapsis-altitude watcher
    PropertyTypeAQN = PM.Missions:GetPropertyWatcher("VehicleApoapsisAltitudePropertyWatcher"),
    propOperator = "GREATER",
    TestWatchedValue = 100000,
    RequireCurrentValue = false,
}
  • PropertyTypeAQN is the watcher, from GetPropertyWatcher.

  • propOperator is the comparison: "GREATER", "LESSER", or "EQUAL".

  • The test value goes in the field that matches the watcher's type: TestWatchedValue for a number, TestWatchedInt for an integer or enum, TestWatchedstring for a string. A boolean watcher needs no test value, the condition is simply true while the watched bool is true.

  • RequireCurrentValue decides whether the condition latches. With false, once the comparison has been true even for a moment the condition stays satisfied. With true, it has to be true at the instant it is checked. Use false for "reached high orbit at some point", true for "is in high orbit right now".

Event conditions

An event condition waits for something to happen rather than testing a value. It is true once a particular game message has fired at least once. Messages are the game's events: a vessel landed after flight, a part was decoupled, a sphere of influence was entered.

As with watchers, you do not write the message's type name by hand. PM.Missions:GetMessage(name) turns a short message name into the fully-qualified type string the condition's EventTypeAQN field expects.

local landedAfterFlight = {
    ConditionType = "EventCondition",
    -- messages are named by their class
    EventTypeAQN = PM.Missions:GetMessage("VesselLandedAfterFlight"),
}

That is the whole condition: there is no value to compare, only an event to wait for. Once the named message fires while the condition is active, it latches to true. (An optional inputString field narrows it to messages carrying a matching tag, for the messages that are parameterized. Most conditions leave it out.)

Both kinds are leaves. Drop them straight onto a branch or stage, or feed them into And / Or / Not to build something larger.

Where conditions go

A built condition is assigned where the mission expects one:

  • On a mission branch, as the condition that triggers the transition to its target stage.

  • On a stage, as its scriptableCondition.

So the typical flow is: build the leaves, combine them with And / Or / Not, and assign the result onto the branch or stage you are patching.

When to use this

If you are tightening or loosening an existing mission's logic, you are often editing the condition already on a branch rather than building one from scratch. If you are adding a new branch or stage, you build the condition with these builders. Either way, the builders keep a multi-part condition readable instead of a hand-nested pile of JSON. The full leaf-condition field forms are in pm-missions.d.lua.