# Code modding guides

These guides are for mods that ship compiled C# code.

* [Custom Launch Locations](/doc/custom-launch-locations-cYFJeT82fg) moves a launch pad to a different body.
* [Subscribe to game Messages](/doc/subscribe-to-game-messages-XxQpsmHaKL) shows how to react to game events.
* [Transform System Guide](/doc/transform-system-guide-jIJDOhFWoM) explains the coordinate frames of the game.

For C# patches to game content, see [Patching from C#](/doc/patching-from-c-729VnFfTeV).

## Campaign features in a C# mod

Each campaign uses a feature set of [campaign features](/doc/campaign-features-9YIewcIArR). Each mod has a default feature, `ModId:ModId`. This feature is on when the mod is on in that campaign.

A mod that is off in the loaded campaign is still installed:

* Its assembly loads.
* The `OnPreInitialized`, `OnInitialized` and `OnPostInitialized` methods of its plugin run as usual.

For a mod that is off, Redux disables these items:

* patches
* content assets (see [Loading assets](#loading-assets))
* campaign hooks
* campaign settings
* app bar buttons

Code that your mod runs directly continues to run. Examples are Harmony patches, message subscriptions and UI that your mod builds directly. Your code must check if the mod is on.

The plugin base classes `GeneralMod`, `MonoBehaviourMod` and `KerbalMod` supply the members for these checks.

### Enabled

`Enabled` is `true` while the default feature of your mod is on in the active feature set. Check it in all code that your mod runs directly:

```csharp
private void OnVesselDeltaVChanged(MessageCenterMessage message)
{
    if (!Enabled)
        return;

    // ...
}
```

The active feature set changes at these times:

* when the game boots into the main menu
* when a campaign loads
* when the player goes back to the main menu

At the main menu, only Redux, Space Warp, Patch Manager and V-SwiFT are on. All other mods are off.

### Loading assets

Load the Addressables assets of your mod through the `SpaceWarp2.API.Loading` API. Do not call `Addressables` directly, because the feature gate does not see those loads.

* `Loading.AddAddressablesLoadingAction` loads a label one time, at boot. Use it for assets that all campaigns use, for example UI and icons. These assets load also when your mod is off. Assets that you keep stay in memory in all campaigns, so keep them small.
* `Loading.AddSetLoadingAction` loads a label for each feature set. Its reset clears the data each time the feature set changes. The label is content, so its assets load only while your mod is on. Use it for content data.
* `SaveLoad.AddStepAfter` adds a step after a named step of the game load, campaign load or campaign save flow.

Register loading actions in `OnPreInitialized` at the latest. Register a step before the step that it follows finishes. The game load flow runs one time, so for a step of that flow, register in `OnPreInitialized` at the latest. If the step that it follows has already finished, the game logs a warning and the step does not run.

### Mod features

Put `[Feature("Id")]` on a `bool` field of your plugin class. The field then shows if that feature is on. A bare id is a feature of your mod. A qualified id, for example `Ksp2Redux:Parts`, is a feature of any mod. Redux writes the field each time a feature set activates.

```csharp
using SpaceWarp2.API.Features;
using SpaceWarp2.API.Mods;

public class MyModPlugin : GeneralMod
{
    [Feature("HardMode")]
    private bool _hardMode;
}
```

Declare the feature in one of these locations:

* the `features` section of your swinfo
* your SDK Mod asset
* your code, with `Patching.Mod.DefineFeature("HardMode")` in `OnPreInitialized`

### Campaign hooks

`OnCampaignLoaded()` runs each time a campaign that has your mod on finishes loading. This includes quickloads and reverts. `OnCampaignUnloading()` runs before that campaign unloads.

* Set up per-campaign state in `OnCampaignLoaded()`.
* Remove that state in `OnCampaignUnloading()`.

Neither hook runs for a campaign that has your mod off.

```csharp
public override void OnCampaignLoaded()
{
    if (_hardMode)
    {
        // Set up hard mode for this campaign
    }
}

public override void OnCampaignUnloading()
{
    // Undo what OnCampaignLoaded set up
}
```

### Feature set changes

`SpaceWarp2.API.Features.ActiveFeatures` reads the active feature set from all code:

* `ActiveFeatures.Enabled` gives the ids of the enabled features. It is `null` before a feature set activates.
* `ActiveFeatures.IsEnabled(id)` examines one qualified feature id.
* `ActiveFeatures.IsModEnabled(modId)` examines if a mod is on.
* `ActiveFeatures.Changed` is an `Action<IReadOnlyCollection<string>?>` event. It fires after each feature set activates. At that time, all `[Feature]` fields and `Enabled` already agree with the new feature set.

```csharp
public override void OnInitialized()
{
    ActiveFeatures.Changed += enabled => SWLogger.LogInfo($"{SWMetadata.Guid} enabled: {Enabled}");
}
```

### Settings

Put `[ConfigFeature("Id")]` or `[ConfigNoFeature("Id")]` next to a `[ConfigValue]`:

* `[ConfigFeature("Id")]` shows the value in the settings menu only while the feature is on.
* `[ConfigNoFeature("Id")]` shows the value only while the feature is off.

All values are also hidden while your mod is off. `SWMetadata.CampaignConfigFile` is the per-campaign config file of your mod. Each campaign keeps its own values in this file.

### Patches

C# patches, like Lua patches, need the default feature of your mod. They also accept these conditions:

* `.Feature(...)` and `.NoFeature(...)`
* `[NeedsFeature(...)]` and `[NoFeature(...)]` in the attribute style

See [Patching from C#](/doc/patching-from-c-729VnFfTeV).

---

**Documents**

- [Custom Launch Locations](https://docs.ksp2redux.org/s/0a4a3e31-0b40-48e0-bd81-e14856f80d3d/doc/custom-launch-locations-cYFJeT82fg)
- [Subscribe to game Messages](https://docs.ksp2redux.org/s/0a4a3e31-0b40-48e0-bd81-e14856f80d3d/doc/subscribe-to-game-messages-XxQpsmHaKL)
- [Transform System Guide](https://docs.ksp2redux.org/s/0a4a3e31-0b40-48e0-bd81-e14856f80d3d/doc/transform-system-guide-jIJDOhFWoM)