diff --git a/.gitignore b/.gitignore index 9a3d8f4b..f25c33f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,30 @@ -.gradle +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + .idea +.gradle out +.DS_Store +bin +build +.vscode diff --git a/build.gradle b/build.gradle index 162c04c3..9510ecf1 100644 --- a/build.gradle +++ b/build.gradle @@ -8,23 +8,27 @@ repositories { mavenCentral() } -def runeLiteVersion = '1.7.5' +def runeLiteVersion = '1.8.18.4' +def joglVersion = '2.4.0-rc-20220318' dependencies { compileOnly group: 'net.runelite', name:'client', version: runeLiteVersion + compileOnly group: 'net.runelite.jogl', name:'jogl-gldesktop-dbg', version: joglVersion - compileOnly 'org.projectlombok:lombok:1.18.4' - annotationProcessor 'org.projectlombok:lombok:1.18.4' + compileOnly 'org.projectlombok:lombok:1.18.20' + annotationProcessor 'org.projectlombok:lombok:1.18.20' - testImplementation 'junit:junit:4.12' - testImplementation 'org.slf4j:slf4j-simple:1.7.12' - testImplementation group: 'net.runelite', name:'client', version: runeLiteVersion, { - exclude group: 'ch.qos.logback', module: 'logback-classic' - } + testImplementation 'junit:junit:4.12' + testImplementation group: 'net.runelite', name:'client', version: runeLiteVersion + testImplementation group: 'net.runelite', name:'jshell', version: runeLiteVersion + testImplementation group: 'net.runelite.jogl', name:'jogl-gldesktop-dbg', version: joglVersion + + testCompileOnly 'org.projectlombok:lombok:1.18.20' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.20' } group = 'shortestpath' -version = '1.2' +version = '1.9' sourceCompatibility = '1.8' tasks.withType(JavaCompile) { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cb83f68d..93853577 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ #Thu Oct 08 20:51:56 EDT 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/shortestpath/PathMapOverlay.java b/src/main/java/shortestpath/PathMapOverlay.java index 32e51126..d0d8fee7 100644 --- a/src/main/java/shortestpath/PathMapOverlay.java +++ b/src/main/java/shortestpath/PathMapOverlay.java @@ -51,8 +51,8 @@ public Dimension render(Graphics2D graphics) { continue; } - for (WorldPoint b : plugin.transports.get(a)) { - Point mapB = worldMapOverlay.mapWorldPointToGraphicsPoint(b); + for (Transport b : plugin.transports.get(a)) { + Point mapB = worldMapOverlay.mapWorldPointToGraphicsPoint(b.getOrigin()); if (mapB == null) { continue; } diff --git a/src/main/java/shortestpath/PathTileOverlay.java b/src/main/java/shortestpath/PathTileOverlay.java index f1ad2eba..21917634 100644 --- a/src/main/java/shortestpath/PathTileOverlay.java +++ b/src/main/java/shortestpath/PathTileOverlay.java @@ -40,8 +40,8 @@ public Dimension render(Graphics2D graphics) { continue; } - for (WorldPoint b : plugin.transports.get(a)) { - Point cb = tileCenter(b); + for (Transport b : plugin.transports.get(a)) { + Point cb = tileCenter(b.getOrigin()); if (cb != null) { graphics.drawLine(ca.x, ca.y, cb.x, cb.y); @@ -49,10 +49,10 @@ public Dimension render(Graphics2D graphics) { } StringBuilder s = new StringBuilder(); - for (WorldPoint b : plugin.transports.get(a)) { - if (b.getPlane() > a.getPlane()) { + for (Transport b : plugin.transports.get(a)) { + if (b.getOrigin().getPlane() > a.getPlane()) { s.append("+"); - } else if (b.getPlane() < a.getPlane()) { + } else if (b.getOrigin().getPlane() < a.getPlane()) { s.append("-"); } else { s.append("="); diff --git a/src/main/java/shortestpath/ShortestPathPlugin.java b/src/main/java/shortestpath/ShortestPathPlugin.java index 418f2d1e..593bbd9a 100644 --- a/src/main/java/shortestpath/ShortestPathPlugin.java +++ b/src/main/java/shortestpath/ShortestPathPlugin.java @@ -1,6 +1,7 @@ package shortestpath; import com.google.inject.Provides; + import net.runelite.api.Point; import net.runelite.api.*; import net.runelite.api.coords.WorldArea; @@ -65,7 +66,7 @@ public class ShortestPathPlugin extends Plugin { public WorldMapPoint marker; private static final BufferedImage MARKER_IMAGE = ImageUtil.getResourceStreamFromClass(ShortestPathPlugin.class, "/marker.png"); public boolean pathUpdateScheduled = false; - public final Map> transports = new HashMap<>(); + public final Map> transports = new HashMap<>(); public Pathfinder pathfinder; private WorldPoint transportStart; private MenuOptionClicked lastClick; @@ -93,6 +94,7 @@ protected void startUp() { try { String s = new String(Util.readAllBytes(ShortestPathPlugin.class.getResourceAsStream("/transports.txt")), StandardCharsets.UTF_8); Scanner scanner = new Scanner(s); + while (scanner.hasNextLine()) { String line = scanner.nextLine(); @@ -100,11 +102,13 @@ protected void startUp() { continue; } - String[] l = line.split(" "); - WorldPoint a = new WorldPoint(Integer.parseInt(l[0]), Integer.parseInt(l[1]), Integer.parseInt(l[2])); - WorldPoint b = new WorldPoint(Integer.parseInt(l[3]), Integer.parseInt(l[4]), Integer.parseInt(l[5])); - transports.computeIfAbsent(a, k -> new ArrayList<>()).add(b); + Transport transport = new Transport(line); + WorldPoint origin = transport.getOrigin(); + + transports.computeIfAbsent(origin, k-> new ArrayList<>()).add(transport); } + + scanner.close(); } catch (IOException e) { throw new RuntimeException(e); } @@ -117,7 +121,8 @@ protected void startUp() { if (target == null) { path = null; } else { - pathfinder = new Pathfinder(map, transports, client.getLocalPlayer().getWorldLocation(), target, config.avoidWilderness() && !isInWilderness(target)); + int agilityLevel = client.getBoostedSkillLevel(Skill.AGILITY); + pathfinder = new Pathfinder(map, transports, client.getLocalPlayer().getWorldLocation(), target, config.avoidWilderness() && !isInWilderness(target), agilityLevel); path = pathfinder.find(); pathUpdateScheduled = false; } @@ -206,11 +211,14 @@ public void onMenuOptionClicked(MenuOptionClicked event) { if (event.getMenuOption().equals("End")) { WorldPoint transportEnd = client.getLocalPlayer().getWorldLocation(); + System.out.println(transportStart.getX() + " " + transportStart.getY() + " " + transportStart.getPlane() + " " + transportEnd.getX() + " " + transportEnd.getY() + " " + transportEnd.getPlane() + " " + lastClick.getMenuOption() + " " + Text.removeTags(lastClick.getMenuTarget()) + " " + lastClick.getId() ); - transports.computeIfAbsent(transportStart, k -> new ArrayList<>()).add(transportEnd); + + Transport transport = new Transport(transportStart, transportEnd); + transports.computeIfAbsent(transportStart, k -> new ArrayList<>()).add(transport); } if (event.getMenuOption().equals("Copy Position")) { @@ -242,6 +250,7 @@ private void setTarget(WorldPoint target) { marker = new WorldMapPoint(target, MARKER_IMAGE); marker.setTarget(marker.getWorldPoint()); marker.setJumpOnClick(true); + marker.setName("Shortest Path"); worldMapPointManager.add(marker); } } @@ -270,12 +279,9 @@ private void addMenuEntry(MenuEntryAdded event, String option) { return; } - MenuEntry entry = new MenuEntry(); + MenuEntry entry = client.createMenuEntry(0); entry.setOption(option); entry.setTarget(event.getTarget()); - entry.setType(MenuAction.RUNELITE.getId()); - entries.add(0, entry); - - client.setMenuEntries(entries.toArray(new MenuEntry[0])); + entry.setType(MenuAction.RUNELITE); } } diff --git a/src/main/java/shortestpath/Transport.java b/src/main/java/shortestpath/Transport.java new file mode 100644 index 00000000..bbbcc404 --- /dev/null +++ b/src/main/java/shortestpath/Transport.java @@ -0,0 +1,65 @@ +package shortestpath; +import net.runelite.api.coords.WorldPoint; + + +/** + * This class represents a travel point between two WorldPoints. + */ +public class Transport { + /** The starting point of this transport */ + private final WorldPoint origin; + /** The ending point of this transport */ + private final WorldPoint destination; + /** The agility level required to use transport */ + private final int agilityLevelRequired; + + Transport(final String line) { + String[] spaceSplit = line.split(" "); + + origin = new WorldPoint(Integer.parseInt(spaceSplit[0]), Integer.parseInt(spaceSplit[1]), Integer.parseInt(spaceSplit[2])); + destination = new WorldPoint(Integer.parseInt(spaceSplit[3]), Integer.parseInt(spaceSplit[4]), Integer.parseInt(spaceSplit[5])); + + int agilityLevel = 1; + + if (line.contains("Agility")) { + // Agility comments are in this for "NN Agility, " where NN is the level required + String[] quoteSplit = line.split("\""); + + assert(quoteSplit.length == 2); + + // There is only one comment per line, always at the end + String[] splitComment = quoteSplit[1].replace("\"", "").split(" "); + String level = splitComment[0]; + + try { + agilityLevel = Integer.parseInt(level); + } catch (Exception e) { + System.out.println("e: " + e.getMessage()); + } + } + + agilityLevelRequired = agilityLevel; + } + + Transport(final WorldPoint origin, final WorldPoint destination, int agilityLevelRequired) { + this.origin = origin; + this.destination = destination; + this.agilityLevelRequired = agilityLevelRequired; + } + + Transport(final WorldPoint origin, final WorldPoint destination) { + this(origin, destination, 0); + } + + public WorldPoint getOrigin() { + return origin; + } + + public WorldPoint getDestination() { + return destination; + } + + public int getAgilityLevelRequired() { + return agilityLevelRequired; + } +} \ No newline at end of file diff --git a/src/main/java/shortestpath/pathfinder/Pathfinder.java b/src/main/java/shortestpath/pathfinder/Pathfinder.java index ed3bc0b2..4e60610d 100644 --- a/src/main/java/shortestpath/pathfinder/Pathfinder.java +++ b/src/main/java/shortestpath/pathfinder/Pathfinder.java @@ -2,6 +2,7 @@ import net.runelite.api.coords.WorldPoint; import shortestpath.ShortestPathPlugin; +import shortestpath.Transport; import java.util.*; @@ -11,17 +12,19 @@ public class Pathfinder { private final WorldPoint target; private final List boundary = new LinkedList<>(); private final Set visited = new HashSet<>(); - private final Map> transports; + private final Map> transports; private final boolean avoidWilderness; private Node nearest; + private final int agilityLevel; - public Pathfinder(CollisionMap map, Map> transports, WorldPoint start, WorldPoint target, boolean avoidWilderness) { + public Pathfinder(CollisionMap map, Map> transports, WorldPoint start, WorldPoint target, boolean avoidWilderness, int agilityLevel) { this.map = map; this.transports = transports; this.target = target; this.start = new Node(start, null); this.avoidWilderness = avoidWilderness; nearest = null; + this.agilityLevel = agilityLevel; } public List find() { @@ -32,24 +35,34 @@ public List find() { while (!boundary.isEmpty()) { Node node = boundary.remove(0); + // if we are on the target block, return a path to this block if (node.position.equals(target)) { return node.path(); } - int distance = Math.max(Math.abs(node.position.getX() - target.getX()), Math.abs(node.position.getY() - target.getY())); + // add all the neighbouring blocks to this node to the graph + addNeighbors(node); + + int distance = node.position.distanceTo2D(target); + if (nearest == null || distance < bestDistance) { nearest = node; bestDistance = distance; } - addNeighbors(node); - } - - if (nearest != null) { - return nearest.path(); + // Check this node for valid transports + for (Transport transport : transports.getOrDefault(node.position, new ArrayList<>())) { + if (canPlayerUseTransport(transport)) { + addNeighbor(node, transport.getOrigin()); + addNeighbor(node, transport.getDestination()); + } else if (bestDistance == distance) { + // Player cannot use this tile, push out the bestDistance to allow selection of a new node + bestDistance += 1; + } + } } - return null; + return currentBest(); } private void addNeighbors(Node node) { @@ -84,14 +97,15 @@ private void addNeighbors(Node node) { if (map.ne(node.position.getX(), node.position.getY(), node.position.getPlane())) { addNeighbor(node, new WorldPoint(node.position.getX() + 1, node.position.getY() + 1, node.position.getPlane())); } + } - for (WorldPoint transport : transports.getOrDefault(node.position, new ArrayList<>())) { - addNeighbor(node, transport); - } + private boolean canPlayerUseTransport(Transport transport) { + // Currently, only supports agility level check + return agilityLevel >= transport.getAgilityLevelRequired(); } public List currentBest() { - return nearest==null ? null : nearest.path(); + return nearest == null ? null : nearest.path(); } private void addNeighbor(Node node, WorldPoint neighbor) { diff --git a/src/main/resources/transports.txt b/src/main/resources/transports.txt index 3616f1eb..a487310a 100644 --- a/src/main/resources/transports.txt +++ b/src/main/resources/transports.txt @@ -3884,3 +3884,6 @@ # Karamja 2910 3049 0 2906 3049 0 Cross A wooden log 23644 2906 3049 0 2910 3049 0 Cross A wooden log 23644 + +# Feldip Hills +2546 2873 0 2546 2871 0 Climb Rocks 31757 "10 Agility" \ No newline at end of file