@@ -1491,62 +1491,162 @@ private static boolean handleDoors(List<WorldPoint> path, int index) {
14911491 TileObject object = (wall != null )
14921492 ? wall
14931493 : Rs2GameObject .getGameObject (o -> o .getWorldLocation ().equals (probe ), probe , 3 );
1494- if (object == null ) continue ;
1494+ if (tryHandleDoorObject (object , probe , fromWp , toWp , doorActions , false )) {
1495+ return true ;
1496+ }
1497+ }
1498+ }
14951499
1496- ObjectComposition comp = Rs2GameObject .convertToObjectComposition (object );
1497- // Ignore imposter objects
1498- if (comp == null || comp .getImpostorIds () != null || comp .getName ().equals ("null" )) continue ;
1500+ TileObject nearbyDoor = findDoorNearSegment (fromWp , toWp , doorActions );
1501+ if (nearbyDoor != null && tryHandleDoorObject (nearbyDoor , nearbyDoor .getWorldLocation (), fromWp , toWp , doorActions , true )) {
1502+ return true ;
1503+ }
14991504
1500- String action = Arrays .stream (comp .getActions ())
1501- .filter (Objects ::nonNull )
1502- .filter (act -> doorActions .stream ().anyMatch (dact -> act .toLowerCase ().startsWith (dact .toLowerCase ())))
1503- .min (Comparator .comparing (act -> doorActions .indexOf (doorActions .stream ().filter (dact -> act .toLowerCase ().startsWith (dact )).findFirst ().orElse ("" ))))
1504- .orElse (null );
1505+ return false ;
1506+ }
15051507
1506- if (action == null ) continue ;
1508+ private static TileObject findDoorNearSegment (WorldPoint fromWp , WorldPoint toWp , List <String > doorActions ) {
1509+ WorldPoint playerLoc = Rs2Player .getWorldLocation ();
1510+ if (playerLoc == null || fromWp == null || toWp == null || fromWp .getPlane () != toWp .getPlane ()) {
1511+ return null ;
1512+ }
15071513
1508- boolean found = false ;
1514+ final int searchDistance = 10 ;
1515+ return Rs2GameObject .getAll (o -> {
1516+ if (o == null || o .getWorldLocation () == null ) return false ;
1517+ WorldPoint loc = o .getWorldLocation ();
1518+ if (loc .getPlane () != playerLoc .getPlane ()) return false ;
1519+ if (loc .distanceTo2D (playerLoc ) > searchDistance ) return false ;
1520+ if (sessionBlacklistedDoors .contains (loc )) return false ;
1521+ if (!(o instanceof WallObject ) && !(o instanceof GameObject )) return false ;
1522+ ObjectComposition comp = Rs2GameObject .convertToObjectComposition (o );
1523+ if (!isDoorComposition (comp , doorActions )) return false ;
1524+ return isDoorOnSegment (o , fromWp , toWp );
1525+ }, playerLoc , searchDistance ).stream ()
1526+ .min (Comparator .comparingInt (o -> o .getWorldLocation ().distanceTo2D (playerLoc )))
1527+ .orElse (null );
1528+ }
15091529
1510- final String name = comp .getName ();
1530+ private static boolean tryHandleDoorObject (TileObject object , WorldPoint probe , WorldPoint fromWp , WorldPoint toWp ,
1531+ List <String > doorActions , boolean allowSegmentProbe ) {
1532+ if (object == null || probe == null ) return false ;
15111533
1512- if (object instanceof WallObject ) {
1513- int orientation = (( WallObject ) object ). getOrientationA () ;
1534+ ObjectComposition comp = Rs2GameObject . convertToObjectComposition (object );
1535+ if (! isDoorComposition ( comp , doorActions )) return false ;
15141536
1515- if (searchNeighborPoint (orientation , probe , fromWp ) || searchNeighborPoint (orientation , probe , toWp )) {
1516- log .info ("Found WallObject door - name {} with action {} at {} - from {} to {}" , name , action , probe , fromWp , toWp );
1517- found = true ;
1518- }
1519- } else {
1520- if (name != null && name .toLowerCase ().contains ("door" )) {
1521- log .info ("Found GameObject door - name {} with action {} at {} - from {} to {}" , name , action , probe , fromWp , toWp );
1522- found = true ;
1523- }
1524- }
1537+ String action = getDoorAction (comp , doorActions );
1538+ if (action == null ) return false ;
15251539
1526- if (found ) {
1527- if (!handleDoorException (object , action )) {
1528- WorldPoint posBefore = Rs2Player .getWorldLocation ();
1529- Rs2GameObject .interact (object , action );
1530- Rs2Player .waitForWalking ();
1531- WorldPoint posAfter = Rs2Player .getWorldLocation ();
1532- boolean moved = posBefore != null && posAfter != null && !posBefore .equals (posAfter );
1533- if (!moved && isQuestLockedDoorDialogue ()) {
1534- String dialogue = Rs2Dialogue .getDialogueText ();
1535- log .warn ("[Walker] Door at {} ({} action={}) appears quest/stat-locked — dialogue=\" {}\" — blacklisting tile, refreshing restrictions, recalculating" ,
1536- probe , name , action , dialogue );
1537- sessionBlacklistedDoors .add (probe );
1538- Rs2Dialogue .clickContinue ();
1539- if (ShortestPathPlugin .pathfinderConfig != null ) {
1540- ShortestPathPlugin .pathfinderConfig .refresh ();
1541- }
1542- recalculatePath ();
1543- }
1544- }
1545- return true ;
1540+ boolean found = false ;
1541+ final String name = comp .getName ();
1542+
1543+ if (object instanceof WallObject ) {
1544+ int orientation = ((WallObject ) object ).getOrientationA ();
1545+
1546+ if (searchNeighborPoint (orientation , probe , fromWp )
1547+ || searchNeighborPoint (orientation , probe , toWp )
1548+ || (allowSegmentProbe && wallDoorTouchesSegment ((WallObject ) object , fromWp , toWp ))) {
1549+ log .info ("Found WallObject door - name {} with action {} at {} - from {} to {}" , name , action , probe , fromWp , toWp );
1550+ found = true ;
1551+ }
1552+ } else if (name != null && name .toLowerCase ().contains ("door" )) {
1553+ log .info ("Found GameObject door - name {} with action {} at {} - from {} to {}" , name , action , probe , fromWp , toWp );
1554+ found = true ;
1555+ }
1556+
1557+ if (!found ) return false ;
1558+
1559+ if (!handleDoorException (object , action )) {
1560+ WorldPoint posBefore = Rs2Player .getWorldLocation ();
1561+ Rs2GameObject .interact (object , action );
1562+ Rs2Player .waitForWalking ();
1563+ WorldPoint posAfter = Rs2Player .getWorldLocation ();
1564+ boolean moved = posBefore != null && posAfter != null && !posBefore .equals (posAfter );
1565+ if (!moved && isQuestLockedDoorDialogue ()) {
1566+ String dialogue = Rs2Dialogue .getDialogueText ();
1567+ log .warn ("[Walker] Door at {} ({} action={}) appears quest/stat-locked — dialogue=\" {}\" — blacklisting tile, refreshing restrictions, recalculating" ,
1568+ probe , name , action , dialogue );
1569+ sessionBlacklistedDoors .add (probe );
1570+ Rs2Dialogue .clickContinue ();
1571+ if (ShortestPathPlugin .pathfinderConfig != null ) {
1572+ ShortestPathPlugin .pathfinderConfig .refresh ();
15461573 }
1574+ recalculatePath ();
1575+ }
1576+ }
1577+ return true ;
1578+ }
1579+
1580+ private static boolean isDoorComposition (ObjectComposition comp , List <String > doorActions ) {
1581+ if (comp == null || comp .getImpostorIds () != null || comp .getName ().equals ("null" ) || comp .getActions () == null ) {
1582+ return false ;
1583+ }
1584+ return getDoorAction (comp , doorActions ) != null ;
1585+ }
1586+
1587+ private static String getDoorAction (ObjectComposition comp , List <String > doorActions ) {
1588+ if (comp == null || comp .getActions () == null ) {
1589+ return null ;
1590+ }
1591+ return Arrays .stream (comp .getActions ())
1592+ .filter (Objects ::nonNull )
1593+ .filter (act -> doorActions .stream ().anyMatch (dact -> act .toLowerCase ().startsWith (dact .toLowerCase ())))
1594+ .min (Comparator .comparing (act -> doorActions .indexOf (doorActions .stream ()
1595+ .filter (dact -> act .toLowerCase ().startsWith (dact ))
1596+ .findFirst ()
1597+ .orElse ("" ))))
1598+ .orElse (null );
1599+ }
1600+
1601+ private static boolean isDoorOnSegment (TileObject object , WorldPoint fromWp , WorldPoint toWp ) {
1602+ if (object == null || object .getWorldLocation () == null ) return false ;
1603+ if (object instanceof WallObject ) {
1604+ return wallDoorTouchesSegment ((WallObject ) object , fromWp , toWp )
1605+ || isPointNearSegment (object .getWorldLocation (), fromWp , toWp , 1 );
1606+ }
1607+ return isPointNearSegment (object .getWorldLocation (), fromWp , toWp , 1 );
1608+ }
1609+
1610+ private static boolean wallDoorTouchesSegment (WallObject wall , WorldPoint fromWp , WorldPoint toWp ) {
1611+ if (wall == null || wall .getWorldLocation () == null || fromWp == null || toWp == null ) return false ;
1612+ if (wall .getWorldLocation ().getPlane () != fromWp .getPlane () || fromWp .getPlane () != toWp .getPlane ()) return false ;
1613+
1614+ int orientation = wall .getOrientationA ();
1615+ int x = fromWp .getX ();
1616+ int y = fromWp .getY ();
1617+ int steps = 0 ;
1618+ while (steps ++ <= 64 ) {
1619+ WorldPoint point = new WorldPoint (x , y , fromWp .getPlane ());
1620+ if (searchNeighborPoint (orientation , wall .getWorldLocation (), point )) {
1621+ return true ;
1622+ }
1623+ if (x == toWp .getX () && y == toWp .getY ()) {
1624+ return false ;
15471625 }
1626+ x += Integer .signum (toWp .getX () - x );
1627+ y += Integer .signum (toWp .getY () - y );
15481628 }
1629+ return false ;
1630+ }
15491631
1632+ private static boolean isPointNearSegment (WorldPoint point , WorldPoint fromWp , WorldPoint toWp , int distance ) {
1633+ if (point == null || fromWp == null || toWp == null || point .getPlane () != fromWp .getPlane () || fromWp .getPlane () != toWp .getPlane ()) {
1634+ return false ;
1635+ }
1636+
1637+ int x = fromWp .getX ();
1638+ int y = fromWp .getY ();
1639+ int steps = 0 ;
1640+ while (steps ++ <= 64 ) {
1641+ if (point .distanceTo2D (new WorldPoint (x , y , fromWp .getPlane ())) <= distance ) {
1642+ return true ;
1643+ }
1644+ if (x == toWp .getX () && y == toWp .getY ()) {
1645+ return false ;
1646+ }
1647+ x += Integer .signum (toWp .getX () - x );
1648+ y += Integer .signum (toWp .getY () - y );
1649+ }
15501650 return false ;
15511651 }
15521652
@@ -2091,20 +2191,29 @@ private static boolean handleTransports(List<WorldPoint> path, int indexOfStartP
20912191 List <TileObject > objects = Rs2GameObject .getAll (o -> {
20922192 if (o .getId () == transportObjectId ) return true ;
20932193 Integer legacyClosed = OPEN_TO_CLOSED_MAPPINGS .get (transportObjectId );
2094- if (legacyClosed != null && o .getId () == legacyClosed ) return true ;
2095- if (!allowClosedVariant ) return false ;
2096- ObjectComposition comp = Rs2GameObject .convertToObjectComposition (o );
2097- if (comp == null || comp .getActions () == null ) return false ;
2098- String nm = comp .getName () == null ? "" : comp .getName ().toLowerCase ();
2099- boolean nameMatches = nm .contains ("trapdoor" ) || nm .contains ("manhole" )
2100- || nm .contains ("grate" ) || nm .contains ("hatch" );
2101- if (!nameMatches ) return false ;
2102- return Arrays .stream (comp .getActions ()).filter (Objects ::nonNull )
2103- .anyMatch (a -> a .equalsIgnoreCase ("Open" ));
2194+ return legacyClosed != null && o .getId () == legacyClosed ;
21042195 }, transport .getOrigin (), 10 ).stream ()
21052196 .sorted (Comparator .comparingInt (o -> o .getWorldLocation ().distanceTo (transport .getOrigin ())))
21062197 .collect (Collectors .toList ());
21072198
2199+ if (objects .isEmpty () && allowClosedVariant ) {
2200+ // The closed-variant fallback needs object composition lookups for name/action
2201+ // matching. Keep it off the common exact-id path; doing this for every
2202+ // climb-down object was the Varrock staircase delay.
2203+ objects = Rs2GameObject .getAll (o -> {
2204+ ObjectComposition comp = Rs2GameObject .convertToObjectComposition (o );
2205+ if (comp == null || comp .getActions () == null ) return false ;
2206+ String nm = comp .getName () == null ? "" : comp .getName ().toLowerCase ();
2207+ boolean nameMatches = nm .contains ("trapdoor" ) || nm .contains ("manhole" )
2208+ || nm .contains ("grate" ) || nm .contains ("hatch" );
2209+ if (!nameMatches ) return false ;
2210+ return Arrays .stream (comp .getActions ()).filter (Objects ::nonNull )
2211+ .anyMatch (a -> a .equalsIgnoreCase ("Open" ));
2212+ }, transport .getOrigin (), 10 ).stream ()
2213+ .sorted (Comparator .comparingInt (o -> o .getWorldLocation ().distanceTo (transport .getOrigin ())))
2214+ .collect (Collectors .toList ());
2215+ }
2216+
21082217 TileObject object = objects .stream ().findFirst ().orElse (null );
21092218 if (object instanceof GroundObject ) {
21102219 object = objects .stream ()
@@ -2150,7 +2259,10 @@ private static boolean handleTransports(List<WorldPoint> path, int indexOfStartP
21502259 }
21512260 }
21522261
2153- handleObject (transport , object );
2262+ prepareTransportObjectForInteraction (object );
2263+ if (!handleObject (transport , object )) {
2264+ return false ;
2265+ }
21542266 sleepUntil (() -> !Rs2Player .isAnimating ());
21552267 return sleepUntilTrue (() -> Rs2Player .getWorldLocation ().distanceTo (transport .getDestination ()) < OFFSET );
21562268 }
@@ -2174,9 +2286,19 @@ private static boolean handlePohTransport(Transport transport) {
21742286 return ((PohTransport )transport ).execute ();
21752287 }
21762288
2177- private static void handleObject (Transport transport , TileObject tileObject ) {
2289+ private static void prepareTransportObjectForInteraction (TileObject tileObject ) {
2290+ if (tileObject == null || tileObject .getLocalLocation () == null ) {
2291+ return ;
2292+ }
2293+ if (!Rs2Camera .isTileOnScreen (tileObject )) {
2294+ Rs2Camera .turnTo (tileObject );
2295+ sleepUntil (() -> Rs2Camera .isTileOnScreen (tileObject ), 1200 );
2296+ }
2297+ }
2298+
2299+ private static boolean handleObject (Transport transport , TileObject tileObject ) {
21782300 Rs2GameObject .interact (tileObject , transport .getAction ());
2179- if (handleObjectExceptions (transport , tileObject )) return ;
2301+ if (handleObjectExceptions (transport , tileObject )) return true ;
21802302 if (transport .getDestination ().getPlane () == Rs2Player .getWorldLocation ().getPlane ()) {
21812303 if (transport .getType () == TransportType .AGILITY_SHORTCUT ) {
21822304 Rs2Player .waitForAnimation ();
@@ -2194,10 +2316,23 @@ private static void handleObject(Transport transport, TileObject tileObject) {
21942316 Rs2Player .waitForWalking ();
21952317 Rs2Dialogue .clickOption ("Yes please" ); //shillo village cart
21962318 }
2319+ return true ;
21972320 } else {
21982321 int z = Rs2Player .getWorldLocation ().getPlane ();
2199- sleepUntil (() -> Rs2Player .getWorldLocation ().getPlane () != z );
2200- sleep ((int ) Rs2Random .gaussRand (1000.0 , 300.0 ));
2322+ boolean started = sleepUntil (() -> Rs2Player .getWorldLocation ().getPlane () != z
2323+ || Rs2Player .isMoving ()
2324+ || Rs2Player .isAnimating (), 1800 );
2325+ if (!started ) {
2326+ log .debug ("[Walker] {} transport click on {} produced no movement/animation; retrying" ,
2327+ transport .getAction (), tileObject .getId ());
2328+ return false ;
2329+ }
2330+ boolean planeChanged = Rs2Player .getWorldLocation ().getPlane () != z
2331+ || sleepUntil (() -> Rs2Player .getWorldLocation ().getPlane () != z , 5000 );
2332+ if (planeChanged ) {
2333+ sleep ((int ) Rs2Random .gaussRand (300.0 , 120.0 ));
2334+ }
2335+ return planeChanged ;
22012336 }
22022337 }
22032338
@@ -2608,7 +2743,7 @@ public static boolean isNearPath() {
26082743 final WorldPoint loc = Rs2Player .getWorldLocation ();
26092744 if (loc == null ) return true ;
26102745
2611- if (config .recalculateDistance () < 0 || lastPosition . equals ( lastPosition = loc ) ) {
2746+ if (config .recalculateDistance () < 0 ) {
26122747 return true ;
26132748 }
26142749
@@ -4463,4 +4598,3 @@ public static boolean closeWorldMap() {
44634598 return sleepUntil (() -> !Rs2Widget .isWidgetVisible (InterfaceID .Worldmap .CLOSE ), 3000 );
44644599 }
44654600}
4466-
0 commit comments