Workflow Builders & Plans Guide
The com.n3plugins.sdk.workflow package provides standard builders for high-level automated behaviors (Combat, Banking, Production, Pouch Management, and Spellbook Swapping). These builders leverage TypesafeCarouselStateMachine to execute tick-driven, robust, and observable game loops.
1. Bank Workflow (BankWorkflowBuilder)
Handles bank opening, item depositing, loadout restocking/reconciling, and equipment updating.
BankRestockPlan
Configures the expected inventory and equipment states during a bank restock operation.
BankRestockPlan plan = BankRestockPlan.builder()
.inventoryLoadout(myInventoryLoadout)
.equipmentLoadout(myEquipmentLoadout)
.build();
TypesafeCarouselStateMachine<BankRestockState> bankMachine = BankWorkflowBuilder.create(plan);
Caller-owned workflows that need selective deposits or withdrawals can reuse the shared interface cycle without handing item policy to the restock plan:
BankWorkflowBuilder.BankCycle<MyState> bank = BankWorkflowBuilder.cycle(
runtime::bankOpen,
runtime::openBank,
runtime::closeBank,
runtime::tick);
bank.open(MyState.DEPOSIT, MyState.VERIFY_BANK_OPEN);
bank.verifyOpen(MyState.DEPOSIT, 80);
bank.close(MyState.RETURN, MyState.VERIFY_BANK_CLOSE, "Returning to work");
bank.verifyClose(MyState.RETURN, 8, "Returning to work");
BankCycle records the dispatch tick and requires a later bank-interface observation. The caller retains deposit, withdrawal, loadout, and return-state policy.
States (BankRestockState)
OPEN_BANK: Locates and opens the nearest accessible registered bank usingBankActions.openNearestAccessible().DEPOSIT: Deposits unneeded inventory and equipped items.WITHDRAW: Withdraws required loadout items until satisfied.EQUIP: Equips required gear directly from the inventory.CLOSE_BANK: Closes the bank interface once all conditions resolve.
Range-aware loadout fulfillment
Use LoadoutFulfillmentBuilder when requirements have minimum/maximum ranges, item variants, noted withdrawal mode, strict foreign-item handling, or an acquisition policy. Its equipment-first sequence reopens the bank before inventory reconciliation after equipping. Non-standard Withdraw-X and Deposit-X actions enter SUBMIT_BANK_QUANTITY, submit the visible prompt on a later pulse, and return to the requesting phase.
The states are OPEN_BANK, EQUIPMENT, INVENTORY, SUBMIT_BANK_QUANTITY, and VERIFY. Read the terminal FulfillmentResponse with LoadoutFulfillmentBuilder.responseOf(machine).
2. Combat Workflow (CombatWorkflowBuilder)
Manages entity targeting, combat eating/potions, special attacks, prayer toggles, and post-kill looting.
CombatPlan
Configures combat triggers, thresholds, and looting rules.
CombatPlan plan = CombatPlan.builder()
.targetPredicate(npc -> npc.getName().equals("Gargoyle"))
.eatFoodBelow(50, ItemID.SHARK)
.toggleSpecAt(50)
.enablePrayersDuring(Prayer.PROTECT_FROM_MELEE)
.lootItems(ItemID.GRANITE_MAUL, ItemID.COINS_995)
.build();
TypesafeCarouselStateMachine<CombatState> combatMachine = CombatWorkflowBuilder.create(plan);
States (CombatState)
FIND_TARGET: Finds a matching target NPC usingtargetPredicate.ATTACK_TARGET: Initiates attack on the resolved NPC.FIGHTING: Manages combat state, eating, drinking potions, special attacks, and prayers.WAIT_FOR_DEATH: Waits for the target to die and disables combat prayers.LOOT_ITEMS: Loots configured item IDs from the ground before searching for a new target.
3. Production Workflow (ProductionWorkflowBuilder)
Handles modern crafting, fletching, cooking, and skilling interfaces.
ProductionPlan
Defines item selection, option index/name, production quantity, and automatic bank restock parameters.
ProductionPlan plan = ProductionPlan.builder()
.optionName("Willow longbow")
.quantity(ProductionQuantity.ALL)
.restockLoadout(fletchingLoadout)
.build();
TypesafeCarouselStateMachine<ProductionState> productionMachine = ProductionWorkflowBuilder.create(plan);
States (ProductionState)
WAIT_FOR_PRODUCTION_OPEN: Waits for the skill/make-interface to open.SELECT_OPTION: Chooses the specified product by name or index.SELECT_QUANTITY: Configures the batch quantity (ALL, ONE, FIVE, etc.).WAIT_FOR_PRODUCTION_COMPLETE: Monitors active player animations and stops when inventory processes or animation ends.BANK_RESTOCK: Triggers bank restocking using the provided loadout once production halts.
4. Pouch Workflow (PouchWorkflowBuilder)
Manages Rune Pouch emptying, contents evaluation, and refilling.
PouchPlan
Defines required rune loadouts inside the Rune Pouch.
PouchPlan plan = PouchPlan.builder()
.requiredRunes(runePouchLoadout)
.build();
TypesafeCarouselStateMachine<PouchState> pouchMachine = PouchWorkflowBuilder.create(plan);
States (PouchState)
EVALUATE: Checks if the current pouch contents match the requested runes.EMPTY: Empties the pouch if contents prove insufficient or incorrect.FILL: Fills the pouch with the required runes from inventory.
5. Spellbook Swapping Workflow (SpellbookWorkflowBuilder)
Handles spellbook changing via Magic Cape or Altar interactions.
SpellbookPlan
Defines target spellbook (e.g. Spellbook.ANCIENT).
SpellbookPlan plan = SpellbookPlan.builder()
.targetSpellbook(Spellbook.ANCIENT)
.build();
TypesafeCarouselStateMachine<SpellbookState> spellbookMachine = SpellbookWorkflowBuilder.create(plan);
States (SpellbookState)
CHECK_CAPE: Verifies the Magic Cape sits equipped or in inventory.INTERACT: Performs the "Spellbook" swap action on the cape or altar.SELECT: Completes the spellbook selection dialogue.
6. Essence Pouch Planning (EssencePouchWorkflowBuilder)
Shared runecrafting pouch planning (extracted from the Guardians of the Rift plugin). Unlike the state machines above, this builder is a pure planner: it returns the operations to perform and leaves dispatch to the caller. Repair routing is deliberately excluded because repair NPCs are minigame-specific (Guardians of the Rift keeps that in GuardiansRiftPouchRules).
EssencePouchPlan
Immutable plan of the pouches the player carries plus the runecrafting level that gates usable capacities.
EssencePouchPlan plan = EssencePouchPlan.builder()
.runecraftLevel(74)
.pouchItemIds(Arrays.asList(ItemID.SMALL_POUCH, ItemID.MEDIUM_POUCH, ItemID.LARGE_POUCH))
.build();
int space = plan.availableSpace();
build() throws IllegalStateException when pouchItemIds was never supplied.
Planning helpers (EssencePouchWorkflowBuilder)
availableSpace(Collection<Integer> itemIds, int runecraftLevel)- total free essence space across usable pouches; a non-degraded colossal pouch supersedes smaller degraded pouchescolossalCapacity(int runecraftLevel)- colossal pouch capacity at the level (25/50/75/85 -> 8/16/27/40)isDegradedPouch(int itemId)- whether the ID is a degraded pouch varianthasDegradationPrevention(Collection<Integer> equipmentIds)- runecrafting, trimmed, or max cape plus Redwood lantern prevent degradationfillOperations(int guardianEssence, List<EssencePouch> pouches)/emptyOperations(int inventoryFreeSlots, List<EssencePouch> pouches)-EnumSet<EssencePouchOperation>ofFILL_SMALL..FILL_COLOSSAL/EMPTY_SMALL..EMPTY_COLOSSAL; the builder consumes the supplied pouch list in caller order, so pass pouches in the order you want them filled or emptied
EssencePouch value type
Immutable new EssencePouch(itemId, capacity, storedEssence) with getItemId(), getCapacity(), getStoredEssence(), and getFreeSpace(); stored essence clamps to [0, capacity].
EssencePouch small = new EssencePouch(ItemID.SMALL_POUCH, 3, 3);
EnumSet<EssencePouchOperation> empties =
EssencePouchWorkflowBuilder.emptyOperations(3, List.of(small));