Skip to main content

Magic Actions

MagicActions manages result-aware spell interactions. For spellbook, autocast, and spell-selection state, use the read methods on MagicActions (canCast, getSpellBook, isAutoCasting, isSpellSelected); for rune-pouch contents, use the com.n3plugins.sdk.query.RunePouch reads described in Query Helpers.

Spell cast, selection, and deselection requests require the Magic tab to be visible. If it is not visible, they return WIDGET_HIDDEN targeting tab/MAGIC and do not change tabs or queue the requested spell action.

Selection and Deselection

MethodBehavior
selectSpell(Spell)Selects the specified spell widget, returning an InteractionResult.
isSpellSelected()Reports whether any Magic-spellbook widget is the active client target.
isSpellSelected(Spell)Reports whether the exact requested spell widget is the active client target.
deselect()Clears the currently selected spell via the client API.
InteractionResult result = MagicActions.selectSpell(Spell.HIGH_LEVEL_ALCHEMY);
if (result.getStatus() == InteractionStatus.PACED) {
return; // Retry on the next tick
}

Passing null to selectSpell returns a TARGET_NULL status. Missing spell widgets yield SPELL_WIDGET_NOT_FOUND, and hidden widgets yield SPELL_NOT_CASTABLE.

Targeted Casting

MethodTarget
cast(Spell)Executes an untargeted spell.
castOnInventoryItem(Spell, Widget)Advances the shared fail-closed inventory-spell transaction.
cast(Spell, NPC)Casts on a resolved NPC target.
cast(Spell, Player)Casts on a resolved player target.
cast(Spell, TileObject)Casts on a resolved scene object.
cast(Spell, ETileItem)Casts on a resolved ground item.
Optional<Widget> item = Inventory.search().withId(ItemID.YEW_LONGBOW).first();
if (item.isPresent()) {
InteractionResult cast = MagicActions.castOnInventoryItem(
Spell.HIGH_LEVEL_ALCHEMY,
item.get()
);
}

Inventory-targeted spell transaction

castOnInventoryItem is designed to be called repeatedly from a tick-driven controller. It owns one synchronized transaction across all consumers:

  1. With no selected widget, it requires the Magic tab to be visible and then selects the exact requested spell. A closed Magic tab returns WIDGET_HIDDEN; the transaction never opens it.
  2. Once the exact requested spell is selected, it validates the current Inventory widget and queues one mouse-click metadata packet followed by one widget-target-on-widget packet.
  3. RuneLite owns the native transition back to Inventory. The transaction never sends an explicit Inventory-tab action.
  4. While the selected spell remains unresolved after target dispatch, further requests return PACED without resending selection, mouse, tab, or target actions. A competing spell or item cannot take over the transaction.
  5. A different selected spell or non-spell widget is cleared and reported as TARGET_STALE. A selection that remains unresolved for five seconds is also cleared and reported as TARGET_STALE; recovery begins with a fresh spell-selection cycle on a later call.

The pending fingerprint includes the spell widget and destination widget ID, slot, item ID, baseline quantity, and monotonic dispatch time. Pending state is recorded only after the target packet is successfully queued and is discarded when selection is consumed or the client logs out.

DISPATCHED means only that an action was accepted for dispatch. PACED means the caller must yield; it does not prove that a new action was queued. The consumer remains responsible for observing its domain postcondition, such as an exact inventory decrement, XP increase, or produced output, before counting the spell as complete.

Other targeted-casting overloads validate the spell widget and destination before queueing their interaction packet. Workflow builders or calling services continue to own rune validation, inventory management, and domain retry policy.