Skip to content

Commit 93b161c

Browse files
authored
Feature/sailing v2.1.0 (#330)
* feat(sailing): fix alch loop, add casket opening and inventory order options * feat(sailing): fix alch loop, add casket opening and inventory order options
1 parent 3426712 commit 93b161c

6 files changed

Lines changed: 234 additions & 55 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package net.runelite.client.plugins.microbot.sailing;
2+
3+
public enum AlchOrder {
4+
LIST_ORDER,
5+
LEFT_TO_RIGHT,
6+
RIGHT_TO_LEFT,
7+
TOP_TO_BOTTOM,
8+
BOTTOM_TO_TOP
9+
}

src/main/java/net/runelite/client/plugins/microbot/sailing/MSailingPlugin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
@Slf4j
3131
public class MSailingPlugin extends Plugin {
3232

33-
static final String version = "2.0.1";
33+
static final String version = "2.1.0";
3434

3535
@Inject
3636
private SailingConfig config;

src/main/java/net/runelite/client/plugins/microbot/sailing/SailingConfig.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import net.runelite.client.config.ConfigGroup;
66
import net.runelite.client.config.ConfigItem;
77
import net.runelite.client.config.ConfigSection;
8+
import net.runelite.client.plugins.microbot.sailing.AlchOrder;
89
import net.runelite.client.plugins.microbot.sailing.features.trials.data.TrialRanks;
910

1011
import java.awt.*;
@@ -62,19 +63,43 @@ default boolean enableAlching()
6263
keyName = "alchItems",
6364
name = "Alch items",
6465
description = "Comma-separated list of items to high alch when salvaging.",
65-
position = 2,
66+
position = 1,
6667
section = generalSection
6768
)
6869
default String alchItems()
6970
{
7071
return "gold ring, sapphire ring, emerald ring, ruby ring, diamond ring, ruby bracelet, emerald bracelet, diamond bracelet, mithril scimitar";
7172
}
7273

74+
@ConfigItem(
75+
keyName = "openCaskets",
76+
name = "Open Caskets",
77+
description = "Automatically open all caskets in your inventory before alching and dropping.",
78+
position = 2,
79+
section = generalSection
80+
)
81+
default boolean openCaskets()
82+
{
83+
return false;
84+
}
85+
86+
@ConfigItem(
87+
keyName = "alchOrder",
88+
name = "Alch Order",
89+
description = "Order in which to high alch items. LIST_ORDER follows your alch list. LEFT_TO_RIGHT sweeps row by row. RIGHT_TO_LEFT sweeps rows right to left. TOP_TO_BOTTOM sweeps column by column. BOTTOM_TO_TOP sweeps columns bottom to top.",
90+
position = 3,
91+
section = generalSection
92+
)
93+
default AlchOrder alchOrder()
94+
{
95+
return AlchOrder.LIST_ORDER;
96+
}
97+
7398
@ConfigItem(
7499
keyName = "dropItems",
75100
name = "Drop items",
76101
description = "Comma-separated list of items to drop when salvaging.",
77-
position = 3,
102+
position = 4,
78103
section = generalSection
79104
)
80105
default String dropItems()

src/main/java/net/runelite/client/plugins/microbot/sailing/features/salvaging/SalvagingScript.java

Lines changed: 90 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
import net.runelite.client.plugins.microbot.api.player.models.Rs2PlayerModel;
88
import net.runelite.client.plugins.microbot.api.tileobject.Rs2TileObjectCache;
99
import net.runelite.client.plugins.microbot.api.tileobject.models.Rs2TileObjectModel;
10+
import net.runelite.client.plugins.microbot.sailing.AlchOrder;
1011
import net.runelite.client.plugins.microbot.sailing.SailingConfig;
1112
import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory;
13+
import net.runelite.client.plugins.microbot.util.inventory.Rs2ItemModel;
1214
import net.runelite.client.plugins.microbot.util.magic.Rs2Magic;
1315
import net.runelite.client.plugins.microbot.util.math.Rs2Random;
1416
import net.runelite.client.plugins.microbot.util.player.Rs2Player;
@@ -17,6 +19,7 @@
1719
import java.util.Arrays;
1820
import java.util.Comparator;
1921
import java.util.List;
22+
import java.util.stream.Collectors;
2023

2124
import static net.runelite.client.plugins.microbot.util.Global.sleep;
2225
import static net.runelite.client.plugins.microbot.util.Global.sleepUntil;
@@ -63,19 +66,22 @@ public void run(SailingConfig config) {
6366
return;
6467
}
6568

69+
// Check inventory FIRST before deciding whether to salvage
70+
if (isInventoryFull()) {
71+
log.info("Inventory full, handling before salvaging");
72+
handleFullInventory(config, player);
73+
return;
74+
}
75+
76+
// Inventory has space — go find a wreck and salvage
6677
var nearbyWreck = findNearestWreck(player.getWorldLocation());
6778
if (nearbyWreck == null) {
6879
log.info("No shipwreck found nearby");
6980
sleep(WAIT_TIME);
70-
dropJunk(config);
7181
return;
7282
}
7383

74-
if (isInventoryFull()) {
75-
handleFullInventory(config, player);
76-
} else {
77-
deploySalvagingHook(player);
78-
}
84+
deploySalvagingHook(player);
7985

8086
} catch (Exception ex) {
8187
log.error("Error in salvaging script", ex);
@@ -117,9 +123,17 @@ private void handleFullInventory(SailingConfig config, Rs2PlayerModel player) {
117123
if (hasSalvageItems() && !isPlayerAnimating(player)) {
118124
depositSalvageOrDrop(config);
119125
} else {
126+
// 1. Drop junk first to make room for casket loot
127+
dropJunk(config);
128+
// 2. Open caskets — now there's space for the loot to land
129+
if (config.openCaskets()) {
130+
openCaskets();
131+
}
132+
// 3. Alch anything on the alch list (including new loot from caskets)
120133
if (config.enableAlching()) {
121134
alchItems(config);
122135
}
136+
// 4. Drop anything leftover from casket loot that's also on the drop list
123137
dropJunk(config);
124138
}
125139
}
@@ -162,30 +176,88 @@ private void deploySalvagingHook(Rs2PlayerModel player) {
162176
}
163177
}
164178

