# Event System

The event system is how plugins communicate without direct dependencies. When something happens in one plugin (item collected, ability used, quest completed), it publishes an event. Any other plugin can subscribe to that event and react.

## ISparkEvent Interface

Every event implements `ISparkEvent`:

```csharp
public interface ISparkEvent
{
    string EventType { get; }
    float Timestamp { get; }
    string Source { get; }
    int Priority { get; }
    bool CanBeCancelled { get; }
}
```

| Property         | Description                                                                          |
| ---------------- | ------------------------------------------------------------------------------------ |
| `EventType`      | A string identifier for the event type (e.g., `"ItemPickedUp"`).                     |
| `Timestamp`      | When the event was created (`Time.time`).                                            |
| `Source`         | Optional string identifying what caused the event.                                   |
| `Priority`       | Event priority. Higher priority events may be processed differently by some systems. |
| `CanBeCancelled` | Whether handlers can consume (stop) this event.                                      |

## Creating Custom Events

Inherit from `SparkEventBase`, which provides default implementations for most properties:

```csharp
public class MountSummonedEvent : SparkEventBase
{
    public override string EventType => "MountSummoned";

    public string CharacterId { get; }
    public string MountId { get; }
    public Vector3 Position { get; }

    public MountSummonedEvent(
        string characterId,
        string mountId,
        Vector3 position,
        string source = null)
    {
        CharacterId = characterId;
        MountId = mountId;
        Position = position;
        Source = source;
    }
}
```

`SparkEventBase` automatically sets `Timestamp` to `Time.time` at construction. `Priority` defaults to `0` and `CanBeCancelled` defaults to `false`.

To make an event cancellable, also implement `IConsumableEvent`:

```csharp
public class MountSummonedEvent : SparkEventBase, IConsumableEvent
{
    public override string EventType => "MountSummoned";
    public override bool CanBeCancelled => true;

    // ... same properties and constructor
}
```

## Publishing Events

Use `SparkEventBus.Publish<T>()` for immediate, synchronous dispatch:

```csharp
SparkEventBus.Publish(new MountSummonedEvent(
    characterId: "player_01",
    mountId: "war_horse",
    position: transform.position,
    source: "ability"));
```

All subscribed handlers run synchronously in priority order before `Publish` returns.

For batch processing, queue events and process them together:

```csharp
SparkEventBus.QueueEvent(new ExperienceGainedEvent(charId, 50));
SparkEventBus.QueueEvent(new ExperienceGainedEvent(charId, 25));
SparkEventBus.QueueEvent(new GoldGainedEvent(charId, 100));

// All three events fire now, in queue order
SparkEventBus.ProcessQueuedEvents();
```

Queuing is useful when multiple state changes happen as part of a single action and you want all events to fire after all changes are applied.

## Subscribing to Events

Implement `ISparkEventHandler<T>`:

```csharp
public class MountUIHandler : ISparkEventHandler<MountSummonedEvent>
{
    public int HandlerPriority => 50;

    public bool Handle(MountSummonedEvent evt)
    {
        // Update UI to show the mount is active
        Debug.Log($"Mount {evt.MountId} summoned for {evt.CharacterId}");
        return true;
    }
}
```

Register the handler with the event bus:

```csharp
var handler = new MountUIHandler();
SparkEventBus.Subscribe<MountSummonedEvent>(handler);
```

Unsubscribe when no longer needed:

```csharp
SparkEventBus.Unsubscribe<MountSummonedEvent>(handler);
```

Always unsubscribe handlers when the subscribing object is destroyed or disabled. Failing to do so can cause memory leaks and errors.

## Handler Priority and Ordering

Handlers execute in descending priority order. Higher `HandlerPriority` values run first:

```csharp
// Priority 100: runs first (e.g., validation/blocking)
// Priority 50:  runs second (e.g., game logic)
// Priority 10:  runs last (e.g., UI updates, logging)
```

This lets you create handlers that can validate or block an event before lower-priority handlers process it.

## Event Consumption

When an event implements `IConsumableEvent`, a handler can stop propagation by returning `true` from `Handle()`. Subsequent lower-priority handlers will not receive the event.

```csharp
public class MountBlockHandler : ISparkEventHandler<MountSummonedEvent>
{
    public int HandlerPriority => 200; // High priority, runs first

    public bool Handle(MountSummonedEvent evt)
    {
        if (IsInCombat(evt.CharacterId))
        {
            // Block the summon, consume the event
            return true; // Lower-priority handlers won't run
        }

        return false; // Let the event continue to other handlers
    }
}
```

For non-consumable events, the return value of `Handle()` is ignored. All handlers always run.

## Type Matching

The event bus supports polymorphic subscriptions. If you subscribe to a base class or interface, your handler receives all events of that type and its subtypes:

```csharp
// This handler receives ALL events that inherit from SparkEventBase
public class GlobalLogger : ISparkEventHandler<SparkEventBase>
{
    public int HandlerPriority => 0;
    public bool Handle(SparkEventBase evt)
    {
        Debug.Log($"[Event] {evt.EventType} at {evt.Timestamp}");
        return false;
    }
}
```

## String-Based Event Listeners

For simpler use cases (often in UI bindings), Spark provides `StringBasedEventListener`. This lets you subscribe to events by their `EventType` string rather than by C# type. This is primarily used by the editor and UI systems.

## Built-in Event Types

Spark and its plugins publish many events. Here are some common categories:

**UI Events** (from Core):

* `UIPanelShownEvent`, `UIPanelHiddenEvent`
* `FirstUIPanelOpenedEvent`, `AllUIPanelsClosedEvent`
* `EscapeKeyPressedEvent`
* `ShowEventMessageEvent`

**Character Events** (from Character plugin):

* Character creation and loading events

**Game Settings Events** (from GameSettings plugin):

* `GameSettingsLoadedEvent`
* `GameSettingsUIOpenedEvent`, `GameSettingsUIClosedEvent`
* `KeybindChangedEvent`

Each optional plugin adds its own events. See the Plugin API Reference section for complete event listings per plugin.

## Error Handling

The event bus catches and logs exceptions thrown by handlers. A failing handler does not prevent other handlers from receiving the event. Exceptions are logged with full stack traces for debugging.

## Best Practices

* Keep events immutable. Set all properties in the constructor and expose them as read-only.
* Use descriptive `EventType` strings that match the class name (e.g., `"MountSummoned"` for `MountSummonedEvent`).
* Always pair `Subscribe` with `Unsubscribe` to prevent leaks.
* Keep handlers lightweight. If a handler needs to do heavy work, queue it for the next frame.
* Use event consumption sparingly. Most events should reach all handlers.
* Prefer specific event types over base class subscriptions. Specific types are faster and clearer.
* Include a `Source` string in events to help with debugging and filtering.


---

# 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/event-system.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.
