# 4. Context-Sensitive Prompt Volumes
Part of [[Context-Sensitive Prompts and Onboarding]]. Prev: [[3. Glyph Prompts in UI Toolkit]]. Next: [[5. Worked Example Door Prompt]].
The view does not know about doors. A small world component says "the player can Interact with me" and a broker picks one. That is the same idea as a single HUD hint slot in [[3.HUD and Inventory Example]].
## 4.1 A prompt source
```csharp
using UnityEngine;
public interface IPromptSource
{
string Verb { get; }
int Priority { get; }
bool IsAvailable { get; }
}
/// <summary>
/// Trigger volume that offers a prompt while the player is inside.
/// NOT compiled in Unity.
/// </summary>
[RequireComponent(typeof(Collider))]
public sealed class PromptVolume : MonoBehaviour, IPromptSource
{
[SerializeField] string verb = "Interact";
[SerializeField] int priority = 0;
[SerializeField] PromptBroker broker;
public string Verb => verb;
public int Priority => priority;
public bool IsAvailable { get; set; } = true;
public void SetVerb(string value)
{
verb = value;
if (_inside) broker?.Offer(this);
}
bool _inside;
void Awake()
{
var col = GetComponent<Collider>();
col.isTrigger = true;
}
void OnTriggerEnter(Collider other)
{
if (!IsPlayer(other)) return;
_inside = true;
broker?.Offer(this);
}
void OnTriggerExit(Collider other)
{
if (!IsPlayer(other)) return;
_inside = false;
broker?.Withdraw(this);
}
void OnDisable()
{
if (_inside) broker?.Withdraw(this);
_inside = false;
}
public void RefreshAvailability()
{
if (_inside) broker?.Offer(this);
}
static bool IsPlayer(Collider other)
{
return other.GetComponentInParent<CharacterController>() != null
|| other.CompareTag("Player");
}
}
```
Set `IsAvailable` false when the interactable should not be used (door opening, empty chest). Call `RefreshAvailability` so the broker can hide the prompt. A locked door can stay available and change the **verb** to "Locked" (or a localized equivalent) rather than disappearing - Level 12 wants the door visible.
## 4.2 The broker
```csharp
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// One visible prompt: the highest-priority available source in range.
/// NOT compiled in Unity.
/// </summary>
public sealed class PromptBroker : MonoBehaviour
{
[SerializeField] PromptView view;
readonly List<IPromptSource> _offered = new();
public void Offer(IPromptSource source)
{
if (source == null) return;
if (!_offered.Contains(source)) _offered.Add(source);
Push();
}
public void Withdraw(IPromptSource source)
{
_offered.Remove(source);
Push();
}
public void Push()
{
IPromptSource best = null;
for (int i = 0; i < _offered.Count; i++)
{
var s = _offered[i];
if (s == null || !s.IsAvailable) continue;
if (best == null || s.Priority > best.Priority)
best = s;
}
if (view == null) return;
if (best == null)
{
view.SetVisible(false);
return;
}
view.SetVerb(best.Verb);
view.SetVisible(true);
view.Refresh();
}
}
```
Interact still goes to the interactable (a `KeyDoor`, a pickup), not to the broker. If you need "the current target," add `Current` and let an interactor query it - but the action must stay on the gameplay object ([[3. Lock-and-Key Data]] already listens for Interact while the player is in its use volume). Two overlap volumes both listening to Interact is a design problem; give the door and the pickup different ranges, or have only the broker's current source accept the press.
## 4.3 Exercise
Place two volumes with different priorities. Walk so both overlap and confirm only the higher one shows. Disable `IsAvailable` on it and confirm the lower one appears, then neither when you walk away.
## Sources
- [[3.HUD and Inventory Example]]
- [[3. Lock-and-Key Data]]