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


---

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