> This is part 1 of 2 of the full documentation (pages 1–100 of 191).
> The content is paginated: fetch every part to see all of it.
> Next part: https://docs.sparkframework.dev/llms-full.txt/1
> Page index: https://docs.sparkframework.dev/llms.txt

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
This site has an agentic ask interface you may use to query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.sparkframework.dev/index.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

---

# Developer Platform

Welcome to your team’s developer platform

<h2 align="center">Spark Framework Documentation</h2>

<p align="center">Welcome to your next-gen Game Creation Framework for Unity</p>

<p align="center"><a href="https://assetstore.unity.com/packages/tools/game-toolkits/rpg-template-bundle-for-spark-365114" class="button primary">Start now</a> <a href="https://discord.gg/fYzpuYwPwJ" class="button secondary">Join Discord</a></p>

<h2 align="center">Join a community of over 5,000 Unity developers</h2>

<table data-view="cards"><thead><tr><th></th><th></th><th data-type="image"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><h4><i class="fa-discord">:discord:</i></h4></td><td><strong>Discord community</strong></td><td></td><td><a href="https://2637508582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F41TqHTRTS4qbhb177UD5%2Fuploads%2Fe3xlqDXBmjAwimM6NF73%2FBlink1280x800.png?alt=media&amp;token=ff851170-896b-4841-9d92-45f264354a31">Blink1280x800.png</a></td><td><a href="https://discord.gg/fYzpuYwPwJ">https://discord.gg/fYzpuYwPwJ</a></td></tr><tr><td><h4><i class="fa-unity">:unity:</i></h4></td><td><strong>Asset Store</strong></td><td></td><td><a href="https://2637508582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F41TqHTRTS4qbhb177UD5%2Fuploads%2FvQSVIn54sHcWbcakKUxr%2Fassetstore1.png?alt=media&amp;token=6ff2ad9e-052e-483e-86de-b24cd636ebd1">assetstore1.png</a></td><td><a href="https://assetstore.unity.com/publishers/49855">https://assetstore.unity.com/publishers/49855</a></td></tr><tr><td>2026 Roadmap</td><td>We have so much more on the way</td><td></td><td><a href="https://2637508582-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F41TqHTRTS4qbhb177UD5%2Fuploads%2FKs4Kd4eJyqXQH3Bp9IZU%2FRoadmap.png?alt=media&amp;token=8c6d8197-ca46-465c-9ea5-c8bb5e55240e">Roadmap.png</a></td><td><a href="https://site.sparkframework.dev/roadmap">https://site.sparkframework.dev/roadmap</a></td></tr></tbody></table>


# Welcome

Spark is a modular game framework for Unity that lets you build RPGs, survival games, and action games without writing code. It provides a complete set of interconnected systems for characters, items, combat, quests, crafting, progression, and more, all managed through a visual editor inside Unity.

## What You Can Build

With Spark and its plugins, you can create games that include:

* Playable characters with customizable stats, equipment, and abilities
* Full inventory and equipment systems with loot tables and item rarities
* Combat with multiple ability types, stat modifiers, and status effects
* Quest systems with objectives, rewards, and NPC interactions
* Crafting recipes with ingredient requirements
* Currency and wallet systems
* Character progression with experience and leveling
* Class and profession systems
* Character customization with presets and color options
* Save/load functionality
* Configurable game settings (audio, video, keybinds)

## How Spark Works

Everything in Spark revolves around **Database Entries**. These are the building blocks of your game: every item, character, ability, quest, recipe, and currency is defined as a database entry inside the Spark Editor window.

You create and configure these entries visually. Then, in your scenes, you place **Entities** (GameObjects with Spark components) that reference those entries. At runtime, Spark connects everything together automatically.

## Spark Editor

The Spark Editor is a custom Unity window where you manage all of your game data. Each plugin adds its own tabs to the editor, giving you a centralized place to create and edit every aspect of your game.

To open it, go to the Unity menu bar and look for the Spark Editor window.

## Plugins

Spark is built as a collection of plugins. Some are included with the core framework, and others are available as optional add-ons from the Unity Asset Store.

**Core Plugins** (always included): Character, Player, GameSettings, Inputs, Playables, Requirements, Rules, Save, Scenes, ScreenTexts, Triggers, UI.

**Optional Plugins** (sold separately): CharacterCustomization, Classes, Combat, Crafting, Currency, Interactables, ItemBar, Items, Professions, Progression, Quests, Races, Spellbooks.

Each plugin is self-contained but can interact with other plugins through Spark's extension system. For example, the Quests plugin can define objectives that track items collected (Items plugin) or enemies defeated (Combat plugin).

## Who This Guide Is For

This User Guide is written for people who want to build games using Spark's visual tools without writing code. Every feature is explained in terms of what you do in the Spark Editor and Unity Inspector.

If you are a programmer looking to create custom plugins, extend Spark's systems, or work with the API directly, see the [Developer Guide](/documentation/developer-guide/welcome).

## Getting Help

If you run into issues or have questions:

