# Extensions

Extensions allow one plugin to add data fields to another plugin's database entries without modifying the original entry class. This is how Spark maintains modularity while letting plugins work together.

## When to Use Extensions

Use extensions when your plugin needs to store data on entries that belong to another plugin. Common examples:

* Combat plugin storing stat values on item entries
* Crafting plugin storing recipe unlock data on profession entries
* Progression plugin storing experience rewards on quest entries

If you control both the entry and the data, just add fields directly to the entry. Extensions are for cross-plugin scenarios.

## How Extensions Work

An extension consists of three parts:

1. **Extension Data**: A ScriptableObject that holds the extra fields, linked to a target entry by ID.
2. **Extension Manifest**: A configuration asset that tells the Spark Editor where and how to display the extension.
3. **Extension Logic** (optional): Event handlers or other code that uses the extension data.

At runtime, extension data is loaded by `SparkExtensionRegistry` and accessed by combining the extension type and target entry ID.

## Creating Extension Data

Inherit from `SparkDatabaseExtensionData`:

```csharp
using UnityEngine;

[CreateAssetMenu(
    fileName = "New Mount Stats Extension",
    menuName = "Spark/Mounts/Mount Stats Extension")]
public class MountStatsExtensionData : SparkDatabaseExtensionData
{
    [Section("Mount Stats")]
    [SerializeField] private float carryCapacity = 100f;
    [SerializeField] private float stamina = 50f;
    [SerializeField] private bool canSwim = false;

    public float CarryCapacity => carryCapacity;
    public float Stamina => stamina;
    public bool CanSwim => canSwim;
}
```

`SparkDatabaseExtensionData` inherits from `ScriptableObject` and has a `targetId` field that stores the ID of the database entry it extends. This is managed automatically by the Spark Editor.

### Storage

Extension data files are stored alongside the plugin that creates them, in a `Resources/` subfolder:

```
Mounts/Extensions/CharacterMountsExtension/
    Runtime/
        Resources/
            CharacterMountsExtensionData/
```

## Accessing Extension Data at Runtime

Use `SparkExtensionRegistry`:

```csharp
// Get extension data for a specific entry
MountStatsExtensionData stats = SparkExtensionRegistry
    .GetExtensionData<MountStatsExtensionData>("war_horse");

// Check if extension data exists
bool hasStats = SparkExtensionRegistry
    .HasExtensionData<MountStatsExtensionData>("war_horse");

// Get all extension data of a type
List<MountStatsExtensionData> allStats = SparkExtensionRegistry
    .GetAllExtensionsOfType<MountStatsExtensionData>();
```

The registry uses a cache key format of `{ExtensionType}|{TargetId}` for O(1) lookups.

## IPluginExtension Interface

To make your extension appear in the Spark Editor, implement `IPluginExtension`:

```csharp
public class MountStatsExtension : IPluginExtension
{
    public Type TargetType => typeof(CharacterEntry);
    public int Priority => 100;
    public string ExtensionDisplayName => "Mount Stats";
    public bool IsCollapsible => true;

    public bool ShouldShowForTarget(SparkDatabaseEntry target)
    {
        // Only show for character entries that can ride mounts
        return target is CharacterEntry;
    }

    public VisualElement CreateExtensionUI(
        SparkDatabaseEntry target,
        SparkDatabaseExtensionData extensionData)
    {
        // Build UI Toolkit elements for the extension fields
        // ...
    }

    // Optional: add header content above the extension
    public VisualElement CreateExtensionHeaderUI(SparkDatabaseEntry target)
    {
        return null; // No header needed
    }
}
```

Properties:

| Property               | Description                                                                |
| ---------------------- | -------------------------------------------------------------------------- |
| `TargetType`           | The entry type this extension applies to (e.g., `typeof(CharacterEntry)`). |
| `Priority`             | Display order in the editor. Higher values appear earlier.                 |
| `ExtensionDisplayName` | Label shown in the Spark Editor.                                           |
| `IsCollapsible`        | Whether the extension section can be collapsed.                            |

## IGlobalPluginExtension

If your extension should apply to multiple entry types, implement `IGlobalPluginExtension` instead:

```csharp
public class MountStatsExtension : IGlobalPluginExtension
{
    public IEnumerable<Type> GetSupportedTypes()
    {
        yield return typeof(CharacterEntry);
        yield return typeof(NPCEntry);
    }

    public int Priority => 100;
    public string ExtensionDisplayName => "Mount Stats";
    public bool IsCollapsible => true;

    // ... same methods as IPluginExtension
}
```

The framework wraps each supported type in a `GlobalExtensionWrapper` for editor rendering.

## Extension Manifest

Create a `PluginExtensionManifest` ScriptableObject to configure how the extension appears:

1. Right-click in your extension's Editor folder.
2. Create a `PluginExtensionManifest` asset.
3. Configure it with the extension data type, target type, and resource path.

The manifest tells the Spark Editor where to find and create extension data assets.

## Extension Folder Structure

The standard folder layout for an extension:

```
YourPlugin/Extensions/
    TargetPluginExtension/
        Editor/
            Scripts/
                YourExtension.cs            # IPluginExtension implementation
                YourExtensionManifest.asset  # Extension manifest
            Spark.YourPlugin.TargetExtension.Editor.asmdef
        Runtime/
            Scripts/
                YourExtensionData.cs        # Extension data class
            Resources/
                YourExtensionData/          # Extension data assets
            Spark.YourPlugin.TargetExtension.asmdef
```

The assembly definition for the extension references both the source plugin and the target plugin's assemblies.

## Null Safety

Always guard extension data lookups. The target plugin may be installed but the extension data may not exist for every entry:

```csharp
var stats = SparkExtensionRegistry.GetExtensionData<MountStatsExtensionData>(entryId);
if (stats != null)
{
    float capacity = stats.CarryCapacity;
    // ...
}
```

This is especially important for optional plugins. If the extension plugin is not installed, the lookup simply returns null.

## Best Practices

* Name extensions clearly: `{Feature}{TargetPlugin}Extension` (e.g., `CombatStatsItemExtension`).
* Keep extension data focused. One extension per concern.
* Use `ShouldShowForTarget()` to filter which entries actually need the extension fields.
* Always null-check extension data at runtime.
* Store extension data under your plugin's folder, not the target plugin's folder.
* Add assembly references only to the plugins you actually need.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.sparkframework.dev/documentation/developer-guide/core-systems/extensions.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
