# 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}");
});
```


---

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