Skip to main content
 Warning: Beta Version
Version: Beta ⚠️

Interaction Bound

The InteractionBounds allows you to constrain the lifetime of an interactions to specific conditions, such as player location or actions. For example, a player staying within range of an NPC during a conversation or while they are sitting on a bench.

States and Behavior

InteractionBounds operate in three states:

  1. INTERRUPTING: When the bound condition is broken (e.g., player moves too far), the interaction ends
  2. BLOCKING: Prevents the bound from being broken (e.g., cancels movement beyond the allowed range)
  3. IGNORING: Bound conditions are not enforced (e.g., player can move freely)
 warning

The bound itself doesn't determine the state - it only responds to the current state provided by the system.

Implementation

Here's how to create a interaction bound:

ExampleInteractionBound.kt
@Entry("example_bound", "An example interaction bound", Colors.MEDIUM_PURPLE, "mdi:square-rounded")
class ExampleBoundEntry(
override val id: String = "",
override val name: String = "",
override val criteria: List<Criteria> = emptyList(),
override val modifiers: List<Modifier> = emptyList(),
override val triggers: List<Ref<TriggerableEntry>> = emptyList(),
) : InteractionBoundEntry {
override fun build(player: Player): InteractionBound =
ExampleBound(player, priority)
}

class ExampleBound(
private val player: Player,
override val priority: Int
) : ListenerInteractionBound {

override fun initialize() {
super.initialize()
// Setup initial state
}

@EventHandler(priority = EventPriority.HIGHEST)
private fun onPlayerAction(event: SomeCancellablePlayerEvent) {
if (event.player.uniqueId != player.uniqueId) return

if (boundConditionBroken()) {
// For PlayerEvents, we have a handy method to handle the breaking
handleEvent(event)

// A manual version of the above
when (event.player.boundState) {
InteractionBoundState.BLOCKING -> event.isCancelled = true
InteractionBoundState.INTERRUPTING -> InteractionEndTrigger.triggerFor(event.player, context())
InteractionBoundState.IGNORING -> {}
}
}
}

private fun boundConditionBroken(): Boolean {
// Check if the bound condition is broken
return false
}

override fun teardown() {
// Cleanup any state
super.teardown()
}
}

Implementation Tips

  1. Always use handleEvent() for consistent state handling
  2. Check player UUID to ensure you're handling the right player
  3. Clean up resources in teardown()
  4. Consider performance for frequently triggered events

Example Flow

Here's how a typical radius bound might work:

1. Player starts interaction
2. Bound initializes with start location
3. Player moves:
- Within range: No action
- Beyond range:
- If BLOCKING: Cancel movement
- If INTERRUPTING: End interaction
- If IGNORING: Allow movement
4. Interaction ends, bound tears down