From f57dd6482e6ef0c58b63a08c7b319c948f9edaea Mon Sep 17 00:00:00 2001 From: chsami <23292806+chsami@users.noreply.github.com> Date: Fri, 16 Oct 2026 16:34:50 +0200 Subject: [PATCH] fix(TutorialIsland): walk to and open rat pen gate before melee attack (1.3.17) The melee rat step only opened gate 9719 when the player already stood on the x=3111 gate strip, and otherwise gated the attack on Rs2Walker.canReach, which routes through the gate transport and tolerates a 2-tile endpoint offset. Rats were clicked from outside the closed pen (spam-clicking) or the step idled. Now the attack is gated on a strict collision reachability check; when the rat is not reachable the player walks to the gate and opens it. The ranged step attacks the rat over the fence without a reachability check. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV --- .../microbot/tutorialisland/RatPen.java | 29 ++++++++++++++ .../tutorialisland/TutorialIslandPlugin.java | 2 +- .../tutorialisland/TutorialIslandScript.java | 31 ++++++++------ .../microbot/tutorialisland/RatPenTest.java | 40 +++++++++++++++++++ 4 files changed, 88 insertions(+), 14 deletions(-) create mode 100644 src/main/java/net/runelite/client/plugins/microbot/tutorialisland/RatPen.java create mode 100644 src/test/java/net/runelite/client/plugins/microbot/tutorialisland/RatPenTest.java diff --git a/src/main/java/net/runelite/client/plugins/microbot/tutorialisland/RatPen.java b/src/main/java/net/runelite/client/plugins/microbot/tutorialisland/RatPen.java new file mode 100644 index 0000000000..ebaa147fde --- /dev/null +++ b/src/main/java/net/runelite/client/plugins/microbot/tutorialisland/RatPen.java @@ -0,0 +1,29 @@ +package net.runelite.client.plugins.microbot.tutorialisland; + +import net.runelite.api.coords.WorldPoint; + +final class RatPen { + static final int GATE_ID = 9719; + static final WorldPoint GATE_OUTSIDE = new WorldPoint(3111, 9518, 0); + static final WorldPoint GATE_INSIDE = new WorldPoint(3110, 9518, 0); + + enum Step { + ATTACK, + OPEN_GATE, + WALK_TO_GATE + } + + private RatPen() { + } + + static Step next(WorldPoint playerLoc, boolean ratReachable) { + if (ratReachable) { + return Step.ATTACK; + } + if (playerLoc != null && playerLoc.getPlane() == GATE_OUTSIDE.getPlane() + && playerLoc.distanceTo2D(GATE_OUTSIDE) <= 1) { + return Step.OPEN_GATE; + } + return Step.WALK_TO_GATE; + } +} diff --git a/src/main/java/net/runelite/client/plugins/microbot/tutorialisland/TutorialIslandPlugin.java b/src/main/java/net/runelite/client/plugins/microbot/tutorialisland/TutorialIslandPlugin.java index d99aac9841..7fa7200298 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/tutorialisland/TutorialIslandPlugin.java +++ b/src/main/java/net/runelite/client/plugins/microbot/tutorialisland/TutorialIslandPlugin.java @@ -25,7 +25,7 @@ isExternal = PluginConstants.IS_EXTERNAL ) public class TutorialIslandPlugin extends Plugin { - public static final String version = "1.3.16"; + public static final String version = "1.3.17"; private boolean toggleMusic; private boolean toggleRoofs; diff --git a/src/main/java/net/runelite/client/plugins/microbot/tutorialisland/TutorialIslandScript.java b/src/main/java/net/runelite/client/plugins/microbot/tutorialisland/TutorialIslandScript.java index a8e567a28d..a22b63e6ac 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/tutorialisland/TutorialIslandScript.java +++ b/src/main/java/net/runelite/client/plugins/microbot/tutorialisland/TutorialIslandScript.java @@ -334,21 +334,26 @@ private boolean walkAndAct(Rs2NpcModel npc, int reach, String action, Runnable a } private boolean walkAndAttackRat() { - // The rat pit gate (id 9719) blocks both Rs2Walker and native pathfinder. - // Explicitly open it if we're standing adjacent; after passing through, attack directly. - WorldPoint playerLoc = Rs2Player.getWorldLocation(); - if (playerLoc != null && playerLoc.getX() == 3111 && playerLoc.getY() >= 9516 && playerLoc.getY() <= 9519) { - if (Microbot.getRs2TileObjectCache().query().withId(9719).interact("Open")) { - sleepUntil(() -> { - WorldPoint p = Rs2Player.getWorldLocation(); - return p != null && p.getY() < 9516; - }, 3000); + Rs2NpcModel rat = Microbot.getRs2NpcCache().query().withName("Giant rat").nearest(); + if (rat == null || rat.getWorldLocation() == null) return false; + switch (RatPen.next(Rs2Player.getWorldLocation(), Rs2Tile.isTileReachable(rat.getWorldLocation()))) { + case ATTACK: + return rat.click("Attack"); + case OPEN_GATE: + if (Microbot.getRs2TileObjectCache().query().withId(RatPen.GATE_ID).interact("Open")) { + sleepUntil(() -> Rs2Tile.isTileReachable(RatPen.GATE_INSIDE), 3000); + } + return false; + default: + Rs2Walker.walkTo(RatPen.GATE_OUTSIDE, 1); + Rs2Player.waitForWalking(); return false; - } } + } + + private boolean attackRatAtRange() { Rs2NpcModel rat = Microbot.getRs2NpcCache().query().withName("Giant rat").nearest(); - if (rat == null || rat.getWorldLocation() == null) return false; - if (!Rs2Walker.canReach(rat.getWorldLocation())) return false; + if (rat == null) return false; return rat.click("Attack"); } @@ -655,7 +660,7 @@ public void CombatGuide() { Rs2Inventory.wield("Bronze arrow"); Rs2Random.waitEx(600, 100); } - walkAndAttackRat(); + attackRatAtRange(); } else if (Microbot.getVarbitPlayerValue(281) == 470) { if (npc == null) return; walkAndTalk(npc); diff --git a/src/test/java/net/runelite/client/plugins/microbot/tutorialisland/RatPenTest.java b/src/test/java/net/runelite/client/plugins/microbot/tutorialisland/RatPenTest.java new file mode 100644 index 0000000000..b5e7b42909 --- /dev/null +++ b/src/test/java/net/runelite/client/plugins/microbot/tutorialisland/RatPenTest.java @@ -0,0 +1,40 @@ +package net.runelite.client.plugins.microbot.tutorialisland; + +import net.runelite.api.coords.WorldPoint; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public final class RatPenTest { + @Test + void attacksWhenRatIsReachable() { + assertEquals(RatPen.Step.ATTACK, RatPen.next(new WorldPoint(3111, 9526, 0), true)); + } + + @Test + void walksToGateWhenRatIsUnreachableFromLadder() { + assertEquals(RatPen.Step.WALK_TO_GATE, RatPen.next(new WorldPoint(3111, 9526, 0), false)); + } + + @Test + void walksToGateWhenRatIsUnreachableFromInstructor() { + assertEquals(RatPen.Step.WALK_TO_GATE, RatPen.next(new WorldPoint(3107, 9509, 0), false)); + } + + @Test + void opensGateWhenStandingAtEitherGateTile() { + assertEquals(RatPen.Step.OPEN_GATE, RatPen.next(new WorldPoint(3111, 9518, 0), false)); + assertEquals(RatPen.Step.OPEN_GATE, RatPen.next(new WorldPoint(3111, 9519, 0), false)); + assertEquals(RatPen.Step.OPEN_GATE, RatPen.next(new WorldPoint(3112, 9517, 0), false)); + } + + @Test + void walksToGateWhenPlayerLocationIsUnknown() { + assertEquals(RatPen.Step.WALK_TO_GATE, RatPen.next(null, false)); + } + + @Test + void doesNotOpenGateFromAnotherPlane() { + assertEquals(RatPen.Step.WALK_TO_GATE, RatPen.next(new WorldPoint(3111, 9518, 1), false)); + } +}