diff --git a/runelite-api/src/main/java/net/runelite/api/Perspective.java b/runelite-api/src/main/java/net/runelite/api/Perspective.java index 2e507782a3..a6cbe297a7 100644 --- a/runelite-api/src/main/java/net/runelite/api/Perspective.java +++ b/runelite-api/src/main/java/net/runelite/api/Perspective.java @@ -27,8 +27,16 @@ package net.runelite.api; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.geom.Area; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import net.runelite.api.model.Jarvis; +import net.runelite.api.model.Triangle; +import net.runelite.api.model.Vertex; public class Perspective { @@ -78,10 +86,55 @@ public class Perspective * 3D-space */ public static Point worldToCanvas(Client client, int x, int y, int plane, int zOffset) + { + return worldToCanvas(client, x, y, plane, x, y, zOffset); + } + + /** + * Translates two-dimensional ground coordinates within the 3D world to + * their corresponding coordinates on the game screen. Calculating heights + * based on the coordinates of the tile provided, rather than the world coordinates + * + * Using the position of each vertex, rather than the location of the object, to determine the + * height of each vertex causes the mesh to be vertically warped, based on the terrain below + * + * @param client + * @param x ground coordinate on the x axis + * @param y ground coordinate on the y axis + * @param plane ground plane on the z axis + * @param tileX the X coordinate of the tile the object is on + * @param tileY the Y coordinate of the tile the object is on + * @return a {@link Point} on screen corresponding to the position in + * 3D-space + */ + public static Point worldToCanvas(Client client, int x, int y, int plane, int tileX, int tileY) + { + return worldToCanvas(client, x, y, plane, tileX, tileY, 0); + } + + /** + * Translates two-dimensional ground coordinates within the 3D world to + * their corresponding coordinates on the game screen. Calculating heights + * based on the coordinates of the tile provided, rather than the world coordinates + * + * Using the position of each vertex, rather than the location of the object, to determine the + * height of each vertex causes the mesh to be vertically warped, based on the terrain below + * + * @param client + * @param x ground coordinate on the x axis + * @param y ground coordinate on the y axis + * @param plane ground plane on the z axis + * @param tileX the X coordinate of the tile the object is on + * @param tileY the Y coordinate of the tile the object is on + * @param zOffset distance from ground on the z axis + * @return a {@link Point} on screen corresponding to the position in + * 3D-space + */ + public static Point worldToCanvas(Client client, int x, int y, int plane, int tileX, int tileY, int zOffset) { if (x >= 128 && y >= 128 && x <= 13056 && y <= 13056) { - int z = getTileHeight(client, x, y, client.getPlane()) - plane; + int z = getTileHeight(client, tileX, tileY, client.getPlane()) - plane; x -= client.getCameraX(); y -= client.getCameraY(); z -= client.getCameraZ(); @@ -379,4 +432,223 @@ public class Perspective return new Point(xOffset, yOffset); } + /** + * You don't want this. Use {@link TileObject#getClickbox()} instead + * + * Get the on-screen clickable area of {@code model} as though it's for the object on the tile at + * ({@code tileX}, {@code tileY}) and rotated to angle {@code orientation} + * + * @param client + * @param model the model to calculate a clickbox for + * @param orientation the orientation of the model (0-2048, where 0 is north) + * @param tileX the X coordinate of the tile that the object using the model is on + * @param tileY the Y coordinate of the tile that the object using the model is on + * @return the clickable area of {@code model}, rotated to angle {@code orientation}, at the height of tile ({@code tileX}, {@code tileY}) + */ + public static Area getClickbox(Client client, Model model, int orientation, int tileX, int tileY) + { + if (model == null) + { + return null; + } + + List triangles = model.getTriangles().stream() + .map(triangle -> triangle.rotate(orientation)) + .collect(Collectors.toList()); + + List vertices = model.getVertices().stream() + .map(v -> v.rotate(orientation)) + .collect(Collectors.toList()); + + Area clickBox = get2DGeometry(client, triangles, orientation, tileX, tileY); + Area visibleAABB = getAABB(client, vertices, orientation, tileX, tileY); + + if (visibleAABB == null || clickBox == null) + { + return null; + } + + clickBox.intersect(visibleAABB); + return clickBox; + } + + private static Area get2DGeometry(Client client, List triangles, int orientation, int tileX, int tileY) + { + int radius = 5; + Area geometry = new Area(); + + for (Triangle triangle : triangles) + { + Vertex _a = triangle.getA(); + Point a = worldToCanvas(client, + tileX - _a.getX(), + tileY - _a.getZ(), + -_a.getY(), tileX, tileY); + if (a == null) + { + continue; + } + + Vertex _b = triangle.getB(); + Point b = worldToCanvas(client, + tileX - _b.getX(), + tileY - _b.getZ(), + -_b.getY(), tileX, tileY); + if (b == null) + { + continue; + } + + Vertex _c = triangle.getC(); + Point c = worldToCanvas(client, + tileX - _c.getX(), + tileY - _c.getZ(), + -_c.getY(), tileX, tileY); + if (c == null) + { + continue; + } + + int minX = Math.min(Math.min(a.getX(), b.getX()), c.getX()); + int minY = Math.min(Math.min(a.getY(), b.getY()), c.getY()); + + // For some reason, this calculation is always 4 pixels short of the actual in-client one + int maxX = Math.max(Math.max(a.getX(), b.getX()), c.getX()) + 4; + int maxY = Math.max(Math.max(a.getY(), b.getY()), c.getY()) + 4; + + // ...and the rectangles in the fixed client are shifted 4 pixels right and down + if (!client.isResized()) + { + minX += 4; + minY += 4; + maxX += 4; + maxY += 4; + } + + Rectangle clickableRect = new Rectangle( + minX - radius, minY - radius, + maxX - minX + radius, maxY - minY + radius + ); + geometry.add(new Area(clickableRect)); + } + + return geometry; + } + + private static Area getAABB(Client client, List vertices, int orientation, int tileX, int tileY) + { + int maxX = 0; + int minX = 0; + int maxY = 0; + int minY = 0; + int maxZ = 0; + int minZ = 0; + + for (Vertex vertex : vertices) + { + int x = vertex.getX(); + int y = vertex.getY(); + int z = vertex.getZ(); + + if (x > maxX) + { + maxX = x; + } + if (x < minX) + { + minX = x; + } + + if (y > maxY) + { + maxY = y; + } + if (y < minY) + { + minY = y; + } + + if (z > maxZ) + { + maxZ = z; + } + if (z < minZ) + { + minZ = z; + } + } + + int centerX = (minX + maxX) / 2; + int centerY = (minY + maxY) / 2; + int centerZ = (minZ + maxZ) / 2; + + int extremeX = (maxX - minX + 1) / 2; + int extremeY = (maxY - minY + 1) / 2; + int extremeZ = (maxZ - minZ + 1) / 2; + + if (extremeX < 32) + { + extremeX = 32; + } + + if (extremeZ < 32) + { + extremeZ = 32; + } + + int x1 = tileX - (centerX - extremeX); + int y1 = centerY - extremeY; + int z1 = tileY - (centerZ - extremeZ); + + int x2 = tileX - (centerX + extremeX); + int y2 = centerY + extremeY; + int z2 = tileY - (centerZ + extremeZ); + + Point p1 = worldToCanvas(client, x1, z1, -y1, tileX, tileY); + Point p2 = worldToCanvas(client, x1, z2, -y1, tileX, tileY); + Point p3 = worldToCanvas(client, x2, z2, -y1, tileX, tileY); + + Point p4 = worldToCanvas(client, x2, z1, -y1, tileX, tileY); + Point p5 = worldToCanvas(client, x1, z1, -y2, tileX, tileY); + Point p6 = worldToCanvas(client, x1, z2, -y2, tileX, tileY); + Point p7 = worldToCanvas(client, x2, z2, -y2, tileX, tileY); + Point p8 = worldToCanvas(client, x2, z1, -y2, tileX, tileY); + + List points = new ArrayList<>(8); + points.add(p1); + points.add(p2); + points.add(p3); + points.add(p4); + points.add(p5); + points.add(p6); + points.add(p7); + points.add(p8); + + try + { + points = Jarvis.convexHull(points); + } + catch (NullPointerException e) + { + // No non-null screen points for this AABB e.g. for an way off-screen model + return null; + } + + if (points == null) + { + return null; + } + + Polygon hull = new Polygon(); + for (Point p : points) + { + if (p != null) + { + hull.addPoint(p.getX(), p.getY()); + } + } + + return new Area(hull); + } + } diff --git a/runelite-api/src/main/java/net/runelite/api/Tile.java b/runelite-api/src/main/java/net/runelite/api/Tile.java index ecb997db40..720afcadbe 100644 --- a/runelite-api/src/main/java/net/runelite/api/Tile.java +++ b/runelite-api/src/main/java/net/runelite/api/Tile.java @@ -50,4 +50,6 @@ public interface Tile Point getRegionLocation(); Point getLocalLocation(); + + int getPlane(); } diff --git a/runelite-api/src/main/java/net/runelite/api/TileObject.java b/runelite-api/src/main/java/net/runelite/api/TileObject.java index 063dcef891..514134251f 100644 --- a/runelite-api/src/main/java/net/runelite/api/TileObject.java +++ b/runelite-api/src/main/java/net/runelite/api/TileObject.java @@ -26,6 +26,7 @@ package net.runelite.api; import java.awt.Graphics2D; import java.awt.Polygon; +import java.awt.geom.Area; public interface TileObject { @@ -54,4 +55,11 @@ public interface TileObject Point getMinimapLocation(); Polygon getConvexHull(Model model, int orientation); + + /** + * Get the on-screen clickable area of {@code object} + * + * @return the clickable area of {@code object} + */ + Area getClickbox(); } diff --git a/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectChanged.java b/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectChanged.java new file mode 100644 index 0000000000..962d8c5acf --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectChanged.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api.events; + +import lombok.Data; +import net.runelite.api.DecorativeObject; +import net.runelite.api.Tile; + +@Data +public class DecorativeObjectChanged +{ + private Tile tile; + private DecorativeObject previous; + private DecorativeObject decorativeObject; +} diff --git a/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectDespawned.java b/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectDespawned.java new file mode 100644 index 0000000000..b949d04714 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectDespawned.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api.events; + +import lombok.Data; +import net.runelite.api.DecorativeObject; +import net.runelite.api.Tile; + +@Data +public class DecorativeObjectDespawned +{ + private Tile tile; + private DecorativeObject decorativeObject; +} diff --git a/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectSpawned.java b/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectSpawned.java new file mode 100644 index 0000000000..0307acdf67 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectSpawned.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api.events; + +import lombok.Data; +import net.runelite.api.DecorativeObject; +import net.runelite.api.Tile; + +@Data +public class DecorativeObjectSpawned +{ + private Tile tile; + private DecorativeObject decorativeObject; +} diff --git a/runelite-api/src/main/java/net/runelite/api/events/GroundObjectChanged.java b/runelite-api/src/main/java/net/runelite/api/events/GroundObjectChanged.java new file mode 100644 index 0000000000..2d3d58d273 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/GroundObjectChanged.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api.events; + +import lombok.Data; +import net.runelite.api.GroundObject; +import net.runelite.api.Tile; + +@Data +public class GroundObjectChanged +{ + private Tile tile; + private GroundObject previous; + private GroundObject groundObject; +} diff --git a/runelite-api/src/main/java/net/runelite/api/events/GroundObjectDespawned.java b/runelite-api/src/main/java/net/runelite/api/events/GroundObjectDespawned.java new file mode 100644 index 0000000000..10b69f672f --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/GroundObjectDespawned.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api.events; + +import lombok.Data; +import net.runelite.api.GroundObject; +import net.runelite.api.Tile; + +@Data +public class GroundObjectDespawned +{ + private Tile tile; + private GroundObject groundObject; +} diff --git a/runelite-api/src/main/java/net/runelite/api/events/GroundObjectSpawned.java b/runelite-api/src/main/java/net/runelite/api/events/GroundObjectSpawned.java new file mode 100644 index 0000000000..2799061281 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/GroundObjectSpawned.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api.events; + +import lombok.Data; +import net.runelite.api.GroundObject; +import net.runelite.api.Tile; + +@Data +public class GroundObjectSpawned +{ + private Tile tile; + private GroundObject groundObject; +} diff --git a/runelite-api/src/main/java/net/runelite/api/model/Triangle.java b/runelite-api/src/main/java/net/runelite/api/model/Triangle.java index 161bf01b2e..11f4deee2e 100644 --- a/runelite-api/src/main/java/net/runelite/api/model/Triangle.java +++ b/runelite-api/src/main/java/net/runelite/api/model/Triangle.java @@ -24,8 +24,9 @@ */ package net.runelite.api.model; -import java.util.Objects; +import lombok.Value; +@Value public class Triangle { private final Vertex a; @@ -39,61 +40,13 @@ public class Triangle this.c = c; } - @Override - public String toString() + public Triangle rotate(int orientation) { - return "Triangle{" + "a=" + a + ", b=" + b + ", c=" + c + '}'; + return new Triangle( + a.rotate(orientation), + b.rotate(orientation), + c.rotate(orientation) + ); } - @Override - public int hashCode() - { - int hash = 7; - hash = 13 * hash + Objects.hashCode(this.a); - hash = 13 * hash + Objects.hashCode(this.b); - hash = 13 * hash + Objects.hashCode(this.c); - return hash; - } - - @Override - public boolean equals(Object obj) - { - if (obj == null) - { - return false; - } - if (getClass() != obj.getClass()) - { - return false; - } - final Triangle other = (Triangle) obj; - if (!Objects.equals(this.a, other.a)) - { - return false; - } - if (!Objects.equals(this.b, other.b)) - { - return false; - } - if (!Objects.equals(this.c, other.c)) - { - return false; - } - return true; - } - - public Vertex getA() - { - return a; - } - - public Vertex getB() - { - return b; - } - - public Vertex getC() - { - return c; - } } diff --git a/runelite-api/src/main/java/net/runelite/api/model/Vertex.java b/runelite-api/src/main/java/net/runelite/api/model/Vertex.java index 70b9c6adff..efde7051e6 100644 --- a/runelite-api/src/main/java/net/runelite/api/model/Vertex.java +++ b/runelite-api/src/main/java/net/runelite/api/model/Vertex.java @@ -24,8 +24,10 @@ */ package net.runelite.api.model; +import lombok.Value; import net.runelite.api.Perspective; +@Value public class Vertex { private final int x; @@ -39,64 +41,6 @@ public class Vertex this.z = z; } - @Override - public String toString() - { - return "Vertex{" + "x=" + x + ", y=" + y + ", z=" + z + '}'; - } - - @Override - public int hashCode() - { - int hash = 7; - hash = 67 * hash + this.x; - hash = 67 * hash + this.y; - hash = 67 * hash + this.z; - return hash; - } - - @Override - public boolean equals(Object obj) - { - if (obj == null) - { - return false; - } - if (getClass() != obj.getClass()) - { - return false; - } - final Vertex other = (Vertex) obj; - if (this.x != other.x) - { - return false; - } - if (this.y != other.y) - { - return false; - } - if (this.z != other.z) - { - return false; - } - return true; - } - - public int getX() - { - return x; - } - - public int getY() - { - return y; - } - - public int getZ() - { - return z; - } - /** * Rotate the vertex by the given orientation * @param orientation @@ -104,6 +48,14 @@ public class Vertex */ public Vertex rotate(int orientation) { + // models are orientated north (1024) and there are 2048 angles total + orientation = (orientation + 1024) % 2048; + + if (orientation == 0) + { + return this; + } + int sin = Perspective.SINE[orientation]; int cos = Perspective.COSINE[orientation]; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityOverlay.java new file mode 100644 index 0000000000..87e663d7f1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityOverlay.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.agilityplugin; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.geom.Area; +import javax.annotation.Nullable; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.Point; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; + +@Slf4j +public class AgilityOverlay extends Overlay +{ + private static final int MAX_DISTANCE = 2350; + + private final Client client; + private final AgilityPlugin plugin; + private final AgilityPluginConfiguration config; + + @Inject + public AgilityOverlay(@Nullable Client client, AgilityPlugin plugin, AgilityPluginConfiguration config) + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.UNDER_WIDGETS); + this.client = client; + this.plugin = plugin; + this.config = config; + } + + @Override + public Dimension render(Graphics2D graphics, java.awt.Point parent) + { + if (!config.enabled()) + { + return null; + } + + Point playerLocation = client.getLocalPlayer().getLocalLocation(); + Point mousePosition = client.getMouseCanvasPosition(); + plugin.getObstacles().forEach((object, tile) -> + { + if (tile.getPlane() == client.getPlane() + && object.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE) + { + Area objectClickbox = object.getClickbox(); + if (objectClickbox != null) + { + if (objectClickbox.contains(mousePosition.getX(), mousePosition.getY())) + { + graphics.setColor(Color.GREEN.darker()); + } + else + { + graphics.setColor(Color.GREEN); + } + graphics.draw(objectClickbox); + graphics.setColor(new Color(0, 0xff, 0, 20)); + graphics.fill(objectClickbox); + } + } + + }); + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityPlugin.java new file mode 100644 index 0000000000..c1790159fc --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityPlugin.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.agilityplugin; + +import com.google.common.eventbus.Subscribe; +import com.google.inject.Binder; +import com.google.inject.Provides; +import java.util.HashMap; +import javax.inject.Inject; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.GameState; +import net.runelite.api.Tile; +import net.runelite.api.TileObject; +import net.runelite.api.events.DecorativeObjectChanged; +import net.runelite.api.events.DecorativeObjectDespawned; +import net.runelite.api.events.DecorativeObjectSpawned; +import net.runelite.api.events.GameObjectChanged; +import net.runelite.api.events.GameObjectDespawned; +import net.runelite.api.events.GameObjectSpawned; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GroundObjectChanged; +import net.runelite.api.events.GroundObjectDespawned; +import net.runelite.api.events.GroundObjectSpawned; +import net.runelite.api.events.WallObjectChanged; +import net.runelite.api.events.WallObjectDespawned; +import net.runelite.api.events.WallObjectSpawned; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; + +@PluginDescriptor( + name = "Agility plugin" +) +@Slf4j +public class AgilityPlugin extends Plugin +{ + @Getter + private final HashMap obstacles = new HashMap<>(); + + @Inject + @Getter + private AgilityOverlay overlay; + + @Provides + AgilityPluginConfiguration getConfig(ConfigManager configManager) + { + return configManager.getConfig(AgilityPluginConfiguration.class); + } + + @Override + public void configure(Binder binder) + { + binder.bind(AgilityOverlay.class); + } + + // This code, brought to you, in part, by the letters C and V + // ... and the words "search" and "replace" + @Subscribe + public void GameObjectSpawned(GameObjectSpawned event) + { + onTileObject(event.getTile(), null, event.getGameObject()); + } + + @Subscribe + public void GameObjectChanged(GameObjectChanged event) + { + onTileObject(event.getTile(), event.getPrevious(), event.getGameObject()); + } + + @Subscribe + public void GameObjectDeSpawned(GameObjectDespawned event) + { + onTileObject(event.getTile(), event.getGameObject(), null); + } + + @Subscribe + public void GroundObjectSpawned(GroundObjectSpawned event) + { + onTileObject(event.getTile(), null, event.getGroundObject()); + } + + @Subscribe + public void GroundObjectChanged(GroundObjectChanged event) + { + onTileObject(event.getTile(), event.getPrevious(), event.getGroundObject()); + } + + @Subscribe + public void GroundObjectDeSpawned(GroundObjectDespawned event) + { + onTileObject(event.getTile(), event.getGroundObject(), null); + } + + @Subscribe + public void WallObjectSpawned(WallObjectSpawned event) + { + onTileObject(event.getTile(), null, event.getWallObject()); + } + + @Subscribe + public void WallObjectChanged(WallObjectChanged event) + { + onTileObject(event.getTile(), event.getPrevious(), event.getWallObject()); + } + + @Subscribe + public void WallObjectDeSpawned(WallObjectDespawned event) + { + onTileObject(event.getTile(), event.getWallObject(), null); + } + + @Subscribe + public void DecorativeObjectSpawned(DecorativeObjectSpawned event) + { + onTileObject(event.getTile(), null, event.getDecorativeObject()); + } + + @Subscribe + public void DecorativeObjectChanged(DecorativeObjectChanged event) + { + onTileObject(event.getTile(), event.getPrevious(), event.getDecorativeObject()); + } + + @Subscribe + public void DecorativeObjectDeSpawned(DecorativeObjectDespawned event) + { + onTileObject(event.getTile(), event.getDecorativeObject(), null); + } + + private void onTileObject(Tile tile, TileObject oldObject, TileObject newObject) + { + obstacles.remove(oldObject); + + if (newObject != null && Obstacles.OBSTACLE_IDS.contains(newObject.getId())) + { + obstacles.put(newObject, tile); + } + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) + { + if (event.getGameState() == GameState.LOADING) + { + obstacles.clear(); + } + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityPluginConfiguration.java b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityPluginConfiguration.java new file mode 100644 index 0000000000..426bbf5204 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/AgilityPluginConfiguration.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.agilityplugin; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup( + keyName = "agilityplugin", + name = "Agility plugin", + description = "Configuration for the agility plugin" +) +public interface AgilityPluginConfiguration extends Config +{ + @ConfigItem( + keyName = "enabled", + name = "Enable overlay", + description = "Configures whether the overlay is enabled" + ) + default boolean enabled() + { + return true; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/Obstacles.java b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/Obstacles.java new file mode 100644 index 0000000000..33738a0d0f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/agilityplugin/Obstacles.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018, SomeoneWithAnInternetConnection + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.agilityplugin; + +import com.google.common.collect.Sets; +import java.util.Set; +import static net.runelite.api.ObjectID.*; + +public class Obstacles +{ + public static final Set OBSTACLE_IDS = Sets.newHashSet( + // Gnome + OBSTACLE_NET_23134, TREE_BRANCH_23559, TREE_BRANCH_23560, OBSTACLE_NET_23135, OBSTACLE_PIPE_23138, + OBSTACLE_PIPE_23139, LOG_BALANCE_23145, BALANCING_ROPE_23557, + // Draynor + ROUGH_WALL, TIGHTROPE, TIGHTROPE_10075, NARROW_WALL, WALL_10084, GAP_10085, CRATE_10086, + // Al-Kharid + ROUGH_WALL_10093, TIGHTROPE_10284, CABLE, ZIP_LINE, TROPICAL_TREE_10357, ROOF_TOP_BEAMS, + TIGHTROPE_10583, GAP_10352, + // Pyramid + STAIRS_10857, LOW_WALL_10865, LEDGE_10860, PLANK_10868, GAP_10882, LEDGE_10886, STAIRS_10857, GAP_10884, + GAP_10859, GAP_10861, LOW_WALL_10865, GAP_10859, LEDGE_10888, PLANK_10868, CLIMBING_ROCKS_10851, DOORWAY_10855, + // Varrock + ROUGH_WALL_10586, CLOTHES_LINE, GAP_10642, WALL_10777, GAP_10778, GAP_10779, GAP_10780, LEDGE_10781, EDGE, + // Penguin + STEPPING_STONE_21120, STEPPING_STONE_21126, STEPPING_STONE_21128, STEPPING_STONE_21129, + STEPPING_STONE_21130, STEPPING_STONE_21131, STEPPING_STONE_21132, STEPPING_STONE_21133, + ICICLES, ICE, ICE_21149, ICE_21150, ICE_21151, ICE_21152, ICE_21153, ICE_21154, ICE_21155, ICE_21156, + // Barbarian + ROPESWING_23131, LOG_BALANCE_23144, OBSTACLE_NET_20211, BALANCING_LEDGE_23547, LADDER_16682, CRUMBLING_WALL_1948, + // Canifis + TALL_TREE_10819, GAP_10820, GAP_10821, GAP_10828, GAP_10822, POLEVAULT, GAP_10823, GAP_10832, + // Ape atoll + STEPPING_STONE_15412, TROPICAL_TREE_15414, MONKEYBARS_15417, SKULL_SLOPE_15483, ROPE_15487, TROPICAL_TREE_16062, + // Falador + ROUGH_WALL_10833, TIGHTROPE_10834, HAND_HOLDS_10836, GAP_11161, GAP_11360, TIGHTROPE_11361, + TIGHTROPE_11364, GAP_11365, LEDGE_11366, LEDGE_11367, LEDGE_11368, LEDGE_11370, EDGE_11371, + // Wilderness + OBSTACLE_PIPE_23137, ROPESWING_23132, STEPPING_STONE_23556, LOG_BALANCE_23542, ROCKS_23640, + // Seers + WALL_11373, GAP_11374, TIGHTROPE_11378, GAP_11375, GAP_11376, EDGE_11377, + // Pollniveach + BASKET_11380, MARKET_STALL_11381, BANNER_11382, GAP_11383, TREE_11384, ROUGH_WALL_11385, + MONKEYBARS, TREE_11389, DRYING_LINE, + // Rellaka + ROUGH_WALL_11391, GAP_11392, TIGHTROPE_11393, GAP_11395, GAP_11396, TIGHTROPE_11397, PILE_OF_FISH, + // Ardougne + GAP_11406, GAP_11429, GAP_11430, STEEP_ROOF, GAP_11630, PLANK_11631, WOODEN_BEAMS + ); +} diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSDecorativeObjectMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSDecorativeObjectMixin.java index 42160674e2..f3fd5c155b 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSDecorativeObjectMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSDecorativeObjectMixin.java @@ -25,18 +25,24 @@ package net.runelite.mixins; import java.awt.Polygon; +import java.awt.geom.Area; import net.runelite.api.Model; +import net.runelite.api.Perspective; import net.runelite.api.Renderable; import net.runelite.api.mixins.Inject; import net.runelite.api.mixins.Mixin; +import net.runelite.api.mixins.Shadow; +import net.runelite.rs.api.RSClient; import net.runelite.rs.api.RSDecorativeObject; @Mixin(RSDecorativeObject.class) public abstract class RSDecorativeObjectMixin implements RSDecorativeObject { + @Shadow("clientInstance") + private static RSClient client; + @Inject - @Override - public Polygon getConvexHull() + private Model getModel() { Renderable renderable = getRenderable(); if (renderable == null) @@ -55,6 +61,22 @@ public abstract class RSDecorativeObjectMixin implements RSDecorativeObject model = renderable.getModel(); } + return model; + } + + @Inject + @Override + public Area getClickbox() + { + return Perspective.getClickbox(client, getModel(), getOrientation(), getX(), getY()); + } + + @Inject + @Override + public Polygon getConvexHull() + { + Model model = getModel(); + if (model == null) { return null; diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSGameObjectMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSGameObjectMixin.java index 1f509ca478..0ff8ffaed1 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSGameObjectMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSGameObjectMixin.java @@ -25,16 +25,23 @@ package net.runelite.mixins; import java.awt.Polygon; +import java.awt.geom.Area; import net.runelite.api.Model; +import net.runelite.api.Perspective; import net.runelite.api.Point; import net.runelite.api.Renderable; import net.runelite.api.mixins.Inject; import net.runelite.api.mixins.Mixin; +import net.runelite.api.mixins.Shadow; +import net.runelite.rs.api.RSClient; import net.runelite.rs.api.RSGameObject; @Mixin(RSGameObject.class) public abstract class RSGameObjectMixin implements RSGameObject { + @Shadow("clientInstance") + private static RSClient client; + @Inject @Override public Point getRegionMinLocation() @@ -50,8 +57,7 @@ public abstract class RSGameObjectMixin implements RSGameObject } @Inject - @Override - public Polygon getConvexHull() + private Model getModel() { Renderable renderable = getRenderable(); if (renderable == null) @@ -59,16 +65,28 @@ public abstract class RSGameObjectMixin implements RSGameObject return null; } - Model model; - if (renderable instanceof Model) { - model = (Model) renderable; + return (Model) renderable; } else { - model = renderable.getModel(); + return renderable.getModel(); } + } + + @Inject + @Override + public Area getClickbox() + { + return Perspective.getClickbox(client, getModel(), getOrientation(), getX(), getY()); + } + + @Inject + @Override + public Polygon getConvexHull() + { + Model model = getModel(); if (model == null) { diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSGroundObjectMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSGroundObjectMixin.java new file mode 100644 index 0000000000..8d84a334d1 --- /dev/null +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSGroundObjectMixin.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018, SomeoneWithAnInternetConnection + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.mixins; + +import java.awt.geom.Area; +import net.runelite.api.Model; +import net.runelite.api.Perspective; +import net.runelite.api.Renderable; +import net.runelite.api.mixins.Inject; +import net.runelite.api.mixins.Mixin; +import net.runelite.api.mixins.Shadow; +import net.runelite.rs.api.RSClient; +import net.runelite.rs.api.RSGroundObject; + +@Mixin(RSGroundObject.class) +public abstract class RSGroundObjectMixin implements RSGroundObject +{ + @Shadow("clientInstance") + private static RSClient client; + + @Inject + private Model getModel() + { + Renderable renderable = getRenderable(); + if (renderable == null) + { + return null; + } + + if (renderable instanceof Model) + { + return (Model) renderable; + } + else + { + return renderable.getModel(); + } + } + + @Inject + @Override + public Area getClickbox() + { + return Perspective.getClickbox(client, getModel(), 0, getX(), getY()); + } +} diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSItemLayerMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSItemLayerMixin.java new file mode 100644 index 0000000000..7ee2411045 --- /dev/null +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSItemLayerMixin.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018, SomeoneWithAnInternetConnection + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.mixins; + +import java.awt.geom.Area; +import net.runelite.api.mixins.Inject; +import net.runelite.api.mixins.Mixin; +import net.runelite.rs.api.RSItemLayer; + +@Mixin(RSItemLayer.class) +public abstract class RSItemLayerMixin implements RSItemLayer +{ + @Inject + @Override + public Area getClickbox() + { + throw new UnsupportedOperationException(); + } + +} diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSPlayerMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSPlayerMixin.java index 93a51af761..80fb65c2da 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSPlayerMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSPlayerMixin.java @@ -72,15 +72,11 @@ public abstract class RSPlayerMixin implements RSPlayer int localX = getX(); int localY = getY(); - // models are orientated north (1024) and there are 2048 angles total - int orientation = (getOrientation() + 1024) % 2048; + int orientation = getOrientation(); List triangles = model.getTriangles(); - if (orientation != 0) - { - triangles = rotate(triangles, orientation); - } + triangles = rotate(triangles, orientation); List polys = new ArrayList(); for (Triangle triangle : triangles) diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSTileMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSTileMixin.java index d212768f1d..a1a76aff89 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSTileMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSTileMixin.java @@ -25,13 +25,21 @@ package net.runelite.mixins; import net.runelite.api.Actor; +import net.runelite.api.DecorativeObject; import net.runelite.api.GameObject; +import net.runelite.api.GroundObject; import net.runelite.api.Perspective; import net.runelite.api.Point; import net.runelite.api.WallObject; +import net.runelite.api.events.DecorativeObjectChanged; +import net.runelite.api.events.DecorativeObjectDespawned; +import net.runelite.api.events.DecorativeObjectSpawned; import net.runelite.api.events.GameObjectChanged; import net.runelite.api.events.GameObjectDespawned; import net.runelite.api.events.GameObjectSpawned; +import net.runelite.api.events.GroundObjectChanged; +import net.runelite.api.events.GroundObjectDespawned; +import net.runelite.api.events.GroundObjectSpawned; import net.runelite.api.events.WallObjectChanged; import net.runelite.api.events.WallObjectDespawned; import net.runelite.api.events.WallObjectSpawned; @@ -53,6 +61,12 @@ public abstract class RSTileMixin implements RSTile @Inject private WallObject previousWallObject; + @Inject + private DecorativeObject previousDecorativeObject; + + @Inject + private GroundObject previousGroundObject; + @Inject private GameObject[] previousGameObjects; @@ -112,6 +126,72 @@ public abstract class RSTileMixin implements RSTile } } + @FieldHook("decorativeObject") + @Inject + public void decorativeObjectChanged(int idx) + { + DecorativeObject previous = previousDecorativeObject; + DecorativeObject current = getDecorativeObject(); + + previousDecorativeObject = current; + + if (current == null && previous != null) + { + DecorativeObjectDespawned decorativeObjectDespawned = new DecorativeObjectDespawned(); + decorativeObjectDespawned.setTile(this); + decorativeObjectDespawned.setDecorativeObject(previous); + eventBus.post(decorativeObjectDespawned); + } + else if (current != null && previous == null) + { + DecorativeObjectSpawned decorativeObjectSpawned = new DecorativeObjectSpawned(); + decorativeObjectSpawned.setTile(this); + decorativeObjectSpawned.setDecorativeObject(current); + eventBus.post(decorativeObjectSpawned); + } + else if (current != null && previous != null) + { + DecorativeObjectChanged decorativeObjectChanged = new DecorativeObjectChanged(); + decorativeObjectChanged.setTile(this); + decorativeObjectChanged.setPrevious(previous); + decorativeObjectChanged.setDecorativeObject(current); + eventBus.post(decorativeObjectChanged); + } + } + + @FieldHook("groundObject") + @Inject + public void groundObjectChanged(int idx) + { + GroundObject previous = previousGroundObject; + GroundObject current = getGroundObject(); + + previousGroundObject = current; + + if (current == null && previous != null) + { + GroundObjectDespawned groundObjectDespawned = new GroundObjectDespawned(); + groundObjectDespawned.setTile(this); + groundObjectDespawned.setGroundObject(previous); + eventBus.post(groundObjectDespawned); + } + else if (current != null && previous == null) + { + GroundObjectSpawned groundObjectSpawned = new GroundObjectSpawned(); + groundObjectSpawned.setTile(this); + groundObjectSpawned.setGroundObject(current); + eventBus.post(groundObjectSpawned); + } + else if (current != null && previous != null) + { + GroundObjectChanged groundObjectChanged = new GroundObjectChanged(); + groundObjectChanged.setTile(this); + groundObjectChanged.setPrevious(previous); + groundObjectChanged.setGroundObject(current); + eventBus.post(groundObjectChanged); + } + } + @FieldHook("objects") @Inject public void gameObjectsChanged(int idx) diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSWallObjectMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSWallObjectMixin.java new file mode 100644 index 0000000000..47443cfcf7 --- /dev/null +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSWallObjectMixin.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018, SomeoneWithAnInternetConnection + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.mixins; + +import java.awt.geom.Area; +import net.runelite.api.Model; +import net.runelite.api.Perspective; +import net.runelite.api.Renderable; +import net.runelite.api.mixins.Inject; +import net.runelite.api.mixins.Mixin; +import net.runelite.api.mixins.Shadow; +import net.runelite.rs.api.RSClient; +import net.runelite.rs.api.RSWallObject; + +@Mixin(RSWallObject.class) +public abstract class RSWallObjectMixin implements RSWallObject +{ + @Shadow("clientInstance") + private static RSClient client; + + @Inject + private Model getModelA() + { + Renderable renderable = getRenderable1(); + if (renderable == null) + { + return null; + } + + if (renderable instanceof Model) + { + return (Model) renderable; + } + else + { + return renderable.getModel(); + } + } + + @Inject + private Model getModelB() + { + Renderable renderable = getRenderable2(); + if (renderable == null) + { + return null; + } + + if (renderable instanceof Model) + { + return (Model) renderable; + } + else + { + return renderable.getModel(); + } + } + + @Inject + @Override + public Area getClickbox() + { + Area clickbox = new Area(); + + Area clickboxA = Perspective.getClickbox(client, getModelA(), getOrientationA(), getX(), getY()); + Area clickboxB = Perspective.getClickbox(client, getModelB(), getOrientationB(), getX(), getY()); + + if (clickboxA == null && clickboxB == null) + { + return null; + } + + if (clickboxA != null) + { + clickbox.add(clickboxA); + } + + if (clickboxB != null) + { + clickbox.add(clickboxB); + } + + return clickbox; + } +} diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/TileObjectMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/TileObjectMixin.java index 79dec96c8f..a5e8ff5688 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/TileObjectMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/TileObjectMixin.java @@ -132,19 +132,13 @@ public abstract class TileObjectMixin implements TileObject int localX = getX(); int localY = getY(); - // models are orientated north (1024) and there are 2048 angles total - orientation = (orientation + 1024) % 2048; - List vertices = model.getVertices(); - if (orientation != 0) + // rotate vertices + for (int i = 0; i < vertices.size(); ++i) { - // rotate vertices - for (int i = 0; i < vertices.size(); ++i) - { - Vertex v = vertices.get(i); - vertices.set(i, v.rotate(orientation)); - } + Vertex v = vertices.get(i); + vertices.set(i, v.rotate(orientation)); } List points = new ArrayList(); diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSGroundObject.java b/runescape-api/src/main/java/net/runelite/rs/api/RSGroundObject.java index d1c0c8ef26..e1d96c500c 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSGroundObject.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSGroundObject.java @@ -25,6 +25,7 @@ package net.runelite.rs.api; import net.runelite.api.GroundObject; +import net.runelite.api.Renderable; import net.runelite.mapping.Import; public interface RSGroundObject extends GroundObject @@ -38,4 +39,7 @@ public interface RSGroundObject extends GroundObject @Import("y") int getY(); + + @Import("renderable") + Renderable getRenderable(); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSTile.java b/runescape-api/src/main/java/net/runelite/rs/api/RSTile.java index 2491175148..b7c9feb58e 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSTile.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSTile.java @@ -71,5 +71,6 @@ public interface RSTile extends Tile int getY(); @Import("plane") + @Override int getPlane(); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSWallObject.java b/runescape-api/src/main/java/net/runelite/rs/api/RSWallObject.java index f68f6715c7..9cf6937e59 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSWallObject.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSWallObject.java @@ -24,6 +24,7 @@ */ package net.runelite.rs.api; +import net.runelite.api.Renderable; import net.runelite.api.WallObject; import net.runelite.mapping.Import; @@ -49,6 +50,12 @@ public interface RSWallObject extends WallObject @Override int getOrientationB(); + @Import("renderable1") + Renderable getRenderable1(); + + @Import("renderable2") + Renderable getRenderable2(); + @Import("config") @Override int getConfig();