* Check the [FAQ & Troubleshooting](broken://pages/D9PcDQvmZcyP9f8YACB2) section
* Join the [Spark Discord community](https://discord.gg/fYzpuYwPwJ)
* Watch the [official tutorial videos on YouTube](https://www.youtube.com/@SparkUnityFramework)


# Getting Started

This page covers what you need to get Spark up and running in Unity.

## Requirements

* Unity 6.3 or newer

Spark does not require any specific rendering pipeline. You have full freedom to use URP, HDRP, or the Built-in Render Pipeline. The included game templates and demo scenes use URP, but the framework itself is pipeline-agnostic.

## Installing Spark

Watch the installation walkthrough video here: [Spark Installation Guide](https://youtu.be/QzOTBlA2jk0)

You can purchase Spark Core and individual plugins from the [Unity Asset Store](https://assetstore.unity.com/publishers/49855). For more information on how Spark is distributed (individual plugins, bundles, and game templates), see [How is Spark Distributed](/documentation/user-guide/getting-started/how-spark-is-distributed).

## The Spark Editor Window

The Spark Editor is where you spend most of your time. To open it:

1. In Unity's menu bar, look for the **Spark** menu or go to **Window > Spark Editor**.
2. The editor window opens with tabs on the left sidebar.

Each installed plugin adds its own tabs. For example, if you have the Items plugin installed, you will see tabs for Items, Equipment Slots, Body Parts, Loot Tables, and so on.

Inside each tab you can browse existing entries in a searchable list, create new entries, edit entry fields in the inspector panel, and delete entries you no longer need.

## Next Steps

Now that you have Spark installed, continue to [Core Concepts](/documentation/user-guide/getting-started/core-concepts) to understand the foundational ideas that every Spark system is built on.


# Core Concepts

Before diving into specific systems, it helps to understand a few ideas that show up everywhere in Spark. Once you know these, everything else will make more sense.

## Database Entries

A **Database Entry** is a piece of game data defined in the Spark Editor. Every item, character, ability, quest, recipe, currency, class, race, and more is a database entry.

Each entry has a few standard fields:

| Field         | Description                                                                                      |
| ------------- | ------------------------------------------------------------------------------------------------ |
| `id`          | A unique identifier, auto-generated when the entry is created. Cannot be changed after creation. |
| `entryName`   | A short internal name, used for organizing entries in the editor.                                |
| `displayName` | The name shown to players in-game (e.g., "Iron Sword").                                          |
| `description` | A text description shown to players (e.g., in tooltips or info panels).                          |
| `icon`        | A sprite used to represent the entry in UI elements.                                             |

Beyond these standard fields, each entry type adds its own specific fields. For example, an Item Entry has fields for stack size, rarity, and equipment slot. A Quest Entry has fields for objectives and rewards.

Entries are stored as ScriptableObjects inside `Resources/` folders and are loaded automatically when the game starts.

## Plugins

A **Plugin** is a self-contained system that adds a specific feature set to Spark. The Character plugin manages characters. The Items plugin manages items and equipment. The Quests plugin manages quests.

Each plugin typically provides one or more database entry types that you create in the Spark Editor, scene components (entities) that you attach to GameObjects, and UI elements for displaying data to players.

Core plugins are always available. Optional plugins are installed separately and integrate automatically with the rest of the framework. See [How is Spark Distributed](/documentation/user-guide/getting-started/how-spark-is-distributed) for more on how plugins are packaged and sold.

## Entities

An **Entity** is a GameObject in your scene that has a `SparkEntity` component. This component gives the object a unique ID and lets other Spark systems find and interact with it.

Most gameplay objects in your scene are entities: the player character, NPCs, interactable objects, chests, crafting stations, and so on.

You add functionality to an entity by attaching additional Spark components. For example, a player character entity might have `InventoryEntity` for carrying items, `EquipmentEntity` for wearing gear, `ProgressionEntity` for tracking experience and level, and `QuestTrackerEntity` for tracking quest progress.

Each of these components connects to the corresponding plugin and its database entries.

## Extensions

An **Extension** lets one plugin add data to another plugin's database entries without modifying the original entry.

For example, the Combat plugin needs to store stat values (strength, agility, etc.) on items. Instead of modifying the Items plugin, the Combat plugin creates an extension that attaches stat data to item entries. In the Spark Editor, when you view an item entry, you will see a "Combat Stats" section added by the Combat extension.

This is how Spark stays modular. Plugins do not depend on each other directly. They connect through extensions.

As an editor user, you do not need to worry about how extensions work technically. You will see extension fields appear on entries when the relevant plugins are installed. Just fill them in like any other field.

## Next Steps

Now that you understand the core concepts, the rest of this guide walks through each system in detail. Continue to the [Plugins](/documentation/user-guide/plugins) section to explore individual plugin features.


# How Spark Is Distributed

Spark is available on the [Unity Asset Store](https://assetstore.unity.com/publishers/49855). It is distributed in three ways: individual plugins, bundles, and game templates.

## Individual Plugins

Each Spark plugin is available as a standalone purchase. This lets you buy only the features you need. For example, if your game needs items and combat but not crafting or quests, you can purchase just those plugins.

Every plugin depends on the **Spark Core** package, which is included with any purchase and provides the foundation: the plugin system, database, entity system, events, commands, triggers, requirements, rules, save system, UI framework, and other shared infrastructure.

See the [Plugins](/documentation/user-guide/plugins) section for details on each plugin.

## Bundles

Bundles combine multiple plugins at a discounted price. If your game needs several plugins, a bundle is more cost-effective than buying them individually.

Bundles are listed on the Asset Store alongside individual plugins. Each bundle page describes which plugins are included.

## Game Templates

Game templates are starter projects that contain template-specific files, scenes, and scripts that combine multiple plugins together into a ready-to-use game genre. Templates do **not** include Spark Core or any plugins — you must purchase those separately.

A template provides pre-built scenes, UI layouts, sample characters, database configurations, and the glue code that ties the plugins together. Once you have the required plugins installed, importing the template gives you a working game out of the box that you can customize and build on.


# Core Systems

Core systems are the foundational features included with Spark Core. They are available in every Spark project regardless of which optional plugins you have installed.

These systems handle characters, triggers, requirements, rules, saving, game settings, scenes, playables, screen texts, and UI.


# Characters

The Character plugin is at the heart of every Spark game. It defines the characters in your game through database entries.

## CharacterEntry

Open the Spark Editor and navigate to the **Characters** tab to view and manage character entries.

Each character entry has the standard fields:

| Field         | Description                                                     |
| ------------- | --------------------------------------------------------------- |
| `displayName` | The name shown to the player (e.g., "Warrior", "Village Elder") |
| `description` | A text description of the character                             |
| `icon`        | A portrait or icon sprite for UI elements                       |

Beyond these, the Character plugin is intentionally lightweight. Characters gain their depth through other plugins: stats come from Combat, items come from Items, abilities come from Spellbooks, progression comes from Progression, and so on. Each of these plugins adds extension fields to the character entry that appear in the Spark Editor when those plugins are installed.

## Character Controllers

Spark includes built-in character controllers. Each game template comes with a pre-configured player prefab that uses one of these controllers.

### Third-Person Controller

A standard third-person controller with camera follow. Good for action RPGs and adventure games. Uses the **SparkThirdPersonController** component with configurable movement speed, rotation speed, and camera settings.

### MMO Controller

An MMO-style controller with point-and-click movement, autorun, and orbit camera. Good for MMO-style games. Uses the **SparkMMOController** and **MMOCamera** components. Supports click-to-move, autorun toggle, camera orbit, and animator layer weight blending.

## Character Save Data

Character data is saved automatically through the Save plugin. You do not need to configure anything for this to work.


# Game Settings

The GameSettings plugin provides a complete settings system for your game, covering audio, video, and keybind configuration with a pre-built UI.

## Audio Settings

The **AudioSettingsData** manages:

* Master volume
* Music volume
* Sound effects volume
* Voice volume
* Mute toggles per channel

Players adjust these through sliders in the settings UI. Changes take effect immediately.

## Video Settings

The **VideoSettingsData** manages:

* Resolution
* Fullscreen mode (fullscreen, windowed, borderless)
* Quality preset
* V-Sync
* Frame rate limit

Video settings use Unity's built-in quality settings system.

## Keybind Settings

The **KeybindSettingsData** manages customizable key bindings. The system integrates with Unity's Input System.

**Keybind Category Entries** group related keybinds together (Movement, Combat, UI, etc.). Create these in the Spark Editor under the **Keybind Categories** tab.

The keybind UI shows all bindings organized by category. Players can click a binding and press a new key to remap it. The system handles:

* Conflict detection (warning when two actions use the same key)
* Reset to defaults
* Per-category organization

## Settings UI

The GameSettings plugin includes a complete settings UI:

* **GameSettingsUI**: The main settings window with tabs for each category.
* **AudioSettingsUI**: Audio settings panel with volume sliders.
* **VideoSettingsUI**: Video settings panel with resolution dropdowns and quality options.
* **KeybindSettingsUI**: Keybind settings panel with a list of remappable bindings.
* **KeybindRowUI**: Individual keybind row showing action name and current key.

## Custom Settings Panels

The settings UI uses a registry system (**GameSettingsUIRegistry**) that allows additional settings panels to be added. If you have a plugin that needs its own settings section, it can register a panel with the settings UI.

## Settings Events

* **GameSettingsLoadedEvent**: Settings were loaded from disk.
* **GameSettingsUIOpenedEvent**: The settings UI was opened.
* **GameSettingsUIClosedEvent**: The settings UI was closed.
* **KeybindChangedEvent**: A keybind was remapped.

## Persistence

Game settings save independently from gameplay saves. They persist globally, not per save slot. This means changing your volume in one save file changes it for all save files, which is the expected behavior for settings.

Settings are saved through **GameSettingsSaveProvider**.


# Playables

Playables are pre-configured animations, sounds, visual effects, and other actions that play during gameplay. They are used in combat (ability cast animations, hit effects), interactions (chest opening animations), and other systems that need visual or audio feedback.

## Playable Types

Spark includes several playable types, each handling a different kind of action:

### Animation Playables

**PlayableAnimationEntry** defines an animation to play on a character or object.

Configure:

* Which animation clip to play
* Blend time (how quickly to transition into and out of the animation)
* Speed and speed curves
* Whether the animation locks the character from performing other actions
* Target selection (who should play the animation)

### Sound Playables

**PlayableSoundEntry** defines a sound to play.

Configure:

* Audio clip to play
* Volume, pitch, and spatial blend
* Whether it plays at a position in the world or as a UI sound

### Transform Playables

**PlayableTransformEntry** defines a position, rotation, or scale change on an object.

Configure:

* Target object
* Destination transform values
* Duration and easing curve
* Whether to use local or world space

Transform playable overrides can be set per entity through **PlayableTransformEntryOverride** and **PlayableTransformEntity**.

### Coordinate Playables

**PlayableCoordinatesEntry** defines movement to specific coordinates. Used for scripted movement sequences (walk to a position, move to a waypoint).

### GameObject Playables

**PlayableGameObjectEntry** defines actions on GameObjects: show, hide, enable, disable, spawn, or destroy.

Used for spawning projectiles, showing/hiding VFX objects, activating/deactivating scene elements.

## Playable Configurations

A **PlayableConfiguration** bundles multiple playables together into a sequence. For example, an ability's visual feedback might include playing the cast animation, playing the cast sound, spawning the projectile, and on impact playing the hit animation, impact sound, and hit VFX on the target.

All of these are configured as playable entries and combined in the ability's playable configuration.

## How Playables Are Used

Playables are referenced from:

* **Abilities** (Combat plugin): Cast animation, projectile, impact effects.
* **Interactions**: Object interaction animations and sounds.
* **Triggers**: "Play this playable" as a trigger action.
* **NPC Interactions**: NPC gesture animations during dialogue.

You do not need to write code to use playables. Configure them as entries in the Spark Editor and reference them from the systems that need them.


# Requirements

A **Requirement** is a condition that must be met before something can happen. "Player must be level 10", "Player must have 5 iron ore", "Player must have the Blacksmithing profession", and "Player's health must be above 50%" are all examples of requirements.

Requirements appear alongside triggers in many places throughout Spark. For example, a quest might require the player to be a certain level before they can accept it. An NPC interaction might only be available if the player has a specific item. A crafting recipe might require certain materials in the inventory.

## Requirement Entries

In the Spark Editor under the **Requirements** tab, each requirement entry has:

| Field                 | Description                                      |
| --------------------- | ------------------------------------------------ |
| `requirementType`     | Which type of condition to check                 |
| `requirementTypeData` | Configuration specific to that requirement type  |
| `failureMessage`      | Message shown to the player when the check fails |

## Requirement Groups

Requirements are organized into **Requirement Groups** that define how multiple checks combine:

| Logic         | Behavior                            |
| ------------- | ----------------------------------- |
| **And**       | All requirements must pass          |
| **Or**        | At least one must pass              |
| **Nand**      | At least one must fail              |
| **Nor**       | All must fail                       |
| **Xor**       | Exactly one must pass               |
| **Threshold** | At least N must pass (configurable) |

Each check within a group can also be **inverted**, which negates its result.

## All Requirement Types

### Core Requirements

| Requirement Type                         | Plugin    | Description                                                         |
| ---------------------------------------- | --------- | ------------------------------------------------------------------- |
| **RuleValueRequirementType**             | Rules     | Checks if a rule on the entity has a specific value (true or false) |
| **DistanceToCoordinatesRequirementType** | Playables | Checks if the entity is within a certain distance of coordinates    |

### Plugin Requirements

| Requirement Type                            | Plugin      | Description                                                           |
| ------------------------------------------- | ----------- | --------------------------------------------------------------------- |
| **HasClassRequirementType**                 | Classes     | Checks if the entity has a specific class (main or secondary)         |
| **HasSecondaryClassRequirementType**        | Classes     | Checks if the entity has a specific secondary class                   |
| **MainClassRequirementType**                | Classes     | Checks if the entity's main class matches                             |
| **HasStatusRequirementType**                | Combat      | Checks if the entity has a specific status effect                     |
| **ResourceStatRequirementType**             | Combat      | Checks a resource stat value (e.g., health, mana) against a threshold |
| **ValueStatRequirementType**                | Combat      | Checks a value stat (e.g., strength, agility) against a threshold     |
| **HasCraftingRecipeRequirementType**        | Crafting    | Checks if a crafting recipe is unlocked                               |
| **CurrencyAmountRequirementType**           | Currency    | Checks if the entity has enough of a currency                         |
| **HasEmptyInventorySlotRequirementType**    | Items       | Checks if the inventory has at least one empty slot                   |
| **HasEquipmentSlotEquippedRequirementType** | Items       | Checks if a specific equipment slot has an item equipped              |
| **HasItemEquippedRequirementType**          | Items       | Checks if a specific item is equipped                                 |
| **HasItemInInventoryRequirementType**       | Items       | Checks if a specific item is in the inventory                         |
| **HasWeaponTypeEquippedRequirementType**    | Items       | Checks if a weapon of a specific type is equipped                     |
| **ProgressionLevelRequirementType**         | Progression | Checks if the entity has reached a specific level                     |
| **QuestStatusRequirementType**              | Quests      | Checks the status of a quest (active, completed, etc.)                |

## Where Requirements Are Used

Requirements appear in many places across Spark:

* **Quest acceptance**: Conditions the player must meet to accept a quest
* **NPC interactions**: Conditions for an interaction option to be available
* **Interactable objects**: Conditions for allowing interaction
* **Ability usage**: Conditions for casting an ability (e.g., enough mana, correct weapon type)
* **Crafting**: Conditions beyond just having materials
* **Spellbook auto-add**: Conditions for abilities to appear on the ability bar


# Rules

Rules are local boolean values on a SparkEntity. They represent simple true/false states like "is dead", "can move", "is grounded", "is in combat", and so on.

## What Rules Are For

Each SparkEntity can have a set of rules attached to it through the **RulesEntity** component. These rules act as flags that other systems and scripts read to determine what the entity is currently allowed to do or what state it is in.

For example:

* A "DEAD" rule set to `true` means the entity is dead. The UI system checks this and blocks opening panels while dead.
* A "CAN\_MOVE" rule set to `false` prevents the character from moving (used during stuns, knockdowns, or ability casts).
* A "GROUNDED" rule tracks whether the character is on the ground.

## Rule Entries

In the Spark Editor under the **Rules** tab, each **RuleEntry** defines a rule with:

| Field          | Description                                                         |
| -------------- | ------------------------------------------------------------------- |
| `key`          | A unique string identifier (e.g., "DEAD", "CAN\_MOVE", "GROUNDED")  |
| `defaultValue` | The starting value when no modifications are active (true or false) |

## How Rules Work at Runtime

Rules are set by scripts at runtime. When something happens in the game (a character dies, gets stunned, lands on the ground), the responsible system sets the appropriate rule value on the entity.

Other systems then check these rules to decide what to do. For example, the character controller checks the "CAN\_MOVE" rule before allowing movement. The UI checks the "DEAD" rule before opening a panel.

## Rule Modifications

Rules support temporary modifications with priorities. Multiple systems can modify the same rule at the same time, and the highest-priority modification wins.

For example, a stun ability might set "CAN\_MOVE" to `false` with a duration of 3 seconds and a priority of 10. When the duration expires, the modification is automatically removed and the rule returns to its previous value.

This prevents conflicts where one system sets a rule to `false` and another sets it back to `true` at the wrong time. The priority system ensures a clean resolution.

## Rule Subscriptions

Systems can subscribe to rule changes and get notified when a rule value changes on a specific entity. This is how UI elements and other reactive systems stay in sync with entity state.


# Save System

The Save plugin handles persisting game state between sessions. It saves and loads player data, inventory contents, quest progress, customization choices, and everything else that needs to survive a game restart.

## How Saving Works

Spark's save system works automatically for all built-in plugins. Each plugin that stores data registers its own save/load logic with the Save plugin. When a save is triggered, each plugin's data is collected and written to a save file. When a load is triggered, the data is read back and applied.

You don't need to configure save behavior for built-in systems. It works out of the box.

## What Gets Saved

Each plugin manages its own save data. Here is what each plugin saves:

| Plugin                  | Saved Data                                          |
| ----------------------- | --------------------------------------------------- |
| Character               | Character selection and state                       |
| Items                   | Inventory contents, equipment loadout               |
| Currency                | Wallet amounts                                      |
| Quests                  | Active quests, objective progress, completed quests |
| Progression             | Experience and level per progression instance       |
| Classes                 | Main class and secondary class selections           |
| Professions             | Learned professions                                 |
| Races                   | Selected race                                       |
| Character Customization | All customization choices (elements, colors)        |
| Crafting                | Unlocked recipes                                    |
| Spellbooks              | Learned abilities                                   |
| Item Bar                | Slot assignments                                    |
| Game Settings           | Audio, video, and keybind settings                  |

## In-Game Save Manager

The **InGameSaveManager** extension provides an in-game UI for save/load operations. It lets players:

* Save to a named slot
* Load from a saved slot
* View existing saves with timestamps
* Delete saves

Add the InGameSaveManager component to your scene to enable this UI.

## Save Data Overview

Save data is stored as serialized files on the player's device. The location and format are managed by the Save plugin's **Save Provider** system.

**GameSettingsSaveProvider** handles game settings separately from gameplay saves, since settings should persist regardless of which save slot is loaded.

## Auto-Save

Some components support auto-save. For example, **InventoryEntity** can be configured to save automatically when items change. This ensures data is not lost if the game crashes.

## Manual Save/Load

Saves and loads can be triggered through commands and the in-game save manager UI. The templates include pre-built save/load screens.


# Scenes

The Scenes plugin manages scene loading and transitions. It provides a structured way to define game scenes as database entries and handle loading between them.

## Scene Entries

Create scenes in the Spark Editor under the **Scenes** tab. Each **Scene Entry** represents a game scene (level, area, town, dungeon, etc.).

Scene entries store:

* **Display Name**: The area name shown to players.
* **Description**: Description of the area.
* **Icon**: A loading screen image or map icon.
* **Scene Reference**: The actual Unity scene file this entry points to.

By defining scenes as database entries, other systems can reference them by ID. Quest objectives can require visiting a specific area. Triggers can teleport the player to a scene. NPCs can offer travel to different locations.

## Scene Management

The Scenes plugin handles:

* Loading scenes with transitions
* Passing data between scenes (which spawn point to use, etc.)
* Loading screen management

## Using Scenes

Scenes are referenced by their entry ID in triggers, quests, and other systems. For example:

* A trigger that teleports the player: references a scene entry ID.
* A quest objective: "Visit the Iron Mines" references the Iron Mines scene entry.
* An NPC travel option: "Travel to the Capital" loads the Capital scene.


# Screen Texts

The ScreenTexts plugin displays floating text on screen: damage numbers, healing numbers, experience gain notifications, level-up messages, and any other text that should appear briefly over a character or on screen.

## How Screen Texts Work

When events happen in the game (damage dealt, items picked up, experience gained), the ScreenTexts system can display a floating text message. These messages appear at a position in the world (above a character's head, at a hit location) or at a fixed screen position, then animate and fade out.

## Screen Text Entries

Create screen text configurations in the Spark Editor under the **Screen Texts** tab. Each **Screen Text Entry** defines a template for a type of floating text:

* **Text format**: How the text is composed (e.g., "+{amount} XP", "-{damage}").
* **Color**: Text color (red for damage, green for healing, yellow for experience).
* **Size**: Font size.
* **Animation**: How the text moves (float up, bounce, expand, etc.).
* **Duration**: How long the text stays visible.

## Formatter Types

Different events produce different screen texts. Each plugin can register formatter types that convert events into screen text displays:

* **ExperienceGainedFormatterType** (Progression plugin): Shows "+50 XP" when experience is gained.
* **LevelChangedFormatterType** (Progression plugin): Shows "Level Up!" when the player levels up.
* Combat formatters show damage numbers, healing numbers, miss text, critical hit text, etc.

## Using Screen Texts

Screen texts work automatically once configured. When an event fires that has a matching formatter, the text appears on screen. You configure what events produce text and how that text looks through the Spark Editor.

To add screen texts to your game:

1. Create screen text entries with the desired visual settings.
2. Ensure the relevant formatter types are active (they activate automatically with their plugins).
3. The text will appear when the corresponding events fire during gameplay.


# Triggers

A **Trigger** is a configurable action that happens in response to something. "Give the player 100 gold", "Start a quest", "Play a sound", "Load a scene", and "Add an item" are all examples of triggers.

Triggers are used throughout Spark: in quest rewards, NPC interactions, interactable objects, level-up rewards, and more. You configure them in the Spark Editor by picking a trigger type and filling in its parameters.

## Trigger Entries

In the Spark Editor under the **Triggers** tab, each trigger entry has:

| Field                     | Description                                            |
| ------------------------- | ------------------------------------------------------ |
| `triggerType`             | Which type of action to perform                        |
| `triggerTypeData`         | Configuration specific to that trigger type            |
| `executionDelay`          | Optional delay before the trigger fires (0-10 seconds) |
| `allowMultipleExecutions` | If false, the trigger only fires once per entity       |

## Trigger Instances

When triggers are referenced from other entries (quest rewards, NPC interactions, etc.), they are wrapped in a **TriggerInstance** which can optionally override the delay defined on the trigger entry.

## All Trigger Types

### Core Triggers

| Trigger Type                         | Plugin   | Description                                                              |
| ------------------------------------ | -------- | ------------------------------------------------------------------------ |
| **AnimatorTriggerType**              | Triggers | Sets a parameter on an Animator component (trigger, bool, int, or float) |
| **ComponentMethodTriggerType**       | Triggers | Calls a method on a specified MonoBehaviour component                    |
| **DestroySelfTriggerType**           | Triggers | Destroys the source GameObject                                           |
| **InstantiateGameObjectTriggerType** | Triggers | Instantiates a prefab at a position                                      |
| **ToggleUIPanelTriggerType**         | Triggers | Opens or closes a UI panel                                               |
| **TriggerSaveTriggerType**           | Save     | Triggers a save operation                                                |
| **LoadSceneTriggerType**             | Scenes   | Loads a scene by its SceneEntry                                          |

### Plugin Triggers

| Trigger Type                          | Plugin      | Description                               |
| ------------------------------------- | ----------- | ----------------------------------------- |
| **AddItemTriggerType**                | Items       | Adds an item to the player's inventory    |
| **RollLootTableTriggerType**          | Items       | Rolls a loot table and gives the results  |
| **GainCurrencyTriggerType**           | Currency    | Adds currency to the player's wallet      |
| **GainExperienceTriggerType**         | Progression | Awards experience points to a progression |
| **LearnRecipeTriggerType**            | Crafting    | Unlocks a crafting recipe                 |
| **UnlearnRecipeTriggerType**          | Crafting    | Locks a crafting recipe                   |
| **AddQuestTriggerType**               | Quests      | Gives a quest to the player               |
| **CompleteQuestObjectiveTriggerType** | Quests      | Completes a specific quest objective      |
| **AddSecondaryClassTriggerType**      | Classes     | Adds a secondary class to the entity      |
| **AddProfessionTriggerType**          | Professions | Grants a profession to the entity         |
| **LearnProfessionTriggerType**        | Professions | Teaches a profession to the entity        |
| **AddSpellbookTriggerType**           | Spellbooks  | Grants or removes a spellbook             |
| **ApplyEffectTriggerType**            | Combat      | Applies a combat effect to the target     |

## Where Triggers Are Used

Triggers appear in many places across Spark:

* **Quest rewards**: Triggers fire when a quest is turned in
* **NPC interactions**: Triggers fire when the player interacts with an NPC
* **Interactable objects**: Triggers fire when the player interacts with an object
* **Level-up rewards**: Triggers fire when a progression reaches a specific level
* **Rule effects**: Triggers can be configured as effects in the Rules system


# UI System

Spark's UI system manages game panels (inventory, quest log, character sheet, etc.) and provides a drag-and-drop framework for moving items and abilities between UI elements.

## UI Panels Overview

Every in-game window in Spark is a **UI Panel**. The inventory screen, quest log, character sheet, settings menu, and crafting window are all panels managed by Spark's panel system.

Panels can be:

* **Shown and hidden** through keybinds, buttons, or triggers
* **Toggled** with a single key press
* **Stacked** with priority ordering (higher priority panels appear on top)
* **Modal** to block interaction with panels behind them (like confirmation popups)

The system tracks which panels are open and publishes events when panels open or close. This is useful for pausing gameplay, switching input modes, or updating the cursor.

## Confirmation Popups

The **ConfirmationPopup** component provides a modal dialog with customizable text and confirm/cancel buttons. Use it for:

* "Are you sure you want to delete this item?"
* "Abandon this quest?"
* "Purchase this item for 500 Gold?"

## Event Messages

The **EventMessagePopup** displays temporary messages in response to events. For example:

* "Quest Accepted: The Missing Merchant"
* "New Item: Iron Sword"
* "Achievement Unlocked!"

It subscribes to **ShowEventMessageEvent** and displays whatever message is published.

## Drag & Drop System

Spark's drag-and-drop system lets players move data between UI slots. Common uses:

* Dragging items between inventory slots
* Dragging an item from inventory to an equipment slot
* Dragging an ability from the spellbook to the action bar
* Dragging items between the inventory and a storage container

The system is built from:

* **Drop Sources**: UI elements you can drag from.
* **Drop Targets**: UI elements you can drop onto.
* **Handlers**: Logic that defines what happens for each source/target combination.

As an editor user, drag and drop is pre-configured in the templates. The Items, Equipment, ItemBar, and Spellbooks plugins all include drag-and-drop handlers that work together automatically.

## Panel Management

You can control panels through:

* **Keybinds**: Press "I" to toggle inventory, "Q" to toggle quest log, etc.
* **Buttons**: Click a UI button to show a panel.
* **Escape key**: The **EscapeKeyPressedEvent** can be used to close the topmost panel or open a pause menu.
* **Triggers**: "Open the crafting window" as a trigger action.

The panel manager ensures that when all panels close, gameplay resumes normally.


# Plugins

Spark is built as a collection of plugins. Each plugin adds a specific feature set to your game and is managed through its own tabs in the Spark Editor.

Plugins are sold individually on the [Unity Asset Store](https://assetstore.unity.com/publishers/49855). See [How is Spark Distributed](/documentation/user-guide/getting-started/how-spark-is-distributed) for details on purchasing options.

## Available Plugins

| Plugin                                                                     | Description                                         |
| -------------------------------------------------------------------------- | --------------------------------------------------- |
| [Combat](/documentation/user-guide/plugins/combat)                         | Abilities, stats, formulas, statuses, and factions  |
| [Items](/documentation/user-guide/plugins/items)                           | Items, inventories, equipment, and loot tables      |
| [Currency](/documentation/user-guide/plugins/currency)                     | Currency types, wallets, and conversions            |
| [Crafting](/documentation/user-guide/plugins/crafting)                     | Crafting recipes and categories                     |
| [Quests](/documentation/user-guide/plugins/quests)                         | Quests, objectives, and rewards                     |
| [Progression](/documentation/user-guide/plugins/progression)               | Experience, leveling, and level-up rewards          |
| [Classes](/documentation/user-guide/plugins/classes)                       | Character classes with main and secondary selection |
| [Professions](/documentation/user-guide/plugins/professions)               | Learnable trade skills                              |
| [Races](/documentation/user-guide/plugins/races)                           | Playable races with character variants              |
| [Character Customization](/documentation/user-guide/plugins/customization) | Visual appearance customization                     |
| [Spellbooks](/documentation/user-guide/plugins/spellbooks)                 | Ability collections                                 |
| [Item Bar](/documentation/user-guide/plugins/item-bar)                     | Quick-use item slots                                |
| [NPCs](/documentation/user-guide/plugins/npcs)                             | NPC definitions and interactions                    |


# Combat

The Combat plugin provides a complete combat system: abilities with multiple targeting types, a powerful stat system with value and resource stats, nCalc-powered formulas, damage and healing type scaling, statuses with ticking and stacking, and faction relationships.

In the Spark Editor, the Combat plugin is organized into these categories:

| Category                                                                | What it contains                                                              |
| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| [Abilities](/documentation/user-guide/plugins/combat/abilities)         | Ability entries, combos, tooltip keywords, and all 8 effect types             |
| [Formulas](/documentation/user-guide/plugins/combat/formulas)           | nCalc calculation formulas used across all systems                            |
| [Damage Types](/documentation/user-guide/plugins/combat/damage-types)   | Main and secondary damage type classifications                                |
| [Healing Types](/documentation/user-guide/plugins/combat/healing-types) | Main and secondary healing type classifications                               |
| [Stats](/documentation/user-guide/plugins/combat/stats)                 | Value stats, resource stats, modifiers, bonuses, templates, and UI categories |
| [Statuses](/documentation/user-guide/plugins/combat/statuses)           | Status effects with stacking, ticking, immunity, VFX, and rules integration   |
| [Factions](/documentation/user-guide/plugins/combat/factions)           | Faction definitions, relationships, and targeting rules                       |


# Abilities

Abilities are the core of the Combat plugin. An ability is any action a character performs: attacking, casting a spell, drinking a potion, buffing an ally, or placing an area effect.

## Creating an Ability

Open the Spark Editor, go to **Combat > Abilities** and click **Create New**.

Each ability entry defines:

| Field           | Description                                               |
| --------------- | --------------------------------------------------------- |
| Display Name    | Name shown in-game.                                       |
| Description     | Tooltip text.                                             |
| Icon            | Sprite for UI display.                                    |
| Ability Type    | Determines targeting behavior and area of effect.         |
| Activation Type | Determines cast timing (instant, channeled, incantation). |
| Effects         | List of effects that execute when the ability hits.       |

## Ability Types

The ability type controls targeting and area of effect:

| Type                         | Description                                                      |
| ---------------------------- | ---------------------------------------------------------------- |
| **Direct Target**            | Targets a single entity. Requires line of sight and range check. |
| **Action Target**            | Targets the entity in front of the caster (action game style).   |
| **Self**                     | Targets the caster. No targeting needed.                         |
| **AoE (Area of Effect)**     | Affects all entities within a radius around a point.             |
| **Direct Target AoE**        | Targets an entity, then applies effects in a radius around them. |
| **Direct Target Projectile** | Fires a projectile at a target.                                  |
| **Action Projectile**        | Fires a projectile in the forward direction.                     |
| **Ground**                   | Targets a point on the ground (click-to-place).                  |
| **Cone**                     | Affects entities in a cone in front of the caster.               |
| **Rectangle**                | Affects entities in a rectangular area.                          |
| **Aura**                     | Continuously applies effects to nearby entities.                 |

Choose the type that matches your ability. A sword slash might use Action Target or Cone. A fireball might use Direct Target Projectile. A heal might use Self or Direct Target.

## Activation Types

The activation type controls how the ability is cast:

| Type            | Description                                                                               |
| --------------- | ----------------------------------------------------------------------------------------- |
| **Instant**     | Fires immediately with no cast time.                                                      |
| **Channel**     | The caster channels for a fixed duration. Uses an animator bool for animation.            |
| **Incantation** | The caster performs a cast animation. Fires when animation completes. Can be interrupted. |

## Effects

Effects are the actions an ability performs when it hits. Each ability has a list of effects that execute in order. Every effect has an optional **Delay** field (0–10 seconds) that delays its execution after the ability fires.

Spark includes **8 built-in effect types**:

### Damage Effect

Deals damage to an entity by reducing a target stat. This is the primary combat effect.

| Field                      | Description                                                                          |
| -------------------------- | ------------------------------------------------------------------------------------ |
| Base Damage                | Base damage amount before formulas and scaling.                                      |
| Main Damage Type           | Primary damage classification (e.g., Physical, Magical).                             |
| Secondary Damage Type      | Optional sub-classification (e.g., Fire, Frost) for more precise scaling.            |
| Calculation Formula        | Optional nCalc formula for custom damage calculation.                                |
| Target Stat                | The stat to reduce (e.g., Health).                                                   |
| Status Condition Modifiers | Bonus damage based on active statuses on the target (flat or percentage, per-stack). |
| Play Hit Animation         | Trigger the "Hit" animator parameter on the target.                                  |
| Play VFX                   | Spawn a visual effect on the target with configurable offset, scale, and duration.   |
| Stagger NPC                | Stop NPC movement on hit for a configurable duration.                                |
| Play Audio                 | Play a sound effect on hit.                                                          |

### Healing Effect

Restores a target stat on an entity. Works identically to Damage but adds instead of subtracts.

| Field                      | Description                                            |
| -------------------------- | ------------------------------------------------------ |
| Base Healing               | Base healing amount before formulas and scaling.       |
| Main Healing Type          | Primary healing classification (e.g., Direct Heal).    |
| Secondary Healing Type     | Optional sub-classification (e.g., Holy, Nature).      |
| Calculation Formula        | Optional nCalc formula for custom healing calculation. |
| Target Stat                | The stat to increase (e.g., Health).                   |
| Status Condition Modifiers | Bonus healing based on active statuses on the target.  |
| Play Audio                 | Play a sound effect on heal.                           |

### Apply Status Effect

Applies a status effect to the target entity.

| Field             | Description                                               |
| ----------------- | --------------------------------------------------------- |
| Status Entry      | The status to apply.                                      |
| Duration Override | Override the status's default duration (0 = use default). |
| Play Audio        | Play a sound effect on application.                       |

### Modify Stat Effect

Temporarily modifies stat values while a status is active. When the status ends, modifications are automatically reversed.

| Field               | Description                                                      |
| ------------------- | ---------------------------------------------------------------- |
| Stat Modifications  | List of modifications, each with:                                |
| — Target Stat       | Which stat to modify.                                            |
| — Target Property   | Which property (e.g., "value", "current", "maximum").            |
| — Modification Type | **Add**, **Subtract**, **Multiply**, **Percentage**, or **Set**. |
| — Value             | The amount for the chosen modification type.                     |

### Knockback Effect

Pushes the target away from the caster. Requires a **KnockbackReceiver** component on the target.

| Field        | Description                                                       |
| ------------ | ----------------------------------------------------------------- |
| Distance     | Distance in meters the target is pushed.                          |
| Duration     | How long the knockback movement takes (0.05–2 seconds).           |
| Curve        | Animation curve controlling knockback speed over time.            |
| Always Apply | Bypass ability restrictions and apply regardless of target state. |
| Play Audio   | Play a sound effect on knockback.                                 |

### Damage Object Effect

Deals damage to **ObjectCombatEntity** targets (destructible objects in the world, separate from normal entity damage).

| Field         | Description                          |
| ------------- | ------------------------------------ |
| Object Damage | Damage amount to deal to the object. |

### Dispel Effect

Removes dispellable status effects from the target.

| Field        | Description                                                          |
| ------------ | -------------------------------------------------------------------- |
| Dispel Count | Number of statuses to remove (0 = remove all dispellable).           |
| Target Tag   | Only dispel statuses with this tag (empty = any dispellable status). |

### Trigger Effect

Executes a Spark trigger sequence as an ability effect. This bridges the ability system with the trigger system, letting you use any trigger type (give items, load scenes, gain currency, etc.) as part of an ability.

| Field              | Description                   |
| ------------------ | ----------------------------- |
| Trigger To Execute | The trigger entry to execute. |

## Combos

Combos are sequences of abilities that chain together. Create combo entries in the **Combat > Abilities > Combos** tab. A combo defines an ordered list of abilities that must be activated in sequence within a time window.

## Tooltip Keywords

Tooltip keywords are special terms that appear highlighted in ability descriptions. For example, "Burning" could be a keyword that shows its own tooltip when hovered.

Create keywords in the **Combat > Abilities > Tooltip Keywords** tab. Each keyword has a display name, description, and color.


# Damage Types

Damage types classify the kind of damage an ability deals. They are split into **Main Damage Types** and **Secondary Damage Types**, each serving a different purpose in the damage pipeline.

## Main Damage Types

Main damage types represent the broad category of damage. They are used in the damage scaling system — stats can provide scaling bonuses based on main damage type.

Create them in **Combat > Formulas > Main Damage Types**.

Examples: Physical, Magical, True, Environmental.

When a damage effect has a main damage type assigned, the damage scaling calculator iterates all stats on the caster and checks for matching damage type scaling entries. This is how a stat like "Spell Power" can increase Magical damage but not Physical damage.

## Secondary Damage Types

Secondary damage types provide finer classification within a main type. They are **optional** — a damage effect can have a main type without a secondary type. Secondary types allow for more precise scaling and categorization.

Create them in **Combat > Formulas > Secondary Damage Types**.

Examples: Fire, Frost, Nature, Lightning, Poison, Holy, Shadow, Blunt, Slash, Pierce.

Secondary types let you create specialized stats like "Fire Damage Bonus" that only affects Fire-typed abilities, or resistances that reduce specific elemental damage.

## How Damage Scaling Works

Stats can define damage type scaling entries that link a stat to damage types. Each scaling entry specifies:

| Field              | Description                                                                                                                                  |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Damage Type        | Which damage type this scaling applies to.                                                                                                   |
| Scaling Mode       | **Flat** (add fixed amount per stat point), **Percentage** (add percentage per stat point), **Formula** (custom nCalc formula), or **None**. |
| Flat Scaling       | For Flat mode: damage bonus per point of this stat.                                                                                          |
| Percentage Scaling | For Percentage mode: percentage bonus per point of this stat.                                                                                |
| Scaling Formula    | For Formula mode: a formula entry for custom scaling logic.                                                                                  |

For example, a "Strength" stat might have a Flat scaling of 0.5 for Physical damage — meaning every point of Strength adds 0.5 to Physical damage abilities.

## Damage Calculation Flow

When a damage effect executes:

1. Start with **Base Damage** × **Stack Multiplier**.
2. If a **Calculation Formula** is set, evaluate it with caster/target stats as variables.
3. If a **Main Damage Type** is set, apply damage type scaling from all caster stats that have scaling for that type.
4. If **Status Condition Modifiers** are configured, apply bonus damage based on active statuses on the target (flat or percentage, optionally per-stack).
5. The final damage is applied to the **Target Stat** (typically Health).


# Factions

Factions define group allegiances and relationships between characters. They control who is friendly, neutral, or hostile to whom, and determine ability targeting, NPC behavior, and combat engagement.

## Creating a Faction

Open the Spark Editor, go to **Combat > Factions** and click **Create New**.

Each faction entry has:

| Field                | Description                                                                             |
| -------------------- | --------------------------------------------------------------------------------------- |
| Display Name         | Faction name (e.g., "Alliance", "Bandits", "Wildlife").                                 |
| Description          | Faction description.                                                                    |
| Icon                 | Sprite for UI display.                                                                  |
| Default Relationship | The default relationship with any faction not explicitly defined (defaults to Neutral). |
| Relationships        | List of explicit relationships with other factions.                                     |

## Faction Relationships

Each relationship entry defines how this faction views another:

| Field          | Description                            |
| -------------- | -------------------------------------- |
| Target Faction | The other faction.                     |
| Relationship   | **Ally**, **Neutral**, or **Hostile**. |

Relationships are **directional** — faction A being hostile to faction B does not automatically mean faction B is hostile to faction A. You must define both directions separately if you want symmetry.

### Relationship Types

| Type        | Description                                                                                                        |
| ----------- | ------------------------------------------------------------------------------------------------------------------ |
| **Self**    | Automatically assigned when checking an entity against itself.                                                     |
| **Ally**    | Entities in the same faction, or factions explicitly set as allies. Targets for friendly abilities (heals, buffs). |
| **Neutral** | Default relationship. Entities do not attack on sight but can be targeted.                                         |
| **Hostile** | Enemies. NPCs will engage in combat, abilities can target them as enemies.                                         |

Relationship types support bitwise combinations, meaning ability effects can specify which relationship types they affect (e.g., "this heal targets Self and Ally").

### Relationship Lookup

When checking the relationship between two entities:

1. If they are the same entity, return **Self**.
2. If they are in the same faction, return **Ally**.
3. Check the explicit relationships list for the target's faction.
4. If no explicit relationship is found, use the **Default Relationship**.

## Faction Settings

Global faction settings are configured in **Combat > Factions > Settings**:

| Field               | Description                                                                                                       |
| ------------------- | ----------------------------------------------------------------------------------------------------------------- |
| Ally Color          | UI color for allied entities (default: green).                                                                    |
| Neutral Color       | UI color for neutral entities (default: yellow).                                                                  |
| Hostile Color       | UI color for hostile entities (default: red).                                                                     |
| Out Of Combat Timer | Seconds after the last hostile action before an entity automatically exits combat state (default: 5, range 0–60). |

## How Factions Affect Combat

Factions integrate with the combat system in several ways:

* **Ability targeting**: Effects filter valid targets by relationship type. A hostile damage ability only hits enemies; a friendly heal only hits allies.
* **Combat state**: When a hostile or neutral effect hits an entity, both the caster and target are set to "IN\_COMBAT" state.
* **NPC behavior**: Hostile NPCs engage players on sight. Neutral NPCs only fight back when attacked.
* **UI**: Nameplates, health bars, and other UI elements use faction colors to indicate friend or foe.


# Formulas

Formulas are the math engine behind Spark's number crunching. They are used for damage calculations, healing calculations, stat scaling, stat bonuses, and anywhere else a custom calculation is needed. Formulas are not limited to combat — any system that needs a calculated value can reference a formula.

Spark uses **nCalc**, a powerful open-source expression evaluator for .NET, as its formula engine. This means formulas support a full range of mathematical operations, conditional logic, and built-in functions.

## Creating a Formula

Open the Spark Editor, go to **Combat > Formulas** and click **Create New**.

Each formula entry has:

| Field              | Description                                                                           |
| ------------------ | ------------------------------------------------------------------------------------- |
| Formula Expression | The nCalc math expression (e.g., `BaseDamage + [AttackPower] * 2.5 - [Armor] * 0.5`). |
| Variable Mappings  | Maps variable names in the expression to stat values from the caster or target.       |

## Variable Mappings

Each variable mapping connects a name used in the formula to an actual stat value at runtime:

| Field         | Description                                                                                                           |
| ------------- | --------------------------------------------------------------------------------------------------------------------- |
| Variable Name | The name used in the expression (e.g., "AttackPower").                                                                |
| Stat          | Which stat entry to read the value from.                                                                              |
| Property ID   | Which property of the stat: `value` (for value stats), `current` or `maximum` (for resource stats). Default: `value`. |
| Source        | Whether to read from the **Caster** or the **Target** entity.                                                         |

## Built-in Variables

These variables are automatically available in every formula without needing a mapping:

| Variable          | Description                                                      |
| ----------------- | ---------------------------------------------------------------- |
| `BaseDamage`      | The base damage value from the damage effect.                    |
| `BaseHealing`     | The base healing value from the healing effect.                  |
| `StackMultiplier` | The current stack count of the status applying this effect.      |
| `Random`          | A random value between 0 and 1, generated fresh each evaluation. |

## nCalc Expression Reference

Spark's formula system supports the full nCalc expression language. Below is a reference of everything you can use in formula expressions.

### Arithmetic Operators

| Operator | Description         | Example                 |
| -------- | ------------------- | ----------------------- |
| `+`      | Addition            | `BaseDamage + 10`       |
| `-`      | Subtraction         | `[MaxHP] - [CurrentHP]` |
| `*`      | Multiplication      | `[AttackPower] * 2.5`   |
| `/`      | Division            | `[Damage] / 2`          |
| `%`      | Modulus (remainder) | `[Level] % 10`          |
| `**`     | Exponentiation      | `[Level] ** 2`          |

### Comparison Operators

| Operator     | Description           | Example             |
| ------------ | --------------------- | ------------------- |
| `=` or `==`  | Equal                 | `[Level] = 10`      |
| `!=` or `<>` | Not equal             | `[Class] != 0`      |
| `<`          | Less than             | `[CurrentHP] < 100` |
| `>`          | Greater than          | `[Armor] > 50`      |
| `<=`         | Less than or equal    | `[Mana] <= 0`       |
| `>=`         | Greater than or equal | `[Level] >= 5`      |

### Logical Operators

| Operator       | Description | Example                         |
| -------------- | ----------- | ------------------------------- |
| `and` or `&&`  | Logical AND | `[Level] > 5 and [HasBuff] = 1` |
| `or` or `\|\|` | Logical OR  | `[IsBoss] = 1 or [IsElite] = 1` |
| `not`          | Logical NOT | `not ([IsDead] = 1)`            |

### Conditional Functions

| Function                                    | Description                                                        | Example                                                         |
| ------------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------- |
| `if(condition, trueValue, falseValue)`      | Returns one of two values based on a condition.                    | `if([CurrentHP] < [MaxHP] * 0.3, BaseDamage * 1.5, BaseDamage)` |
| `switch(condition1, result1, ..., default)` | Returns a value from a list based on the first matching condition. | `switch([Element] = 1, 'Fire', [Element] = 2, 'Ice', 'Normal')` |
| `in(value, set...)`                         | Checks if a value is in a set.                                     | `in([DamageType], 1, 3, 5)`                                     |

### Built-in Math Functions

nCalc provides all standard math functions from .NET's `System.Math`:

| Function              | Description                       | Example                                |
| --------------------- | --------------------------------- | -------------------------------------- |
| `Abs(x)`              | Absolute value                    | `Abs([Armor] - [Penetration])`         |
| `Ceiling(x)`          | Round up to nearest integer       | `Ceiling(BaseDamage * 1.3)`            |
| `Floor(x)`            | Round down to nearest integer     | `Floor([CritMultiplier] * BaseDamage)` |
| `Round(x)`            | Round to nearest integer          | `Round([AttackPower] * 0.7)`           |
| `Round(x, decimals)`  | Round to specified decimal places | `Round([DPS], 2)`                      |
| `Truncate(x)`         | Remove decimal portion            | `Truncate([RawDamage])`                |
| `Max(x, y)`           | Return the larger value           | `Max(1, BaseDamage - [Armor])`         |
| `Min(x, y)`           | Return the smaller value          | `Min([CurrentHP], [MaxHP])`            |
| `Pow(x, y)`           | Raise x to the power of y         | `Pow([Level], 1.5) * 10`               |
| `Sqrt(x)`             | Square root                       | `Sqrt([Intelligence]) * 5`             |
| `Log(x)`              | Natural logarithm                 | `Log([Level] + 1) * 50`                |
| `Log10(x)`            | Base-10 logarithm                 | `Log10([Experience])`                  |
| `Log(x, base)`        | Logarithm with custom base        | `Log([Value], 2)`                      |
| `Exp(x)`              | e raised to the power of x        | `Exp(-0.1 * [Armor])`                  |
| `Sign(x)`             | Returns -1, 0, or 1               | `Sign([Direction])`                    |
| `Sin(x)`              | Sine (radians)                    | `Sin([Angle])`                         |
| `Cos(x)`              | Cosine (radians)                  | `Cos([Angle])`                         |
| `Tan(x)`              | Tangent (radians)                 | `Tan([Angle])`                         |
| `Asin(x)`             | Arc sine                          | `Asin([Value])`                        |
| `Acos(x)`             | Arc cosine                        | `Acos([Value])`                        |
| `Atan(x)`             | Arc tangent                       | `Atan([Value])`                        |
| `Atan2(y, x)`         | Arc tangent of y/x                | `Atan2([DeltaY], [DeltaX])`            |
| `IEEERemainder(x, y)` | IEEE remainder                    | `IEEERemainder([Value], 360)`          |

## Formula Examples

Here are some practical formula examples for common game mechanics:

**Simple damage with armor reduction:**

```
Max(1, BaseDamage + [AttackPower] * 2 - [TargetArmor] * 0.5)
```

**Critical hit with random chance:**

```
if(Random < [CritChance] / 100, BaseDamage * [CritMultiplier], BaseDamage)
```

**Percentage-based healing:**

```
[TargetMaxHP] * 0.15 + [HealingPower] * 0.5
```

**Level-scaled damage with diminishing returns:**

```
BaseDamage * (1 + Log([Level] + 1) * 0.5) + [AttackPower] * 1.2
```

**Conditional damage bonus (execute-style, bonus damage on low HP targets):**

```
if([TargetCurrentHP] < [TargetMaxHP] * 0.3, BaseDamage * 2.0, BaseDamage) + [AttackPower]
```

## Where Formulas Are Used

Formulas appear in several places throughout Spark:

* **Damage effects**: Custom damage calculation replacing the default BaseDamage.
* **Healing effects**: Custom healing calculation.
* **Stat scaling**: Stats can scale damage/healing types using a formula instead of flat/percentage.
* **Stat bonuses**: Stat-to-stat bonuses can use formulas for complex scaling relationships.

Because formulas are database entries, the same formula can be reused across multiple abilities, stats, and effects.

## Performance

Spark caches compiled formula expressions and uses version tracking to invalidate the cache only when a formula changes. This means formula evaluation is fast at runtime even with complex expressions. The result is always clamped to a minimum of 0 to prevent negative values.


# Healing Types

Healing types classify the kind of healing an ability provides. Like damage types, they are split into **Main Healing Types** and **Secondary Healing Types**.

## Main Healing Types

Main healing types represent the broad category of healing. They are used in the healing scaling system — stats can provide scaling bonuses based on main healing type.

Create them in **Combat > Formulas > Main Healing Types**.

Examples: Direct Heal, Heal Over Time, Absorption, Regeneration.

When a healing effect has a main healing type assigned, the healing scaling calculator checks all caster stats for matching healing type scaling entries.

## Secondary Healing Types

Secondary healing types provide finer classification. They are **optional** and allow more precise scaling.

Create them in **Combat > Formulas > Secondary Healing Types**.

Examples: Holy, Nature, Arcane, Blood, Divine, Light.

Secondary types let you create specialized stats like "Holy Healing Bonus" that only affects Holy-typed healing abilities.

## How Healing Scaling Works

Stats can define healing type scaling entries, identical in structure to damage type scaling:

| Field              | Description                                                   |
| ------------------ | ------------------------------------------------------------- |
| Healing Type       | Which healing type this scaling applies to.                   |
| Scaling Mode       | **Flat**, **Percentage**, **Formula**, or **None**.           |
| Flat Scaling       | For Flat mode: healing bonus per point of this stat.          |
| Percentage Scaling | For Percentage mode: percentage bonus per point of this stat. |
| Scaling Formula    | For Formula mode: a formula entry for custom scaling logic.   |

## Healing Calculation Flow

The healing calculation follows the same flow as damage:

1. Start with **Base Healing** × **Stack Multiplier**.
2. If a **Calculation Formula** is set, evaluate it with caster/target stats as variables.
3. If a **Main Healing Type** is set, apply healing type scaling from all caster stats.
4. If **Status Condition Modifiers** are configured, apply bonus healing based on active statuses.
5. The final healing is applied to the **Target Stat** (typically Health).


# Stats

Stats are numerical values on characters that drive combat and gameplay mechanics. Health, Mana, Strength, Agility, Armor, and Attack Power are all stats. The stat system is one of the most powerful and flexible systems in Spark.

## Creating a Stat

Open the Spark Editor, go to **Combat > Stats** and click **Create New**.

Each stat entry has:

| Field                | Description                                               |
| -------------------- | --------------------------------------------------------- |
| Display Name         | Stat name shown in UI (e.g., "Strength").                 |
| Description          | Tooltip text explaining the stat.                         |
| Icon                 | Sprite for the character sheet.                           |
| Stat Type            | Either **Value Stat** or **Resource Stat** (see below).   |
| Show In UI           | Whether the stat appears in the character sheet.          |
| UI Prefix            | Text displayed before the stat value (e.g., "$", "Lv. "). |
| UI Suffix            | Text displayed after the stat value (e.g., "%", " HP").   |
| UI Category          | Which UI category this stat belongs to.                   |
| Damage Type Scaling  | List of damage type scaling entries for this stat.        |
| Healing Type Scaling | List of healing type scaling entries for this stat.       |
| Stat Bonuses         | List of bonuses this stat grants to other stats.          |

## Stat Types

Every stat is one of two types, each with different behavior and properties.

### Value Stats

Value stats represent a single number — like Strength, Agility, or Armor. They have one property: `value`.

| Field             | Description                                      |
| ----------------- | ------------------------------------------------ |
| Default Value     | Starting value for this stat.                    |
| Has Min Value     | Whether the stat has a minimum.                  |
| Min Value         | Minimum value (if enabled).                      |
| Has Max Value     | Whether the stat has a maximum.                  |
| Max Value         | Maximum value (if enabled).                      |
| Auto Clamp Values | Automatically clamp the value to min/max bounds. |

### Resource Stats

Resource stats have two properties: `current` and `maximum`. They represent depletable resources like Health, Mana, or Stamina.

| Field                     | Description                                                   |
| ------------------------- | ------------------------------------------------------------- |
| Default Maximum           | The default maximum value.                                    |
| Min Maximum               | The minimum the maximum can be reduced to.                    |
| Initialize Current To Max | Whether current starts equal to maximum.                      |
| Default Current           | Override starting current value (if not initializing to max). |

#### Time-Based Changes (Regeneration / Degeneration)

Resource stats can automatically change over time. This is configured per stat:

| Field               | Description                                                                                                    |
| ------------------- | -------------------------------------------------------------------------------------------------------------- |
| Enable Time Changes | Turn on automatic regeneration or degeneration.                                                                |
| Disable In Combat   | Pause time-based changes while the entity is in combat.                                                        |
| Change Direction    | **Regeneration** (increase over time) or **Degeneration** (decrease over time).                                |
| Change Type         | **Fixed Amount** (add/subtract a flat number per tick) or **Percentage** (add/subtract a percentage per tick). |
| Change Amount       | The amount per tick.                                                                                           |
| Percentage Basis    | For percentage mode: calculate based on **Current** value or **Maximum** value.                                |
| Change Interval     | Seconds between each tick.                                                                                     |

For example, you could set Health to regenerate 2% of maximum every 3 seconds, but only out of combat.

#### On Value Reached Triggers

Resource stats can fire triggers when their current value reaches a specific threshold. This is configured as a list of trigger conditions on the stat:

| Field           | Description                                                                                                      |
| --------------- | ---------------------------------------------------------------------------------------------------------------- |
| Value           | The threshold value to check against.                                                                            |
| Comparison Type | **Equal**, **Not Equal**, **Greater Than**, **Greater Than Or Equal**, **Less Than**, or **Less Than Or Equal**. |
| Trigger Entry   | The Spark trigger to execute when the condition is met.                                                          |
| Execute Once    | If true, the trigger fires only once. If false, it fires every time the condition is met.                        |

This system enables game mechanics like "when Health reaches 0, execute a death trigger" or "when Mana drops below 10, play a warning effect."

## Stat Modifiers

Stat modifiers change stat values temporarily or permanently. Each modifier targets a specific stat and property.

| Field           | Description                                        |
| --------------- | -------------------------------------------------- |
| Target Stat     | Which stat to modify.                              |
| Target Property | Which property (`value`, `current`, or `maximum`). |
| Modifier Type   | How the modification is applied (see below).       |
| Value           | The modifier amount.                               |
| Duration        | Duration in seconds (0 = permanent until removed). |

### Modifier Types

Modifiers are applied in a specific order during stat recalculation:

| Type                  | Priority      | Description                                                    |
| --------------------- | ------------- | -------------------------------------------------------------- |
| **Override**          | 1st (highest) | Sets the stat to an exact value, ignoring all other modifiers. |
| **Additive**          | 2nd           | Adds a flat amount to the base value.                          |
| **Percentage**        | 3rd           | Adds a percentage of the base + additive value.                |
| **Percentage Of Max** | 4th           | Adds a percentage of a reference maximum value.                |
| **Multiplicative**    | 5th (lowest)  | Multiplies the final result by a factor.                       |
| **Conditional**       | Special       | Only applies if a condition on another stat is met.            |

Conditional modifiers let you create effects like "increase Attack Power by 20% while Health is below 30%." Each conditional modifier specifies a condition stat, property, comparison type, and threshold value.

## Stat Bonuses

Stats can grant bonuses to other stats. For example, every point of Strength could add 2 points to Attack Power. Bonuses are configured on the source stat entry.

Each bonus specifies:

| Field           | Description                    |
| --------------- | ------------------------------ |
| Target Stat     | Which stat receives the bonus. |
| Target Property | Which property to modify.      |
| Bonus Type      | How the bonus is calculated.   |

### Bonus Types

| Type           | Description                                                                 |
| -------------- | --------------------------------------------------------------------------- |
| **Flat**       | Each point of the source stat adds a flat amount to the target.             |
| **Percentage** | Each point of the source stat adds a percentage of the target's base value. |
| **Multiplier** | Each point of the source stat multiplies the target by a factor.            |
| **Formula**    | A custom nCalc formula determines the bonus amount.                         |

Bonuses are calculated in this order: flat bonuses first, then percentage, then multiplier, then formula. The final calculation is: `(baseValue + flatBonus) × (1 + percentageBonus/100) × multiplier`.

Spark automatically prevents circular dependencies — if stat A gives a bonus to stat B, and stat B gives a bonus to stat A, Spark uses the raw value (without bonuses) of the source stat to avoid infinite loops.

## Damage and Healing Type Scaling

Stats can provide scaling bonuses to abilities based on their damage or healing type. Each stat entry has lists for damage type scaling and healing type scaling. See [Damage Types](/documentation/user-guide/plugins/combat/damage-types) and [Healing Types](/documentation/user-guide/plugins/combat/healing-types) for full details.

## UI Categories

UI categories organize stats into groups in the character sheet. Create them in **Combat > Stats > UI Categories**.

Each category has a display name and a sort order (lower numbers display first). Examples: "Primary Attributes," "Defensive Stats," "Offensive Stats."

## Stat Templates

Stat templates bundle a set of stat values into a reusable configuration. Create them in **Combat > Stats > Templates**.

Each template defines a list of stat configurations:

| Field         | Description                                 |
| ------------- | ------------------------------------------- |
| Stat Entry    | Which stat.                                 |
| Value         | Initial value (for value stats).            |
| Current Value | Initial current value (for resource stats). |
| Maximum Value | Initial maximum value (for resource stats). |

Assign a stat template to a character entry to give them a starting stat profile. For example, a "Warrior Template" might have high Strength and Health, while a "Mage Template" has high Intelligence and Mana.

## Stat Entity

Add a **StatEntity** component to a character's GameObject to give them stats.

| Field                     | Description                                                                   |
| ------------------------- | ----------------------------------------------------------------------------- |
| Auto Initialize All Stats | Automatically create instances for every stat in the database.                |
| Stat Entries              | Manual list of specific stats this entity has (if not using auto-initialize). |
| Enable Time Based Changes | Enable regeneration and degeneration for resource stats.                      |
| Pause When Disabled       | Pause time-based changes when the GameObject is disabled.                     |

The StatEntity manages all stat instances at runtime, handles modifier application and removal, processes time-based changes, checks on-value-reached triggers, and caches calculated values with bonuses for performance.


# Statuses

Statuses are temporary effects applied to characters. Buffs, debuffs, damage-over-time, crowd control, shields, and auras are all statuses.

## Creating a Status

Open the Spark Editor, go to **Combat > Statuses** and click **Create New**.

Each status entry has:

| Field        | Description                                   |
| ------------ | --------------------------------------------- |
| Display Name | Name shown in UI.                             |
| Description  | Tooltip text.                                 |
| Icon         | Sprite for the status bar.                    |
| Show In UI   | Whether the status appears in the status bar. |

## Duration

| Field                     | Description                                                           |
| ------------------------- | --------------------------------------------------------------------- |
| Base Duration             | Duration in seconds. Set to 0 for permanent (until manually removed). |
| Persist After Self Death  | Status remains if the affected entity dies.                           |
| Persist After Owner Death | Status remains if the entity that applied it dies.                    |

## Stacking

| Field         | Description                                                    |
| ------------- | -------------------------------------------------------------- |
| Stacking Mode | How multiple applications of this status interact (see below). |
| Max Stacks    | Maximum stack count (only used in Stack mode).                 |

### Stacking Modes

| Mode            | Description                                                                                                      |
| --------------- | ---------------------------------------------------------------------------------------------------------------- |
| **Refresh**     | Reapplication resets the duration to its base value. Only one instance exists.                                   |
| **Extend**      | Reapplication adds the full duration to the remaining time.                                                      |
| **Independent** | Each application creates a separate instance. Multiple copies can exist simultaneously.                          |
| **Replace**     | Reapplication removes the old instance and creates a new one.                                                    |
| **Stack**       | Increases the stack count (up to Max Stacks) and refreshes the duration. Stack count can multiply effect values. |

## Periodic Effects (Ticks)

| Field               | Description                                                                                                        |
| ------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Has Periodic Effect | Whether the status ticks periodically.                                                                             |
| Tick Count          | Number of times the effect ticks over the full duration. The tick interval is calculated as duration ÷ tick count. |

## Effect Lists

Statuses have four separate lists of effects that execute at different points in the status lifecycle:

| Effect List              | When It Executes                                            |
| ------------------------ | ----------------------------------------------------------- |
| **On Apply Effects**     | When the status is first applied to an entity.              |
| **On Tick Effects**      | Each time the periodic timer fires (if ticking is enabled). |
| **On End Effects**       | When the status expires naturally (duration runs out).      |
| **On Dispelled Effects** | When the status is removed early by a dispel effect.        |

Each list can contain any combination of the 8 effect types (Damage, Healing, Apply Status, Modify Stat, Knockback, Damage Object, Dispel, Trigger).

## Visual Effects

| Field               | Description                                                                                                         |
| ------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Status VFX Prefab   | A GameObject prefab spawned on the target while the status is active. Automatically destroyed when the status ends. |
| VFX Position Offset | Local position offset for the VFX relative to the target.                                                           |

## Dispel Configuration

| Field            | Description                                                                                                                                |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| Can Be Dispelled | Whether this status can be removed by dispel effects. Set to false for effects that should not be removable (e.g., transformation, death). |

## Immunity

| Field              | Description                                                                                                                                                      |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Grants Immunity To | List of status tags this status grants immunity to while active. For example, a "Cleanse" buff might grant immunity to all statuses tagged "Poison" or "Debuff." |

When a status grants immunity, any new status application matching those tags is blocked and a StatusImmuneEvent is published.

## Rules Integration

| Field        | Description                                                                                             |
| ------------ | ------------------------------------------------------------------------------------------------------- |
| Apply Rules  | Rule modifications applied when the status is applied (e.g., set CAN\_MOVE to false for a root effect). |
| Remove Rules | Rule modifications applied when the status ends (e.g., restore CAN\_MOVE to true).                      |

This connects statuses to the Rules system, letting you control character behavior while a status is active.

## Status Tags

Status tags categorize statuses for grouping, filtering, and immunity. Create them in **Combat > Statuses > Status Tags**.

Each tag has a display name and a color for UI visualization.

Examples: Buff, Debuff, Crowd Control, Damage Over Time, Heal Over Time, Poison, Stun, Slow, Shield, Bleed.

Tags are used by the Dispel effect type to target specific categories of statuses, by the immunity system to block categories of statuses, and by UI to color-code or filter status displays.

## Status Removal

Statuses can be removed for several reasons:

| Reason          | Description                                                                            |
| --------------- | -------------------------------------------------------------------------------------- |
| **Expired**     | Duration ran out naturally. Executes On End Effects.                                   |
| **Dispelled**   | Removed by a Dispel effect. Executes On Dispelled Effects.                             |
| **Manual**      | Removed by code or game logic.                                                         |
| **Death**       | Target entity died.                                                                    |
| **Owner Death** | The entity that applied the status died (unless Persist After Owner Death is enabled). |


# Items

The Items plugin handles everything related to items: defining item entries with types (weapons, armor, resources, currency), storing them in inventories with slot filters and stacking, equipping them on characters with full visual support (weapon sheathing, armor mesh swapping, animator overrides), and generating randomized loot from weighted drop tables.

In the Spark Editor, the Items plugin is organized into these categories:

| Category                                                       | What it contains                                                                                                                                                                                                                         |
| -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Items](/documentation/user-guide/plugins/items/items)         | Item entries with weapon, armor, resource, and currency types; item rarities; weapon types; loot tables with drop groups and nested tables                                                                                               |
| [Inventory](/documentation/user-guide/plugins/items/inventory) | Inventory entries with slot filters and stacking; InventoryEntity setup; inventory operations; temporary inventories; drag-and-drop; persistence                                                                                         |
| [Equipment](/documentation/user-guide/plugins/items/equipment) | Equipment slots with slot types; character sheets; body parts and attachment points; body mesh types and configurations; EquipmentEntity setup; weapon combat/non-combat transitions; armor visual system; animator and camera overrides |


# Items

Items are database entries that define objects players can collect, use, equip, and trade. Every item has a type that determines its behavior and what extra fields it exposes.

## Creating an Item Entry

Open the Spark Editor, go to **Items > Items** and click **Create New**.

Every item has these core fields:

| Field          | Description                                                               |
| -------------- | ------------------------------------------------------------------------- |
| Display Name   | Name shown in-game (e.g., "Iron Sword").                                  |
| Description    | Tooltip or info panel text.                                               |
| Icon           | Sprite for inventory UI.                                                  |
| Max Stack Size | How many can stack in one slot. Set to 1 for unique items like equipment. |
| Item Rarity    | Reference to an Item Rarity entry.                                        |
| Item Type      | Determines behavior and additional type-specific fields (see below).      |

## Item Types

Each item has a type that controls its behavior and exposes type-specific configuration. The available types depend on which plugins and extensions are installed.

### Weapon Item Type

Weapons are equippable items that attach a visual model to the character and can override animations.

| Field                        | Description                                                                                                               |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Hand Type                    | **Main Hand**, **Off Hand**, **Any Hand** (can go in either), **Two Handed**, or **Ranged**.                              |
| Equipment Slot               | Which equipment slot this weapon occupies.                                                                                |
| Weapon Type                  | Reference to a Weapon Type entry (Sword, Axe, Bow, etc.).                                                                 |
| Weapon Prefab                | The GameObject prefab instantiated when the weapon is equipped.                                                           |
| Weapon Scale                 | Scale applied to the weapon model (default: 1,1,1).                                                                       |
| Animator Override Controller | Overrides the character's animations while this weapon is equipped (e.g., different idle/attack anims for sword vs. bow). |
| Equip Requirements           | List of requirement groups the player must meet to equip this weapon.                                                     |

**Hand-specific visual configuration:**

For each hand type, you configure where the weapon appears on the character in combat and non-combat states:

| Field                             | Description                                                   |
| --------------------------------- | ------------------------------------------------------------- |
| Body Part (Non-Combat)            | Where the weapon is attached when sheathed (e.g., back, hip). |
| Position Coordinates (Non-Combat) | Local position and rotation when sheathed.                    |
| Body Part (Combat)                | Where the weapon is attached when drawn (e.g., right hand).   |
| Position Coordinates (Combat)     | Local position and rotation when drawn.                       |
| Character Coordinates             | Per-character overrides for different character models.       |

**Sheath/Unsheath animations:**

| Field                    | Description                                                                     |
| ------------------------ | ------------------------------------------------------------------------------- |
| Play Unsheath Animation  | Trigger an unsheath animation when entering combat.                             |
| Play Sheathe Animation   | Trigger a sheath animation when exiting combat.                                 |
| Unsheath Animation Delay | Seconds to wait before repositioning the weapon during unsheath (default: 0.3). |
| Sheathe Animation Delay  | Seconds to wait before repositioning during sheath (default: 0.3).              |

When combat state changes, the weapon transitions between its non-combat body part (e.g., back) and combat body part (e.g., hand) with optional animation.

### Armor Item Type

Armor items are equippable items that show/hide GameObjects on the character and can control body mesh visibility.

| Field                   | Description                                                                                 |
| ----------------------- | ------------------------------------------------------------------------------------------- |
| Equipment Slot          | Which equipment slot this armor occupies.                                                   |
| Armor GameObject Name   | Name of a child GameObject on the character to show when equipped and hide when unequipped. |
| Body Mesh Configuration | Which body meshes to hide while this armor is worn (e.g., hide hair under helmet).          |
| Equip Requirements      | List of requirement groups the player must meet to equip this armor.                        |

When a character equips armor:

1. The named child GameObject is found on the character and activated (made visible).
2. Body meshes defined in the configuration are hidden (e.g., hair under helmet, skin under gloves).
3. When unequipped, the process reverses: the armor object is hidden and body meshes are restored.

### Resource Item Type

Resource items are generic items that serve as crafting materials, quest items, trade goods, or any other non-equippable item. They can be consumed through the item use system, and their specific behavior is defined through extensions.

### Currency Item Type

Currency items bypass the inventory entirely. When a character obtains a currency item, the item is automatically converted into the corresponding currency and added to the character's wallet. The item's stack size determines the amount of currency granted.

| Field          | Description                                       |
| -------------- | ------------------------------------------------- |
| Currency Entry | The currency to grant when this item is obtained. |

This is useful for currency drops from enemies or loot tables — instead of taking up inventory space, the currency is added to the wallet immediately.

## Item Rarities

Create item rarities in the **Items > Rarity** tab.

Each rarity has:

| Field        | Description                                                    |
| ------------ | -------------------------------------------------------------- |
| Display Name | The rarity label (e.g., "Legendary").                          |
| UI Color     | Color used for item name text, borders, and backgrounds in UI. |

You can create as many rarities as you need. Items reference a rarity entry, and the UI uses the rarity's color to highlight them. Common examples: Common (white), Uncommon (green), Rare (blue), Epic (purple), Legendary (orange), Mythic (red).

## Weapon Types

Weapon types classify weapons for targeting and requirement checking. Create them in the **Items > Weapon Types** tab.

Each weapon type is a simple entry with a display name. Examples: Sword, Axe, Bow, Staff, Dagger, Mace, Spear, Crossbow.

Weapon types are referenced by:

* **HasWeaponTypeEquippedRequirementType**: Check if the player has a specific weapon type equipped (used to gate abilities, quests, etc.).
* **Weapon items**: Each weapon item references its weapon type.

## Loot Tables

Loot tables define randomized item drops. Create them in the **Items > Loot Tables** tab.

### Loot Table Fields

| Field                         | Description                                                                                          |
| ----------------------------- | ---------------------------------------------------------------------------------------------------- |
| Drop Groups                   | Sets of items with weights and conditions.                                                           |
| Guaranteed Drops              | Items that always drop regardless of RNG.                                                            |
| Global Drop Chance Multiplier | Multiplier applied to all drop chances (range 0.1–5.0, default: 1.0). Useful for difficulty scaling. |
| Allow Duplicates              | Whether the same item can drop multiple times in a single roll.                                      |
| Max Total Drops               | Maximum items from one roll (0 = unlimited).                                                         |

### Guaranteed Drops

Each guaranteed drop specifies:

| Field        | Description                                              |
| ------------ | -------------------------------------------------------- |
| Item         | The item entry that always drops.                        |
| Min Quantity | Minimum amount to drop.                                  |
| Max Quantity | Maximum amount to drop (randomized between min and max). |

### Drop Groups

Each drop group is a pool of items with its own configuration:

| Field        | Description                                                                                                     |
| ------------ | --------------------------------------------------------------------------------------------------------------- |
| Group Name   | Descriptive label (e.g., "Rare Weapons", "Common Materials").                                                   |
| Group Weight | Relative weight for group selection (range 0.1–1000, default: 1.0). Higher weight = more likely to be selected. |
| Force Select | If true, this group is always processed regardless of weight.                                                   |
| Min Drops    | Minimum items to drop from this group when selected.                                                            |
| Max Drops    | Maximum items to drop from this group when selected.                                                            |
| Requirements | Requirement groups that must be met for this group to be eligible.                                              |
| Items        | Pool of items in this group.                                                                                    |

### Loot Items (within a Drop Group)

Each item in a drop group has:

| Field        | Description                                                                                                                      |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| Item         | The item entry that can drop.                                                                                                    |
| Weight       | Relative weight for weighted random selection (range 0.1–100, default: 1.0).                                                     |
| Min Quantity | Minimum quantity when this item drops.                                                                                           |
| Max Quantity | Maximum quantity when this item drops.                                                                                           |
| Drop Chance  | Additional chance modifier (0.0–1.0). At 1.0, the item is always in the pool. At 0.5, there's a 50% chance it's even considered. |
| Nested Table | Instead of dropping an item, roll another loot table. This lets you create complex nested loot hierarchies.                      |

To roll a loot table in-game, use the **Roll Loot Table** trigger type with the loot table entry and a target inventory.

## Item Lifecycle

Each item type can respond to key moments in an item's life:

| Moment           | When it happens                                |
| ---------------- | ---------------------------------------------- |
| **On Use**       | The player uses the item from their inventory. |
| **On Obtained**  | The item is added to an inventory.             |
| **On Destroyed** | The item is removed or destroyed.              |
| **On Drop**      | The item is dropped from the inventory.        |

Different item types handle these moments differently. For example, currency items add currency to the wallet on obtained, while resource items may trigger crafting-related logic on use.


# Inventory

Inventories store items on characters. Each inventory is a configurable set of slots that can hold item stacks, with support for filtering, locking, stacking, drag-and-drop, temporary inventories, and persistence.

## Creating an Inventory Entry

Open the Spark Editor, go to **Items > Inventory** and click **Create New**.

Each inventory entry defines:

| Field              | Description                                                         |
| ------------------ | ------------------------------------------------------------------- |
| Slot Count         | Number of slots in the inventory (default: 20).                     |
| Allow Stacking     | Whether items can stack in a single slot.                           |
| Auto Stack Similar | Automatically merge matching items into existing stacks when added. |
| Inventory Type     | Optional type reference for custom inventory behavior.              |

## Slot Filters

Individual slots can have filters that restrict what items they accept:

| Field              | Description                                                                      |
| ------------------ | -------------------------------------------------------------------------------- |
| Slot Index         | Which slot this filter applies to.                                               |
| Allowed Item Types | Whitelist of item types this slot accepts (empty = accept all).                  |
| Blocked Item Types | Blacklist of item types this slot rejects.                                       |
| Max Stack Override | Override the item's default max stack size for this slot (0 = use item default). |

This lets you create specialized inventory layouts. For example, the first row of slots could only accept potions, or a specific slot could be locked to quest items only.

## Setting Up an Inventory Entity

To give a character (or any GameObject) an inventory at runtime, add an **InventoryEntity** component in the Inspector.

| Field                | Description                                                                                |
| -------------------- | ------------------------------------------------------------------------------------------ |
| Inventory Entry      | The inventory entry to use as a template.                                                  |
| Custom Inventory Id  | Optional custom ID. Leave empty for auto-generated.                                        |
| Auto Create On Start | Automatically create the inventory when the component starts (default: true).              |
| Destroy On Disable   | Destroy the inventory when the component is disabled (default: false).                     |
| Is Persistent        | Save and load inventory contents across sessions (default: false).                         |
| Auto Save On Change  | Automatically save whenever the inventory changes (default: true). Requires Is Persistent. |
| Load On Create       | Load saved data when creating the inventory (default: true). Requires Is Persistent.       |
| Spark Entity         | Reference to the SparkEntity on this character. Required for persistence.                  |

## Inventory Slots

Each slot in an inventory is an independent container that holds one item stack.

**Slot features:**

| Feature      | Description                                                                                                                                       |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Locking      | Locked slots cannot have items added or removed by the player. Useful for quest items that should not be moved, or reserved slots unlocked later. |
| Highlighting | Visual highlight state for UI feedback — for example, highlighting valid slots when the player is dragging an item.                               |
| Quick Use    | Marks a slot for quick-use functionality such as hotbar binding.                                                                                  |

## Inventory Operations

The inventory supports these operations:

| Operation | Description                                                                                                |
| --------- | ---------------------------------------------------------------------------------------------------------- |
| Add       | Add an item to the inventory. If stacking is enabled and a matching stack exists, the item merges into it. |
| Remove    | Remove an item or quantity from the inventory.                                                             |
| Move      | Move an item between slots within the same inventory.                                                      |
| Transfer  | Move an item from one inventory to another.                                                                |
| Split     | Split a stack into two separate slots.                                                                     |
| Merge     | Combine two matching stacks into one slot.                                                                 |
| Sort      | Sort all items in the inventory.                                                                           |
| Clear     | Remove all items. Optionally skip locked slots.                                                            |
| Equip     | Move an item to an equipment slot.                                                                         |
| Unequip   | Return an item from an equipment slot to the inventory.                                                    |
| Use       | Consume or use an item.                                                                                    |

## Temporary Inventories

Inventories can be created as temporary with an optional expiration duration. Temporary inventories are useful for timed loot windows, vendor inventories, or any container that should not persist. When a temporary inventory expires, it is automatically cleaned up.

## Drag-and-Drop

The inventory system includes built-in drag-and-drop for UI interaction.

| Source         | Target            | Result                                                                                                                     |
| -------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------- |
| Inventory Slot | Inventory Slot    | Move item between slots. If the target has a compatible item, stacks merge. If both slots have different items, they swap. |
| Inventory Slot | Equipment Slot    | Equip the item (if compatible).                                                                                            |
| Equipment Slot | Inventory Slot    | Unequip the item back to the inventory.                                                                                    |
| Inventory Slot | Outside Inventory | Triggers an item drop or deletion confirmation.                                                                            |

The drag-and-drop system validates all operations before executing — it checks slot locks, item type filters, stack limits, and slot acceptance rules. Invalid drops are rejected and the item returns to its source slot.

## Persistence

When **Is Persistent** is enabled on the InventoryEntity, the inventory contents are saved and loaded through Spark's save system.

**What is saved per slot:**

| Data       | Description                           |
| ---------- | ------------------------------------- |
| Slot Index | Which slot the item is in.            |
| Item Entry | Reference to the item database entry. |
| Quantity   | Stack count.                          |
| Is Locked  | Lock state of the slot.               |

If **Auto Save On Change** is enabled, the inventory saves automatically whenever any change occurs. If **Load On Create** is enabled, saved data is loaded when the inventory is first created.


# Equipment

The equipment system lets characters wear items in defined slots. Equipped weapons and armor attach visual models, override animations, control body mesh visibility, and transition between combat and non-combat states.

## Equipment Slots

Create equipment slots in the Spark Editor under **Items > Equipment > Slots**. Each slot defines a position where an item can be worn.

| Field                 | Description                                                  |
| --------------------- | ------------------------------------------------------------ |
| Display Name          | Slot label shown in UI (e.g., "Head", "Main Hand", "Chest"). |
| Slot Type             | Determines slot behavior. See below.                         |
| Slot Icon Placeholder | Sprite displayed in UI when the slot is empty.               |

### Slot Types

| Type      | Description                                                             |
| --------- | ----------------------------------------------------------------------- |
| Generic   | Standard slot for armor, accessories, rings, etc.                       |
| Main Hand | Primary weapon slot. Used for main-hand and two-handed weapons.         |
| Off Hand  | Secondary weapon slot. Used for off-hand weapons and shields.           |
| Ranged    | Ranged weapon slot. Used for bows, crossbows, and other ranged weapons. |

Create one slot entry per equipment position your game needs: head, chest, legs, feet, hands, main hand, off hand, ring, necklace, etc. The slot type only matters for weapon slots — set armor and accessory slots to Generic.

## Character Sheets

Character sheets define which equipment slots are available for a character. Create them in **Items > Equipment > Character Sheets**.

| Field           | Description                                            |
| --------------- | ------------------------------------------------------ |
| Available Slots | List of equipment slot entries this character can use. |

A character sheet is a layout definition. Different character types can have different sheets — for example, a warrior might have main hand, off hand, head, chest, legs, feet, and two ring slots, while a mage might replace the off hand slot with a focus slot.

You can add multiple entries of the same slot type. For example, adding two ring slot entries lets the character wear two rings.

## Body Parts

Body parts define physical attachment points on a character model where weapons and other equipment visuals are placed. Create them in **Items > Equipment > Body Parts**.

| Field            | Description                                              |
| ---------------- | -------------------------------------------------------- |
| Display Name     | The part name (e.g., "Right Hand", "Left Hip", "Back").  |
| Default Position | Default local position offset for attached items.        |
| Default Rotation | Default local rotation offset for attached items.        |
| Default Scale    | Default local scale for attached items (default: 1,1,1). |

Body parts are referenced by weapon configurations to determine where a weapon appears on the character. For example, a sword might reference a "Right Hand" body part for combat and a "Left Hip" body part when sheathed.

## Body Mesh Types and Configurations

The body mesh system controls character mesh visibility when equipment is worn.

### Body Mesh Types

Create body mesh types in **Items > Equipment > Body Mesh Types**. Each type represents a category of meshes on the character model — for example: Hair, Beard, Hands, Head, Upper Body, Lower Body.

Body mesh types are simple named entries with no extra fields.

### Body Mesh Configurations

Create body mesh configurations in **Items > Equipment > Body Mesh Configurations**. Each configuration defines which body mesh types to hide when applied.

| Field                  | Description                                                                             |
| ---------------------- | --------------------------------------------------------------------------------------- |
| Hidden Body Mesh Types | List of body mesh type entries that should be hidden when this configuration is active. |

Armor items reference a body mesh configuration. When the armor is equipped, the specified mesh types are hidden. When unequipped, the meshes are restored.

**Example:** A helmet's body mesh configuration might hide the "Hair" mesh type. When the player equips the helmet, their hair mesh is hidden. When they unequip it, the hair reappears.

## Setting Up an Equipment Entity

To let a character wear equipment at runtime, add an **EquipmentEntity** component to the character's GameObject in the Inspector.

| Field                        | Description                                                                                      |
| ---------------------------- | ------------------------------------------------------------------------------------------------ |
| Character Sheet Entry        | The character sheet that defines available equipment slots.                                      |
| Custom Equipment Id          | Optional custom ID for this equipment set. Leave empty for auto-generated.                       |
| Auto Initialize On Start     | Automatically create equipment slots on Start (default: true).                                   |
| Auto Create Missing Items    | Automatically create item instances when equipping (default: true).                              |
| Is Persistent                | Save and load equipped items across sessions (default: false).                                   |
| Auto Save On Change          | Automatically save when equipment changes (default: true). Requires Is Persistent.               |
| Load On Initialize           | Load saved equipment data when initializing (default: true). Requires Is Persistent.             |
| Spark Entity                 | Reference to the SparkEntity on this character. Required for persistence.                        |
| Auto Update Visual Equipment | Automatically update weapon/armor visuals when items are equipped or unequipped (default: true). |
| Body Entity                  | Reference to the BodyEntity component for visual attachment points and mesh management.          |
| Is In Combat                 | Current combat state of the character (default: false). Determines weapon positioning.           |

## Setting Up a Body Entity

The **BodyEntity** component maps body part entries and body mesh types to actual transforms and GameObjects on the character model. Add it to the character's avatar GameObject.

| Field                | Description                                                                                                                      |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Body Part Transforms | List of body part entries paired with their corresponding Transform on the character (e.g., "Right Hand" → the right hand bone). |
| Body Mesh References | List of body mesh types paired with their corresponding GameObjects on the character (e.g., "Hair" → the hair mesh objects).     |

Set up one body part transform reference for each attachment point on your character model (right hand, left hand, back, hip, head, etc.). Set up one body mesh reference for each mesh category — each reference can contain multiple GameObjects that all belong to that mesh type.

## Weapon Visual System

When a weapon is equipped, Spark instantiates the weapon prefab and attaches it to the correct body part on the character. The weapon's position changes based on combat state.

### Combat vs. Non-Combat Positioning

Each weapon item configures two attachment states:

| State                     | Description                                                                                                  |
| ------------------------- | ------------------------------------------------------------------------------------------------------------ |
| **Non-Combat** (sheathed) | The weapon is attached to a resting body part (e.g., back, hip) with its own position and rotation offsets.  |
| **Combat** (drawn)        | The weapon is attached to an active body part (e.g., right hand) with its own position and rotation offsets. |

For example, a sword might be attached to the "Left Hip" body part when sheathed and the "Right Hand" body part when drawn.

### Per-Character Coordinate Overrides

Different character models may have slightly different proportions, so weapon placement may need per-character adjustments. Each weapon visual configuration includes a list of character-specific coordinate overrides that map a specific character entry to custom position and rotation values.

When positioning a weapon, Spark checks for a character-specific override first. If none exists, it falls back to the default coordinates.

### Sheath and Unsheath Transitions

When the character's combat state changes, equipped weapons transition between their non-combat and combat positions.

| Field                    | Description                                                                     |
| ------------------------ | ------------------------------------------------------------------------------- |
| Play Unsheath Animation  | Trigger an unsheath animation when entering combat (default: true).             |
| Play Sheathe Animation   | Trigger a sheath animation when exiting combat (default: true).                 |
| Unsheath Animation Delay | Seconds to wait before repositioning the weapon during unsheath (default: 0.3). |
| Sheathe Animation Delay  | Seconds to wait before repositioning during sheath (default: 0.3).              |

The delay lets you sync the weapon reposition with the animation — for example, waiting until the character's hand reaches the hip before the sword moves from hip to hand.

### Active Weapon Slot Type

The equipment system tracks an **active weapon slot type** which determines which weapon is currently drawn. When the active slot type changes, only the relevant weapon transitions to combat position while other weapons remain sheathed.

This supports weapon swapping systems — for example, switching from melee to ranged draws the bow while keeping the sword sheathed on the hip.

### Weapon Hand Configurations

The visual config used depends on the weapon's hand type:

| Hand Type  | Config Used                                                                                 |
| ---------- | ------------------------------------------------------------------------------------------- |
| Main Hand  | Single weapon visual config.                                                                |
| Off Hand   | Single weapon visual config.                                                                |
| Two Handed | Single weapon visual config.                                                                |
| Any Hand   | Separate main hand and off hand configs, depending on which slot the weapon is equipped in. |
| Ranged     | Ranged-specific config.                                                                     |

## Armor Visual System

When armor is equipped, Spark shows the armor piece and optionally hides body meshes.

**Equip flow:**

1. Find the child GameObject on the character matching the armor's **Armor GameObject Name**.
2. Activate (show) that GameObject.
3. If the armor has a **Body Mesh Configuration**, hide the specified body mesh types on the BodyEntity.

**Unequip flow:**

1. Deactivate (hide) the armor GameObject.
2. Restore any body meshes that were hidden by this armor's configuration.

## Animator Override Controllers

Both weapons and armor can specify an **Animator Override Controller**. When an item with an override is equipped, Spark replaces the character's current animator controller with the override. This lets different weapons play different animation sets (e.g., sword idle vs. bow idle vs. staff idle).

When the item is unequipped, the original animator controller is restored.

## Camera Overrides

Weapons can specify a **Camera Override** entry that adjusts camera settings when the weapon is equipped. This is useful for changing the camera angle or distance for different weapon types — for example, zooming out further for ranged weapons.

## Equipment-Related Requirements

The Items plugin adds requirement types that check equipment state. These can be used in triggers, abilities, quests, or anywhere requirements are supported:

| Requirement                 | Description                                                                 |
| --------------------------- | --------------------------------------------------------------------------- |
| Has Equipment Slot Equipped | Checks if the character has any item equipped in a specific equipment slot. |
| Has Item Equipped           | Checks if the character has a specific item equipped.                       |
| Has Weapon Type Equipped    | Checks if the character has a weapon of a specific weapon type equipped.    |

## Drag-and-Drop

Equipment slots participate in the inventory drag-and-drop system:

| Source         | Target         | Result                                               |
| -------------- | -------------- | ---------------------------------------------------- |
| Inventory Slot | Equipment Slot | Equip the item (if compatible and requirements met). |
| Equipment Slot | Inventory Slot | Unequip the item back to the inventory.              |
| Equipment Slot | Equipment Slot | Swap equipped items (if both slots are compatible).  |

## Persistence

When **Is Persistent** is enabled on the EquipmentEntity, equipped items are saved and loaded through Spark's save system. What is saved per slot includes the slot definition, slot index (for duplicate slots), and the equipped item entry. The character sheet entry is also stored so the correct slot layout is restored on load.


# Currency

The Currency plugin adds a wallet-based currency system to your game. Define multiple currency types with display formatting, symbols, and max limits. Characters hold currencies in wallets that support adding, removing, transferring, and automatic overflow conversion between currencies. The plugin integrates with the Items plugin through currency item types and with the Quests plugin through quest objectives and rewards.

In the Spark Editor, the Currency plugin is organized into these categories:

| Category                                                              | What it contains                                                                                                                                                                           |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [Currencies](/documentation/user-guide/plugins/currency/currencies)   | Currency entries with symbols, max amounts, display formatting, and starting values; WalletEntity setup; currency triggers and requirements; quest integration; UI components; persistence |
| [Conversions](/documentation/user-guide/plugins/currency/conversions) | Conversion entries defining exchange rates between currencies; automatic overflow conversion; conversion chains                                                                            |


# Currencies

Currencies are database entries that define types of money or points in your game. Characters hold currencies in wallets, and currencies can be added, removed, transferred, and automatically converted.

## Creating a Currency Entry

Open the Spark Editor, go to **Currency > Currencies** and click **Create New**.

Each currency entry defines:

| Field            | Description                                                                 |
| ---------------- | --------------------------------------------------------------------------- |
| Display Name     | Name shown in-game (e.g., "Gold", "Gems", "Honor Points").                  |
| Description      | Tooltip or info panel text.                                                 |
| Icon             | Sprite for UI display.                                                      |
| Currency Symbol  | Symbol shown alongside amounts (e.g., "$", "G", "₡").                       |
| Max Amount       | Maximum amount a wallet can hold. Set to 0 for unlimited.                   |
| Starting Amount  | Default amount when a new wallet is created (default: 0).                   |
| Display Color    | UI color used when displaying this currency.                                |
| Display Format   | How amounts are formatted in UI. See below.                                 |
| Conversion Entry | Optional reference to a conversion entry for automatic overflow conversion. |

### Display Formats

| Format     | Description                                                           |
| ---------- | --------------------------------------------------------------------- |
| Full       | Shows the full number with thousand separators (e.g., "1,250").       |
| Abbreviate | Abbreviates large numbers with suffixes (e.g., "1.2K", "3.5M", "1B"). |

## Setting Up a Wallet Entity

To give a character (or any GameObject) a wallet at runtime, add a **WalletEntity** component in the Inspector.

| Field                | Description                                                                             |
| -------------------- | --------------------------------------------------------------------------------------- |
| Custom Wallet Id     | Optional custom ID. Leave empty for auto-generated.                                     |
| Auto Create On Start | Automatically create the wallet when the component starts (default: true).              |
| Destroy On Disable   | Destroy the wallet when the component is disabled (default: false).                     |
| Is Persistent        | Save and load wallet contents across sessions (default: false).                         |
| Auto Save On Change  | Automatically save whenever the wallet changes (default: true). Requires Is Persistent. |
| Load On Create       | Load saved data when creating the wallet (default: true). Requires Is Persistent.       |
| Spark Entity         | Reference to the SparkEntity on this character. Required for persistence.               |

When a wallet is created, it initializes each currency to its **Starting Amount** value.

## Auto-Conversion on Overflow

If a currency has a **Max Amount** set and a **Conversion Entry** assigned, Spark automatically converts overflow currency when the maximum is reached.

For example, if you have a "Copper" currency with a max of 100 and a conversion to "Silver" (100 Copper → 1 Silver), then when a character gains copper that would exceed 100, the excess is automatically converted to silver.

Auto-conversion supports chains. If Silver also has a max and converts to Gold, then overflow from Copper → Silver → Gold happens automatically. Spark prevents infinite conversion loops by limiting chain depth.

## Currency Items

Items can be configured with the **Currency** item type (see [Items](/documentation/user-guide/plugins/items/items)). Currency items bypass the inventory entirely — when a character picks them up, the item is automatically converted to the corresponding currency and added to the character's wallet. The item's stack size determines the amount granted.

This is useful for currency drops in loot tables. Instead of taking up inventory space, the currency is added to the wallet immediately.

## Currency-Related Triggers

| Trigger       | Description                                                                                                                                                               |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Gain Currency | Adds or removes a specified amount of a currency from the player's wallet. Configure the currency entry, amount, whether to remove instead of add, and an optional delay. |

## Currency-Related Requirements

| Requirement     | Description                                                                                                                                                                                                           |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Currency Amount | Checks if the character's wallet has a specific amount of a currency. Configure the target currency, comparison type (equal, not equal, greater than, greater or equal, less than, less or equal), and target amount. |

## Quest Integration

When both the Currency and Quests plugins are installed, additional quest features become available:

**Quest Objectives:**

| Objective            | Description                                                                                                              |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Gain Currency        | Tracks how much of a specific currency the player has gained. Progress increases each time the currency is added.        |
| Reach Currency Total | Completes when the player's total amount of a specific currency reaches a target value. Supports retroactive completion. |

**Quest Rewards:**

| Reward        | Description                                                               |
| ------------- | ------------------------------------------------------------------------- |
| Gain Currency | Grants a specified amount of a currency when the quest reward is claimed. |

## UI Components

The Currency plugin includes two UI components for displaying wallet information:

**Wallet UI** — Displays all currencies in a wallet. Attach it to a UI panel and configure:

| Field                   | Description                                                        |
| ----------------------- | ------------------------------------------------------------------ |
| Wallet Entity           | Reference to the WalletEntity to display.                          |
| Currency Display Prefab | Prefab spawned for each currency.                                  |
| Container               | Parent transform for display items.                                |
| Filter Currencies       | Optional list to show only specific currencies (empty = show all). |
| Show Only Non-Zero      | Only display currencies with amounts greater than zero.            |
| Auto Update             | Automatically refresh when the wallet changes.                     |

**Currency Display** — Displays a single currency amount. Useful for HUD elements showing gold count, etc.

| Field               | Description                                                                                                                             |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Connection Type     | How to find the WalletEntity: **Scene Reference** (drag and drop), **Find By Tag** (auto-find by tag), or **By Code** (set at runtime). |
| Wallet Entity       | Direct reference when using Scene Reference.                                                                                            |
| Container Tag       | Tag to search for when using Find By Tag (e.g., "Player").                                                                              |
| Currency            | The currency entry to display.                                                                                                          |
| Text Component      | TextMeshPro text for the amount.                                                                                                        |
| Icon Image          | Image component for the currency icon.                                                                                                  |
| Use Currency Format | Use the currency's own display format settings.                                                                                         |
| Prefix / Suffix     | Optional text before or after the amount.                                                                                               |
| Update On Change    | Automatically update when the wallet changes.                                                                                           |
| Show Tooltip        | Show a tooltip on hover with currency info.                                                                                             |
| Tooltip Delay       | Seconds before the tooltip appears.                                                                                                     |

## Persistence

When **Is Persistent** is enabled on the WalletEntity, all currency amounts are saved and loaded through Spark's save system. What is saved per wallet includes the entity ID, wallet ID, and all currency amounts. If **Auto Save On Change** is enabled, the wallet saves automatically whenever any currency changes.


# Conversions

Currency conversions define exchange rates between two currencies. They are used both for manual currency exchange and for automatic overflow conversion.

## Creating a Conversion Entry

Open the Spark Editor, go to **Currency > Conversions** and click **Create New**.

Each conversion entry defines:

| Field           | Description                                                                  |
| --------------- | ---------------------------------------------------------------------------- |
| Display Name    | Label for this conversion (e.g., "Copper to Silver").                        |
| Source Currency | The currency to convert from.                                                |
| Target Currency | The currency to convert to.                                                  |
| Source Amount   | How much of the source currency is needed for one conversion (default: 100). |
| Target Amount   | How much of the target currency is received per conversion (default: 1).     |

**Example:** To set up "100 Copper = 1 Silver", set Source Currency to Copper, Target Currency to Silver, Source Amount to 100, and Target Amount to 1.

## How Conversions Are Used

### Automatic Overflow Conversion

When a currency entry has a **Max Amount** and a **Conversion Entry** assigned, Spark automatically converts excess currency using that conversion whenever the maximum would be exceeded.

For example, if Copper has a max of 100 and a conversion of 100 Copper → 1 Silver, then earning 150 Copper when you already have 50 would result in: 100 Copper converted to 1 Silver, leaving 50 Copper remaining.

### Conversion Chains

If the target currency also has a max amount and its own conversion, the overflow cascades. For example:

* 100 Copper → 1 Silver (Copper max: 100)
* 100 Silver → 1 Gold (Silver max: 100)

Earning a large amount of Copper could cascade through Silver and into Gold. Spark limits chain depth to 10 steps to prevent infinite loops.

### Manual Conversion

Conversions can also be triggered manually through the currency system. This is useful for shops or exchange NPCs where the player can convert currencies on demand.

## Conversion Validation

A conversion entry is valid when both the source and target currencies are assigned and both amounts are greater than zero. Invalid conversions are ignored by the system.


# Crafting

The Crafting plugin adds a recipe-based crafting system to your game. Define recipes with ingredients, craft durations, and output items. Recipes can be locked and unlocked through triggers or quest rewards, and the plugin includes a full crafting UI with category filtering, ingredient validation, amount selection, and progress tracking.

In the Spark Editor, the Crafting plugin is organized into these categories:

| Category                                                            | What it contains                                                                                                                                                                               |
| ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Recipes](/documentation/user-guide/plugins/crafting/recipes)       | Recipe entries with ingredients, craft duration, auto-learn, and on-craft triggers; recipe unlocking through triggers and quest rewards; crafting requirements; quest integration; crafting UI |
| [Categories](/documentation/user-guide/plugins/crafting/categories) | Category entries for organizing recipes in the crafting UI                                                                                                                                     |


# Recipes

Crafting recipes define what items can be crafted, what ingredients are required, how long crafting takes, and what happens when crafting succeeds.

## Creating a Recipe Entry

Open the Spark Editor, go to **Crafting > Recipes** and click **Create New**.

Each recipe entry defines:

| Field               | Description                                                                                                                                                                  |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Display Name        | Name shown in the crafting UI (e.g., "Iron Sword", "Health Potion").                                                                                                         |
| Description         | Text describing the recipe or its result.                                                                                                                                    |
| Icon                | Sprite for the recipe in the crafting UI.                                                                                                                                    |
| Recipe Category     | The category this recipe belongs to (see [Categories](/documentation/user-guide/plugins/crafting/categories)). Used to organize recipes in the crafting UI.                  |
| Crafted Item        | The item entry that is produced when this recipe is crafted.                                                                                                                 |
| Craft Duration      | Time in seconds required to craft one instance (default: 1).                                                                                                                 |
| Auto Learn          | If enabled, the recipe is automatically unlocked for the player when they are first initialized. If disabled, the recipe must be unlocked through triggers or quest rewards. |
| Required Components | List of ingredients needed to craft this recipe. See below.                                                                                                                  |
| On Craft Triggers   | List of trigger entries that execute when the recipe is successfully crafted.                                                                                                |

## Required Components

Each required component specifies an ingredient:

| Field  | Description                                    |
| ------ | ---------------------------------------------- |
| Item   | The item entry required as an ingredient.      |
| Amount | How many of this item are needed (default: 1). |

You can add as many components as needed. All components must be present in the player's inventory for the recipe to be craftable. When crafting begins, the required items are consumed from the inventory and the crafted item is added.

## Recipe Unlocking

Recipes can be locked or unlocked. Only unlocked recipes appear in the crafting UI.

**Auto Learn** recipes are unlocked by default when the player is first initialized. All other recipes start locked and must be unlocked through one of these methods:

| Method                 | Description                                                                                                                          |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| Learn Recipe trigger   | A trigger that unlocks a specific recipe for the player. Configure the recipe entry and an optional delay.                           |
| Unlearn Recipe trigger | A trigger that locks a previously unlocked recipe. Configure the recipe entry and an optional delay.                                 |
| Quest reward           | When both Crafting and Quests plugins are installed, the "Unlock Recipe" reward type grants a recipe when a quest reward is claimed. |

Unlocked recipes are tracked in save data and persist across sessions.

## On Craft Triggers

The **On Craft Triggers** list lets you fire any triggers when a recipe is successfully crafted. This is useful for granting experience, advancing quests, playing effects, or any other trigger-based behavior.

## Crafting-Related Requirements

| Requirement         | Description                                                                                      |
| ------------------- | ------------------------------------------------------------------------------------------------ |
| Has Crafting Recipe | Checks if the player has unlocked a specific crafting recipe. Configure the target recipe entry. |

## Quest Integration

When both the Crafting and Quests plugins are installed, additional quest features become available:

**Quest Objectives:**

| Objective     | Description                                                                                                 |
| ------------- | ----------------------------------------------------------------------------------------------------------- |
| Craft Recipe  | Tracks how many times the player crafts a specific recipe (or any recipe if "Allow Any Recipe" is enabled). |
| Unlock Recipe | Completes when the player unlocks a specific recipe.                                                        |

**Quest Rewards:**

| Reward        | Description                                                          |
| ------------- | -------------------------------------------------------------------- |
| Unlock Recipe | Unlocks a specific crafting recipe when the quest reward is claimed. |

## Crafting UI

The Crafting plugin includes a full crafting UI that can be added to your game. The crafting interface is organized into several sections:

**Category Selection** — A sidebar showing all recipe categories plus an "All" category. Each category button shows the category name, icon, and recipe count. Clicking a category filters the recipe list.

**Recipe List** — Shows all recipes in the selected category, organized under category headers. Each recipe slot displays the crafted item's icon, rarity, and how many the player can craft with their current inventory. Recipes the player can craft are highlighted in green; recipes with insufficient materials are highlighted in red.

**Recipe Details** — When a recipe is selected, shows the crafted item icon and rarity alongside a list of required ingredients. Each ingredient displays its icon, name, and an "owned / required" count — green if the player has enough, red if not.

**Craft Controls** — An amount selector with input field, slider, increase/decrease buttons, and a craft button. The maximum craftable amount is calculated from the player's current inventory.

**Crafting Progress** — When crafting is in progress, shows a progress bar with remaining time and a cancel button. Recipes are crafted one at a time from a queue.

The crafting UI automatically updates when the player's inventory changes (gaining or losing ingredients) and when recipes are unlocked or locked.


# Categories

Crafting categories organize recipes into groups for the crafting UI.

## Creating a Category Entry

Open the Spark Editor, go to **Crafting > Categories** and click **Create New**.

Each category entry uses the standard database entry fields:

| Field        | Description                                                                            |
| ------------ | -------------------------------------------------------------------------------------- |
| Display Name | Category label shown in the crafting UI (e.g., "Weapons", "Potions", "Armor", "Food"). |
| Description  | Optional description for the category.                                                 |
| Icon         | Sprite displayed in the category selection sidebar.                                    |

## How Categories Are Used

Each recipe references a single category through its **Recipe Category** field. In the crafting UI, categories appear as filter buttons in the sidebar. Players can click a category to show only recipes in that group, or select "All" to see every unlocked recipe.

The crafting UI automatically counts how many recipes belong to each category and displays the count alongside the category name.

## Organization Tips

Create categories that match how players think about crafting. Common category structures include organizing by item type (Weapons, Armor, Potions, Food, Materials), by profession (Blacksmithing, Alchemy, Cooking, Tailoring), or by tier (Beginner Recipes, Advanced Recipes, Master Recipes).

Every recipe should have a category assigned. Recipes without a category will still appear in the "All" view but will not show under any specific category filter.


# Quests

The Quests plugin adds a full quest system to your game. Define quests with objectives, acceptance requirements, and rewards (both guaranteed and player-selectable). Quests progress through states from acceptance to turn-in, integrate with NPCs for giving and turning in quests, and persist automatically through the save system. Other plugins extend the quest system with their own objective and reward types.

In the Spark Editor, the Quests plugin is organized into these categories:

| Category                                                          | What it contains                                                                                                                                                  |
| ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Quests](/documentation/user-guide/plugins/quests/quests)         | Quest entries with objectives, requirements, and rewards; quest states; quest triggers and requirements; NPC quest integration with state indicators; persistence |
| [Categories](/documentation/user-guide/plugins/quests/categories) | Category entries for organizing quests in the Quest Journal with colors and display order                                                                         |


# Quests

Quests are database entries that define tasks for the player to complete. Each quest has objectives to track progress, requirements to gate acceptance, and rewards granted on turn-in.

## Creating a Quest Entry

Open the Spark Editor, go to **Quests > Quests** and click **Create New**.

Each quest entry defines:

| Field                   | Description                                                                                                 |
| ----------------------- | ----------------------------------------------------------------------------------------------------------- |
| Display Name            | Quest name shown in the journal and UI.                                                                     |
| Description             | Quest text describing what the player needs to do.                                                          |
| Icon                    | Sprite for the quest in UI.                                                                                 |
| Quest Category          | The category this quest belongs to (see [Categories](/documentation/user-guide/plugins/quests/categories)). |
| Quest Journal Turn In   | If enabled, the player can turn in this quest directly from the Quest Journal without returning to an NPC.  |
| Accept Requirements     | List of requirement groups that must be met before the player can accept this quest.                        |
| Objectives              | List of objectives the player must complete (see below).                                                    |
| Rewards To Select Count | How many rewards the player can choose from the selectable rewards list.                                    |
| Selectable Rewards      | Rewards the player can choose from — they pick a number of these equal to Rewards To Select Count.          |
| Guaranteed Rewards      | Rewards that are always given when the quest is turned in, regardless of selection.                         |

## Quest States

A quest progresses through these states:

| State            | Description                                                    |
| ---------------- | -------------------------------------------------------------- |
| Not Started      | Quest has not been accepted yet.                               |
| Active           | Quest has been accepted and objectives are in progress.        |
| Ready To Turn In | All objectives are complete. The player can turn in the quest. |
| Completed        | Quest has been turned in and rewards granted.                  |
| Abandoned        | Quest was abandoned by the player.                             |

## Objectives

Each objective tracks a specific task. Objectives use a type system — different objective types track different things.

| Field           | Description                                                           |
| --------------- | --------------------------------------------------------------------- |
| Objective Type  | The type of task to track (ScriptableObject dropdown).                |
| Description     | Short description shown in the quest journal (e.g., "Kill 5 wolves"). |
| Amount Required | How many times this objective must be completed (default: 1).         |

**Built-in objective types include:**

| Objective Type       | What it tracks                                                             |
| -------------------- | -------------------------------------------------------------------------- |
| Enter Scene          | Player enters a specific scene.                                            |
| Craft Recipe         | Player crafts a specific recipe (or any recipe). Requires Crafting plugin. |
| Unlock Recipe        | Player unlocks a specific crafting recipe. Requires Crafting plugin.       |
| Gain Currency        | Player gains a specific amount of currency. Requires Currency plugin.      |
| Reach Currency Total | Player's total currency reaches a target amount. Requires Currency plugin. |

Objective types from other plugins become available when those plugins are installed. Custom objective types can be created by developers.

## Rewards

Rewards are granted when the quest is turned in.

**Guaranteed rewards** are always given. **Selectable rewards** let the player choose — they pick a number equal to **Rewards To Select Count**.

| Field       | Description                                     |
| ----------- | ----------------------------------------------- |
| Reward Type | The type of reward (ScriptableObject dropdown). |
| Reward Data | Type-specific configuration.                    |

**Built-in reward types include:**

| Reward Type   | What it grants                                                                                                          |
| ------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Trigger       | Executes a trigger entry when claimed. Use this to grant items, currency, experience, or anything else triggers can do. |
| Unlock Recipe | Unlocks a crafting recipe. Requires Crafting plugin.                                                                    |
| Gain Currency | Grants a currency amount. Requires Currency plugin.                                                                     |

## Quest-Related Triggers

| Trigger                  | Description                                                                                                                            |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| Add Quest                | Adds a quest to the player's quest list (or removes it if the remove option is enabled). Configure the quest entry and optional delay. |
| Complete Quest Objective | Completes a specific objective by index, or fully completes all objectives if the "fully complete" option is enabled.                  |

## Quest-Related Requirements

| Requirement  | Description                                                                                                |
| ------------ | ---------------------------------------------------------------------------------------------------------- |
| Quest Status | Checks if a quest is in a specific state (Not Started, Active, Ready To Turn In, Completed, or Abandoned). |

## NPC Integration

When both the Quests and NPCs plugins are installed, NPC interaction types for quests become available:

| Interaction Type | Description                                                                                                                                                                                                        |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Give Quest       | Offers a quest to the player. The NPC shows the quest details and the player can accept or decline. Only available if the player hasn't already accepted or completed the quest and meets the accept requirements. |
| Turn In Quest    | Allows the player to turn in a completed quest at this NPC. Only available if the quest is Active or Ready To Turn In.                                                                                             |

NPCs with quest interactions display quest state indicators above their heads:

| Indicator         | Meaning                                       |
| ----------------- | --------------------------------------------- |
| Quest Available   | This NPC has a quest the player can accept.   |
| Quest In Progress | The player has an active quest from this NPC. |
| Quest Completed   | The player can turn in a quest at this NPC.   |

The quest state indicator component can be configured with:

| Field                 | Description                                               |
| --------------------- | --------------------------------------------------------- |
| Quest State UI Prefab | Prefab to instantiate for the quest state display.        |
| Position Offset       | Local position offset above the NPC (default: 0, 2.5, 0). |
| Update Interval       | How often to check quest state in seconds (default: 0.5). |

## Persistence

Quest progress is automatically saved and loaded through Spark's save system. What is saved per quest includes the quest state, objective progress (current count and completion status for each objective), selected reward indices, and timestamps for when the quest was accepted and completed.


# Categories

Quest categories organize quests into groups in the Quest Journal UI.

## Creating a Category Entry

Open the Spark Editor, go to **Quests > Categories** and click **Create New**.

Each category entry defines:

| Field          | Description                                                                             |
| -------------- | --------------------------------------------------------------------------------------- |
| Display Name   | Category label shown in the quest journal (e.g., "Main Story", "Side Quests", "Daily"). |
| Category Color | Display color for quests in this category in the journal (default: yellow).             |
| Order          | Display order in the quest journal — lower values appear first (default: 100).          |

## How Categories Are Used

Each quest references a single category through its **Quest Category** field. In the Quest Journal UI, categories organize quests so players can quickly find what they're looking for.

Use the **Order** field to control category sorting. For example, set "Main Story" to order 1, "Side Quests" to order 10, and "Daily" to order 20 so they appear in that sequence.

The **Category Color** is used in the journal UI to visually distinguish quest types — for example, gold for main quests, silver for side quests, blue for daily quests.


# Progression

The Progression plugin adds experience and leveling systems to your game. Define XP curves with linear, exponential, or logarithmic scaling, create multiple independent progression tracks (character level, crafting skill, reputation, etc.), and configure level-up rewards that fire triggers at specific levels. Supports both finite and infinite leveling with persistence.

In the Spark Editor, the Progression plugin is organized into these categories:

| Category                                                                                     | What it contains                                                                                                                                            |
| -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Leveling](/documentation/user-guide/plugins/progression/leveling)                           | Leveling entries with progression type (finite/infinite), experience curves (linear/exponential/logarithmic), and custom XP tables                          |
| [Progression Instances](/documentation/user-guide/plugins/progression/progression-instances) | Progression instance entries with leveling templates, level-up rewards with condition types, ProgressionEntity setup, progression triggers and requirements |


# Progression Instances

Progression instances are individual progression tracks that can be assigned to characters. Each instance references a leveling entry for its XP curve and defines rewards that trigger at specific levels.

## Creating a Progression Instance Entry

Open the Spark Editor, go to **Progression > Progression Instances** and click **Create New**.

Each progression instance defines:

| Field                    | Description                                                                                                   |
| ------------------------ | ------------------------------------------------------------------------------------------------------------- |
| Display Name             | Name for this progression track (e.g., "Character Level", "Crafting Skill", "Fishing").                       |
| Leveling Template        | Reference to a Leveling entry that defines the XP curve for this progression.                                 |
| Allow Runtime Assignment | Whether this progression can be assigned to characters during gameplay (default: true).                       |
| Default Unlocked         | If enabled, this progression is automatically unlocked for all characters on initialization (default: false). |
| Level Up Rewards         | List of rewards that trigger at specific levels (see below).                                                  |

## Level Up Rewards

Each level up reward fires triggers when the character reaches certain levels. Add as many rewards as needed.

| Field       | Description                                                            |
| ----------- | ---------------------------------------------------------------------- |
| Reward Type | When this reward triggers. See reward types below.                     |
| Description | Player-readable description of the reward.                             |
| Triggers    | List of trigger entries to execute when the reward conditions are met. |

### Reward Types

| Type           | Description                                           | Extra Fields                                                                                                                                                 |
| -------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Any Level      | Triggers on every level up.                           | None.                                                                                                                                                        |
| Specific Level | Triggers when the character reaches a specific level. | **Required Level** — the target level.                                                                                                                       |
| Every X Levels | Triggers every N levels (e.g., every 5 levels).       | **Level Interval** — how often (default: 5). **Skip Level One** — exclude level 1 from the interval. **Starting Level** — the base for interval calculation. |
| Max Level      | Triggers when the character reaches the level cap.    | None.                                                                                                                                                        |

**Example:** To grant a talent point every 5 levels starting from level 5, set Reward Type to "Every X Levels", Level Interval to 5, and Skip Level One to true.

## Setting Up a Progression Entity

To give a character progression tracking, add a **ProgressionEntity** component in the Inspector.

| Field               | Description                                                                          |
| ------------------- | ------------------------------------------------------------------------------------ |
| Is Persistent       | Save and load progression data across sessions (default: false).                     |
| Auto Save On Change | Automatically save when XP or level changes (default: true). Requires Is Persistent. |
| Load On Start       | Load saved progression data on Start (default: true). Requires Is Persistent.        |
| Spark Entity        | Reference to the SparkEntity. Required for persistence.                              |

Progressions marked as **Default Unlocked** are automatically created when the entity initializes. Other progressions must be unlocked through triggers or gameplay logic.

## Progression-Related Triggers

| Trigger         | Description                                                                                                            |
| --------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Gain Experience | Grants XP to a specific progression instance. Configure the progression instance entry, XP amount, and optional delay. |

## Progression-Related Requirements

| Requirement       | Description                                                                                                                                                                |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Progression Level | Checks if a progression instance has reached a specific level. Configure the target progression, comparison type (equal, greater than, less than, etc.), and target level. |


# Leveling

Leveling entries define experience curves that control how much XP is needed to reach each level. They support finite and infinite progression with multiple curve types.

## Creating a Leveling Entry

Open the Spark Editor, go to **Progression > Leveling** and click **Create New**.

### Progression Configuration

| Field                  | Description                                                                                                                                 |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Progression Type       | **Finite** (has a level cap) or **Infinite** (no hard cap, XP scales infinitely).                                                           |
| Starting Level         | Initial level when a character begins (default: 1).                                                                                         |
| Level Cap              | Maximum level the character can reach. Only available when Progression Type is Finite (default: 50).                                        |
| Soft Cap Level         | Level at which XP requirements start scaling with the post-cap multiplier. Only available when Progression Type is Infinite (default: 100). |
| Post Cap XP Multiplier | Multiplier applied to XP requirements after the soft cap. Only available when Progression Type is Infinite (default: 1.5).                  |

### Experience Curve

| Field                    | Description                                                                                                                  |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| Curve Type               | **Linear**, **Exponential**, or **Logarithmic**.                                                                             |
| Base Experience Required | XP needed for the first level up (default: 100).                                                                             |
| Experience Growth Factor | How XP requirements increase per level. For Linear: added each level. For Exponential: multiplied each level (default: 1.1). |
| Logarithmic Scale        | Scaling factor for the logarithmic curve. Only available when Curve Type is Logarithmic (default: 100.0).                    |

### Custom XP Table

| Field                       | Description                                                                                    |
| --------------------------- | ---------------------------------------------------------------------------------------------- |
| Custom Experience Per Level | Optional manual override for XP requirements at each level. Index 0 = XP needed for level 1→2. |

The editor provides an **Auto-Generate** button that populates the custom table from the curve settings, and a **Clear** button that empties it. When a custom table is provided, it takes priority over the calculated curve.

## How Curves Work

**Linear:** Each level adds the growth factor to the previous requirement. Level 2 needs `base`, level 3 needs `base + growth`, level 4 needs `base + 2×growth`, etc.

**Exponential:** Each level multiplies the previous requirement by the growth factor. Level 2 needs `base`, level 3 needs `base × growth`, level 4 needs `base × growth²`, etc.

**Logarithmic:** XP scales logarithmically, producing a curve that rises steeply at first then flattens. The logarithmic scale controls how quickly the curve flattens.


# Classes

The Classes plugin lets you define character classes and assign them to characters. Characters can have one main class and multiple secondary classes.

## Creating a Class Entry

Open the Spark Editor, go to **Classes** and click **Create New**.

Each class entry uses the standard database entry fields:

| Field        | Description                                                   |
| ------------ | ------------------------------------------------------------- |
| Display Name | Class name shown in-game (e.g., "Warrior", "Mage", "Ranger"). |
| Description  | Class description text.                                       |
| Icon         | Sprite for the class in UI.                                   |

Classes are primarily used as identifiers that other systems reference through requirements. For example, you can gate abilities behind a class requirement, restrict equipment to specific classes, or unlock quest content based on the player's class.

## Main Class vs. Secondary Classes

Each character has one **main class** and can have any number of **secondary classes**. This supports systems like multi-classing or specializations.

Classes can also be **unlocked** separately from being assigned. Unlocked classes are available for the player to choose from, while assigned classes are actively applied to the character.

## Class-Related Triggers

| Trigger             | Description                                                                                                           |
| ------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Add Secondary Class | Adds or removes a secondary class from the character. Configure the class entry and whether to remove instead of add. |

## Class-Related Requirements

| Requirement         | Description                                                              |
| ------------------- | ------------------------------------------------------------------------ |
| Has Class           | Checks if the character has a specific class (either main or secondary). |
| Has Secondary Class | Checks if the character has a specific class as a secondary class.       |
| Main Class          | Checks if the character's main class is a specific class.                |

## Use Cases

Classes are commonly used to restrict abilities (e.g., only Warriors can use "Shield Bash"), gate equipment (e.g., heavy armor requires Warrior class), provide class-specific quests, and define progression paths. Combined with the Requirements system, classes become a powerful tool for gating content throughout your game.


# Professions

The Professions plugin lets you define professions that characters can learn. Unlike classes, professions are permanent once learned and cannot be removed.

## Creating a Profession Entry

Open the Spark Editor, go to **Professions** and click **Create New**.

Each profession entry uses the standard database entry fields:

| Field        | Description                                                                               |
| ------------ | ----------------------------------------------------------------------------------------- |
| Display Name | Profession name shown in-game (e.g., "Blacksmithing", "Alchemy", "Mining", "Enchanting"). |
| Description  | Profession description text.                                                              |
| Icon         | Sprite for the profession in UI.                                                          |

Professions are identifiers that other systems reference through requirements. For example, you can restrict crafting recipes to characters with a specific profession, gate resource gathering behind profession requirements, or tie progression tracks to professions.

## Learning Professions

Professions are learned through the trigger system and are permanent. Once a character learns a profession, it cannot be unlearned.

## Profession-Related Triggers

| Trigger        | Description                                                                               |
| -------------- | ----------------------------------------------------------------------------------------- |
| Add Profession | Teaches a profession to the character. Configure the profession entry and optional delay. |

## Profession-Related Requirements

| Requirement    | Description                                                |
| -------------- | ---------------------------------------------------------- |
| Has Profession | Checks if the character has learned a specific profession. |

## Use Cases

Professions work well alongside the Crafting and Progression plugins. For example, you might require the "Blacksmithing" profession to craft weapon recipes, tie a "Blacksmithing Skill" progression instance to the profession, and gate advanced recipes behind both the profession and a minimum skill level.


# Races

The Races plugin lets you define character races with visual variants tied to different character entries.

## Creating a Race Entry

Open the Spark Editor, go to **Races** and click **Create New**.

Each race entry defines:

| Field           | Description                                                     |
| --------------- | --------------------------------------------------------------- |
| Display Name    | Race name shown in-game (e.g., "Human", "Elf", "Dwarf", "Orc"). |
| Description     | Race description text.                                          |
| Icon            | Sprite for the race in UI.                                      |
| Race Characters | List of character variants available for this race. See below.  |

## Race Characters

Each race can have multiple character variants. Each variant links to a character entry:

| Field           | Description                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------- |
| Display Name    | Variant label (e.g., "Human Male", "Human Female", "Elf Tall", "Elf Short").                        |
| Icon            | Sprite for this specific variant.                                                                   |
| Character Entry | Reference to a Character entry that defines the character model and configuration for this variant. |

This lets you offer visual diversity within a single race. For example, the "Human" race might have "Human Male" and "Human Female" variants, each pointing to a different character entry with its own model, animations, and customization options.

## Use Cases

Races are commonly used during character creation to let the player choose their race and variant, which then determines the character entry used for their in-game avatar. Combined with the Character Customization plugin, races can offer further visual personalization within each variant.


# Character Customization

The Character Customization plugin lets players personalize their character's appearance. Define customizable visual elements (hairstyles, facial features, accessories) and color options (hair color, skin tone, eye color) organized into categories. Presets bundle available options together with defaults, and customization persists across sessions.

In the Spark Editor, the Character Customization plugin is organized into these categories:

| Category                                                                 | What it contains                                                                                                        |
| ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| [Categories](/documentation/user-guide/plugins/customization/categories) | Category entries for organizing customization options with sort order                                                   |
| [Elements](/documentation/user-guide/plugins/customization/elements)     | Element entries for toggleable visual options like hairstyles, scars, and accessories                                   |
| [Colors](/documentation/user-guide/plugins/customization/colors)         | Color entries for material property modifications like hair color, skin tone, and eye color                             |
| [Presets](/documentation/user-guide/plugins/customization/presets)       | Preset entries that bundle available elements and colors with defaults; CharacterCustomizationEntity setup; persistence |


# Categories

Customization categories group related visual options together in the customization UI.

## Creating a Category Entry

Open the Spark Editor, go to **Character Customization > Categories** and click **Create New**.

Each category entry defines:

| Field        | Description                                                                                        |
| ------------ | -------------------------------------------------------------------------------------------------- |
| Display Name | Category label shown in the customization UI (e.g., "Hair", "Eyes", "Face", "Body", "Skin Color"). |
| Sort Order   | Display order in the UI — lower values appear first.                                               |

Categories organize both elements and colors. For example, a "Hair" category might contain hair style elements and a hair color option, while a "Face" category might contain facial feature elements.


# Elements

Customization elements are individual visual options that can be toggled on or off — such as hairstyles, facial hair, scars, or tattoos.

## Creating an Element Entry

Open the Spark Editor, go to **Character Customization > Elements** and click **Create New**.

Each element entry defines:

| Field        | Description                                                                                                                                |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| Display Name | Element name shown in the customization UI (e.g., "Short Hair", "Long Hair", "Ponytail").                                                  |
| Category     | The customization category this element belongs to (e.g., "Hair").                                                                         |
| Is Required  | If enabled, at least one element from this category must always be active. The player cannot deselect all elements in a required category. |
| Sort Order   | Display order within the category — lower values appear first.                                                                             |

## How Elements Work

Elements map to GameObjects on the character. In the **CharacterCustomizationEntity** component, each element entry is paired with a list of visual options — each option points to a GameObject that gets activated when selected and deactivated when not selected.

For example, a "Hair" element might have three options: "Short Hair" (activates the short hair mesh), "Long Hair" (activates the long hair mesh), and "Ponytail" (activates the ponytail mesh). Only one option per element is active at a time.


# Colors

Customization colors modify material properties on the character — such as hair color, skin tone, or eye color.

## Creating a Color Entry

Open the Spark Editor, go to **Character Customization > Colors** and click **Create New**.

Each color entry defines:

| Field                  | Description                                                                                               |
| ---------------------- | --------------------------------------------------------------------------------------------------------- |
| Display Name           | Color option name shown in the customization UI (e.g., "Hair Color", "Skin Tone", "Eye Color").           |
| Category               | The customization category this color belongs to.                                                         |
| Material Property Name | The shader property to modify on the character's material (e.g., "\_BaseColor", "\_HairTint").            |
| Available Colors       | List of color options the player can choose from. Each option has a **Color Name** and a **Color** value. |
| Sort Order             | Display order within the category.                                                                        |

## How Colors Work

Colors modify material properties on specific renderers. In the **CharacterCustomizationEntity** component, each color entry is paired with a list of target renderers on the character model. When the player selects a color, the specified material property is updated on all target renderers.

For example, a "Hair Color" entry with material property "\_HairTint" and available colors like "Black", "Brown", "Blonde", and "Red" would let the player change their character's hair color by modifying the \_HairTint property on the hair mesh renderer.


# Presets

Customization presets define which elements and colors are available for a specific character setup, along with default selections.

## Creating a Preset Entry

Open the Spark Editor, go to **Character Customization > Presets** and click **Create New**.

Each preset entry defines:

| Field             | Description                                                     |
| ----------------- | --------------------------------------------------------------- |
| Display Name      | Preset name (e.g., "Human Male Preset", "Elf Female Preset").   |
| Category Elements | Visual elements available for this preset, grouped by category. |
| Category Colors   | Color options available for this preset, grouped by category.   |

### Category Elements

Each category element group defines:

| Field                 | Description                                                         |
| --------------------- | ------------------------------------------------------------------- |
| Category              | The customization category.                                         |
| Elements              | List of element entries available in this category for this preset. |
| Default Element Index | Which element is selected by default (0-based index).               |

### Category Colors

Each category color group defines:

| Field               | Description                                                       |
| ------------------- | ----------------------------------------------------------------- |
| Category            | The customization category.                                       |
| Colors              | List of color entries available in this category for this preset. |
| Default Color Index | Which color is selected by default (0-based index).               |

## Setting Up the CharacterCustomizationEntity

To enable customization on a character, add a **CharacterCustomizationEntity** component in the Inspector.

| Field                | Description                                                                                                                                                                             |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Customization Preset | The preset entry that defines available options for this character.                                                                                                                     |
| Element Entry Slots  | Visual element slots populated from the preset. Each slot pairs an element entry with a list of visual options — each option has a name and a target GameObject to activate/deactivate. |
| Element Color Slots  | Color slots populated from the preset. Each slot pairs a color entry with target renderers to apply colors to.                                                                          |

The editor provides an **Initialize From Preset** button that auto-populates the element and color slots from the assigned preset. After initialization, assign the target GameObjects and renderers for each slot.

Additional editor buttons available in Play Mode: **Randomize** (applies random customization for testing) and **Apply Default** (resets to preset defaults).

## Persistence

Character customization is automatically saved and loaded through Spark's save system. What is saved includes the active element option index for each element and the current color value for each color slot.


# Spellbooks

The Spellbooks plugin organizes abilities into collections that can be learned, granted, and displayed. Each spellbook contains a list of abilities, and abilities can be automatically added to the player's ability bar when the spellbook is learned.

## Creating a Spellbook Entry

Open the Spark Editor, go to **Spellbooks** and click **Create New**.

Each spellbook entry defines:

| Field        | Description                                                                              |
| ------------ | ---------------------------------------------------------------------------------------- |
| Display Name | Spellbook name shown in-game (e.g., "Fire Magic", "Holy Prayers", "Warrior Techniques"). |
| Description  | Spellbook description text.                                                              |
| Icon         | Sprite for the spellbook in UI.                                                          |
| Abilities    | List of abilities in this spellbook. Each ability has the fields below.                  |

### Spellbook Abilities

Each ability in the spellbook has:

| Field           | Description                                                                                                                                             |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Ability         | Reference to an ability entry from the Combat plugin.                                                                                                   |
| Auto Add To Bar | If enabled, this ability is automatically added to the player's ability bar when the character enters the game (if the ability's requirements are met). |

## Learning and Removing Spellbooks

Spellbooks are learned and removed through the trigger system.

| Trigger       | Description                                                                                        |
| ------------- | -------------------------------------------------------------------------------------------------- |
| Add Spellbook | Grants or removes a spellbook. Configure the spellbook entry and whether to remove instead of add. |

Learned spellbooks persist through the save system.

## Auto-Add to Ability Bar

Abilities marked with **Auto Add To Bar** are automatically placed in the player's ability bar when the character first enters the game. This uses the **SpellbookAutoAddHandler** component.

To set up auto-add, add a **SpellbookAutoAddHandler** component in the Inspector:

| Field              | Description                                                                                          |
| ------------------ | ---------------------------------------------------------------------------------------------------- |
| Ability Bar Entity | Reference to the AbilityBarEntity where abilities should be placed.                                  |
| Bar Identifier     | Name of the ability bar to target (default: "AbilityBar\_1").                                        |
| Number Of Slots    | Number of slots to create if the bar doesn't already exist.                                          |
| Process On Enable  | If enabled, auto-add runs when the component is enabled. If disabled, it must be triggered manually. |

Auto-add only processes once per game session and only fills empty slots. It also respects ability requirements — abilities whose requirements aren't met are skipped.

## Spellbook Panel UI

The Spellbooks plugin includes a **SpellbookPanelUI** component for displaying learned spellbooks and their abilities.

| Field                 | Description                                         |
| --------------------- | --------------------------------------------------- |
| Toggle Input Entry    | Input entry to toggle the panel open and closed.    |
| Close Button          | Button to close the panel.                          |
| Spellbooks Content    | Parent transform where spellbook slots are created. |
| Spellbook Slot Prefab | Prefab for each spellbook button in the sidebar.    |
| Abilities Content     | Parent transform where ability slots are created.   |
| Ability Slot Prefab   | Prefab for each ability in the detail view.         |

The panel shows a list of learned spellbooks on the left. When a spellbook is selected, its abilities are displayed on the right. Abilities that don't meet their requirements appear locked.


# Item Bar

The Item Bar plugin provides a hotbar system where players can assign items to numbered slots for quick access. Slots can be cycled with scroll wheel input, and the selected item can auto-equip.

## Creating an Item Bar Entry

Open the Spark Editor, go to **Item Bar** and click **Create New**.

Each item bar entry defines:

| Field              | Description                                                                        |
| ------------------ | ---------------------------------------------------------------------------------- |
| Display Name       | Item bar name (e.g., "Main Hotbar", "Consumables Bar").                            |
| Allowed Item Types | Whitelist of item types that can be placed in this bar (empty = accept all types). |

## Setting Up an Item Bar Entity

To give a character an item bar, add an **ItemBarEntity** component in the Inspector.

### Slot Configuration

| Field           | Description                                       |
| --------------- | ------------------------------------------------- |
| Number Of Slots | Total number of hotbar slots (1–20, default: 10). |

### Input Configuration

| Field                   | Description                                               |
| ----------------------- | --------------------------------------------------------- |
| Scroll Input Action     | Input action reference for scroll wheel slot cycling.     |
| Enable Scroll Cycling   | Allow scrolling to switch between slots (default: false). |
| Invert Scroll Direction | Reverse the scroll direction (default: false).            |

### Equipment Integration

| Field            | Description                                                         |
| ---------------- | ------------------------------------------------------------------- |
| Auto Equip Items | Automatically equip the item in the selected slot (default: false). |
| Equipment Entity | Reference to the EquipmentEntity for auto-equipping.                |

### Persistence

| Field               | Description                                                       |
| ------------------- | ----------------------------------------------------------------- |
| Is Persistent       | Save and load item bar contents across sessions (default: false). |
| Load On Initialize  | Auto-load saved data on startup (default: true).                  |
| Auto Save On Change | Auto-save when items change (default: true).                      |

## Slot Features

Each item bar slot supports:

| Feature         | Description                                                                                |
| --------------- | ------------------------------------------------------------------------------------------ |
| Item Assignment | Place any allowed item into the slot.                                                      |
| Locking         | Lock slots to prevent changes.                                                             |
| Selection       | One slot is always the "selected" slot. Switching selection can trigger equipment changes. |

Slot switching is blocked while a UI is open or during an ability cast (using the Rules system).

## Item Bar Display UI

The **ItemBarDisplayView** component displays the item bar in-game.

| Field              | Description                                                                                                      |
| ------------------ | ---------------------------------------------------------------------------------------------------------------- |
| Item Bar Entry     | Type restrictions for item validation.                                                                           |
| Connection Type    | How to find the ItemBarEntity: **Scene Reference** (direct drag-and-drop) or **Find By Tag** (auto-find by tag). |
| Item Bar Entity    | Direct reference when using Scene Reference.                                                                     |
| Item Bar Tag       | Tag to search for when using Find By Tag (default: "Player").                                                    |
| Search In Children | Search in child GameObjects when finding by tag.                                                                 |
| Slot UIs           | List of pre-existing slot UI components.                                                                         |

## Drag-and-Drop

Item bar slots support drag-and-drop with inventories:

| Source         | Target         | Result                              |
| -------------- | -------------- | ----------------------------------- |
| Item Bar Slot  | Item Bar Slot  | Swap items between slots.           |
| Inventory Slot | Item Bar Slot  | Move item from inventory to hotbar. |
| Item Bar Slot  | Inventory Slot | Move item from hotbar to inventory. |

The drag-and-drop system respects the item bar's allowed item types and slot lock states.


# NPCs

The NPCs plugin handles non-player characters: defining NPC entries with death triggers, setting up interactable NPCs with interaction menus, spawning NPCs in configurable areas with weighted selection, and integrating with the Quests plugin for quest-giving and turn-in interactions. It also provides interactable objects for non-NPC interactables like chests, levers, and doors.

In the Spark Editor, the NPCs plugin is organized into these categories:

| Category                                                            | What it contains                                                                                                                                                                                                                 |
| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [NPCs](/documentation/user-guide/plugins/npcs/npcs)                 | NPC entries with on-death triggers and player participation; NPCEntity setup; NPC Spawner with spawn shapes, validation, weighted NPC selection, respawn timers, and limits                                                      |
| [Interactions](/documentation/user-guide/plugins/npcs/interactions) | Interaction entries with types (Trigger, Give Quest, Turn In Quest); InteractableNPCEntity setup with indicator distances, cursor, and interaction lists; InteractableObjectEntity for non-NPC objects; NPC Interaction Panel UI |


# NPCs

NPC entries define non-player characters in the database with death behavior configuration.

## Creating an NPC Entry

Open the Spark Editor, go to **NPCs > NPCs** and click **Create New**.

Each NPC entry defines:

| Field             | Description                                                        |
| ----------------- | ------------------------------------------------------------------ |
| Display Name      | NPC name shown in-game (e.g., "Blacksmith Kara", "Guard Captain"). |
| Description       | NPC description text.                                              |
| Icon              | Sprite for the NPC in UI.                                          |
| On Death Triggers | List of triggers to execute when this NPC dies (see below).        |

### On Death Triggers

Each on-death trigger specifies:

| Field                        | Description                                                                                                  |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Trigger                      | The trigger entry to execute when this NPC dies (e.g., roll loot table, grant experience, advance quest).    |
| Require Player Participation | If enabled, the trigger only fires if the player was in the NPC's threat table when it died (default: true). |

This lets you configure rewards that only fire when the player contributed to the kill, preventing exploits where NPCs kill each other and the player gets credit.

## Setting Up an NPC Entity

To mark a GameObject as an NPC, add an **NPCEntity** component in the Inspector.

| Field | Description                                             |
| ----- | ------------------------------------------------------- |
| NPC   | Reference to the NPC database entry for this character. |

The NPCEntity component automatically sets the character's display name from the database entry and listens for the character's death to execute on-death triggers.

## NPC Spawner

The **NPCSpawner** component automates spawning NPCs in a defined area with weighted selection and respawn support.

### Spawn Area

| Field            | Description                                                                     |
| ---------------- | ------------------------------------------------------------------------------- |
| Spawn Shape      | **Box**, **Sphere**, or **Circle**. Defines the shape of the spawn volume.      |
| Box Size         | Size of the box area (default: 10, 5, 10). Only for Box shape.                  |
| Sphere Radius    | Radius of the sphere area (default: 10). Only for Sphere shape.                 |
| Circle Radius    | Radius of the circle area in the XZ plane (default: 10). Only for Circle shape. |
| Spawn On Surface | If enabled, spawns on the surface of the shape instead of within the volume.    |

### Spawn Validation

| Field                   | Description                                                                   |
| ----------------------- | ----------------------------------------------------------------------------- |
| Use Spawn Validation    | Enable spawn point validation (default: true).                                |
| Validation Method       | **Raycast Down**, **Raycast Up**, **Collision Check**, or **NavMesh Sample**. |
| Raycast Layer Mask      | Layers the raycast should hit for valid points.                               |
| Raycast Distance        | Maximum raycast distance (default: 100).                                      |
| Collision Check Radius  | Radius for overlap checks — a valid point has no collisions (default: 0.5).   |
| Max Validation Attempts | Attempts to find a valid point per spawn (default: 10).                       |

### NPC Entries

| Field             | Description                                                                                                                                                 |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| NPC Spawn Entries | List of NPC prefabs that can spawn, each with a **Spawn Weight** (higher = more likely), **Random Rotation** toggle, and optional fixed **Spawn Rotation**. |

### Spawn Limits

| Field            | Description                                                                                  |
| ---------------- | -------------------------------------------------------------------------------------------- |
| Max Active NPCs  | Maximum NPCs alive at once. Set to 0 for unlimited (default: 10).                            |
| Max Total Spawns | Maximum total NPCs spawned over the spawner's lifetime. Set to 0 for unlimited (default: 0). |
| Spawn Parent     | Parent transform for spawned NPCs. Null uses the spawner's transform.                        |

### Spawn Timing

| Field                   | Description                                                   |
| ----------------------- | ------------------------------------------------------------- |
| Spawn On Start          | Spawn initial NPCs when the component starts (default: true). |
| Initial Spawn Count     | Number of NPCs to spawn immediately (default: 3).             |
| Use Continuous Spawning | Enable interval-based spawning (default: false).              |
| Spawn Interval          | Seconds between spawn attempts (default: 5).                  |
| NPCs Per Interval       | NPCs to spawn each interval (default: 1).                     |
| Use Respawn On Death    | Respawn NPCs when they die (default: true).                   |
| Respawn Delay           | Seconds to wait before respawning after death (default: 3).   |

### Gizmos

The spawner draws spawn area gizmos in the Scene view (configurable color, toggle for area and spawn point display) to help visualize the spawn volume during level design.


# Interactions

NPC interactions define what happens when a player interacts with an NPC. Each interaction has a type that determines its behavior and data that configures it.

## Creating an Interaction Entry

Open the Spark Editor, go to **NPCs > Interactions** and click **Create New**.

Each interaction entry defines:

| Field            | Description                                                                                          |
| ---------------- | ---------------------------------------------------------------------------------------------------- |
| Display Name     | Internal name for this interaction.                                                                  |
| Interaction Type | The type of interaction (ScriptableObject dropdown). Determines what happens when selected.          |
| Interaction Text | Text shown in the NPC interaction menu (e.g., "Talk", "Trade", "Accept Quest"). Default: "Interact". |
| Text Color       | Color of the interaction text in the menu (default: white).                                          |

### Interaction Types

| Type          | Description                                                                                                                                                                                       |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Trigger       | Executes a trigger entry when the player selects this interaction. Configure the trigger entry in the interaction data. Use this for dialogue, cutscenes, rewards, or any trigger-based behavior. |
| Give Quest    | Offers a quest to the player. Configure the quest entry. Only appears if the player hasn't accepted or completed the quest and meets accept requirements. Requires Quests plugin.                 |
| Turn In Quest | Allows the player to turn in a completed quest. Configure the quest entry. Only appears if the quest is active or ready to turn in. Requires Quests plugin.                                       |

## Setting Up an Interactable NPC

To make an NPC interactable, add an **InteractableNPCEntity** component in the Inspector.

### Basic Settings

| Field                 | Description                                                                |
| --------------------- | -------------------------------------------------------------------------- |
| Display Name          | Name shown above the NPC as a nameplate (default: "NPC").                  |
| Indicator Y Offset    | Height above the NPC where the interaction indicator appears (default: 2). |
| Indicator Distance    | Distance at which the indicator becomes visible (default: 5).              |
| Can Interact Distance | Distance the player must be within to interact (default: 3).               |

### Cursor Settings

| Field       | Description                                                                       |
| ----------- | --------------------------------------------------------------------------------- |
| Talk Cursor | The cursor to display when hovering over this NPC and interactions are available. |

### Interaction Settings

| Field                          | Description                                                 |
| ------------------------------ | ----------------------------------------------------------- |
| Enable Key Interaction         | Allow interaction via the interact key (default: true).     |
| Enable Right Click Interaction | Allow interaction via right-click (default: false).         |
| Max Distance                   | Maximum distance for right-click interaction (default: 10). |

### Interactions List

| Field               | Description                                                                                  |
| ------------------- | -------------------------------------------------------------------------------------------- |
| Interactions        | List of NPC Interaction database entries shown in the interaction menu.                      |
| Inline Interactions | Interactions defined directly on this NPC without needing a database entry (scene-specific). |

**Inline interactions** are useful for one-off NPCs that don't need reusable interaction entries. Each inline interaction can either reference a database entry or define its own type, data, text, and color directly.

When the player interacts with the NPC, a menu panel appears showing all available interactions. The NPC automatically faces the player during interaction.

## Interactable Objects

For non-NPC interactables (chests, levers, doors, etc.), use the **InteractableObjectEntity** component instead:

| Field                             | Description                                                            |
| --------------------------------- | ---------------------------------------------------------------------- |
| Display Name                      | Label for this interactable.                                           |
| Indicator Y Offset                | Height for the indicator (default: 1.5).                               |
| Indicator Distance                | Visibility distance for the indicator (default: 5).                    |
| Can Interact Distance             | Required distance to interact (default: 3).                            |
| Interaction Requirements          | Requirement groups that must be met to interact.                       |
| Show UI When Requirements Not Met | Show the indicator even when requirements aren't met (default: false). |
| Can Interact Highlight            | A GameObject to activate/deactivate based on interactability.          |
| On Interaction Triggers           | List of triggers to execute when interacted with.                      |
| Allow Multiple Interactions       | If disabled, can only be interacted with once (default: true).         |

## NPC Interaction Panel UI

The **NPCInteractionPanel** is a singleton UI that displays the interaction menu.

| Field             | Description                                                    |
| ----------------- | -------------------------------------------------------------- |
| Options Container | Parent transform where interaction option buttons are spawned. |
| Option Prefab     | Prefab for individual interaction buttons.                     |
| Close Button      | Button to close the panel.                                     |
| NPC Name Text     | TextMeshPro component displaying the NPC's name.               |


# Welcome

This guide is for programmers who want to extend Spark with custom plugins, new database entry types, events, commands, and editor tooling. If you are looking to build games using Spark's existing visual tools without writing code, see the [User Guide](/documentation) instead.

## Who This Is For

You should read this guide if you want to:

* Create your own Spark plugins
* Define custom database entry types
* Add extension data to existing entry types
* Write custom triggers, requirements, or rules
* Implement custom commands and event handlers
* Build custom editor tabs and property drawers
* Integrate Spark with networking middleware
* Understand Spark's architecture for debugging or optimization

## Prerequisites

* Familiarity with C# and Unity development
* Basic understanding of ScriptableObjects, MonoBehaviours, and Unity's component model
* Spark Core installed in your project

## Architecture at a Glance

Spark is built on four pillars:

1. **Spark Service Locator** (`Spark.cs`): A static registry where plugins register themselves and can be retrieved by interface type. This is how plugins find each other without hard dependencies.
2. **SparkDatabaseRegistry**: A runtime cache of all `SparkDatabaseEntry` ScriptableObjects. Entries are loaded from `Resources/` folders at startup and indexed by ID and type for O(1) lookups.
3. **SparkEventBus**: A priority-based publish/subscribe event bus. Plugins communicate by publishing events and subscribing to events from other plugins. No direct references needed.
4. **SparkEntityRegistry**: A runtime registry of all `SparkEntity` components in the scene. Provides O(1) lookups by instance ID, entity ID, GameObject, or collider.

Data flows through the framework in a consistent pattern:

```
Input -> Command -> Handler -> State Change -> Event -> Subscribers -> UI Update
```

A player action triggers a command. The command handler validates and executes the action, then publishes an event. Other systems subscribe to that event and react accordingly (update UI, play sounds, check quest objectives, etc.).

## Recommended Reading Order

If you are new to Spark development, read these sections in order:

1. [Architecture Deep Dive](/documentation/developer-guide/core-systems/architecture) for a thorough understanding of each pillar
2. [Creating Your First Plugin](/documentation/developer-guide/creating-your-first-plugin) for a hands-on walkthrough
3. [Database Entries](/documentation/developer-guide/core-systems/database-entries) to learn how to define custom data types
4. [Extensions](/documentation/developer-guide/core-systems/extensions) to learn how plugins add data to other plugins
5. [Event System](/documentation/developer-guide/core-systems/event-system) and [Command System](/documentation/developer-guide/core-systems/command-system) for inter-plugin communication
6. [Entity System](/documentation/developer-guide/core-systems/entity-system) for scene-level integration

After that, read whichever sections are relevant to what you are building.

## Community Plugins

The Spark community has already created and released custom plugins without any official developer documentation. This guide aims to make that process much easier. If you build something, consider sharing it with the community.

## Conventions Used in This Guide

* Code examples use C# and target Unity 6.2+.
* `Spark.GetPlugin<T>()` calls always include null checks in production code. Examples may omit them for brevity, but you should always guard against null in real implementations.
* Assembly definition names follow the pattern `Spark.YourPluginName`.
* File paths are relative to your plugin's root folder unless stated otherwise.


# Creating Your First Plugin

This walkthrough builds a complete Spark plugin from scratch. By the end, you will have a working plugin with a database entry, a scene entity, a command, an event, and a tab in the Spark Editor.

We will build a generic plugin called **MyPlugin** that you can adapt to any purpose.

## Step 1: Create the Folder Structure

Inside your project, create the following folder structure:

```
Assets/MyPlugins/MyPlugin/
    Editor/
        Scripts/
    Runtime/
        Scripts/
            API/
            Data/
            Components/
            Events/
            Commands/
            Persistence/
        Resources/
            MyPluginData/
```

This follows the standard Spark plugin layout. `Runtime/` holds gameplay code, `Editor/` holds editor-only code, and `Resources/MyPluginData/` is where database entries will be stored.

## Step 2: Create Assembly Definitions

Create two assembly definition files:

**Runtime/Spark.MyPlugin.asmdef**

```json
{
    "name": "Spark.MyPlugin",
    "rootNamespace": "",
    "references": [
        "Spark.Core"
    ],
    "includePlatforms": [],
    "excludePlatforms": [],
    "allowUnsafeCode": false,
    "overrideReferences": false,
    "precompiledReferences": [],
    "autoReferenced": true,
    "defineConstraints": [],
    "versionDefines": [],
    "noEngineReferences": false
}
```

**Editor/Spark.MyPlugin.Editor.asmdef**

```json
{
    "name": "Spark.MyPlugin.Editor",
    "rootNamespace": "",
    "references": [
        "Spark.Core",
        "Spark.Core.Editor",
        "Spark.MyPlugin"
    ],
    "includePlatforms": ["Editor"],
    "excludePlatforms": [],
    "allowUnsafeCode": false,
    "overrideReferences": false,
    "precompiledReferences": [],
    "autoReferenced": true,
    "defineConstraints": [],
    "versionDefines": [],
    "noEngineReferences": false
}
```

## Step 3: Define the Plugin Interface

Create the interface that other plugins will use to access your plugin's functionality.

**Runtime/Scripts/API/IMyPlugin.cs**

```csharp
using System.Collections.Generic;

public interface IMyPlugin
{
    List<MyPluginEntry> GetAllEntries();
    MyPluginEntry GetEntry(string entryId);
    bool HasEntry(string entryId);
}
```

Keep the interface focused on read operations. Actions that modify state should go through commands.

## Step 4: Create the Database Entry

This is the data type that appears in the Spark Editor.

**Runtime/Scripts/Data/MyPluginEntry.cs**

```csharp
using UnityEngine;

[CreateAssetMenu(
    fileName = "New MyPlugin Entry",
    menuName = "Spark/MyPlugin/Entry")]
public class MyPluginEntry : SparkDatabaseEntry
{
    [Section("General Settings")]
    [SerializeField] private int defaultValue = 0;

    [SerializeField] private int minValue = 0;

    [SerializeField] private int maxValue = 1000;

    public int DefaultValue => defaultValue;
    public int MinValue => minValue;
    public int MaxValue => maxValue;
}
```

The `[Section]` attribute creates collapsible groups in the Spark Editor's inspector. The `[SerializeField] private` pattern with public getters is the standard Spark convention for entry fields.

## Step 5: Define Events

Events notify other systems when something happens in your plugin.

**Runtime/Scripts/Events/MyPluginValueChangedEvent.cs**

```csharp
public class MyPluginValueChangedEvent : SparkEventBase
{
    public override string EventType => "MyPluginValueChanged";
    public string CharacterId { get; }
    public string EntryId { get; }
    public int OldValue { get; }
    public int NewValue { get; }
    public int Delta { get; }

    public MyPluginValueChangedEvent(
        string characterId,
        string entryId,
        int oldValue,
        int newValue,
        string source = null)
    {
        CharacterId = characterId;
        EntryId = entryId;
        OldValue = oldValue;
        NewValue = newValue;
        Delta = newValue - oldValue;
        Source = source;
    }
}
```

## Step 6: Define Commands and Handlers

Commands are the actions your plugin supports. Handlers contain the logic.

**Runtime/Scripts/Commands/ModifyMyPluginValueCommand.cs**

```csharp
public class ModifyMyPluginValueCommand : ICommand
{
    public string CharacterId { get; set; }
    public string EntryId { get; set; }
    public int Amount { get; set; }
    public string Source { get; set; }
}
```

**Runtime/Scripts/Commands/ModifyMyPluginValueCommandHandler.cs**

```csharp
using UnityEngine;

public class ModifyMyPluginValueCommandHandler
    : ICommandHandler<ModifyMyPluginValueCommand>
{
    public void Handle(ModifyMyPluginValueCommand command)
    {
        // Find the entity
        if (!SparkEntityRegistry.TryGetEntity(
                command.CharacterId, out SparkEntity entity))
        {
            Debug.LogWarning($"Entity not found: {command.CharacterId}");
            return;
        }

        // Get the plugin component
        var tracker = entity.GetSparkComponent<MyPluginTrackerEntity>();
        if (tracker == null)
        {
            Debug.LogWarning(
                $"No MyPluginTrackerEntity on {command.CharacterId}");
            return;
        }

        // Get the entry for validation
        MyPluginEntry entry =
            SparkDatabaseRegistry.GetEntry<MyPluginEntry>(command.EntryId);
        if (entry == null)
        {
            Debug.LogWarning($"Entry not found: {command.EntryId}");
            return;
        }

        // Apply the change
        int oldVal = tracker.GetValue(command.EntryId);
        int newVal = Mathf.Clamp(
            oldVal + command.Amount,
            entry.MinValue,
            entry.MaxValue);

        tracker.SetValue(command.EntryId, newVal);

        // Publish event
        SparkEventBus.Publish(new MyPluginValueChangedEvent(
            command.CharacterId,
            command.EntryId,
            oldVal,
            newVal,
            command.Source));
    }
}
```

The handler follows the standard pattern: validate inputs, apply state changes, publish events.

## Step 7: Create the Scene Entity

This MonoBehaviour goes on a character's GameObject to track plugin data at runtime.

**Runtime/Scripts/Components/MyPluginTrackerEntity.cs**

```csharp
using System.Collections.Generic;
using UnityEngine;

public class MyPluginTrackerEntity : MonoBehaviour
{
    private Dictionary<string, int> _values =
        new Dictionary<string, int>();

    public int GetValue(string entryId)
    {
        return _values.TryGetValue(entryId, out int val)
            ? val
            : GetDefaultValue(entryId);
    }

    public void SetValue(string entryId, int value)
    {
        _values[entryId] = value;
    }

    public Dictionary<string, int> GetAllValues()
    {
        return new Dictionary<string, int>(_values);
    }

    private int GetDefaultValue(string entryId)
    {
        MyPluginEntry entry =
            SparkDatabaseRegistry.GetEntry<MyPluginEntry>(entryId);
        return entry != null ? entry.DefaultValue : 0;
    }
}
```

## Step 8: Implement the Plugin Class

This ties everything together. The plugin class registers itself with Spark and registers its command handlers with the network provider.

**Runtime/Scripts/API/MyPluginImplementation.cs**

```csharp
using System.Collections.Generic;
using UnityEngine;

public class MyPluginImplementation : IMyPlugin
{
    private static MyPluginImplementation _instance;
    private static bool _hasRegistered = false;

    [RuntimeInitializeOnLoadMethod(
        RuntimeInitializeLoadType.SubsystemRegistration)]
    private static void Reset()
    {
        _hasRegistered = false;
        _instance = null;
    }

    [RuntimeInitializeOnLoadMethod(
        RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void RegisterPlugin()
    {
        if (!_hasRegistered)
        {
            _instance = new MyPluginImplementation();
            Spark.RegisterPlugin<IMyPlugin>(_instance);
            _hasRegistered = true;

            // Register command handlers
            Spark.Network?.RegisterHandler<ModifyMyPluginValueCommand>(
                new ModifyMyPluginValueCommandHandler());
        }
    }

    #if UNITY_EDITOR
    [UnityEditor.InitializeOnLoadMethod]
    private static void RegisterPluginInEditor()
    {
        Reset();
        RegisterPlugin();
    }
    #endif

    public List<MyPluginEntry> GetAllEntries()
    {
        return SparkDatabaseRegistry.GetAllEntries<MyPluginEntry>();
    }

    public MyPluginEntry GetEntry(string entryId)
    {
        return SparkDatabaseRegistry.GetEntry<MyPluginEntry>(entryId);
    }

    public bool HasEntry(string entryId)
    {
        return SparkDatabaseRegistry.HasEntry(entryId);
    }
}
```

## Step 9: Create the Plugin Manifest

The manifest tells the Spark Editor about your plugin. Create a ScriptableObject asset:

1. In Unity, right-click in your Editor folder.
2. Select **Create > Spark > Plugin Manifest** (or create a `PluginManifest` ScriptableObject).
3. Fill in the fields:
   * **Plugin Name**: MyPlugin
   * **Unique ID**: myplugin
   * **Version**: 1.0.0
   * **Author**: Your Name
   * **Folder Name**: MyPlugin
4. Add a Category called "My Plugin" with a tab:
   * **Tab ID**: myplugin\_tab
   * **Display Name**: My Plugin
   * **Asset Type**: Select `MyPluginEntry`
   * **Path**: The Resources path where entries are stored
   * **Tab Type**: Database

This creates a "My Plugin" tab in the Spark Editor where users can create and manage entries.

## Step 10: Using Your Plugin

Once everything is set up, here is how you use the plugin from other code:

```csharp
// Get the plugin
var myPlugin = Spark.GetPlugin<IMyPlugin>();

// Read entry data
MyPluginEntry entry = myPlugin?.GetEntry("my_entry_01");

// Modify values via command
Spark.Network?.ExecuteCommand(new ModifyMyPluginValueCommand
{
    CharacterId = "player_01",
    EntryId = "my_entry_01",
    Amount = 50,
    Source = "quest_completed"
});

// Listen for value changes
SparkEventBus.Subscribe<MyPluginValueChangedEvent>(myHandler);
```

## What You Have Built

Your plugin now has:

* A plugin interface (`IMyPlugin`) for other plugins to access
* A database entry type (`MyPluginEntry`) editable in the Spark Editor
* An event (`MyPluginValueChangedEvent`) that fires on data changes
* A command (`ModifyMyPluginValueCommand`) for modifying values
* A command handler that validates, applies changes, and publishes events
* A scene entity (`MyPluginTrackerEntity`) that stores data per character
* A plugin class that registers everything at startup
* A manifest that adds a tab to the Spark Editor

This follows the exact same patterns used by Spark's built-in plugins. From here, you could add save data persistence, custom trigger types, requirement types, and extensions to other plugins' entries.

## Next Steps

* [Database Entries](/documentation/developer-guide/core-systems/database-entries) for a deeper look at custom entry types
* [Extensions](/documentation/developer-guide/core-systems/extensions) to learn how to add fields to other plugins' entries
* [Event System](/documentation/developer-guide/core-systems/event-system) for advanced event patterns
* [Save System](/documentation/developer-guide/core-systems/save) to persist your plugin's data


# Core Systems

This section covers the internal architecture and APIs of Spark's core systems. These are the foundational systems that every plugin builds upon.

You will find deep dives into how each system works under the hood, along with full API references for programmatic access.


# Architecture

This page covers the internals of Spark's four core pillars and how they connect. Understanding this architecture will make everything else in the Developer Guide click.

## The Spark Service Locator

`Spark.cs` is a static class that acts as the central registry for all plugins. Every plugin registers itself here at startup, and any system can retrieve a plugin by its interface type.

### API

```csharp
// Register a plugin instance
Spark.RegisterPlugin<IMyPlugin>(instance);

// Retrieve a plugin (returns null if not registered)
IMyPlugin plugin = Spark.GetPlugin<IMyPlugin>();

// Remove a plugin
Spark.UnregisterPlugin<IMyPlugin>();

// Access the network provider
INetworkProvider network = Spark.Network;

// Listen for network provider readiness
Spark.OnNetworkProviderReady += (provider) => { /* ... */ };
```

### How Registration Works

Plugins register using Unity's `[RuntimeInitializeOnLoadMethod]` attribute, which runs automatically before any scene loads. Each plugin follows this pattern:

```csharp
public class MyPlugin : IMyPlugin
{
    private static MyPlugin _instance;
    private static bool _hasRegistered = false;

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
    private static void Reset()
    {
        _hasRegistered = false;
        _instance = null;
    }

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void RegisterPlugin()
    {
        if (!_hasRegistered)
        {
            _instance = new MyPlugin();
            Spark.RegisterPlugin<IMyPlugin>(_instance);
            _hasRegistered = true;
        }
    }

    #if UNITY_EDITOR
    [UnityEditor.InitializeOnLoadMethod]
    private static void RegisterPluginInEditor()
    {
        Reset();
        RegisterPlugin();
    }
    #endif
}
```

The two-phase approach (Reset at `SubsystemRegistration`, Register at `BeforeSceneLoad`) handles Unity's domain reload correctly. The `#if UNITY_EDITOR` block ensures the plugin is also available in edit mode for the Spark Editor window.

### Initialization Order

Unity runs `RuntimeInitializeOnLoadMethod` in this order:

1. `SubsystemRegistration` - Reset static state (domain reload safety)
2. `AfterAssembliesLoaded` - Assemblies ready
3. `BeforeSplashScreen` - Before splash
4. `BeforeSceneLoad` - Plugin registration happens here
5. `AfterSceneLoad` - Scene is loaded, entities are active

Spark itself initializes at `SubsystemRegistration` to clear its internal dictionaries, then plugins register at `BeforeSceneLoad`.

## SparkDatabaseRegistry

The database registry is a static cache of all `SparkDatabaseEntry` ScriptableObjects in the project. It loads entries at startup and provides fast lookups.

### How It Loads

In the editor, the registry uses `AssetDatabase` to find all entries. In builds, it loads from `Resources/` folders. An `AssetPostprocessor` handles hot-reloading when entries are created, modified, or deleted in the editor.

Entries are indexed in two ways:

* **By ID**: `Dictionary<string, SparkDatabaseEntry>` for O(1) lookup by unique ID
* **By Type**: `Dictionary<Type, List<SparkDatabaseEntry>>` for retrieving all entries of a given type

### API

```csharp
// Get a specific entry by ID
ItemEntry item = SparkDatabaseRegistry.GetEntry<ItemEntry>("iron_sword");

// Get all entries of a type
List<QuestEntry> quests = SparkDatabaseRegistry.GetAllEntries<QuestEntry>();

// Check if an entry exists
bool exists = SparkDatabaseRegistry.HasEntry("iron_sword");

// Get total count
int total = SparkDatabaseRegistry.GetTotalEntryCount();

// Force refresh (usually not needed)
SparkDatabaseRegistry.Refresh();
```

### Entry ID Format

Entry IDs are auto-generated in lowercase with underscores. They follow the pattern `entrytype_name` or similar. Once created, an ID cannot be changed. This ensures references to entries remain stable.

## SparkEventBus

The event bus is the primary way plugins communicate with each other. It uses a priority-based publish/subscribe pattern.

### Publishing Events

```csharp
// Publish immediately (handlers run synchronously)
SparkEventBus.Publish(new QuestCompletedEvent(questId, characterId));

// Queue for batch processing
SparkEventBus.QueueEvent(new ExperienceGainedEvent(charId, 50));
SparkEventBus.QueueEvent(new ExperienceGainedEvent(charId, 25));
SparkEventBus.ProcessQueuedEvents(); // Both fire now
```

### Subscribing to Events

```csharp
// Subscribe a handler
SparkEventBus.Subscribe<QuestCompletedEvent>(new MyQuestHandler());

// Unsubscribe
SparkEventBus.Unsubscribe<QuestCompletedEvent>(handler);
```

### Handler Priority

Handlers run in priority order, highest first. This lets you control execution order when multiple systems need to respond to the same event.

```csharp
public class UrgentHandler : ISparkEventHandler<DamageEvent>
{
    public int HandlerPriority => 100; // Runs first
    public bool Handle(DamageEvent evt) { /* ... */ return true; }
}

public class NormalHandler : ISparkEventHandler<DamageEvent>
{
    public int HandlerPriority => 50; // Runs second
    public bool Handle(DamageEvent evt) { /* ... */ return true; }
}
```

### Event Consumption

If an event implements `IConsumableEvent`, a handler can stop propagation by returning `true` from `Handle()`. This prevents lower-priority handlers from receiving the event.

### Type Matching

The event bus supports compatible type matching. If you subscribe to a base event class or an interface, your handler will receive all events that inherit from that class or implement that interface.

### Event Contract

All events implement `ISparkEvent`:

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

The `SparkEventBase` abstract class provides a default implementation where `Timestamp` is set at construction and `Priority` defaults to `0`.

## SparkEntityRegistry

The entity registry tracks all `SparkEntity` instances in the scene. Entities register themselves automatically when they awaken and unregister when destroyed.

### Lookup Methods

All lookups are O(1) dictionary access:

```csharp
// By entity ID string
SparkEntity entity = SparkEntityRegistry.GetEntityById("player_01");

// By GameObject reference
SparkEntity entity = SparkEntityRegistry.GetEntityFromGameObject(gameObject);

// By Collider (memoized)
SparkEntity entity = SparkEntityRegistry.GetEntityFromCollider(collider);

// The player entity (cached)
SparkEntity player = SparkEntityRegistry.GetPlayerEntity();

// By tag
List<SparkEntity> enemies = SparkEntityRegistry.GetEntitiesByTag("Enemy");

// All entities
List<SparkEntity> all = SparkEntityRegistry.GetAllEntities();
```

### TryGet Variants

Every lookup method has a `TryGet` variant that returns `bool` and outputs the result, avoiding null checks:

```csharp
if (SparkEntityRegistry.TryGetEntity("npc_01", out SparkEntity npc))
{
    // Use npc
}
```

### Entity IDs

Each `SparkEntity` has an ID source that determines how its ID is generated:

* **Local**: Auto-generated GUID + timestamp. Used for dynamically spawned objects.
* **Static**: Manually assigned in the inspector. Used for persistent world objects (NPCs, interactables) that must be the same across sessions.
* **Networked**: Assigned by the server at runtime. Used for multiplayer-synced objects.

## Assembly Definitions

Spark uses assembly definitions extensively. Each plugin has its own `Spark.PluginName.asmdef` for the runtime code and optionally `Spark.PluginName.Editor.asmdef` for editor code.

This structure provides:

* **Compile isolation**: Changes to one plugin don't recompile others.
* **Dependency control**: Plugins declare explicit references to only the assemblies they need.
* **Optional dependencies**: A plugin can check for another plugin's existence at runtime without requiring a compile-time reference.

The core assembly is `Spark.Core`. All plugins reference it. Plugins that need to interact with each other (like extensions) add explicit assembly references.

### Optional Dependencies Pattern

When a plugin wants to interact with another plugin that may or may not be installed:

```csharp
var combatPlugin = Spark.GetPlugin<ICombatPlugin>();
if (combatPlugin != null)
{
    // Combat plugin is available, use it
}
```

This null-check pattern is used throughout Spark. Plugins never assume other optional plugins are present.

## Data Flow Summary

Here is the complete lifecycle of a typical game action:

```
1. Player Input
   |
2. Command Created (e.g., UseItemCommand)
   |
3. Command Executed via Spark.Network.ExecuteCommand()
   |
4. Command Handler validates and processes
   |
5. State changes applied (inventory updated, stats modified)
   |
6. Event published (e.g., ItemUsedEvent)
   |
7. Event handlers react:
       - UI handler updates the inventory display
       - Quest handler checks if an objective was completed
       - Sound handler plays a use sound
       - Screen text handler shows a floating message
```

This pattern is consistent across all of Spark. Once you understand it, you can predict how any system works.

## Next Steps

Now that you understand the architecture, continue to [Creating Your First Plugin](/documentation/developer-guide/creating-your-first-plugin) to put this knowledge into practice.


# Database Entries

Every piece of game data in Spark is a `SparkDatabaseEntry`. This page covers how to create custom entry types, use nested data, and access entries at runtime.

## Anatomy of a SparkDatabaseEntry

`SparkDatabaseEntry` is an abstract class that extends Unity's `ScriptableObject`. It provides these base fields:

| Field         | Type     | Description                                                  |
| ------------- | -------- | ------------------------------------------------------------ |
| `id`          | `string` | Unique identifier. Auto-generated, read-only after creation. |
| `entryName`   | `string` | Internal name for organization in the editor.                |
| `displayName` | `string` | Player-facing name (shown in UI).                            |
| `description` | `string` | Player-facing description (tooltips, info panels).           |
| `icon`        | `Sprite` | Visual representation in UI elements.                        |

All fields are serialized with `[SerializeField] private` and exposed through public getters. This is the standard pattern for Spark entries.

## Creating a Custom Entry Type

To define a new database entry, inherit from `SparkDatabaseEntry`:

```csharp
using UnityEngine;

[CreateAssetMenu(
    fileName = "New Mount",
    menuName = "Spark/Mounts/Mount Entry")]
public class MountEntry : SparkDatabaseEntry
{
    [Section("Movement")]
    [SerializeField] private float speed = 10f;
    [SerializeField] private float sprintMultiplier = 1.5f;

    [Section("Appearance")]
    [SerializeField] private GameObject mountPrefab;
    [SerializeField] private RuntimeAnimatorController animatorController;

    [Section("Requirements")]
    [DatabaseEntryDropdown(typeof(ClassEntry))]
    [SerializeField] private string requiredClassId;

    [ConditionalField("requiredClassId", "")]
    [SerializeField] private int requiredLevel = 1;

    public float Speed => speed;
    public float SprintMultiplier => sprintMultiplier;
    public GameObject MountPrefab => mountPrefab;
    public RuntimeAnimatorController AnimatorController => animatorController;
    public string RequiredClassId => requiredClassId;
    public int RequiredLevel => requiredLevel;
}
```

Key points:

* Use `[Section("Title")]` to group related fields under collapsible headers.
* Use `[DatabaseEntryDropdown(typeof(T))]` to create a dropdown selector for entries of a specific type.
* Use `[ConditionalField("fieldName", value)]` to show/hide fields based on another field's value.
* Use `[ReadOnly]` to make a field visible but not editable.
* Always use `[SerializeField] private` with public getter properties.

## Entry Storage

Entries must be stored in a `Resources/` folder so they can be loaded at runtime. The standard location is:

```
YourPlugin/Runtime/Resources/YourPluginData/
```

For example, mount entries would go in:

```
Mounts/Runtime/Resources/MountData/
```

The Spark Editor handles creating entries in the correct location based on your plugin manifest configuration.

## Accessing Entries at Runtime

`SparkDatabaseRegistry` provides O(1) access to any entry:

```csharp
// Get a single entry by ID
MountEntry mount = SparkDatabaseRegistry.GetEntry<MountEntry>("war_horse");

// Get all entries of a type
List<MountEntry> allMounts = SparkDatabaseRegistry.GetAllEntries<MountEntry>();

// Check existence
bool exists = SparkDatabaseRegistry.HasEntry("war_horse");

// Get total entry count across all types
int total = SparkDatabaseRegistry.GetTotalEntryCount();
```

The registry caches entries on first access. In the editor, it uses `AssetDatabase` for discovery. In builds, it loads from `Resources/` folders.

## Nested Data

For complex entries that contain lists of sub-objects, use `SparkDatabaseEntryNestedData`:

```csharp
[System.Serializable]
public class MountAbilityData : SparkDatabaseEntryNestedData
{
    [SerializeField] private string abilityName;
    [SerializeField] private float cooldown = 5f;
    [SerializeField] private Sprite abilityIcon;

    public string AbilityName => abilityName;
    public float Cooldown => cooldown;
    public Sprite AbilityIcon => abilityIcon;
}
```

Then reference it from your entry:

```csharp
public class MountEntry : SparkDatabaseEntry
{
    [Section("Abilities")]
    [NestedData]
    [SerializeField] private List<MountAbilityData> abilities = new List<MountAbilityData>();

    public List<MountAbilityData> Abilities => abilities;
}
```

`SparkDatabaseEntryNestedData` tracks its parent entry and data type name for editor tooling. The `[NestedData]` attribute tells the Spark Editor to render it as an inline editable list.

## Validation

Override `OnValidate()` to add custom validation to your entries:

```csharp
protected override void OnValidate()
{
    base.OnValidate();

    if (speed < 0) speed = 0;
    if (sprintMultiplier < 1f) sprintMultiplier = 1f;
}
```

This runs in the editor whenever a field is changed, providing immediate feedback.

## Available Attributes

Here is a quick reference of attributes you can use on entry fields:

| Attribute                               | Purpose                                       |
| --------------------------------------- | --------------------------------------------- |
| `[Section("Title")]`                    | Group fields under a collapsible header       |
| `[Section("Title", collapsible: true)]` | Same, with explicit collapsible flag          |
| `[DisplayNameTooltip("Label", "Tip")]`  | Custom label and tooltip                      |
| `[ReadOnly]`                            | Non-editable field                            |
| `[ConditionalField("field", value)]`    | Show only when another field matches a value  |
| `[ConditionalMultiField(...)]`          | Show based on multiple field conditions       |
| `[DatabaseEntryDropdown(typeof(T))]`    | Dropdown picker for entries of type T         |
| `[SparkDatabaseEntrySelector]`          | Generic entry picker (any type)               |
| `[ScriptableObjectDropdown(typeof(T))]` | Dropdown for ScriptableObject assets          |
| `[InlineField]`                         | Render nested object fields inline            |
| `[NestedData]`                          | Render as editable nested data list           |
| `[TagField]`                            | Unity tag dropdown                            |
| `[Switch("field", "value1", "value2")]` | Switch field rendering based on another field |

See [Custom Attributes Reference](/documentation/developer-guide/core-systems/custom-attributes) for the full details on each attribute.

## Best Practices

* Keep entry classes focused. If an entry has too many fields, consider splitting into a base entry and extensions.
* Use `[Section]` generously to keep the editor organized.
* Use conditional fields to hide irrelevant options. This reduces clutter for users.
* Always validate serialized data in `OnValidate()`.
* Never store runtime state in entries. Entries are configuration data, not runtime data.
* Use meaningful ID prefixes. The auto-generated ID format uses the entry name, but you can influence it by naming entries clearly.


# 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.


# 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.


# Command System

Commands represent actions that change game state. They are the standard way to perform mutations in Spark: giving items, dealing damage, accepting quests, modifying reputation, and so on.

## Why Commands?

Commands exist for two reasons:

1. **Validation**: Every action goes through a handler that can validate inputs before applying changes.
2. **Networking**: Commands are routed through the network provider. In single-player, they execute locally. In multiplayer, they can be sent to the server for authoritative execution. The calling code doesn't need to know which mode it's in.

## ICommand Interface

`ICommand` is a marker interface with no methods or properties:

```csharp
public interface ICommand { }
```

Commands are simple data containers. They hold the parameters needed to perform an action but contain no logic:

```csharp
public class GiveItemCommand : ICommand
{
    public string TargetEntityId { get; set; }
    public string ItemEntryId { get; set; }
    public int Quantity { get; set; }
}
```

## ICommandHandler Interface

The handler contains all the logic for processing a command:

```csharp
public interface ICommandHandler<T> where T : ICommand
{
    void Handle(T command);
}
```

A typical handler follows this pattern:

```csharp
public class GiveItemCommandHandler : ICommandHandler<GiveItemCommand>
{
    public void Handle(GiveItemCommand command)
    {
        // 1. Validate inputs
        if (string.IsNullOrEmpty(command.TargetEntityId))
        {
            Debug.LogWarning("GiveItemCommand: No target entity specified.");
            return;
        }

        var entry = SparkDatabaseRegistry.GetEntry<ItemEntry>(command.ItemEntryId);
        if (entry == null)
        {
            Debug.LogWarning($"GiveItemCommand: Item not found: {command.ItemEntryId}");
            return;
        }

        // 2. Find the target entity
        if (!SparkEntityRegistry.TryGetEntity(command.TargetEntityId, out SparkEntity entity))
        {
            Debug.LogWarning($"GiveItemCommand: Entity not found: {command.TargetEntityId}");
            return;
        }

        var inventory = entity.GetSparkComponent<InventoryEntity>();
        if (inventory == null)
        {
            Debug.LogWarning($"GiveItemCommand: No inventory on entity.");
            return;
        }

        // 3. Apply the state change
        inventory.AddItem(command.ItemEntryId, command.Quantity);

        // 4. Publish an event
        SparkEventBus.Publish(new ItemGivenEvent(
            command.TargetEntityId,
            command.ItemEntryId,
            command.Quantity));
    }
}
```

The four-step pattern is consistent across all Spark command handlers: validate, find, apply, publish.

## Registering Command Handlers

Register handlers in your plugin's registration method:

```csharp
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void RegisterPlugin()
{
    if (!_hasRegistered)
    {
        _instance = new MyPlugin();
        Spark.RegisterPlugin<IMyPlugin>(_instance);

        // Register command handlers
        Spark.Network?.RegisterHandler<GiveItemCommand>(
            new GiveItemCommandHandler());

        _hasRegistered = true;
    }
}
```

Each command type has exactly one handler. Registering a handler for a type that already has one will overwrite the previous handler.

## Executing Commands

Execute commands through the network provider:

```csharp
Spark.Network?.ExecuteCommand(new GiveItemCommand
{
    TargetEntityId = "player_01",
    ItemEntryId = "iron_sword",
    Quantity = 1
});
```

The `?.` null-conditional is important. If no network provider is set up, the command silently does nothing rather than throwing.

## How Execution Works

When `ExecuteCommand` is called:

1. The network provider receives the command.
2. It looks up the registered handler for the command's type.
3. It invokes `Handle()` on the handler using reflection-based dispatch (in `NetworkProviderBase`).
4. The handler runs its validation and logic.

In single-player mode, `LocalNetworkProvider` executes everything immediately and locally. In multiplayer, a custom network provider could serialize the command and send it to the server.

## LocalNetworkProvider

`LocalNetworkProvider` is the default provider. It:

* Registers automatically if no other provider is set
* Executes all commands immediately and locally
* Reports `IsServer => true` (since single-player is effectively the server)
* Registers built-in handlers like `DragDropCommandHandler`

You don't need to configure it. It just works.

## Command Design Guidelines

Keep commands as simple data containers:

```csharp
// Good: flat, serializable properties
public class HealCommand : ICommand
{
    public string TargetId { get; set; }
    public float Amount { get; set; }
    public string SourceAbilityId { get; set; }
}

// Avoid: complex objects, logic, or Unity references
public class BadCommand : ICommand
{
    public GameObject Target { get; set; }    // Not serializable
    public void Execute() { /* ... */ }       // Logic belongs in handler
}
```

Commands should be serializable for networking compatibility. Use entry IDs (strings) instead of direct object references.

## Best Practices

* One command per action. Don't bundle multiple actions into one command.
* Validate everything in the handler. Don't assume inputs are valid.
* Always publish an event after applying state changes. This keeps the rest of the framework in sync.
* Use descriptive names: `{Verb}{Noun}Command` (e.g., `AcceptQuestCommand`, `CraftItemCommand`).
* Keep commands idempotent where possible. Running the same command twice should produce predictable results.
* Log warnings for validation failures. This makes debugging much easier.


# Entity System

The entity system connects your scene objects to the Spark framework. Every gameplay object (player, NPC, interactable, chest, crafting station) uses a `SparkEntity` component to identify itself and provide fast component access.

## SparkEntity Component

`SparkEntity` is a MonoBehaviour that you add to any GameObject that needs to participate in Spark systems. It provides:

* A unique entity ID for lookups
* Cached component access (faster than `GetComponent<T>()`)
* Automatic registration with `SparkEntityRegistry`
* Display name binding for UI

### Entity IDs

Each entity has an ID and an ID source that determines how it's generated:

| ID Source   | When to Use                                           | How It Works                                                                 |
| ----------- | ----------------------------------------------------- | ---------------------------------------------------------------------------- |
| `Local`     | Dynamically spawned objects                           | Auto-generated GUID + timestamp. Unique per session but different each time. |
| `Static`    | Persistent world objects (NPCs, interactables, doors) | Manually assigned in the inspector. Stable across sessions for save/load.    |
| `Networked` | Multiplayer-synced objects                            | Assigned by the server at runtime. Shared across all clients.                |

For most gameplay objects, use `Static` so they can be saved and referenced by ID. Use `Local` for objects that are created dynamically at runtime (spawned enemies, dropped items).

### Component Caching

`SparkEntity` caches references to other components on the same GameObject. This is faster than Unity's `GetComponent<T>()` for repeated lookups:

```csharp
SparkEntity entity = SparkEntityRegistry.GetPlayerEntity();

// Fast cached lookup (preferred)
var inventory = entity.GetSparkComponent<InventoryEntity>();

// Also available
bool hasEquipment = entity.HasComponent<EquipmentEntity>();

// Try pattern
if (entity.TryGetSparkComponent<QuestTrackerEntity>(out var tracker))
{
    // Use tracker
}

// Get all components of a type (for multi-instance)
var allBuffs = entity.GetSparkComponents<BuffComponent>();
```

The cache is built on `Awake()` and can be refreshed with `RefreshCache()` if components are added or removed at runtime.

### API Reference

```csharp
// Core properties
string entity.EntityId           // The unique ID
string entity.DisplayName        // Display name for UI
bool entity.IsLocalPlayer        // Whether this is the local player

// Component access
T GetSparkComponent<T>()                    // Single component (cached)
bool TryGetSparkComponent<T>(out T result)  // Try pattern
T[] GetSparkComponents<T>()                 // All components of type
bool HasComponent<T>()                      // Existence check
bool HasComponent(Type type)                // Existence check by Type
void RefreshCache()                         // Rebuild the cache
Type[] GetCachedComponentTypes()            // All cached types

// ID management (usually set in inspector)
void SetIdSource(EntityIdSource source)
void GenerateStaticId()                     // Editor only
void ResetStaticId()                        // Editor only
void AssignNetworkId(string networkId)      // For multiplayer
void RestoreLocalId()                       // Revert from network ID
void SetIsLocalPlayer(bool value)
```

## SparkEntityRegistry

The registry is a static service that tracks all active `SparkEntity` instances. Entities register automatically in `OnEnable()` and unregister in `OnDestroy()`.

### Lookups

All lookups are O(1) dictionary access:

```csharp
// By entity ID string
SparkEntity entity = SparkEntityRegistry.GetEntityById("npc_blacksmith");

// By GameObject reference
SparkEntity entity = SparkEntityRegistry.GetEntityFromGameObject(gameObject);

// By Collider (memoized for repeated calls)
SparkEntity entity = SparkEntityRegistry.GetEntityFromCollider(hitCollider);

// The player entity (cached for fast access)
SparkEntity player = SparkEntityRegistry.GetPlayerEntity();
```

### TryGet Variants

Every lookup has a safe `TryGet` version:

```csharp
if (SparkEntityRegistry.TryGetEntity("npc_blacksmith", out SparkEntity npc))
{
    var dialogue = npc.GetSparkComponent<DialogueEntity>();
    // ...
}
```

### Tag-Based Lookups

Query entities by their Unity tag:

```csharp
List<SparkEntity> enemies = SparkEntityRegistry.GetEntitiesByTag("Enemy");
SparkEntity firstEnemy = SparkEntityRegistry.GetFirstEntityByTag("Enemy");
```

### All Entities

Get every registered entity:

```csharp
List<SparkEntity> allEntities = SparkEntityRegistry.GetAllEntities();
```

## InteractorEntity

`InteractorEntity` handles player interaction detection. It uses camera raycasting to find `IInteractable` objects in the world and manages targeting, indicator ranges, and input.

Attach it to the player character alongside `SparkEntity`. Configure:

* **Targeting Angle Threshold**: How precisely the player needs to aim at an interactable.
* **Input Action**: The Input System action that triggers interaction.

It automatically communicates with `InteractablesManager` to show/hide interaction UI indicators.

## Creating Custom Entity Components

To create a component that works with the Spark entity system:

```csharp
using UnityEngine;

public class StaminaEntity : MonoBehaviour
{
    [SerializeField] private float maxStamina = 100f;
    [SerializeField] private float regenRate = 5f;

    private float _currentStamina;

    private void Awake()
    {
        _currentStamina = maxStamina;
    }

    public float CurrentStamina => _currentStamina;
    public float MaxStamina => maxStamina;

    public void UseStamina(float amount)
    {
        _currentStamina = Mathf.Max(0, _currentStamina - amount);
    }

    public void Regenerate(float deltaTime)
    {
        _currentStamina = Mathf.Min(maxStamina, _currentStamina + regenRate * deltaTime);
    }
}
```

Then access it through the entity's cache:

```csharp
var entity = SparkEntityRegistry.GetPlayerEntity();
var stamina = entity.GetSparkComponent<StaminaEntity>();
if (stamina != null && stamina.CurrentStamina > 10f)
{
    stamina.UseStamina(10f);
}
```

Any MonoBehaviour on the same GameObject as a `SparkEntity` is automatically included in the component cache.

## Best Practices

* Use `Static` IDs for any object that needs to persist across save/load.
* Prefer `TryGetEntity` or `TryGetSparkComponent` over direct lookups followed by null checks.
* Cache entity references in long-running systems rather than looking them up every frame.
* Call `RefreshCache()` after adding or removing components at runtime.
* Never store `SparkEntity` references across scene loads. Look them up again after loading.
* Use tags for group queries (enemies, collectibles) but entity IDs for specific objects (a particular NPC).


# Networking

Spark's networking layer is an abstraction that routes commands through a provider. In single-player, commands execute locally. In multiplayer, a custom provider can route them to a server. Your game logic stays the same either way.

## Architecture

The networking system has three pieces:

1. **INetworkProvider**: The interface that all providers implement.
2. **NetworkProviderBase**: An abstract base class with reflection-based command dispatch.
3. **LocalNetworkProvider**: The default provider for single-player/offline games.

## INetworkProvider

```csharp
public interface INetworkProvider
{
    bool IsServer { get; }
    void ExecuteCommand(ICommand command);
    void RegisterHandler<T>(ICommandHandler<T> handler) where T : ICommand;
}
```

| Member                        | Description                                                                                 |
| ----------------------------- | ------------------------------------------------------------------------------------------- |
| `IsServer`                    | Whether this instance has authority. True in single-player, depends on role in multiplayer. |
| `ExecuteCommand(ICommand)`    | Dispatches a command to its registered handler.                                             |
| `RegisterHandler<T>(handler)` | Associates a handler with a command type.                                                   |

## NetworkProviderBase

The abstract base class stores handlers in a `Dictionary<Type, object>` and uses reflection to invoke the correct `Handle()` method when a command is executed:

```csharp
public abstract class NetworkProviderBase : INetworkProvider
{
    private Dictionary<Type, object> _handlers = new();

    public abstract bool IsServer { get; }

    public void RegisterHandler<T>(ICommandHandler<T> handler) where T : ICommand
    {
        _handlers[typeof(T)] = handler;
    }

    public void ExecuteCommand(ICommand command)
    {
        var commandType = command.GetType();
        if (_handlers.TryGetValue(commandType, out var handler))
        {
            // Reflection-based dispatch to Handle(T command)
            var method = handler.GetType().GetMethod("Handle");
            method.Invoke(handler, new object[] { command });
        }
    }
}
```

When you create a custom provider, inherit from `NetworkProviderBase` and override `RegisterDefaultHandlers()`:

```csharp
public abstract class NetworkProviderBase : INetworkProvider
{
    protected abstract void RegisterDefaultHandlers();
}
```

## LocalNetworkProvider

The default provider used in single-player games. It:

* Auto-registers at `BeforeSceneLoad` if no other provider is set
* Executes commands immediately and synchronously
* Always reports `IsServer => true`
* Registers the built-in `DragDropCommandHandler`

You don't need to do anything to use it. It's set up automatically.

## Setting Up a Network Provider

To use a custom provider (for multiplayer), register it before any plugins try to execute commands:

```csharp
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void SetupNetworking()
{
    var provider = new MyMultiplayerProvider();
    Spark.SetNetworkProvider(provider);
}
```

Listen for provider readiness:

```csharp
Spark.OnNetworkProviderReady += (provider) =>
{
    Debug.Log($"Network provider ready. IsServer: {provider.IsServer}");
};
```

## Implementing a Custom Network Provider

Here is a skeleton for a multiplayer provider:

```csharp
public class MyMultiplayerProvider : NetworkProviderBase
{
    public override bool IsServer => _isServer;
    private bool _isServer;

    public MyMultiplayerProvider(bool isServer)
    {
        _isServer = isServer;
        RegisterDefaultHandlers();
    }

    protected override void RegisterDefaultHandlers()
    {
        // Register handlers for built-in commands
        RegisterHandler<DragDropCommand>(new DragDropCommandHandler());
    }

    public override void ExecuteCommand(ICommand command)
    {
        if (_isServer)
        {
            // Server: execute locally
            base.ExecuteCommand(command);
        }
        else
        {
            // Client: serialize and send to server
            byte[] data = Serialize(command);
            SendToServer(data);
        }
    }

    // Called when the server receives a command from a client
    public void OnCommandReceived(byte[] data)
    {
        ICommand command = Deserialize(data);
        base.ExecuteCommand(command);
    }
}
```

The key idea is that `ExecuteCommand` on the client sends the command over the network, while the server calls `base.ExecuteCommand()` to dispatch it locally through the handler.

## Command Serialization

For multiplayer, commands need to be serializable. This is why commands should use simple types (strings, ints, floats, bools) and entry IDs instead of direct object references.

```csharp
// Good: all fields are serializable
public class DealDamageCommand : ICommand
{
    public string AttackerId { get; set; }  // Entity ID string
    public string TargetId { get; set; }    // Entity ID string
    public string AbilityId { get; set; }   // Database entry ID
    public float Damage { get; set; }
}

// Bad: contains Unity references that can't be serialized over the network
public class BadCommand : ICommand
{
    public GameObject Target { get; set; }
    public Transform SpawnPoint { get; set; }
}
```

## Best Practices

* Design commands for serialization from the start, even if you are building single-player.
* Use `Spark.Network?.ExecuteCommand()` with the null-conditional operator. This gracefully handles the case where no provider is set.
* Register command handlers in your plugin's `RegisterPlugin()` method.
* For multiplayer, validate commands on the server. Don't trust client input.
* Keep the `LocalNetworkProvider` as your development default. Swap to a multiplayer provider when your networking layer is ready.


# Custom Attributes

Spark provides a set of custom attributes for controlling how fields appear in the Spark Editor. These attributes work on fields in `SparkDatabaseEntry` subclasses and other serialized objects.

## Inspector Attributes

### \[Section]

Groups fields under a collapsible header in the inspector.

```csharp
[Section("Movement Settings")]
[SerializeField] private float speed = 5f;
[SerializeField] private float jumpHeight = 2f;

[Section("Combat Settings", collapsible: true)]
[SerializeField] private float attackDamage = 10f;
```

All fields between one `[Section]` and the next (or the end of the class) are grouped under that section. The `collapsible` parameter defaults to true.

### \[DisplayNameTooltip]

Overrides the field label and adds a tooltip.

```csharp
[DisplayNameTooltip("Walk Speed", "Base movement speed in units per second.")]
[SerializeField] private float speed = 5f;
```

### \[ReadOnly]

Makes a field visible but not editable in the inspector. Useful for displaying computed or system-managed values.

```csharp
[ReadOnly]
[SerializeField] private string generatedId;
```

### \[InlineField]

Renders a nested object's fields directly in the parent inspector instead of as a foldout.

```csharp
[InlineField]
[SerializeField] private DamageSettings damageSettings;
```

### \[SparkInspector]

Marks a class or field for full Spark inspector customization. This attribute signals the editor to use Spark's custom rendering pipeline for the entire inspector.

### \[SparkFoldout]

Controls foldout behavior for nested objects.

```csharp
[SparkFoldout(defaultOpen: true)]
[SerializeField] private AdvancedSettings settings;
```

## Field Attributes

### \[ConditionalField]

Shows or hides a field based on the value of another field on the same object.

```csharp
[SerializeField] private bool useCustomSpeed = false;

[ConditionalField("useCustomSpeed", true)]
[SerializeField] private float customSpeed = 10f;
```

The field `customSpeed` only appears when `useCustomSpeed` is `true`. Works with bools, enums, strings, and ints.

```csharp
[SerializeField] private DamageType damageType;

[ConditionalField("damageType", DamageType.Fire)]
[SerializeField] private float burnDuration = 3f;
```

### \[ConditionalMultiField]

Shows a field based on multiple conditions.

```csharp
[ConditionalMultiField("isRanged", true, "hasAmmo", true)]
[SerializeField] private int ammoCapacity = 30;
```

Both conditions must be true for the field to show.

### \[Switch]

Switches field rendering based on another field's value. Similar to conditional fields but designed for switching between different field layouts.

```csharp
[SerializeField] private TargetingMode targetMode;

[Switch("targetMode", "Single", "AoE")]
[SerializeField] private float radius = 5f; // Only shown for AoE
```

### \[NestedField]

Renders nested data objects inline with full editing support.

```csharp
[NestedField]
[SerializeField] private List<RewardData> rewards;
```

### \[TagField]

Renders a dropdown with Unity tags.

```csharp
[TagField]
[SerializeField] private string targetTag = "Player";
```

### \[CustomFieldUI]

Marks a field for custom UI rendering. The corresponding custom UI handler must be registered in the editor.

```csharp
[CustomFieldUI("ColorPicker")]
[SerializeField] private Color tintColor = Color.white;
```

## Database Attributes

### \[SparkDatabaseEntrySelector]

Creates a dropdown picker that lets you select any `SparkDatabaseEntry` from the project.

```csharp
[SparkDatabaseEntrySelector]
[SerializeField] private string targetEntryId;
```

This opens the entry selector window with search, filtering, and type selection.

### \[DatabaseEntryDropdown]

Creates a dropdown picker filtered to a specific entry type.

```csharp
[DatabaseEntryDropdown(typeof(ItemEntry))]
[SerializeField] private string requiredItemId;

[DatabaseEntryDropdown(typeof(QuestEntry))]
[SerializeField] private string prerequisiteQuestId;
```

This is more focused than `[SparkDatabaseEntrySelector]` and only shows entries of the specified type.

### \[ScriptableObjectDropdown]

Creates a dropdown for selecting ScriptableObject assets of a specific type.

```csharp
[ScriptableObjectDropdown(typeof(LootTableEntry))]
[SerializeField] private LootTableEntry lootTable;
```

### \[NestedData]

Marks a list of `SparkDatabaseEntryNestedData` for inline editing.

```csharp
[NestedData]
[SerializeField] private List<QuestObjective> objectives;
```

The Spark Editor renders these as an editable list with add/remove/reorder controls.

### \[ShowInEventDropdown]

Exposes a field or event for use in the string-based event dropdown system. This makes it selectable in trigger and rule configurations.

## Trigger Attributes

### \[TriggerRefresh]

Marks a field that should trigger a UI refresh when changed. Used in trigger and requirement type editors.

```csharp
[TriggerRefresh]
[SerializeField] private TriggerMode mode;
```

### \[HorizontalRuleTriggers]

Adds a visual separator before trigger fields in the editor.

### \[HorizontalRuleChecks]

Adds a visual separator before requirement check fields in the editor.

## Combining Attributes

Attributes can be combined on a single field:

```csharp
[Section("Rewards")]
[DisplayNameTooltip("Gold Reward", "Amount of gold given on completion.")]
[ConditionalField("rewardType", RewardType.Currency)]
[SerializeField] private int goldAmount = 100;
```

The `[Section]` groups the field, `[DisplayNameTooltip]` customizes the label, and `[ConditionalField]` controls visibility. They all work together.

## Usage Guidelines

* Use `[Section]` on every entry with more than 5 fields. It keeps the inspector organized.
* Use `[ConditionalField]` to hide irrelevant options. Show only what matters for the current configuration.
* Use `[DatabaseEntryDropdown]` instead of raw string fields for entry references. It prevents typos and shows available options.
* Use `[ReadOnly]` for system-managed fields that users should see but not edit.
* Use `[DisplayNameTooltip]` for fields where the variable name alone is not clear enough.


# Editor Tooling

This page covers how to extend the Spark Editor with custom tabs, views, and property drawers for your plugin.

## The Spark Editor Window

The Spark Editor Window (`SparkEditorWindow`) is the central hub for managing game data. It uses a tabbed interface where each plugin adds its own tabs.

The window architecture:

1. **Sidebar**: Lists all plugin categories and their tabs.
2. **Content Area**: Displays the currently selected tab's content.
3. **Inspector Panel**: Shows the selected entry's fields for editing.

Tabs are loaded dynamically from plugin manifests.

## Creating Custom Editor Tabs

When you create a plugin manifest, you define tabs that appear in the Spark Editor. There are two tab types:

### Database Tabs

These show a list of database entries with CRUD (create, read, update, delete) operations. Most plugins use database tabs.

In your plugin manifest, configure a tab with:

* **Tab Type**: Database
* **Asset Type**: Your entry class (e.g., `FactionEntry`)
* **Path**: The Resources path where entries are stored

The Spark Editor automatically generates the list view, search, filtering, and inspector for database entries. Your entry's fields (with Spark attributes) render automatically.

### Plugin Settings Tabs

These show a single settings asset for plugin configuration. Used for plugins that need global settings rather than a list of entries.

In your plugin manifest, configure a tab with:

* **Tab Type**: PluginSettings
* **Asset Type**: Your settings class (inheriting `PluginSettingsBase`)

## Custom Editor Views

For more control over how your plugin appears in the Spark Editor, implement `ISparkPluginViewProvider`:

```csharp
public class FactionEditorView : SparkPluginViewProviderBase
{
    public override VisualElement CreateEditorView(PluginManifest manifest)
    {
        var root = new VisualElement();

        // Build custom UI using UI Toolkit
        var header = new Label("Faction Manager");
        header.AddToClassList("spark-header");
        root.Add(header);

        // Add custom controls, graphs, visualizations, etc.

        return root;
    }
}
```

`SparkPluginViewProviderBase` provides the scaffolding. Override `CreateEditorView()` to build your custom UI using Unity's UI Toolkit (VisualElements, UXML, USS).

## Plugin View Loader

The `SparkPluginViewLoader` handles dynamic loading of editor views. It finds the correct view provider for each tab based on assembly and type scanning.

## Custom Property Drawers

Spark includes several custom property drawers for its attributes. If you need custom rendering for a specific field type in your plugin, create a property drawer:

```csharp
[CustomPropertyDrawer(typeof(MyCustomAttribute))]
public class MyCustomDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // Custom rendering logic
    }
}
```

### Built-in Drawers

Spark provides drawers for all its custom attributes:

* `ConditionalFieldDrawer`: Shows/hides fields based on conditions.
* `DatabaseEntryDropdownDrawer`: Renders entry selection dropdowns.
* `ReadOnlyDrawer`: Makes fields non-editable.
* `SectionDrawer`: Renders collapsible section headers.
* `SwitchDrawer`: Switches field layouts based on another field.

## Entry Selector Windows

The `SparkDatabaseEntrySelectorWindow` is a popup window for selecting database entries. It provides:

* Search by name
* Filtering by type
* Preview of entry details
* Quick creation of new entries

You can open this window from your custom editor code:

```csharp
SparkDatabaseEntrySelectorWindow.Show<FactionEntry>(
    selectedId: currentValue,
    onSelected: (entry) => { /* Handle selection */ });
```

## Editor Field Creation

The `EditorFields.CreateField()` utility creates appropriate editor fields based on the property type. It handles:

* Primitive types (int, float, string, bool)
* Unity types (Vector3, Color, Sprite, GameObject)
* Enum dropdowns
* Object references
* Collections (lists, arrays)
* Custom types with Spark attributes

Use this when building custom editor views to get consistent field rendering.

## UI Toolkit Patterns

Spark's editor UI uses Unity's UI Toolkit. Key patterns:

* **UXML templates**: Define layout structure.
* **USS stylesheets**: Define visual styling.
* **Element querying**: `root.Q<Label>("my-label")` to find elements.
* **Value change callbacks**: Bind to field changes for auto-saving.

```csharp
var nameField = new TextField("Faction Name");
nameField.RegisterValueChangedCallback(evt =>
{
    entry.name = evt.newValue;
    EditorUtility.SetDirty(entry);
});
root.Add(nameField);
```

## Extension Data Cleanup

The `ExtensionDataCleanupUtility` handles orphaned extension data. When a database entry is deleted, its extension data should be cleaned up. This utility scans for extension data whose target entries no longer exist.

## Best Practices

* Use database tabs for entry management. They come with free CRUD, search, and inspector.
* Only create custom views when you need non-standard UI (graphs, visualizations, drag-and-drop editors).
* Follow Spark's USS class naming for consistent styling.
* Use `EditorFields.CreateField()` for field rendering instead of building fields manually.
* Always mark assets dirty (`EditorUtility.SetDirty()`) when modifying them through custom editor code.
* Test your editor extensions with domain reload enabled to catch static state issues.


# Interactables

The interactable system lets entities in the scene be targeted and interacted with by the player. Chests, NPCs, doors, crafting stations, and collectibles all use this system.

## IInteractable Interface

Any component that implements `IInteractable` can be targeted and interacted with by the player's `InteractorEntity`:

```csharp
public interface IInteractable
{
    string DisplayName { get; }
    float IndicatorYOffset { get; }
    float IndicatorDistance { get; }
    float CanInteractDistance { get; }

    bool IsInIndicatorRange { get; }
    bool IsInInteractRange { get; }
    bool IsTargeted { get; }
    bool CanInteract { get; }

    void SetInIndicatorRange(bool value);
    void SetInInteractRange(bool value);
    void SetTargeted(bool value);
    void Interact(GameObject interactor);
    void UpdateRequirementsCheck();
}
```

### Properties

| Property              | Description                                                                      |
| --------------------- | -------------------------------------------------------------------------------- |
| `DisplayName`         | Text shown in the interaction prompt (e.g., "Open Chest", "Talk to Blacksmith"). |
| `IndicatorYOffset`    | Vertical offset for the interaction indicator above the object.                  |
| `IndicatorDistance`   | Distance at which the interaction indicator becomes visible.                     |
| `CanInteractDistance` | Distance at which the player can actually interact.                              |
| `IsInIndicatorRange`  | Whether the player is close enough to see the indicator.                         |
| `IsInInteractRange`   | Whether the player is close enough to interact.                                  |
| `IsTargeted`          | Whether the player is currently looking at this interactable.                    |
| `CanInteract`         | Whether interaction is currently possible (requirements met, not locked, etc.).  |

### Methods

| Method                      | Description                                              |
| --------------------------- | -------------------------------------------------------- |
| `SetInIndicatorRange(bool)` | Called by InteractorEntity when range state changes.     |
| `SetInInteractRange(bool)`  | Called by InteractorEntity when range state changes.     |
| `SetTargeted(bool)`         | Called by InteractorEntity when targeting state changes. |
| `Interact(GameObject)`      | Called when the player activates the interaction.        |
| `UpdateRequirementsCheck()` | Called to re-evaluate whether interaction is allowed.    |

## Creating Custom Interactables

Implement `IInteractable` on a MonoBehaviour and add it to a GameObject with a collider:

```csharp
using UnityEngine;

public class TreasureChest : MonoBehaviour, IInteractable
{
    [SerializeField] private string chestName = "Treasure Chest";
    [SerializeField] private float indicatorDistance = 8f;
    [SerializeField] private float interactDistance = 3f;
    [SerializeField] private string lootTableId;

    private bool _isOpen = false;

    public string DisplayName => _isOpen ? "Empty Chest" : chestName;
    public float IndicatorYOffset => 1.5f;
    public float IndicatorDistance => indicatorDistance;
    public float CanInteractDistance => interactDistance;
    public bool IsInIndicatorRange { get; private set; }
    public bool IsInInteractRange { get; private set; }
    public bool IsTargeted { get; private set; }
    public bool CanInteract => !_isOpen;

    public void SetInIndicatorRange(bool value) => IsInIndicatorRange = value;
    public void SetInInteractRange(bool value) => IsInInteractRange = value;
    public void SetTargeted(bool value) => IsTargeted = value;

    public void Interact(GameObject interactor)
    {
        if (_isOpen) return;
        _isOpen = true;

        // Roll the loot table and give items to the player
        var entity = interactor.GetComponent<SparkEntity>();
        if (entity == null) return;

        Spark.Network?.ExecuteCommand(new RollLootTableCommand
        {
            LootTableId = lootTableId,
            TargetEntityId = entity.EntityId
        });
    }

    public void UpdateRequirementsCheck()
    {
        // Re-check if the chest can be interacted with
    }
}
```

The `InteractorEntity` on the player handles detection via camera raycasting. When the player aims at an object with an `IInteractable` component and is within range, the interaction system activates.

## InteractableObjectEntity

The Interactables plugin provides a built-in **InteractableObjectEntity** component for simple interactable objects. Instead of writing a custom class, you can add this component to a GameObject and configure its behavior through the inspector and triggers.

## Interaction Flow

1. Player moves near an interactable. `SetInIndicatorRange(true)` is called. The indicator appears.
2. Player aims at the interactable. `SetTargeted(true)` is called. The indicator highlights.
3. Player moves within interact distance. `SetInInteractRange(true)` is called. The interact prompt appears.
4. Player presses the interact key. `Interact(interactorGameObject)` is called. The action happens.
5. Player moves away. The range/target methods are called with `false`.

## InteractablesManager

The `InteractablesManager` singleton coordinates interaction UI. It receives updates from `InteractorEntity` and manages the display of interaction indicators and prompts.

## Best Practices

* Keep `Interact()` lightweight. Execute commands rather than doing heavy logic directly.
* Use `CanInteract` to gate interactions (locked doors, already-looted chests, cooldowns).
* Set appropriate distances for indicator and interact ranges. Indicators should be visible from further away than the interact range.
* Add colliders to interactable objects. The raycast system needs colliders to detect them.
* Use `UpdateRequirementsCheck()` when external conditions change (player gains a key item, time of day changes).


# Characters

Spark includes three built-in character controllers. This page covers their architecture and how to customize or replace them.

## Controller Architecture

All Spark controllers are MonoBehaviours that handle player input, movement, and camera control. They interact with Unity's character controller or rigidbody systems and integrate with Spark's entity and event systems.

Controllers are independent of the Spark plugin system. You can use any Unity controller alongside Spark entities.

## Third-Person Controller

**SparkThirdPersonController** provides a standard third-person setup.

Key features:

* WASD/stick movement relative to camera direction
* Camera follow with adjustable distance, angle, and smoothing
* Jump support
* Ground detection
* Animation parameter updates for movement blend trees

Customize by modifying the public fields in the inspector: movement speed, rotation speed, jump height, camera distance, camera sensitivity, etc.

## MMO Controller

**SparkMMOController** provides an MMO-style controller with:

* Click-to-move navigation
* Keyboard movement (WASD)
* Autorun toggle
* Camera orbit with right-click drag
* Target locking

**MMOCamera** handles the orbit camera behavior separately. It supports zoom (scroll wheel), orbit (right-click drag), and smooth following.

**AnimatorLayerWeightController** blends animator layers based on movement state. For example, the upper body layer might play an attack animation while the lower body runs.

## Creating a Custom Controller

If the built-in controllers don't fit your game, create your own:

```csharp
using UnityEngine;

[RequireComponent(typeof(SparkEntity))]
public class TopDownController : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 5f;
    [SerializeField] private float rotationSpeed = 720f;

    private SparkEntity _entity;
    private CharacterController _characterController;
    private Animator _animator;

    private void Awake()
    {
        _entity = GetComponent<SparkEntity>();
        _characterController = GetComponent<CharacterController>();
        _animator = GetComponent<Animator>();
    }

    private void Update()
    {
        // Skip if animation locked
        // (subscribe to AnimationLockStartedEvent/AnimationLockEndedEvent)

        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");
        Vector3 input = new Vector3(h, 0, v).normalized;

        if (input.magnitude > 0.1f)
        {
            // Move
            Vector3 move = input * moveSpeed * Time.deltaTime;
            _characterController.Move(move);

            // Rotate toward movement direction
            Quaternion targetRotation = Quaternion.LookRotation(input);
            transform.rotation = Quaternion.RotateTowards(
                transform.rotation,
                targetRotation,
                rotationSpeed * Time.deltaTime);
        }

        // Update animator
        if (_animator != null)
        {
            _animator.SetFloat("Speed", input.magnitude);
        }
    }
}
```

The controller is just a regular MonoBehaviour. It doesn't need to inherit from any Spark base class. As long as it's on the same GameObject as a `SparkEntity`, it integrates with the rest of the framework automatically.

## Animation Lock Integration

Combat abilities and playables can lock the character, preventing movement. To respect this in your controller:

```csharp
private bool _isLocked = false;

private void OnEnable()
{
    SparkEventBus.Subscribe<AnimationLockStartedEvent>(_lockHandler);
    SparkEventBus.Subscribe<AnimationLockEndedEvent>(_unlockHandler);
}

private void OnDisable()
{
    SparkEventBus.Unsubscribe<AnimationLockStartedEvent>(_lockHandler);
    SparkEventBus.Unsubscribe<AnimationLockEndedEvent>(_unlockHandler);
}

private void Update()
{
    if (_isLocked) return;
    // Normal movement logic
}
```

## Best Practices

* Keep controllers focused on movement and input. Don't put combat or UI logic in the controller.
* Use `SparkEntity` for identification, not the controller itself.
* Respect animation locks from the playable/combat systems.
* Use Unity's Input System for input handling (not the legacy Input class) to support rebinding.
* Make camera and movement parameters serialized fields so designers can tune them without code changes.


# API

**Assembly:** `Spark.Character` **Interface:** `ICharacterPlugin` **Implementation:** `CharacterPlugin`

## Interface

```csharp
public interface ICharacterPlugin
{
    List<CharacterEntry> GetAllCharacterEntries();
    CharacterEntry GetCharacterEntry(string characterId);
    bool HasCharacterEntry(string characterId);
}
```

## Methods

### GetAllCharacterEntries

```csharp
List<CharacterEntry> GetAllCharacterEntries()
```

Returns all character entries in the database.

### GetCharacterEntry

```csharp
CharacterEntry GetCharacterEntry(string characterId)
```

Returns the character entry with the given ID, or `null` if not found.

### HasCharacterEntry

```csharp
bool HasCharacterEntry(string characterId)
```

Returns `true` if a character entry with the given ID exists.

## CharacterEntry

Inherits from `SparkDatabaseEntry`. Defines a character template.

Standard fields inherited from `SparkDatabaseEntry`:

* `id` (string): Unique identifier
* `entryName` (string): Internal name
* `displayName` (string): Player-facing name
* `description` (string): Player-facing description
* `icon` (Sprite): Character portrait/icon

The Character entry is intentionally minimal. Character depth comes from extensions added by other plugins (Combat stats, Progression, Items, etc.).

## Components

### CharacterEntity

MonoBehaviour that links a scene GameObject to a `CharacterEntry`.

```csharp
// Attach to a GameObject with SparkEntity
public class CharacterEntity : MonoBehaviour
{
    // Set in inspector: reference to the character entry
    // Provides runtime access to character data
}
```

### PlayerAvatarEntity

MonoBehaviour that marks an entity as the local player character. Registers with `SparkEntityRegistry` as the player entity.

## Save Data

| Class                           | Description                              |
| ------------------------------- | ---------------------------------------- |
| `CharacterSaveData`             | Stores character state for persistence   |
| `CharacterSaveDataRegistration` | Registers save/load with the Save plugin |

## Usage

```csharp
var charPlugin = Spark.GetPlugin<ICharacterPlugin>();
if (charPlugin != null)
{
    CharacterEntry entry = charPlugin.GetCharacterEntry("warrior_01");
    Debug.Log($"Character: {entry.DisplayName}");

    List<CharacterEntry> all = charPlugin.GetAllCharacterEntries();
    Debug.Log($"Total characters: {all.Count}");
}
```


# Player API

**Assembly:** `Spark.Player`

The Player plugin does not expose a public interface. It provides save data persistence for the player entity and a component for persistent player identification.

## Save Data

### PlayerSaveData

Stores player state between sessions. Extends `SaveDataEntry`.

| Field                  | Type                   | Description                                   |
| ---------------------- | ---------------------- | --------------------------------------------- |
| `playerEntityId`       | string                 | Persistent player identifier                  |
| `playerPosition`       | Vector3Serializable    | Last saved world position                     |
| `playerRotation`       | QuaternionSerializable | Last saved rotation                           |
| `sessionStartTime`     | long                   | Unix timestamp when the current session began |
| `sessionCount`         | int                    | Total number of play sessions                 |
| `totalPlaytimeSeconds` | long                   | Accumulated playtime across all sessions      |

**Methods:**

```csharp
void SetPlayerEntityId(string id)
string GetPlayerEntityId()

void SetPlayerPosition(Vector3 position)
Vector3 GetPlayerPosition()

void SetPlayerRotation(Quaternion rotation)
Quaternion GetPlayerRotation()

void UpdatePlayerTransform()   // Syncs position/rotation from SparkEntity before save
void UpdateTotalPlaytime()     // Calculates and updates session duration
```

## Components

### PersistentPlayerEntityId

MonoBehaviour that assigns a persistent ID to the player entity. Requires `SparkEntity`.

On initialization, it checks for an existing ID in `PlayerSaveData`. If none exists, it generates one in the format `player.{12-char-guid}.{unix-timestamp}` and stores it.

This component supports both Local and Networked entity ID sources.

## Usage

```csharp
// Access player save data through the Save plugin
var savePlugin = Spark.GetPlugin<ISaveDataPlugin>();
if (savePlugin != null)
{
    var playerData = savePlugin.GetSaveData<PlayerSaveData>();
    Debug.Log($"Player ID: {playerData.GetPlayerEntityId()}");
    Debug.Log($"Sessions: {playerData.sessionCount}");
    Debug.Log($"Total playtime: {playerData.totalPlaytimeSeconds}s");
}
```


# Game Settings

The GameSettings system manages game configuration, keybinds, and user preferences. It provides a settings UI framework and persistent storage for player options.


# API

**Assembly:** `Spark.GameSettings` **Interface:** `IGameSettingsPlugin` **Implementation:** `GameSettingsPlugin`

## Interface

```csharp
public interface IGameSettingsPlugin
{
    // Video
    void SetResolution(int width, int height, FullScreenMode mode);
    int GetResolutionWidth();
    int GetResolutionHeight();
    void SetWindowMode(FullScreenMode mode);
    FullScreenMode GetWindowMode();
    void SetVSync(bool enabled);
    bool GetVSync();
    void SetFrameRateLimit(int limit);
    int GetFrameRateLimit();
    void SetQualityLevel(int level);
    int GetQualityLevel();
    void SetAntiAliasing(int level);
    int GetAntiAliasing();
    void SetTextureQuality(int quality);
    int GetTextureQuality();

    // Audio
    void SetVolume(AudioChannel channel, float volume);
    float GetVolume(AudioChannel channel);
    void SetAudioMixer(AudioMixer mixer);

    // Keybinds
    void RegisterInputActionAsset(InputActionAsset asset);
    void StartInteractiveRebind(string actionId, int bindingIndex,
        Action onComplete, Action onCancel);
    void CancelInteractiveRebind();
    void ResetBinding(string actionId, int bindingIndex);
    void ClearBinding(string actionId, int bindingIndex);
    void ResetAllBindings();
    string GetBindingDisplayString(string actionId, int bindingIndex);
    InputAction FindConflictingBinding(string bindingPath, string excludeActionId);

    // Persistence
    Task SaveSettingsAsync();
    void SaveSettings();
    Task LoadSettingsAsync();
    void ResetToDefaults();

    // Data access
    VideoSettingsData GetVideoSettings();
    AudioSettingsData GetAudioSettings();
    KeybindSettingsData GetKeybindSettings();
}
```

## AudioChannel Enum

```csharp
public enum AudioChannel
{
    Master,
    Music,
    SFX,
    UI
}
```

## Save Data Classes

### VideoSettingsData

Extends `SaveDataEntry`.

| Field              | Type           | Description                         |
| ------------------ | -------------- | ----------------------------------- |
| `resolutionWidth`  | int            | Screen width in pixels              |
| `resolutionHeight` | int            | Screen height in pixels             |
| `windowMode`       | FullScreenMode | Fullscreen, windowed, or borderless |
| `vSyncEnabled`     | bool           | Vertical sync toggle                |
| `frameRateLimit`   | int            | Max framerate (0 = unlimited)       |
| `qualityLevel`     | int            | Unity quality settings index        |
| `antiAliasing`     | int            | Anti-aliasing level                 |
| `textureQuality`   | int            | Texture resolution level            |

### AudioSettingsData

Extends `SaveDataEntry`.

| Field          | Type  | Description                    |
| -------------- | ----- | ------------------------------ |
| `masterVolume` | float | Master volume in dB (-80 to 0) |
| `musicVolume`  | float | Music volume in dB (-80 to 0)  |
| `sfxVolume`    | float | SFX volume in dB (-80 to 0)    |
| `uiVolume`     | float | UI volume in dB (-80 to 0)     |

### KeybindSettingsData

Extends `SaveDataEntry`.

| Field            | Type                            | Description                |
| ---------------- | ------------------------------- | -------------------------- |
| `assetOverrides` | List\<InputActionAssetOverride> | Per-asset rebind overrides |

Each `InputActionAssetOverride` contains:

| Field           | Type   | Description                  |
| --------------- | ------ | ---------------------------- |
| `assetGuid`     | string | GUID of the InputActionAsset |
| `assetName`     | string | Human-readable asset name    |
| `overridesJson` | string | Serialized rebind data       |

## Events

| Event                     | Description                                   |
| ------------------------- | --------------------------------------------- |
| `KeybindChangedEvent`     | Published when a keybind is rebound           |
| `GameSettingsLoadedEvent` | Published after settings are loaded from disk |

## Usage

```csharp
var settings = Spark.GetPlugin<IGameSettingsPlugin>();
if (settings != null)
{
    // Audio
    settings.SetVolume(AudioChannel.Master, -10f);
    float masterVol = settings.GetVolume(AudioChannel.Master);

    // Video
    settings.SetResolution(1920, 1080, FullScreenMode.FullScreenWindow);
    settings.SetVSync(true);

    // Keybinds
    settings.RegisterInputActionAsset(myInputActions);
    settings.StartInteractiveRebind("Jump", 0,
        onComplete: () => Debug.Log("Rebound!"),
        onCancel: () => Debug.Log("Cancelled"));

    // Save
    await settings.SaveSettingsAsync();
}
```


# Events

All events published by the GameSettings system through `SparkEventBus`.

| Event                       | Properties                          | Description               |
| --------------------------- | ----------------------------------- | ------------------------- |
| `GameSettingsLoadedEvent`   | —                                   | Settings loaded from disk |
| `GameSettingsUIOpenedEvent` | —                                   | Settings UI opened        |
| `GameSettingsUIClosedEvent` | —                                   | Settings UI closed        |
| `KeybindChangedEvent`       | `ActionId`, `BindingIndex`, `Asset` | A keybind was remapped    |

See [Event System](/documentation/developer-guide/core-systems/event-system) for full details on subscribing, publishing, and handling events.


# Commands

All commands executed by the GameSettings system through `Spark.Network.ExecuteCommand()`.

| Command                     | Properties      | Description                                 |
| --------------------------- | --------------- | ------------------------------------------- |
| `OpenGameSettingsCommand`   | `categoryIndex` | Open the settings UI to a specific category |
| `CloseGameSettingsCommand`  | —               | Close the settings UI                       |
| `ToggleGameSettingsCommand` | —               | Toggle the settings UI open/closed          |

See [Command System](/documentation/developer-guide/core-systems/command-system) for full details on creating and executing commands.


# Inputs

The Inputs system manages input actions, keybind categories, and the integration with Unity's Input System. It provides database entries for defining input actions and organising them into rebindable categories.


# API

**Assembly:** `Spark.Inputs` **Interface:** `IInputPlugin` **Implementation:** `InputPlugin`

## Interface

```csharp
public interface IInputPlugin
{
    InputEntry GetInputEntry(string id);
    InputEntry[] GetAllInputEntries();
    bool HasInputEntry(string id);
}
```

## Methods

### GetInputEntry

```csharp
InputEntry GetInputEntry(string id)
```

Returns the input entry with the given ID, or `null` if not found.

### GetAllInputEntries

```csharp
InputEntry[] GetAllInputEntries()
```

Returns all input entries in the database.

### HasInputEntry

```csharp
bool HasInputEntry(string id)
```

Returns `true` if an input entry with the given ID exists.

## InputEntry

Extends `SparkDatabaseEntry`. Wraps a Unity Input System action reference.

| Field         | Type                 | Description                         |
| ------------- | -------------------- | ----------------------------------- |
| `inputAction` | InputActionReference | Reference to an Input System action |

**Methods:**

```csharp
InputAction GetInputAction()   // Returns the resolved InputAction
bool IsValid()                 // True if inputAction is assigned and resolves
```

The entry auto-populates its `entryName` and `displayName` from the referenced action's name.

## Initialization

On startup, the Inputs plugin automatically enables all `InputActionAsset` objects referenced by registered `InputEntry` entries. This ensures input actions are active without manual setup.

## Usage

```csharp
var inputPlugin = Spark.GetPlugin<IInputPlugin>();
if (inputPlugin != null)
{
    InputEntry jumpEntry = inputPlugin.GetInputEntry("input_jump");
    if (jumpEntry != null && jumpEntry.IsValid())
    {
        InputAction jumpAction = jumpEntry.GetInputAction();
        jumpAction.performed += ctx => Debug.Log("Jump!");
    }
}
```


# Playables

This page covers the playable system's architecture and how to create custom playable types.

## Playable Architecture

The playable system is built around three concepts:

1. **Playable Entries**: Database entries that define what should happen (play an animation, spawn a VFX, etc.).
2. **PlayableExecutor**: The runtime engine that processes playable entries and executes them.
3. **PlayableEntity**: Scene components that link GameObjects to the playable system.

### Execution Flow

When a playable is triggered (by an ability, interaction, trigger, etc.):

1. The `PlayableExecutor` receives the playable entry and an execution context.
2. It reads the entry's configuration.
3. It resolves targets (who/what is affected).
4. It executes the action (play animation, spawn object, play sound, etc.).
5. It handles timing (duration, speed curves, delays).

### PlayableExecutionContext

The execution context carries information about who triggered the playable and who the targets are:

```csharp
public class PlayableExecutionContext
{
    public SparkEntity Source { get; }     // Who triggered it
    public SparkEntity Target { get; }     // Who is affected
    public Vector3 Position { get; }       // World position
    public Quaternion Rotation { get; }    // World rotation
}
```

## Built-in Playable Types

Each playable type handles a specific action:

| Type            | Entry Class                   | Purpose                                                   |
| --------------- | ----------------------------- | --------------------------------------------------------- |
| Animation       | `PlayableAnimationEntry`      | Play character animations with blend, speed, and locking  |
| Sound           | `PlayableSoundEntry`          | Play audio clips with volume, pitch, and spatial settings |
| Transform       | `PlayableTransformEntry`      | Move, rotate, or scale objects over time                  |
| Coordinates     | `PlayableCoordinatesEntry`    | Move entities to specific world positions                 |
| GameObject      | `PlayableGameObjectEntry`     | Show, hide, spawn, or destroy GameObjects                 |
| Camera Override | `PlayableCameraOverrideEntry` | Temporarily switch to a different camera                  |

## Creating Custom Playable Types

To add a new playable type:

### Step 1: Create the Entry

Define a database entry for your playable type:

```csharp
[CreateAssetMenu(
    fileName = "New Screen Shake Playable",
    menuName = "Spark/Playables/Screen Shake")]
public class PlayableScreenShakeEntry : SparkDatabaseEntry
{
    [Section("Shake Settings")]
    [SerializeField] private float intensity = 0.5f;
    [SerializeField] private float duration = 0.3f;
    [SerializeField] private AnimationCurve falloff = AnimationCurve.EaseInOut(0, 1, 1, 0);

    public float Intensity => intensity;
    public float Duration => duration;
    public AnimationCurve Falloff => falloff;
}
```

### Step 2: Create the Executor Logic

Implement the logic that runs when this playable type is executed. This typically involves a coroutine or tween:

```csharp
public static class ScreenShakePlayableHandler
{
    public static IEnumerator Execute(
        PlayableScreenShakeEntry entry,
        PlayableExecutionContext context)
    {
        Camera cam = Camera.main;
        if (cam == null) yield break;

        Vector3 originalPos = cam.transform.localPosition;
        float elapsed = 0f;

        while (elapsed < entry.Duration)
        {
            float t = elapsed / entry.Duration;
            float currentIntensity = entry.Intensity * entry.Falloff.Evaluate(t);

            cam.transform.localPosition = originalPos + Random.insideUnitSphere * currentIntensity;
            elapsed += Time.deltaTime;
            yield return null;
        }

        cam.transform.localPosition = originalPos;
    }
}
```

### Step 3: Register with the Executor

Register your playable type handler so the `PlayableExecutor` knows how to run it.

## Playable Entities and Overrides

**PlayableEntity** is a MonoBehaviour that marks a GameObject as participating in the playable system. It's the bridge between scene objects and playable entries.

**PlayableTransformEntity** extends this for transform playables, providing per-entity transform override support.

**PlayableTransformEntryOverride** lets you override transform playable settings per entity. For example, the same "weapon swing" animation might need different offsets for different character models.

**PlayableAudioSource** is a component that acts as a spatial audio source for sound playables.

**CameraOverrideEntity** is attached to camera objects that can be activated by camera override playables.

## Animation Integration

### PlayableAnimationTracker

Tracks currently playing animations on an entity. Used to prevent conflicts (two animations trying to play on the same layer simultaneously).

### PlayableAnimationTargeting

Resolves which animator and which layers an animation playable should affect, based on the entity's configuration.

### AnimationSpeedCurveController

Applies speed curves to animations. This lets you speed up or slow down parts of an animation (e.g., a fast swing with a slow followthrough).

### AnimationLockEvents

Events that fire when animation locks start and end. An animation lock prevents other animations from playing and can prevent player input.

* **AnimationLockStartedEvent**: An animation lock began.
* **AnimationLockEndedEvent**: An animation lock ended.

## PlayableCoroutineHelper

A utility for running playable coroutines outside of MonoBehaviours. Since playables may be triggered from non-MonoBehaviour code (plugins, command handlers), this helper manages coroutine execution.

## Best Practices

* Keep playable entries focused on a single action. Combine multiple playables in a PlayableConfiguration for sequences.
* Use speed curves for polish. They make animations feel much more dynamic.
* Always handle null cases (missing cameras, missing animators, missing audio sources).
* Test playables in isolation before combining them in configurations.
* Use PlayableTransformEntryOverride when the same playable needs to work on characters with different proportions.


# API

**Assembly:** `Spark.Playables` **Interface:** `IPlayablesPlugin` **Implementation:** `PlayablesPlugin`

## Interface

```csharp
public interface IPlayablesPlugin
{
    // Animation
    void ExecutePlayableAnimation(PlayableAnimationEntry entry,
        PlayableExecutionContext context);
    void ExecutePlayableAnimation(PlayableAnimationEntry entry,
        PlayableExecutionContext context, float delay);
    void ExecutePlayableAnimation(PlayableAnimationEntry entry,
        PlayableExecutionContext context, PlayableAnimationTargeting targeting);

    // Sound
    void ExecutePlayableSound(PlayableSoundEntry entry,
        PlayableExecutionContext context);
    void ExecutePlayableSound(PlayableSoundEntry entry,
        PlayableExecutionContext context, Vector3 position);
    void ExecutePlayableSound(PlayableSoundEntry entry,
        PlayableExecutionContext context, float delay);

    // GameObject
    void ExecutePlayableGameObject(PlayableGameObjectEntry entry,
        PlayableExecutionContext context);
    void ExecutePlayableGameObject(PlayableGameObjectEntry entry,
        PlayableExecutionContext context, Vector3 position, Quaternion rotation);
    void ExecutePlayableGameObject(PlayableGameObjectEntry entry,
        PlayableExecutionContext context, float delay);
    void ExecutePlayableGameObject(PlayableGameObjectEntry entry,
        PlayableExecutionContext context, float delay, float duration);

    // Generic
    void ExecutePlayable(SparkDatabaseEntry entry,
        PlayableExecutionContext context);
    bool IsPlayableSupported(SparkDatabaseEntry entry);
}
```

## PlayableExecutionContext

Carries source, target, position, and rotation data for playable execution.

| Property         | Type                  | Description                        |
| ---------------- | --------------------- | ---------------------------------- |
| `source`         | GameObject            | The entity initiating the playable |
| `manualPosition` | Optional\<Vector3>    | Override world position            |
| `manualRotation` | Optional\<Quaternion> | Override rotation                  |
| `manualParent`   | Transform             | Override parent transform          |
| `manualTarget`   | GameObject            | Override target object             |

**Methods:**

```csharp
SparkEntity GetSparkEntity()         // Gets SparkEntity from source
T GetComponent<T>()                  // SparkEntity-aware component lookup
bool TryGetComponent<T>(out T comp)  // Safe component lookup

// Fluent builders
PlayableExecutionContext WithPosition(Vector3 pos)
PlayableExecutionContext WithRotation(Quaternion rot)
PlayableExecutionContext WithParent(Transform parent)
PlayableExecutionContext WithTarget(GameObject target)
```

## Position, Rotation, and Target Modes

```csharp
public enum PlayablePositionMode { Source, Offset, Manual }
public enum PlayableRotationMode { Source, Fixed, Identity, Manual }
public enum PlayableTargetMode  { Source, ChildWithTag, Manual }
```

## Database Entries

### PlayableAnimationEntry

Plays character animations.

| Field                  | Type                  | Description                       |
| ---------------------- | --------------------- | --------------------------------- |
| `parameterName`        | string                | Animator parameter name           |
| `isRandom`             | bool                  | Enable random parameter selection |
| `randomParameterNames` | List\<string>         | Pool of random parameter names    |
| `parameterType`        | AnimatorParameterType | Trigger, Bool, Int, or Float      |
| `boolValue`            | bool                  | Value for Bool parameters         |
| `intValue`             | int                   | Value for Int parameters          |
| `floatValue`           | float                 | Value for Float parameters        |

**Methods:**

```csharp
string GetParameterName()          // Returns selected parameter (random if enabled)
void ApplyToAnimator(Animator anim) // Sets the parameter on the animator
bool IsValid()                      // True if properly configured
```

### PlayableSoundEntry

Plays audio clips.

| Field             | Type             | Description                  |
| ----------------- | ---------------- | ---------------------------- |
| `audioClip`       | AudioClip        | Single audio clip            |
| `randomClip`      | bool             | Enable random clip selection |
| `audioClips`      | List\<AudioClip> | Pool of random clips         |
| `audioMixerGroup` | AudioMixerGroup  | Audio routing group          |
| `volume`          | float            | Playback volume              |
| `pitch`           | float            | Playback pitch               |
| `spatialBlend`    | float            | 0 = 2D, 1 = 3D               |
| `maxDistance`     | float            | 3D audio max distance        |
| `rolloffMode`     | AudioRolloffMode | Distance attenuation curve   |
| `loop`            | bool             | Loop the audio               |

**Methods:**

```csharp
AudioClip GetAudioClip()                      // Returns selected clip
bool IsValid()                                 // True if clip is assigned
void ConfigureAudioSource(AudioSource source)  // Applies all settings to source
```

### PlayableGameObjectEntry

Instantiates prefabs with positioning, rotation, parenting, and optional auto-destruction.

### PlayableTransformEntry

Moves, rotates, or scales objects over a duration with easing. Supports per-entity overrides via `PlayableTransformEntryOverride`.

### PlayableCoordinatesEntry

Moves an entity to specific world coordinates. Used for scripted movement sequences.

### PlayableCameraOverrideEntry

Temporarily switches to a different camera view for a duration. Used for cutscenes.

## Events

| Event                   | Description                                     |
| ----------------------- | ----------------------------------------------- |
| `PlayableRequestEvent`  | Requesting a playable to execute (priority 100) |
| `PlayableExecutedEvent` | A playable finished executing (priority 50)     |

**PlayableRequestEvent fields:**

| Field           | Type                     | Description             |
| --------------- | ------------------------ | ----------------------- |
| `playableEntry` | SparkDatabaseEntry       | The playable to execute |
| `context`       | PlayableExecutionContext | Execution context       |

**PlayableExecutedEvent fields:**

| Field           | Type                     | Description                 |
| --------------- | ------------------------ | --------------------------- |
| `playableEntry` | SparkDatabaseEntry       | The playable that executed  |
| `context`       | PlayableExecutionContext | Execution context           |
| `success`       | bool                     | Whether execution succeeded |
| `errorMessage`  | string                   | Error details if failed     |

## Components

| Component                        | Description                                   |
| -------------------------------- | --------------------------------------------- |
| `PlayableEntity`                 | Links a GameObject to the playable system     |
| `PlayableTransformEntity`        | Entity with per-entity transform overrides    |
| `PlayableTransformEntryOverride` | Override data for transform playables         |
| `PlayableAudioSource`            | Spatial audio source for sound playables      |
| `CameraOverrideEntity`           | Camera activated by camera override playables |
| `PlayableAnimationTracker`       | Tracks currently playing animations           |

## Execution Infrastructure

| Class                           | Description                                 |
| ------------------------------- | ------------------------------------------- |
| `PlayableExecutor`              | Core engine that runs all playable types    |
| `PlayableConfiguration`         | Bundles multiple playables into a sequence  |
| `PlayableCoroutineHelper`       | Runs coroutines from non-MonoBehaviour code |
| `AnimationSpeedCurveController` | Applies speed curves to animations          |

## Usage

```csharp
var playables = Spark.GetPlugin<IPlayablesPlugin>();
if (playables != null)
{
    var context = new PlayableExecutionContext
    {
        source = playerObject
    }.WithPosition(spawnPoint);

    // Play a sound
    playables.ExecutePlayableSound(hitSoundEntry, context);

    // Play an animation
    playables.ExecutePlayableAnimation(attackAnimEntry, context);

    // Spawn a VFX prefab
    playables.ExecutePlayableGameObject(vfxEntry, context, delay: 0.5f);

    // Generic execution (auto-detects type)
    playables.ExecutePlayable(someEntry, context);
}
```


# Playable Types

Complete list of playable entry types in the Playables system.

## Playable Entry Types

| Type            | Entry Class                   | Description                                                                                                      |
| --------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| Animation       | `PlayableAnimationEntry`      | Plays a character animation with blend time, speed curves, and optional animation locking.                       |
| Sound           | `PlayableSoundEntry`          | Plays an audio clip with volume, pitch, and spatial blend controls.                                              |
| Transform       | `PlayableTransformEntry`      | Moves, rotates, or scales an object over a duration with easing. Supports per-entity overrides.                  |
| Coordinates     | `PlayableCoordinatesEntry`    | Moves an entity to specific world coordinates. Used for scripted movement sequences.                             |
| GameObject      | `PlayableGameObjectEntry`     | Shows, hides, enables, disables, spawns, or destroys a GameObject. Used for VFX, projectiles, and scene objects. |
| Camera Override | `PlayableCameraOverrideEntry` | Temporarily switches to a different camera view for a duration. Used for cutscenes and dramatic effects.         |

## Supporting Components

| Component                        | Description                                                      |
| -------------------------------- | ---------------------------------------------------------------- |
| `PlayableEntity`                 | Links a GameObject to the playable system.                       |
| `PlayableTransformEntity`        | Entity with transform playable support and per-entity overrides. |
| `PlayableTransformEntryOverride` | Per-entity override for transform playable settings.             |
| `PlayableAudioSource`            | Spatial audio source for sound playables.                        |
| `CameraOverrideEntity`           | Camera that can be activated by camera override playables.       |
| `PlayableAnimationTracker`       | Tracks currently playing animations on an entity.                |

## Execution Infrastructure

| Class                           | Description                                                   |
| ------------------------------- | ------------------------------------------------------------- |
| `PlayableExecutor`              | Core execution engine for all playable types.                 |
| `PlayableExecutionContext`      | Carries source, target, position, and rotation for execution. |
| `PlayableConfiguration`         | Bundles multiple playables into a sequence.                   |
| `PlayableCoroutineHelper`       | Runs playable coroutines from non-MonoBehaviour code.         |
| `AnimationSpeedCurveController` | Applies speed curves to running animations.                   |
| `PlayableAnimationTargeting`    | Resolves which animator to target for animation playables.    |
| `PlayableGameObjectPositioning` | Handles positioning for spawned GameObjects.                  |


# Events

All events fired by the Spark Playables system. Subscribe via `SparkEventBus.Subscribe<T>()`.

***

## AnimationLockStateChangedEvent

Fired when a playable's animation lock state changes, preventing or allowing new playable execution.

| Property   | Type            | Description                                |
| ---------- | --------------- | ------------------------------------------ |
| `Entity`   | `NetworkEntity` | The entity whose lock state changed        |
| `IsLocked` | `bool`          | Whether the entity is now animation-locked |

```csharp
SparkEventBus.Subscribe<AnimationLockStateChangedEvent>(e =>
{
    Debug.Log($"Entity {e.Entity} lock state: {e.IsLocked}");
});
```

***

## PlayableRequestEvent

Fired when a playable execution is requested, before validation and execution.

| Property        | Type            | Description                                 |
| --------------- | --------------- | ------------------------------------------- |
| `Entity`        | `NetworkEntity` | The entity requesting the playable          |
| `PlayableEntry` | `PlayableEntry` | The playable database entry being requested |

***

## PlayableExecutedEvent

Fired after a playable has been successfully executed.

| Property        | Type            | Description                                   |
| --------------- | --------------- | --------------------------------------------- |
| `Entity`        | `NetworkEntity` | The entity that executed the playable         |
| `PlayableEntry` | `PlayableEntry` | The playable database entry that was executed |


# Commands

All commands available in the Spark Playables system. Execute via `Spark.Network.ExecuteCommand()`.

***

## ExecutePlayableCommand

Executes a playable on an entity. This is the primary way to trigger playable execution through code.

| Property        | Type            | Description                            |
| --------------- | --------------- | -------------------------------------- |
| `Entity`        | `NetworkEntity` | The entity to execute the playable on  |
| `PlayableEntry` | `PlayableEntry` | The playable database entry to execute |

```csharp
var command = new ExecutePlayableCommand
{
    Entity = playerEntity,
    PlayableEntry = myPlayableEntry
};
Spark.Network.ExecuteCommand(command);
```


# Rules

This page covers the Rules plugin architecture and how to create custom condition and effect types.

## Rule Architecture

The Rules system uses two interfaces:

* **ISparkCondition**: A condition that evaluates to true or false given a context.
* **ISparkEffect**: An action that executes given a context.

A rule pairs conditions with effects: if all conditions pass, all effects execute.

## Context System

Conditions and effects receive context objects that provide information about the evaluation:

```csharp
public class ConditionContext
{
    public SparkEntity Source { get; }     // The entity being evaluated
    public SparkEntity Target { get; }     // Optional target entity
    public object CustomData { get; }      // Plugin-specific data
}

public class EffectContext
{
    public SparkEntity Source { get; }
    public SparkEntity Target { get; }
    public object CustomData { get; }
}
```

## Creating Custom Conditions

Implement `ISparkCondition`:

```csharp
public class IsNighttimeCondition : ISparkCondition
{
    public bool Evaluate(ConditionContext context)
    {
        // Check if the in-game time is nighttime
        float hour = GameTimeManager.CurrentHour;
        return hour >= 20f || hour <= 6f;
    }
}
```

For conditions with configurable parameters, use serialized fields:

```csharp
[System.Serializable]
public class HasReputationCondition : ISparkCondition
{
    [DatabaseEntryDropdown(typeof(FactionEntry))]
    [SerializeField] private string factionId;

    [SerializeField] private ComparisonType comparison = ComparisonType.GreaterThanOrEqual;

    [SerializeField] private int value = 0;

    public bool Evaluate(ConditionContext context)
    {
        var tracker = context.Source?.GetSparkComponent<FactionTrackerEntity>();
        if (tracker == null) return false;

        int reputation = tracker.GetReputation(factionId);
        return ComparisonHelper.Compare(reputation, value, comparison);
    }
}
```

## Creating Custom Effects

Implement `ISparkEffect`:

```csharp
[System.Serializable]
public class SpawnParticleEffect : ISparkEffect
{
    [SerializeField] private GameObject particlePrefab;
    [SerializeField] private float duration = 2f;

    public void Execute(EffectContext context)
    {
        if (particlePrefab == null || context.Source == null) return;

        var instance = Object.Instantiate(
            particlePrefab,
            context.Source.transform.position,
            Quaternion.identity);

        Object.Destroy(instance, duration);
    }
}
```

## Built-in Condition Categories

Spark and its plugins include conditions across these domains:

* **Inventory**: Has item, item count checks
* **Stats**: Stat value comparisons
* **Character**: Class, race, profession checks
* **Combat**: In combat, health thresholds, has buff/debuff
* **World**: Time of day, zone, weather (if implemented)
* **Quest**: Quest state checks

## Built-in Effect Categories

* **Inventory**: Give/remove items
* **Stats**: Modify stats, apply buffs/debuffs
* **Character**: Change class, grant profession
* **Combat**: Deal damage, heal, apply status effects
* **World**: Change scene, spawn objects, play effects
* **Quest**: Accept/complete quests

## Integration Examples

Rules are used by many systems:

```csharp
// Evaluate a rule in custom code
var conditions = new List<ISparkCondition> { new HasReputationCondition(), ... };
var effects = new List<ISparkEffect> { new SpawnParticleEffect(), ... };

var conditionContext = new ConditionContext { Source = playerEntity };
bool allPass = conditions.All(c => c.Evaluate(conditionContext));

if (allPass)
{
    var effectContext = new EffectContext { Source = playerEntity };
    foreach (var effect in effects)
    {
        effect.Execute(effectContext);
    }
}
```

## Best Practices

* Keep conditions pure (no side effects). They should only read state, never modify it.
* Keep effects focused on a single action.
* Use `ComparisonType` for numeric conditions. Don't hardcode comparison logic.
* Always null-check entities and components in both conditions and effects.
* Make conditions and effects reusable across different contexts (quests, NPCs, interactables).
* Use serialized fields and Spark attributes so designers can configure them in the editor.


# API

**Assembly:** `Spark.Rules` **Interface:** `IRulesPlugin` **Implementation:** `RulesPlugin`

## Interface

```csharp
public interface IRulesPlugin
{
    // Query
    List<RuleEntry> GetAllRuleEntries();
    RuleEntry GetRuleEntry(string id);
    RuleEntry GetRuleEntryByKey(string key);

    // GameObject target
    bool GetRuleValue(GameObject target, string key);
    void SetRuleValue(GameObject target, string key, bool value);
    Dictionary<string, bool> GetAllRuleValues(GameObject target);
    void ApplyRuleModification(GameObject target, string key, bool value,
        float duration, string sourceId, int priority);
    void RemoveRuleModifications(GameObject target, string sourceId);

    // RulesEntity target (same signatures using RulesEntity)
    bool GetRuleValue(RulesEntity entity, string key);
    void SetRuleValue(RulesEntity entity, string key, bool value);
    Dictionary<string, bool> GetAllRuleValues(RulesEntity entity);
    void ApplyRuleModification(RulesEntity entity, string key, bool value,
        float duration, string sourceId, int priority);
    void RemoveRuleModifications(RulesEntity entity, string sourceId);
}
```

## RuleEntry

Extends `SparkDatabaseEntry`. Defines a boolean rule.

| Field          | Type   | Description                                        |
| -------------- | ------ | -------------------------------------------------- |
| `key`          | string | Unique rule identifier (e.g., "CAN\_MOVE", "DEAD") |
| `defaultValue` | bool   | Initial state when no modifications apply          |

**Methods:**

```csharp
string GetKey()
bool GetDefaultValue()
```

## RulesEntity

MonoBehaviour that stores and manages rule state on a per-entity basis.

### Fields

| Field        | Type             | Description                 |
| ------------ | ---------------- | --------------------------- |
| `ruleStates` | List\<RuleState> | Serialized base rule values |

### Core Methods

```csharp
void InitializeRules()
// Loads all RuleEntry definitions and merges with persisted state.

bool GetRuleValue(string key)
// Returns the current effective value of a rule.

void SetRuleValue(string key, bool value)
// Sets the base value of a rule. Notifies subscribers.

Dictionary<string, bool> GetAllRuleValues()
// Returns a snapshot of all current rule values.
```

### Modification System

```csharp
void ApplyRuleModification(string key, bool value, float duration,
    string sourceId, int priority)
```

Applies a temporary rule override. The highest-priority modification wins when multiple modifications target the same rule. Modifications with `duration > 0` auto-expire. Permanent modifications use `duration = 0`.

```csharp
void RemoveRuleModifications(string sourceId)
```

Removes all modifications from the given source.

**RuleModification struct:**

| Field         | Type   | Description                     |
| ------------- | ------ | ------------------------------- |
| `ruleKey`     | string | Target rule                     |
| `value`       | bool   | Override value                  |
| `startTime`   | float  | When applied                    |
| `duration`    | float  | 0 = permanent                   |
| `sourceId`    | string | Who applied it                  |
| `priority`    | int    | Higher wins conflicts           |
| `IsExpired`   | bool   | Property: true if past duration |
| `IsPermanent` | bool   | Property: true if duration is 0 |

### Subscription System

```csharp
void SubscribeToRule(string key, RuleValueChangedCallback callback)
void UnsubscribeFromRule(string key, RuleValueChangedCallback callback)

void SubscribeToAllRules(RuleValueChangedCallback callback)
void UnsubscribeFromAllRules(RuleValueChangedCallback callback)

// Delegate signature
delegate void RuleValueChangedCallback(string key, bool oldValue, bool newValue);
```

### Refresh

```csharp
void RefreshRules()
```

Reloads all rules from the database. Useful after runtime database changes.

## Built-in Rule Keys

Some rules have special behavior in the framework:

| Key           | Description                                             |
| ------------- | ------------------------------------------------------- |
| `DEAD`        | When true, blocks UI panel opening                      |
| `UI_OPENED`   | Set true/false by GamePanel when any panel opens/closes |
| `ROOT_MOTION` | Blocked from becoming true while MOTION\_WARP is active |
| `MOTION_WARP` | Motion warping state                                    |

## Internal Behavior

A cleanup coroutine runs every 0.1 seconds to remove expired modifications and recalculate effective rule values.

## Usage

```csharp
var rulesPlugin = Spark.GetPlugin<IRulesPlugin>();
if (rulesPlugin != null)
{
    // Read a rule
    bool canMove = rulesPlugin.GetRuleValue(playerObject, "CAN_MOVE");

    // Set a rule
    rulesPlugin.SetRuleValue(playerObject, "IN_COMBAT", true);

    // Apply a temporary modification (stun for 3 seconds)
    rulesPlugin.ApplyRuleModification(
        target: playerObject,
        key: "CAN_MOVE",
        value: false,
        duration: 3f,
        sourceId: "stun_ability",
        priority: 10
    );

    // Remove all modifications from a source
    rulesPlugin.RemoveRuleModifications(playerObject, "stun_ability");
}

// Subscribe to rule changes on a specific entity
var rulesEntity = playerObject.GetComponent<RulesEntity>();
rulesEntity.SubscribeToRule("CAN_MOVE", (key, oldVal, newVal) =>
{
    Debug.Log($"{key} changed from {oldVal} to {newVal}");
});
```


# Save System

This page covers how to add save/load support to your custom plugin.

## Save Data Architecture

The Save plugin uses a registration pattern. Each plugin that needs persistence creates a save data class and registers it with the Save plugin. When a save is triggered, the Save plugin calls each registered provider to collect data. When loading, it distributes the saved data back.

## Creating Save Data for Your Plugin

### Step 1: Define the Save Data Class

Create a serializable class that holds your plugin's persistent state:

```csharp
[System.Serializable]
public class FactionSaveData
{
    public List<FactionReputationData> reputations = new List<FactionReputationData>();
}

[System.Serializable]
public class FactionReputationData
{
    public string factionId;
    public int reputation;
}
```

Keep save data simple and serializable. Use primitive types, strings, lists, and simple nested classes. Avoid Unity object references (they can't be serialized to a save file).

### Step 2: Implement Save Data Collection

Your plugin needs to collect its current state into the save data class and restore state from it:

```csharp
public class FactionPlugin : IFactionPlugin
{
    public FactionSaveData CollectSaveData(string characterId)
    {
        if (!SparkEntityRegistry.TryGetEntity(characterId, out SparkEntity entity))
            return null;

        var tracker = entity.GetSparkComponent<FactionTrackerEntity>();
        if (tracker == null)
            return null;

        var saveData = new FactionSaveData();
        foreach (var kvp in tracker.GetAllReputations())
        {
            saveData.reputations.Add(new FactionReputationData
            {
                factionId = kvp.Key,
                reputation = kvp.Value
            });
        }
        return saveData;
    }

    public void ApplySaveData(string characterId, FactionSaveData data)
    {
        if (data == null) return;

        if (!SparkEntityRegistry.TryGetEntity(characterId, out SparkEntity entity))
            return;

        var tracker = entity.GetSparkComponent<FactionTrackerEntity>();
        if (tracker == null)
            return;

        foreach (var rep in data.reputations)
        {
            tracker.SetReputation(rep.factionId, rep.reputation);
        }
    }
}
```

### Step 3: Register with the Save Plugin

Create a registration class that hooks into the Save plugin's lifecycle:

```csharp
public class FactionSaveDataRegistration
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void Register()
    {
        var savePlugin = Spark.GetPlugin<ISavePlugin>();
        if (savePlugin == null) return;

        savePlugin.RegisterSaveProvider("factions", new FactionSaveProvider());
    }
}
```

The save provider implements the collection and application methods and is called by the Save plugin when saving and loading.

## Save Providers

A save provider wraps the collect/apply logic for the Save plugin to call:

```csharp
public class FactionSaveProvider
{
    public object CollectData(string characterId)
    {
        var plugin = Spark.GetPlugin<IFactionPlugin>() as FactionPlugin;
        return plugin?.CollectSaveData(characterId);
    }

    public void ApplyData(string characterId, object data)
    {
        if (data is FactionSaveData factionData)
        {
            var plugin = Spark.GetPlugin<IFactionPlugin>() as FactionPlugin;
            plugin?.ApplySaveData(characterId, factionData);
        }
    }
}
```

## Best Practices

* Use a unique string key when registering your save provider (e.g., `"factions"`). This key identifies your data in the save file.
* Handle missing data gracefully. If a player loads a save from before your plugin was installed, your data will be null.
* Keep save data flat and simple. Complex object graphs are harder to serialize and more prone to versioning issues.
* Test save/load with different scenarios: fresh start, mid-game save, save from older version without your plugin.
* Guard against the Save plugin not being available (`Spark.GetPlugin<ISavePlugin>()` returns null). Your plugin should still work, it just won't persist data.
* Don't store references to Unity objects in save data. Use entry IDs (strings) instead.


# API

**Assembly:** `Spark.Save` **Interface:** `ISaveDataPlugin` **Implementation:** `SaveDataPlugin`

## Interface

```csharp
public interface ISaveDataPlugin
{
    // Type registration
    void RegisterSaveDataType<T>();

    // Data access
    T GetSaveData<T>(string key = null);
    void SetSaveData<T>(T data, string key = null);
    bool HasSaveData<T>(string key = null);
    string[] GetRegisteredTypeNames();

    // Persistence
    Task<bool> SaveAsync();
    Task<bool> LoadAsync();
    void ClearAllData();

    // Storage providers
    void RegisterStorageProvider(ISaveDataProvider provider);
    void SetActiveStorageProvider(string providerId);

    // Slot management
    SaveSlotManager GetSlotManager();
    Task<SaveSlotMetadata> CreateNewSlot(string displayName);
    Task<bool> LoadSlot(string slotId);
    Task<bool> DeleteSlot(string slotId);
    Task<List<SaveSlotMetadata>> GetAllSlots();
    Task<SaveSlotMetadata> GetCurrentSlotMetadata();
    Task<bool> SaveAndUpdateMetadata();

    // Version management
    Task<SaveVersionMetadata> CreateManualVersion(string displayName,
        long totalPlaytimeSeconds);
    Task<List<SaveVersionMetadata>> GetAllVersions();
    Task<bool> LoadVersion(string versionId);
    Task<bool> DeleteVersion(string versionId);
    bool IsLoadingVersion();
}
```

## SaveDataEntry

Abstract base class for all save data types.

| Field          | Type   | Description                    |
| -------------- | ------ | ------------------------------ |
| `saveKey`      | string | Storage key identifier         |
| `lastModified` | long   | Timestamp of last modification |

**Virtual Methods:**

```csharp
void OnBeforeSave()       // Called before serialization
void OnAfterLoad()        // Called after deserialization
void InitializeDefaults() // Sets default values for new data
void ValidateData()       // Validates data integrity after load
```

## SaveSlotMetadata

Represents a save slot.

| Field                   | Type   | Description                     |
| ----------------------- | ------ | ------------------------------- |
| `slotId`                | string | Unique slot identifier          |
| `displayName`           | string | Player-facing name              |
| `creationTimestamp`     | long   | When the slot was created       |
| `lastModifiedTimestamp` | long   | When last saved                 |
| `activeVersionId`       | string | Current version within the slot |

**Methods:**

```csharp
string GetFormattedCreationDate()
string GetFormattedLastModified()
void UpdateTimestamp()
```

## SaveVersionMetadata

Represents a versioned snapshot within a slot.

| Field                  | Type            | Description                         |
| ---------------------- | --------------- | ----------------------------------- |
| `versionId`            | string          | Unique version identifier           |
| `displayName`          | string          | Player-facing name                  |
| `creationTimestamp`    | long            | When created                        |
| `versionType`          | SaveVersionType | Initial, Auto, Manual, or QuickSave |
| `fileName`             | string          | Storage file name                   |
| `savedTimestamp`       | long            | When saved                          |
| `fileSizeBytes`        | long            | File size                           |
| `totalPlaytimeSeconds` | long            | Playtime at save                    |

```csharp
public enum SaveVersionType
{
    Initial,
    Auto,
    Manual,
    QuickSave
}
```

**Methods:**

```csharp
string GetFormattedCreationDate()
string GetFormattedSaveDate()
string GetFormattedFileSize()
string GetFormattedPlaytime()
```

## SaveDataContainer

Internal JSON serialization wrapper.

| Field             | Type                      | Description                  |
| ----------------- | ------------------------- | ---------------------------- |
| `saveDataEntries` | List\<TypedSaveDataEntry> | Type-keyed data              |
| `keyedSaveData`   | List\<KeyedSaveDataEntry> | String-keyed data            |
| `saveTimestamp`   | long                      | When the container was saved |
| `sparkVersion`    | string                    | Spark version at save time   |

## Commands

| Command                | Description                                           |
| ---------------------- | ----------------------------------------------------- |
| `SaveDataCommand`      | Triggers async save with optional completion callback |
| `LoadDataCommand`      | Triggers async load with optional completion callback |
| `ClearSaveDataCommand` | Clears all in-memory data with optional callback      |

## Storage Providers

The Save plugin supports pluggable storage backends through `ISaveDataProvider`:

| Provider                         | Description                              |
| -------------------------------- | ---------------------------------------- |
| `LocalFileSaveProvider`          | Writes to local JSON files               |
| `SlotAwareLocalFileSaveProvider` | Multi-slot local storage with versioning |

Custom providers implement `ISaveDataProvider` and register via `RegisterStorageProvider()`.

## Usage

```csharp
var savePlugin = Spark.GetPlugin<ISaveDataPlugin>();
if (savePlugin != null)
{
    // Register a custom save data type
    savePlugin.RegisterSaveDataType<MyCustomSaveData>();

    // Store data
    var data = new MyCustomSaveData();
    data.score = 1000;
    savePlugin.SetSaveData(data);

    // Retrieve data
    var loaded = savePlugin.GetSaveData<MyCustomSaveData>();
    Debug.Log($"Score: {loaded.score}");

    // Save to disk
    bool success = await savePlugin.SaveAsync();

    // Slot management
    var slot = await savePlugin.CreateNewSlot("Adventure 1");
    var allSlots = await savePlugin.GetAllSlots();
    await savePlugin.LoadSlot(slot.slotId);

    // Version snapshots
    var version = await savePlugin.CreateManualVersion("Before Boss", 3600);
    await savePlugin.LoadVersion(version.versionId);
}
```

## Creating Custom Save Data

```csharp
[System.Serializable]
public class MyCustomSaveData : SaveDataEntry
{
    public int score;
    public List<string> unlockedItems = new List<string>();

    public override void InitializeDefaults()
    {
        score = 0;
        unlockedItems.Clear();
    }

    public override void ValidateData()
    {
        if (score < 0) score = 0;
    }
}
```

Register the type during your plugin's initialization:

```csharp
Spark.GetPlugin<ISaveDataPlugin>()?.RegisterSaveDataType<MyCustomSaveData>();
```


# Commands

All commands available in the Spark Save system. Execute via `Spark.Network.ExecuteCommand()`.

***

## SaveDataCommand

Triggers a save of the current game data to the active save slot.

| Property    | Type  | Description                    |
| ----------- | ----- | ------------------------------ |
| `SlotIndex` | `int` | The save slot index to save to |

```csharp
Spark.Network.ExecuteCommand(new SaveDataCommand { SlotIndex = 0 });
```

***

## LoadDataCommand

Loads game data from a specified save slot.

| Property    | Type  | Description                      |
| ----------- | ----- | -------------------------------- |
| `SlotIndex` | `int` | The save slot index to load from |

***

## ClearSaveDataCommand

Clears all saved data from a specified save slot.

| Property    | Type  | Description                  |
| ----------- | ----- | ---------------------------- |
| `SlotIndex` | `int` | The save slot index to clear |

***

## CreateNewSlotCommand

Creates a new save slot for storing game data.

| Property   | Type     | Description                        |
| ---------- | -------- | ---------------------------------- |
| `SlotName` | `string` | Display name for the new save slot |

***

## LoadSlotCommand

Loads a specific save slot, making it the active slot.

| Property    | Type  | Description                 |
| ----------- | ----- | --------------------------- |
| `SlotIndex` | `int` | The save slot index to load |

***

## DeleteSlotCommand

Deletes a save slot and all its associated data.

| Property    | Type  | Description                   |
| ----------- | ----- | ----------------------------- |
| `SlotIndex` | `int` | The save slot index to delete |

***

## QuickSaveCommand

Performs a quick save to the default quick-save slot.

```csharp
Spark.Network.ExecuteCommand(new QuickSaveCommand());
```


# Triggers

All trigger types provided by the Spark Save system.

***

## TriggerSaveTriggerType

A trigger that initiates a save operation when activated. Useful for auto-save checkpoints or save-on-event setups.

| Property    | Type  | Description                                   |
| ----------- | ----- | --------------------------------------------- |
| `SlotIndex` | `int` | The save slot index to save to when triggered |

### Usage

Attach this trigger type to any Spark trigger to automatically save game data when specific conditions are met, such as entering a new area or completing an objective.


# Scenes

The Scenes system manages scene loading, transitions, and teleportation. It wraps Unity's scene management with database entries for each game scene and provides loading screen support.


# API

**Assembly:** `Spark.Scenes` **Interface:** `IScenesPlugin` **Implementation:** `ScenesPlugin`

## Interface

```csharp
public interface IScenesPlugin
{
    List<SceneEntry> GetAllSceneEntries();
    SceneEntry GetSceneEntry(string sceneId);
    bool HasSceneEntry(string sceneId);
    void LoadScene(SceneEntry sceneEntry);
    void LoadSceneById(string sceneId);
    void LoadSceneByName(string sceneName);
}
```

## Methods

### GetAllSceneEntries

```csharp
List<SceneEntry> GetAllSceneEntries()
```

Returns all scene entries in the database.

### GetSceneEntry

```csharp
SceneEntry GetSceneEntry(string sceneId)
```

Returns the scene entry with the given ID, or `null` if not found.

### HasSceneEntry

```csharp
bool HasSceneEntry(string sceneId)
```

Returns `true` if a scene entry exists with the given ID.

### LoadScene / LoadSceneById / LoadSceneByName

```csharp
void LoadScene(SceneEntry sceneEntry)
void LoadSceneById(string sceneId)
void LoadSceneByName(string sceneName)
```

Loads a scene using the `SceneLoader` system. Triggers loading screen, publishes events, and repositions the player entity on completion.

## SceneEntry

Extends `SparkDatabaseEntry`. Defines a game scene.

| Field                  | Type          | Description                                |
| ---------------------- | ------------- | ------------------------------------------ |
| `sceneFileName`        | string        | Scene name as registered in Build Settings |
| `defaultSpawnPosition` | Vector3       | Where the player spawns                    |
| `defaultSpawnRotation` | Vector3       | Player facing direction (Euler angles)     |
| `loadingImages`        | List\<Sprite> | Images shown during loading                |

**Methods:**

```csharp
Vector3 GetDefaultSpawnPosition()
Quaternion GetDefaultSpawnRotation()
Sprite GetRandomLoadingImage()
bool HasLoadingImages()
string GetSceneFileName()
```

## Commands

| Command            | Fields                | Description           |
| ------------------ | --------------------- | --------------------- |
| `LoadSceneCommand` | `string SceneEntryId` | Requests a scene load |

## Events

### SceneLoadStartedEvent

Published when a scene begins loading.

| Property        | Type   | Description           |
| --------------- | ------ | --------------------- |
| `SceneEntryId`  | string | ID of the scene entry |
| `SceneFileName` | string | Scene file name       |

EventType: `"Scenes.LoadStarted"`

### SceneLoadCompletedEvent

Published when a scene finishes loading.

| Property        | Type   | Description           |
| --------------- | ------ | --------------------- |
| `SceneEntryId`  | string | ID of the scene entry |
| `SceneFileName` | string | Scene file name       |

EventType: `"Scenes.LoadCompleted"`

## SceneLoader

Static utility class that handles async scene loading with progress tracking. Integrates with `LoadingScreenManager` for visual feedback.

## Usage

```csharp
var scenesPlugin = Spark.GetPlugin<IScenesPlugin>();
if (scenesPlugin != null)
{
    // Load by entry
    SceneEntry dungeon = scenesPlugin.GetSceneEntry("dungeon_01");
    scenesPlugin.LoadScene(dungeon);

    // Load by ID
    scenesPlugin.LoadSceneById("town_01");

    // Load by name
    scenesPlugin.LoadSceneByName("MainMenu");
}

// Listen for scene load events
SparkEventBus.Subscribe<SceneLoadCompletedEvent>(this);

public bool Handle(SceneLoadCompletedEvent evt)
{
    Debug.Log($"Scene loaded: {evt.SceneFileName}");
    return false;
}
```


# Events

All events published by the Scenes system through `SparkEventBus`.

| Event                         | Properties                                                                                       | Description                 |
| ----------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------- |
| `SceneLoadStartedEvent`       | `SceneEntryId`, `SceneFileName`                                                                  | Scene load began            |
| `SceneLoadCompletedEvent`     | `SceneEntryId`, `SceneFileName`                                                                  | Scene load finished         |
| `SceneTeleportRequestedEvent` | `SceneEntryId`, `TargetPosition`, `TargetRotation`, `HasPositionOverride`, `HasRotationOverride` | Teleport to scene requested |

See [Event System](/documentation/developer-guide/core-systems/event-system) for full details on subscribing, publishing, and handling events.


# Commands

All commands executed by the Scenes system through `Spark.Network.ExecuteCommand()`.

| Command            | Properties     | Description              |
| ------------------ | -------------- | ------------------------ |
| `LoadSceneCommand` | `SceneEntryId` | Load a scene by entry ID |

See [Command System](/documentation/developer-guide/core-systems/command-system) for full details on creating and executing commands.


# Triggers

Trigger types provided by the Scenes system.

| Trigger Type           | Description                               |
| ---------------------- | ----------------------------------------- |
| `LoadSceneTriggerType` | Load a scene with loading screen support. |

See [Triggers & Requirements](/documentation/developer-guide/core-systems/triggers-system) for how to create custom trigger types.


# Screen Texts

The Screen Texts system manages floating text displays such as damage numbers, XP popups, and notification messages. It provides database entries for text templates and runtime spawning.


# API

**Assembly:** `Spark.ScreenTexts` **Interface:** `IScreenTextsPlugin` **Implementation:** `ScreenTextsPlugin`

## Interface

```csharp
public interface IScreenTextsPlugin
{
    void DisplayScreenText(string eventName, string text, Vector3 worldPosition);
    void DisplayScreenText(string eventName, string text);
    List<ScreenTextEntry> GetAllScreenTextEntries();
    ScreenTextEntry GetScreenTextEntryByEventName(string eventName);
}
```

## Methods

### DisplayScreenText (world position)

```csharp
void DisplayScreenText(string eventName, string text, Vector3 worldPosition)
```

Displays floating text at a world position, converted to screen space. Used for damage numbers, XP gains, and similar feedback.

### DisplayScreenText (screen position)

```csharp
void DisplayScreenText(string eventName, string text)
```

Displays text using the entry's configured screen position and alignment.

### GetAllScreenTextEntries

```csharp
List<ScreenTextEntry> GetAllScreenTextEntries()
```

Returns all screen text entries.

### GetScreenTextEntryByEventName

```csharp
ScreenTextEntry GetScreenTextEntryByEventName(string eventName)
```

Returns the entry configured for a specific event name.

## ScreenTextEntry

Extends `SparkDatabaseEntry`. Configures how floating text appears.

### Event Configuration

| Field              | Type   | Description                              |
| ------------------ | ------ | ---------------------------------------- |
| `eventName`        | string | Event identifier that triggers this text |
| `playerOnlyEvents` | bool   | Only show for player-involved events     |

### Text Formatting

| Field           | Type                        | Description             |
| --------------- | --------------------------- | ----------------------- |
| `formatterType` | ScreenTextFormatterTypeBase | Custom text formatter   |
| `formatterData` | ScreenTextFormatterData     | Formatter configuration |

### Visual Configuration

| Field             | Type           | Description                   |
| ----------------- | -------------- | ----------------------------- |
| `fontAsset`       | TMP\_FontAsset | Custom font (optional)        |
| `textColor`       | Color          | Text color                    |
| `fontSize`        | float          | Font size (12-72)             |
| `displayDuration` | float          | Time visible (0.5-5 seconds)  |
| `fadeDuration`    | float          | Fade-out time (0.1-2 seconds) |

### Animation

| Field              | Type           | Description                     |
| ------------------ | -------------- | ------------------------------- |
| `animationEnabled` | bool           | Enable movement/scale animation |
| `yMovementCurve`   | AnimationCurve | Vertical movement over time     |
| `xMovementCurve`   | AnimationCurve | Horizontal movement over time   |
| `scaleCurve`       | AnimationCurve | Scale over time                 |

### Positioning

| Field                  | Type            | Description                          |
| ---------------------- | --------------- | ------------------------------------ |
| `positionMode`         | PositionMode    | Screen, Target, or Custom            |
| `screenAlignment`      | ScreenAlignment | 9-point screen alignment             |
| `screenPositionOffset` | Vector2         | Pixel offset from alignment          |
| `followTarget`         | bool            | Track moving targets                 |
| `worldSpaceOffset`     | Vector3         | Offset from world position           |
| `randomizePosition`    | bool            | Add random offset to prevent overlap |
| `randomXPosition`      | Vector2         | Random X range                       |
| `randomYPosition`      | Vector2         | Random Y range                       |
| `parentName`           | string          | Name of a custom parent transform    |

### Enums

```csharp
public enum PositionMode { Screen, Target, Custom }

public enum ScreenAlignment
{
    Center, Top, Bottom, Left, Right,
    TopLeft, TopRight, BottomLeft, BottomRight
}
```

**Methods:**

```csharp
bool IsValid()
string GetFormattedText(SparkEventBase sparkEvent)
```

## Components

### ScreenTextsManager

MonoBehaviour that handles spawning and pooling screen text entities.

| Field               | Type             | Description                    |
| ------------------- | ---------------- | ------------------------------ |
| `container`         | Transform        | Parent for spawned text        |
| `screenTextPrefab`  | ScreenTextEntity | Prefab template                |
| `customParents`     | Transform\[]     | Named custom parent transforms |
| `initialPoolSize`   | int              | Starting pool size (5-50)      |
| `maxPoolSize`       | int              | Maximum pool size (10-200)     |
| `defaultTextFormat` | string           | Fallback format string         |

Subscribes to `SparkEventBase` at priority 25. Automatically matches events to entries by event name and spawns formatted text.

**Methods:**

```csharp
void SpawnScreenText(ScreenTextEntry entry, string text, Vector3 worldPos)
void ReturnEntityToPool(ScreenTextEntity entity)
void RefreshEntryCache()
void TestScreenText(ScreenTextEntry entry)
string[] GetCachedEventNames()
```

### ScreenTextEntity

MonoBehaviour that renders and animates a single floating text instance.

Handles world-to-screen positioning, animated movement, scale, fade, target following, and visibility culling. Returns to pool on completion.

## Custom Formatters

Create custom text formatters by extending `ScreenTextFormatterTypeBase`:

```csharp
[CreateAssetMenu(menuName = "Spark/ScreenTexts/My Formatter")]
public class MyFormatterType : ScreenTextFormatterTypeBase
{
    public override Type GetExpectedDataType() => typeof(MyFormatterData);
    public override ScreenTextFormatterData CreateDefaultData() => new MyFormatterData();
    public override string GetDescription() => "My custom formatter";
}
```

## Usage

```csharp
var screenTexts = Spark.GetPlugin<IScreenTextsPlugin>();
if (screenTexts != null)
{
    // Show damage number at a world position
    screenTexts.DisplayScreenText("Combat.DamageDealt", "-50", targetPosition);

    // Show a notification at configured screen position
    screenTexts.DisplayScreenText("Quest.Completed", "Quest Complete!");
}
```


# Triggers System

This page covers how to create custom trigger types and requirement types that integrate with Spark's systems.

## Creating Custom Trigger Types

A trigger type defines an action that can be configured in the Spark Editor and executed at runtime. To create one, you need to understand the trigger system's architecture.

### Trigger Type Structure

Each trigger type is a class that:

1. Defines its configurable fields (shown in the Spark Editor)
2. Implements execution logic (what happens when the trigger fires)
3. Registers itself with the Triggers plugin

Here is the pattern, based on how built-in trigger types work:

```csharp
using UnityEngine;

[System.Serializable]
public class GiveReputationTriggerType
{
    [DatabaseEntryDropdown(typeof(FactionEntry))]
    [SerializeField] private string factionId;

    [SerializeField] private int amount = 10;

    [DisplayNameTooltip("Source", "What caused this reputation change.")]
    [SerializeField] private string source = "trigger";

    public void Execute(string characterId)
    {
        Spark.Network?.ExecuteCommand(new ModifyReputationCommand
        {
            CharacterId = characterId,
            FactionId = factionId,
            Amount = amount,
            Source = source
        });
    }
}
```

Key points:

* Use `[SerializeField]` for fields the designer configures in the editor.
* Use Spark attributes (`[DatabaseEntryDropdown]`, `[ConditionalField]`, etc.) for a good editor experience.
* Execute actions through commands, not directly. This ensures networking compatibility.
* Keep the trigger focused on a single action.

### Trigger Attributes

Use these attributes on trigger type fields:

* `[TriggerRefresh]`: Marks a field that should refresh the trigger UI when changed.
* `[HorizontalRuleTriggers]`: Adds a visual separator line before the field.

## Creating Custom Requirement Types

A requirement type defines a condition that can be checked at runtime.

### Requirement Type Structure

```csharp
using UnityEngine;

[System.Serializable]
public class FactionReputationRequirementType
{
    [DatabaseEntryDropdown(typeof(FactionEntry))]
    [SerializeField] private string factionId;

    [SerializeField] private ComparisonType comparison = ComparisonType.GreaterThanOrEqual;

    [SerializeField] private int requiredAmount = 0;

    public bool Check(string characterId)
    {
        if (!SparkEntityRegistry.TryGetEntity(characterId, out SparkEntity entity))
            return false;

        var tracker = entity.GetSparkComponent<FactionTrackerEntity>();
        if (tracker == null)
            return false;

        int current = tracker.GetReputation(factionId);

        return comparison switch
        {
            ComparisonType.Equal => current == requiredAmount,
            ComparisonType.NotEqual => current != requiredAmount,
            ComparisonType.GreaterThan => current > requiredAmount,
            ComparisonType.GreaterThanOrEqual => current >= requiredAmount,
            ComparisonType.LessThan => current < requiredAmount,
            ComparisonType.LessThanOrEqual => current <= requiredAmount,
            _ => false
        };
    }
}
```

Key points:

* Use `ComparisonType` for numeric comparisons. Spark provides this enum.
* Return `bool` from the check method.
* Always handle null cases gracefully (entity not found, component missing).
* Use `[HorizontalRuleChecks]` attribute for visual separators in the editor.

## Registration

Trigger types and requirement types are discovered by the Triggers and Requirements plugins through reflection and assembly scanning. Place them in an assembly that references the Triggers or Requirements plugin assembly, and they will be picked up automatically.

## Best Practices

* Name trigger types as `{Action}TriggerType` (e.g., `GiveReputationTriggerType`).
* Name requirement types as `{Condition}RequirementType` (e.g., `FactionReputationRequirementType`).
* Always execute state changes through commands in triggers.
* Always validate all inputs in requirements (null checks, entity checks).
* Use Spark's custom attributes for a polished editor experience.
* Keep triggers and requirements stateless. They should work purely from their serialized fields and the current game state.


# API

**Assembly:** `Spark.Triggers` **Interface:** `ITriggersPlugin` **Implementation:** `TriggersPlugin`

## Interface

```csharp
public interface ITriggersPlugin
{
    void ExecuteTrigger(TriggerEntry triggerEntry,
        TriggerExecutionContext context);
    void ExecuteTriggers(List<TriggerEntry> triggerEntries,
        TriggerExecutionContext context);
    void ExecuteTriggerInstance(TriggerInstance triggerInstance,
        TriggerExecutionContext context);
    void ExecuteTriggerInstances(List<TriggerInstance> triggerInstances,
        TriggerExecutionContext context);
    void ExecuteTriggersWithDelay(List<TriggerEntry> triggerEntries,
        TriggerExecutionContext context, float delay);
    void RegisterTriggerEntity(TriggerEntity entity);
    void UnregisterTriggerEntity(TriggerEntity entity);
}
```

## TriggerEntry

Extends `SparkDatabaseEntry`. Defines a configurable action.

| Field                     | Type             | Description                           |
| ------------------------- | ---------------- | ------------------------------------- |
| `triggerType`             | TriggerTypeBase  | The trigger definition script         |
| `triggerTypeData`         | TriggerDataAsset | Nested configuration data             |
| `executionDelay`          | float            | Delay before execution (0-10 seconds) |
| `allowMultipleExecutions` | bool             | If false, fires only once per entity  |

**Methods:**

```csharp
T GetTriggerData<T>()              // Cast data to expected type
TriggerTypeBase GetTriggerType()   // Get the trigger type
```

## TriggerInstance

Wraps a `TriggerEntry` with optional delay overrides. Used in lists where each trigger may have a custom delay.

| Field           | Type         | Description                    |
| --------------- | ------------ | ------------------------------ |
| `template`      | TriggerEntry | Reference to the trigger entry |
| `overrideDelay` | bool         | Override the entry's delay     |
| `customDelay`   | float        | Custom delay value (0-10)      |

**Methods:**

```csharp
float GetEffectiveDelay()  // Returns custom delay if overridden, otherwise entry delay
T GetTriggerData<T>()      // Shortcut to template's trigger data
```

## TriggerExecutionContext

Carries data for trigger execution.

| Property          | Type            | Description                   |
| ----------------- | --------------- | ----------------------------- |
| `Source`          | GameObject      | Entity executing the trigger  |
| `Target`          | GameObject      | Target of the trigger         |
| `Position`        | Vector3         | World position                |
| `TriggerEntry`    | TriggerEntry    | The trigger being executed    |
| `TriggerInstance` | TriggerInstance | Instance data (if applicable) |
| `AdditionalData`  | object          | Arbitrary extra data          |
| `EventName`       | string          | Associated event name         |
| `EventValue`      | float           | Associated numeric value      |

**Builder Methods:**

```csharp
TriggerExecutionContext WithTarget(GameObject target)
TriggerExecutionContext WithPosition(Vector3 position)
TriggerExecutionContext WithEventData(string eventName, float eventValue)
TriggerExecutionContext WithAdditionalData(object data)
```

**Accessor Methods:**

```csharp
SparkEntity GetSourceEntity()
SparkEntity GetTargetEntity()
T GetComponent<T>()
T GetAdditionalDataAs<T>()
```

## TriggerTypeBase

Abstract ScriptableObject base for trigger implementations.

```csharp
public abstract class TriggerTypeBase : ScriptableObject
{
    public TriggerDataAsset defaultData;

    public abstract void Execute(TriggerExecutionContext context);
    public abstract Type GetExpectedDataType();

    public virtual bool CanExecute(TriggerExecutionContext context) => true;
    public virtual T GetData<T>() where T : TriggerDataAsset;
}
```

## TriggerDataAsset

Abstract base for trigger-specific configuration. Extends `SparkDatabaseEntryNestedData`.

```csharp
public abstract class TriggerDataAsset : SparkDatabaseEntryNestedData
{
    public virtual float GetDelay() => 0f;
}
```

## TriggerEntity

MonoBehaviour that tracks one-shot trigger execution state.

**Methods:**

```csharp
void ExecuteDelayed(TriggerEntry entry, TriggerExecutionContext context,
    float delay)
// Executes a trigger after a delay via coroutine.

void ResetExecutedTriggers()
// Clears the one-shot tracking, allowing triggers to fire again.

static TriggerEntity EnsureExistsOn(GameObject gameObject)
// Gets or adds a TriggerEntity component.
```

## Commands

| Command                 | Fields                                    | Description                            |
| ----------------------- | ----------------------------------------- | -------------------------------------- |
| `ExecuteTriggerCommand` | `TriggerEntry`, `TriggerExecutionContext` | Executes a trigger through the network |

## Creating Custom Triggers

```csharp
[CreateAssetMenu(menuName = "Spark/Triggers/Teleport")]
public class TeleportTriggerType : TriggerTypeBase
{
    public override void Execute(TriggerExecutionContext context)
    {
        var data = context.TriggerEntry.GetTriggerData<TeleportTriggerData>();
        if (data == null || context.Source == null) return;

        context.Source.transform.position = data.targetPosition;
    }

    public override Type GetExpectedDataType() => typeof(TeleportTriggerData);
}

[System.Serializable]
public class TeleportTriggerData : TriggerDataAsset
{
    public Vector3 targetPosition;
}
```

## Usage

```csharp
var triggers = Spark.GetPlugin<ITriggersPlugin>();
if (triggers != null)
{
    var context = new TriggerExecutionContext
    {
        Source = playerObject
    }.WithTarget(npcObject);

    // Execute a single trigger
    triggers.ExecuteTrigger(rewardTrigger, context);

    // Execute a list of triggers
    triggers.ExecuteTriggers(questRewards, context);

    // Execute with delay
    triggers.ExecuteTriggersWithDelay(delayedRewards, context, 2f);
}
```


# Trigger Types

All trigger types provided by the Spark Triggers core system. These are general-purpose triggers available regardless of which plugins are installed.

***

## AnimatorTriggerType

Sets an Animator parameter or triggers an animation state when activated.

| Property            | Type                    | Description                                       |
| ------------------- | ----------------------- | ------------------------------------------------- |
| `AnimatorParameter` | `string`                | The Animator parameter name to set                |
| `ParameterType`     | `AnimatorParameterType` | The type of parameter (Bool, Float, Int, Trigger) |
| `Value`             | `string`                | The value to set the parameter to                 |

***

## ComponentMethodTriggerType

Invokes a method on a specified component when triggered. Useful for calling custom MonoBehaviour methods from the trigger system.

| Property          | Type        | Description                           |
| ----------------- | ----------- | ------------------------------------- |
| `TargetComponent` | `Component` | The component to invoke the method on |
| `MethodName`      | `string`    | The name of the method to call        |

***

## DestroySelfTriggerType

Destroys the GameObject this trigger is attached to when activated.

***

## InstantiateGameObjectTriggerType

Instantiates a prefab at a specified location when triggered.

| Property     | Type         | Description                                           |
| ------------ | ------------ | ----------------------------------------------------- |
| `Prefab`     | `GameObject` | The prefab to instantiate                             |
| `SpawnPoint` | `Transform`  | The transform to use for position and rotation        |
| `Parent`     | `Transform`  | Optional parent transform for the instantiated object |

***

## ToggleUIPanelTriggerType

Toggles the visibility of a UI panel when triggered.

| Property    | Type     | Description                        |
| ----------- | -------- | ---------------------------------- |
| `PanelName` | `string` | The name of the UI panel to toggle |

***

## ExecuteTriggerCommand

A command that executes a trigger group. While technically a command, it bridges the command and trigger systems.

| Property       | Type            | Description                                  |
| -------------- | --------------- | -------------------------------------------- |
| `TriggerGroup` | `TriggerGroup`  | The trigger group to execute                 |
| `Entity`       | `NetworkEntity` | The entity context for the trigger execution |

```csharp
var command = new ExecuteTriggerCommand
{
    TriggerGroup = myTriggerGroup,
    Entity = playerEntity
};
Spark.Network.ExecuteCommand(command);
```


# Requirements System

The Requirements system provides condition checking across all Spark systems. Requirement types are evaluated before allowing actions such as quest acceptance, ability usage, or NPC interactions.

See [Triggers & Requirements](/documentation/developer-guide/core-systems/triggers-system) for the combined architecture overview.


# API

**Assembly:** `Spark.Requirements` **Interface:** `IRequirementsPlugin` **Implementation:** `RequirementsPlugin`

## Interface

```csharp
public interface IRequirementsPlugin
{
    RequirementCheckResult CheckRequirement(RequirementEntry entry,
        RequirementContext context);
    RequirementCheckResult CheckRequirements(
        List<RequirementGroupEntry> groups, RequirementContext context);
    RequirementCheckResult CheckRequirementGroup(
        RequirementGroupEntry group, RequirementContext context);
    DetailedRequirementCheckResult CheckRequirementsDetailed(
        List<RequirementGroupEntry> groups, RequirementContext context);

    List<RequirementEntry> GetAllRequirementEntries();
    RequirementEntry GetRequirementEntry(string id);
}
```

## Methods

### CheckRequirement

```csharp
RequirementCheckResult CheckRequirement(RequirementEntry entry,
    RequirementContext context)
```

Checks a single requirement. Delegates to the entry's `RequirementTypeBase` evaluator.

### CheckRequirements

```csharp
RequirementCheckResult CheckRequirements(
    List<RequirementGroupEntry> groups, RequirementContext context)
```

Checks all requirement groups. All groups must pass for the result to pass.

### CheckRequirementGroup

```csharp
RequirementCheckResult CheckRequirementGroup(
    RequirementGroupEntry group, RequirementContext context)
```

Checks a single group using its configured logic (AND, OR, etc.).

### CheckRequirementsDetailed

```csharp
DetailedRequirementCheckResult CheckRequirementsDetailed(
    List<RequirementGroupEntry> groups, RequirementContext context)
```

Returns per-requirement breakdowns for UI display or debugging.

## RequirementEntry

Extends `SparkDatabaseEntry`. Defines a single requirement.

| Field                 | Type                 | Description                        |
| --------------------- | -------------------- | ---------------------------------- |
| `requirementType`     | RequirementTypeBase  | The evaluator script               |
| `requirementTypeData` | RequirementDataAsset | Configuration for the evaluator    |
| `failureMessage`      | string               | Message shown when the check fails |

## RequirementGroupEntry

Groups multiple requirements with logic operators.

| Field             | Type                         | Description                        |
| ----------------- | ---------------------------- | ---------------------------------- |
| `groupName`       | string                       | Label for the group                |
| `logic`           | RequirementGroupLogic        | How checks combine                 |
| `minimumRequired` | int                          | Required passes for Threshold mode |
| `checks`          | List\<RequirementCheckEntry> | Individual checks                  |

Each `RequirementCheckEntry` references a `RequirementEntry` and has an `invertResult` flag to negate the outcome.

## RequirementGroupLogic

```csharp
public enum RequirementGroupLogic
{
    And,        // All must pass
    Or,         // At least one must pass
    Nand,       // At least one must fail
    Nor,        // All must fail
    Xor,        // Exactly one must pass
    Threshold   // At least N must pass (uses minimumRequired)
}
```

## RequirementContext

Carries data for requirement evaluation.

| Property      | Type                        | Description                     |
| ------------- | --------------------------- | ------------------------------- |
| `Source`      | GameObject                  | The entity being checked        |
| `Target`      | GameObject                  | Optional target entity          |
| `SourceEntry` | SparkDatabaseEntry          | Optional related database entry |
| `CustomData`  | Dictionary\<string, object> | Arbitrary key-value data        |

**Methods:**

```csharp
// Fluent builders
RequirementContext WithTarget(GameObject target)
RequirementContext WithEntry(SparkDatabaseEntry entry)
RequirementContext WithData(string key, object value)

// Data access
T GetData<T>(string key, T defaultValue)
```

## RequirementCheckResult

Returned by all check methods.

```csharp
public struct RequirementCheckResult
{
    public bool Passed;
    public string FailureReason;
    public RequirementEntry RequirementEntry;

    public static RequirementCheckResult Success();
    public static RequirementCheckResult Failure(string reason,
        RequirementEntry entry);
}
```

## DetailedRequirementCheckResult

Extended result with per-group and per-requirement breakdowns, including pass/fail counts.

## Events

| Event                    | Description                                               |
| ------------------------ | --------------------------------------------------------- |
| `RequirementFailedEvent` | Published when a requirement check fails (for logging/UI) |

## Creating Custom Requirement Types

Extend `RequirementTypeBase`:

```csharp
[CreateAssetMenu(menuName = "Spark/Requirements/My Requirement")]
public class MyRequirementType : RequirementTypeBase
{
    public override RequirementCheckResult Check(
        RequirementContext context, RequirementDataAsset data)
    {
        // Your validation logic
        if (conditionMet)
            return RequirementCheckResult.Success();
        return RequirementCheckResult.Failure("Condition not met", null);
    }

    public override Type GetExpectedDataType()
    {
        return typeof(MyRequirementData);
    }
}
```

## Usage

```csharp
var reqPlugin = Spark.GetPlugin<IRequirementsPlugin>();
if (reqPlugin != null)
{
    var context = new RequirementContext { Source = playerObject }
        .WithTarget(npcObject)
        .WithData("questId", "main_quest_01");

    // Simple check
    var result = reqPlugin.CheckRequirements(requirementGroups, context);
    if (result.Passed)
    {
        Debug.Log("All requirements met!");
    }
    else
    {
        Debug.Log($"Failed: {result.FailureReason}");
    }

    // Detailed check for UI display
    var detailed = reqPlugin.CheckRequirementsDetailed(requirementGroups, context);
    // Iterate per-group and per-requirement results
}
```


# Requirement Types

Requirement types provided by Spark's core systems.

| Requirement Type                       | System    | Description                                                |
| -------------------------------------- | --------- | ---------------------------------------------------------- |
| `DistanceToCoordinatesRequirementType` | Playables | Entity must be within a distance of specified coordinates. |
| `RuleValueRequirementType`             | Rules     | A rule must have a specific boolean value.                 |

Plugin-specific requirement types are listed under each plugin's Requirements page.

See [Triggers & Requirements](/documentation/developer-guide/core-systems/triggers-system) for how to create custom requirement types.




---

[Next Page](https://docs.sparkframework.dev/llms-full.txt/1)

