# 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.md) 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.


---

# Agent Instructions: Querying This Documentation

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

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

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

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

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