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


---

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