179+
private void openCaskets() {
180+
while (Rs2Inventory.hasItem("casket")) {
181+
int slotsBefore = Rs2Inventory.emptySlotCount();
182+
log.info("Opening casket ({} casket(s) remaining)", Rs2Inventory.count("casket"));
183+
Rs2Inventory.interact("casket", "Open");
184+
sleepUntil(() -> !Rs2Inventory.hasItem("casket") ||
185+
Rs2Inventory.emptySlotCount() != slotsBefore, 5000);
186+
if (Rs2Inventory.hasItem("casket") && Rs2Inventory.emptySlotCount() == slotsBefore) {
187+
log.warn("Casket open had no effect, stopping casket loop");
188+
break;
189+
}
190+
sleep(300, 600);
191+
}
192+
log.info("All caskets opened");
193+
}
194+
165195
private void alchItems(SailingConfig config) {
166196
var alchItems = config.alchItems();
167-
if (alchItems == null || alchItems.isBlank()) {
168-
return;
169-
}
197+
if (alchItems == null || alchItems.isBlank()) return;
170198

171-
var itemsToAlch = Arrays.stream(alchItems.split(","))
199+
var itemNamesToAlch = Arrays.stream(alchItems.split(","))
172200
.map(String::trim)
201+
.map(String::toLowerCase)
173202
.filter(item -> !item.isEmpty())
174-
.toArray(String[]::new);
203+
.collect(Collectors.toList());
175204

176-
for (String itemName : itemsToAlch) {
177-
if (Rs2Inventory.hasItem(itemName)) {
178-
Rs2Magic.alch(itemName);
179-
Rs2Player.waitForXpDrop(Skill.MAGIC, 10000, false);
205+
AlchOrder order = config.alchOrder();
206+
207+
if (order == AlchOrder.LIST_ORDER) {
208+
for (String itemName : itemNamesToAlch) {
209+
while (Rs2Inventory.hasItem(itemName)) {
210+
log.info("Alching (list order): {}", itemName);
211+
Rs2Magic.alch(itemName);
212+
Rs2Player.waitForXpDrop(Skill.MAGIC, 10000, false);
213+
}
214+
}
215+
} else {
216+
final int COLUMNS = 4;
217+
Comparator<Rs2ItemModel> slotOrder;
218+
switch (order) {
219+
case RIGHT_TO_LEFT:
220+
slotOrder = Comparator
221+
.comparingInt((Rs2ItemModel i) -> i.getSlot() / COLUMNS)
222+
.thenComparingInt(i -> -(i.getSlot() % COLUMNS));
223+
break;
224+
case TOP_TO_BOTTOM:
225+
slotOrder = Comparator
226+
.comparingInt((Rs2ItemModel i) -> i.getSlot() % COLUMNS)
227+
.thenComparingInt(i -> i.getSlot() / COLUMNS);
228+
break;
229+
case BOTTOM_TO_TOP:
230+
slotOrder = Comparator
231+
.comparingInt((Rs2ItemModel i) -> i.getSlot() % COLUMNS)
232+
.thenComparingInt(i -> -(i.getSlot() / COLUMNS));
233+
break;
234+
default: // LEFT_TO_RIGHT
235+
slotOrder = Comparator.comparingInt(Rs2ItemModel::getSlot);
236+
break;
180237
}
238+
239+
boolean alched;
240+
do {
241+
alched = false;
242+
List<Rs2ItemModel> candidates = Rs2Inventory.all().stream()
243+
.filter(item -> itemNamesToAlch.stream()
244+
.anyMatch(name -> item.getName().toLowerCase().contains(name)))
245+
.sorted(slotOrder)
246+
.collect(Collectors.toList());
247+
if (!candidates.isEmpty()) {
248+
Rs2ItemModel next = candidates.get(0);
249+
log.info("Alching ({}) slot {}: {}", order, next.getSlot(), next.getName());
250+
Rs2Magic.alch(next);
251+
Rs2Player.waitForXpDrop(Skill.MAGIC, 10000, false);
252+
alched = true;
253+
}
254+
} while (alched);
181255
}
182256
}
183257

184258
private void dropJunk(SailingConfig config) {
185259
var dropItems = config.dropItems();
186-
if (dropItems == null || dropItems.isBlank()) {
187-
return;
188-
}
260+
if (dropItems == null || dropItems.isBlank()) return;
189261

190262
var junkItems = Arrays.stream(dropItems.split(","))
191263
.map(String::trim)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Sailing Plugin — Changelog
2+
3+
All notable changes to this plugin are documented here.
4+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5+
6+
---
7+
8+
## [2.1.0]
9+
10+
### Fixed
11+
- **Alch loop exhausts all stacks**: Previously the plugin alched one of each named
12+
item per loop pass (e.g. one gold ring, then one diamond ring, then moved on).
13+
The loop now continues alching each item name until none remain in the inventory
14+
before moving to the next entry on the list.
15+
- **Inventory check before wreck detection**: The plugin previously located a nearby
16+
shipwreck first, then checked whether inventory was full. The check order has been
17+
corrected — inventory is now evaluated on every tick before any decision to salvage
18+
is made, preventing the hook from being deployed when the inventory is already full.
19+
20+
### Added
21+
- **Open Caskets** (config checkbox, default: off)
22+
When enabled, all caskets in the inventory are opened as part of the full-inventory
23+
routine. The order of operations ensures loot always has space to land:
24+
1. Drop configured junk items (frees slots)
25+
2. Open all caskets
26+
3. High alch configured items, including any loot from the caskets
27+
4. Drop again to catch any casket loot that is also on the drop list
28+
29+
- **Alch Order** (config dropdown, default: LIST_ORDER)
30+
Controls the order in which alchable items are targeted across the inventory.
31+
Useful for making the alching behaviour look more natural and less robotic.
32+
33+
| Option | Behaviour |
34+
|---|---|
35+
| `LIST_ORDER` | Alches by item name order in the Alch Items config list (original behaviour) |
36+
| `LEFT_TO_RIGHT` | Sweeps inventory row by row, left to right (slot 0 → 27) |
37+
| `RIGHT_TO_LEFT` | Sweeps inventory row by row, right to left (slot 3 → 24) |
38+
| `TOP_TO_BOTTOM` | Sweeps inventory column by column, top to bottom (0, 4, 8... then 1, 5, 9...) |
39+
| `BOTTOM_TO_TOP` | Sweeps inventory column by column, bottom to top (24, 20, 16... then 25, 21...) |
40+
41+
---
42+
43+
## [2.0.1] — Original Release
44+
45+
### Added
46+
- Automatic shipwreck detection and salvaging within a 15-tile radius
47+
- Hook deployment on nearby active shipwrecks
48+
- Inventory management: high alching configured items and dropping configured junk
49+
when inventory is full
50+
- Salvaging station support: deposits salvage at the station on your boat if present,
51+
otherwise falls back to dropping junk
52+
- Shipwreck highlighting overlay with configurable colours for active wrecks,
53+
inactive stumps, and wrecks above the player's sailing level
54+
- Barracuda Trials automation support

0 commit comments

Comments
 (0)