> ## Documentation Index
> Fetch the complete documentation index at: https://runelite.zip/llms.txt
> Use this file to discover all available pages before exploring further.

# Equipment and loadouts

> Equipment actions, loadout targets, tab switching, and portable inventory setup documents.

Loadouts declare a target state; plans assert current state; actions change state. This page covers all three plus the tab-switching contract that gates them.

## Equipment actions

`EquipmentActions` provides result-aware wear and remove dispatches. All writes return `InteractionResult` and route through the pacer.

| Method                                 | Behavior                                                                                                                       |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `equip(String name)`                   | Wears or wields the first inventory item matching the name; the wear action (`Wear`, `Wield`, `Equip`) is resolved dynamically |
| `unequip(EquipmentInventorySlot slot)` | Removes the item in a slot                                                                                                     |
| `unequip(String name)`                 | Removes the equipped item with the exact name                                                                                  |
| `isEquipped(String name)`              | Query: whether an item with the name is equipped                                                                               |
| `getEquipped(slot)`                    | `Optional<EquippedItem>` for the slot                                                                                          |

`EquipmentInventorySlot` (RuneLite API) defines the slots: `HEAD`, `CAPE`, `AMULET`, `WEAPON`, `BODY`, `SHIELD`, `ARMS`, `LEGS`, `HAIR`, `GLOVES`, `BOOTS`, `JAW`, `RING`, and `AMMO`. Granular reads use `com.n3plugins.sdk.query.Equipment` and `EquipmentItemQuery`.

<Warning>
  `equip` requires the Inventory tab to be visible. Both `unequip` overloads require the Equipment tab. A closed prerequisite returns `WIDGET_HIDDEN` targeting the required tab; these actions never open a tab for you.
</Warning>

```java theme={null}
if (!EquipmentActions.isEquipped("Dragon scimitar")) {
    InteractionResult result = EquipmentActions.equip("Dragon scimitar");
    if (result.getStatus() == InteractionStatus.PACED) {
        return; // retry next tick
    }
}
```

Check `isEquipped(...)` before calling `equip(...)` to avoid redundant widget interactions.

## Loadouts

`com.n3plugins.sdk.loadouts` defines the desired state for inventory and equipment. A loadout is a target, not an action: bank and production workflow builders reconcile live game state against it.

| Type                    | Purpose                                             |
| ----------------------- | --------------------------------------------------- |
| `LoadoutItem`           | Immutable item requirement: ID, amount, slot, flags |
| `InventoryLoadout`      | The 28-slot inventory target                        |
| `EquipmentLoadout`      | The worn-equipment target                           |
| `ItemDepletionListener` | Callback when a required item exhausts              |

<Info>
  **InventoryPlan** asserts current state ("do I have this right now?"). **InventoryLoadout** and **EquipmentLoadout** declare target state ("what should I hold or wear after restocking?").
</Info>

Build items fluently:

```java theme={null}
LoadoutItem logs = LoadoutItem.builder(ItemID.YEW_LOGS)
    .amount(27)
    .build();

LoadoutItem natures = LoadoutItem.builder(ItemID.NATURE_RUNE)
    .amount(100)
    .stackable(true)
    .build();

LoadoutItem staff = LoadoutItem.builder(ItemID.STAFF_OF_FIRE)
    .slot(EquipmentInventorySlot.WEAPON)
    .build();
```

Builder options: `amount(int)` (default 1), `optional(boolean)`, `noted(boolean)`, `stackable(boolean)`, and `slot(EquipmentInventorySlot)` (required for equipment loadouts).

```java theme={null}
InventoryLoadout inv = new InventoryLoadout();
inv.add(LoadoutItem.builder(ItemID.YEW_LOGS).amount(27).build());
inv.add(LoadoutItem.builder(ItemID.KNIFE).amount(1).build());

boolean done = inv.isFulfilled();

EquipmentLoadout eq = new EquipmentLoadout();
eq.add(LoadoutItem.builder(ItemID.STAFF_OF_FIRE)
        .slot(EquipmentInventorySlot.WEAPON).build());

List<LoadoutItem> missing = eq.getMissingItems();
List<LoadoutItem> toEquip = eq.getUnequippedItems();
```

`InventoryLoadout.add` throws `IllegalArgumentException` when items exceed the 28-slot capacity. Both loadout types expose `isFulfilled()`, `getRequiredItems()`, `getExcessItems()`, `getForeignItemIds()`, and `setItemDepletionListener(...)`. Builders use `getForeignItemIds()`, `getMissingItems()`, and `getUnequippedItems()` to plan deposits, withdrawals, and equips.

`ItemMetadataResolver` derives stackability from client metadata. In tests without a client, set `stackable(...)` explicitly or inject a resolver with `ItemMetadataResolver.setResolverForTesting(...)`.

`BankActions.withdraw(loadout, maxActions)` withdraws missing loadout items with a depletion listener and an action cap:

```java theme={null}
loadout.setItemDepletionListener(item -> replan(item.getItemId()));
InteractionResult result = BankActions.withdraw(loadout, 10);
```

## Tab actions

`TabActions` separates deliberate tab changes from check-only prerequisites.

| Method             | Behavior                                                                                                                                                                                         |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `open(Tab)`        | Finds the first visible widget across the tab's three packed IDs (fixed, resizable, bottom-line) and clicks it with `Switch`, `Open`, or `Select`. Returns `TAB_NOT_FOUND` when none are visible |
| `requireOpen(Tab)` | Returns `CONFIRMED` when visible; otherwise `WIDGET_HIDDEN` targeting `tab/<TAB_NAME>` without clicking                                                                                          |
| `isOpen(Tab)`      | Idempotency guard                                                                                                                                                                                |

```java theme={null}
if (!TabActions.isOpen(Tab.PRAYER)) {
    TabActions.open(Tab.PRAYER);
}
```

The `Tab` enum covers `COMBAT`, `SKILLS`, `QUESTS`, `INVENTORY`, `EQUIPMENT`, `PRAYER`, `MAGIC`, `FRIENDS_CHAT`, `FRIENDS`, `LOGOUT`, `SETTINGS`, `EMOTES`, and `MUSIC`. Each constant holds three packed widget IDs; a zero value means the slot is absent in that layout.

<Warning>
  `open` is the only shared tab API that changes the selected tab, and a dispatched click is not proof of convergence. Interface states such as an open bank hide sidebar tabs entirely, which surfaces as `TAB_NOT_FOUND`. Observe `isOpen(...)` after dispatch instead of relying on the result alone.
</Warning>

Requested inventory, equipment, prayer, magic, combat, and minigame actions never select a side tab for their caller. Open the tab explicitly first.
