Merge pull request #6133 from WooxSolo/npc-unaggro-timer
Add NPC unaggression timer
This commit is contained in:
@@ -114,8 +114,7 @@ public class WorldArea
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
Point distances = getAxisDistances(other);
|
||||
return Math.max(distances.getX(), distances.getY());
|
||||
return distanceTo2D(other);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,6 +128,29 @@ public class WorldArea
|
||||
return distanceTo(new WorldArea(other, 1, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the shortest distance to another area while ignoring the plane.
|
||||
*
|
||||
* @param other the passed area
|
||||
* @return the distance
|
||||
*/
|
||||
public int distanceTo2D(WorldArea other)
|
||||
{
|
||||
Point distances = getAxisDistances(other);
|
||||
return Math.max(distances.getX(), distances.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the shortest distance to a world coordinate.
|
||||
*
|
||||
* @param other the passed coordinate
|
||||
* @return the distance
|
||||
*/
|
||||
public int distanceTo2D(WorldPoint other)
|
||||
{
|
||||
return distanceTo2D(new WorldArea(other, 1, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this area is within melee distance of another.
|
||||
* <p>
|
||||
|
||||
@@ -0,0 +1,454 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Woox <https://github.com/wooxsolo>
|
||||
* 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.geometry;
|
||||
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Geometry
|
||||
{
|
||||
/**
|
||||
* Find the point where two lines intersect.
|
||||
*
|
||||
* @param x1 X coordinate of the first endpoint of the first line.
|
||||
* @param y1 Y coordinate of the first endpoint of the first line.
|
||||
* @param x2 X coordinate of the second endpoint of the first line.
|
||||
* @param y2 Y coordinate of the second endpoint of the first line.
|
||||
* @param x3 X coordinate of the first endpoint of the second line.
|
||||
* @param y3 Y coordinate of the first endpoint of the second line.
|
||||
* @param x4 X coordinate of the second endpoint of the second line.
|
||||
* @param y4 Y coordinate of the second endpoint of the second line.
|
||||
* @return The intersection point of the lines, or null if the lines don't intersect.
|
||||
*/
|
||||
public static Point2D.Float lineIntersectionPoint(
|
||||
float x1, float y1, float x2, float y2,
|
||||
float x3, float y3, float x4, float y4)
|
||||
{
|
||||
// https://stackoverflow.com/a/1968345
|
||||
|
||||
float p1x = x2 - x1;
|
||||
float p1y = y2 - y1;
|
||||
float p2x = x4 - x3;
|
||||
float p2y = y4 - y3;
|
||||
|
||||
float s = (-p1y * (x1 - x3) + p1x * (y1 - y3)) / (-p2x * p1y + p1x * p2y);
|
||||
float t = ( p2x * (y1 - y3) - p2y * (x1 - x3)) / (-p2x * p1y + p1x * p2y);
|
||||
|
||||
if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
|
||||
{
|
||||
float x = x1 + (t * p1x);
|
||||
float y = y1 + (t * p1y);
|
||||
return new Point2D.Float(x, y);
|
||||
}
|
||||
|
||||
// No intersection
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the intersection points between a Shape and a line.
|
||||
*
|
||||
* @param shape The shape.
|
||||
* @param x1 X coordinate of the first endpoint of the line.
|
||||
* @param y1 Y coordinate of the first endpoint of the line.
|
||||
* @param x2 X coordinate of the second endpoint of the line.
|
||||
* @param y2 Y coordinate of the second endpoint of the line.
|
||||
* @return A list with the intersection points.
|
||||
*/
|
||||
public static List<Point2D.Float> intersectionPoints(Shape shape, float x1, float y1, float x2, float y2)
|
||||
{
|
||||
List<Point2D.Float> intersections = new LinkedList<>();
|
||||
|
||||
PathIterator it = shape.getPathIterator(new AffineTransform());
|
||||
float[] coords = new float[2];
|
||||
float[] prevCoords = new float[2];
|
||||
float[] start = new float[2];
|
||||
while (!it.isDone())
|
||||
{
|
||||
int type = it.currentSegment(coords);
|
||||
if (type == PathIterator.SEG_MOVETO)
|
||||
{
|
||||
start[0] = coords[0];
|
||||
start[1] = coords[1];
|
||||
prevCoords[0] = coords[0];
|
||||
prevCoords[1] = coords[1];
|
||||
}
|
||||
else if (type == PathIterator.SEG_LINETO)
|
||||
{
|
||||
Point2D.Float intersection = lineIntersectionPoint(
|
||||
prevCoords[0], prevCoords[1], coords[0], coords[1], x1, y1, x2, y2);
|
||||
if (intersection != null)
|
||||
{
|
||||
intersections.add(intersection);
|
||||
}
|
||||
prevCoords[0] = coords[0];
|
||||
prevCoords[1] = coords[1];
|
||||
}
|
||||
else if (type == PathIterator.SEG_CLOSE)
|
||||
{
|
||||
Point2D.Float intersection = lineIntersectionPoint(
|
||||
coords[0], coords[1], start[0], start[1], x1, y1, x2, y2);
|
||||
if (intersection != null)
|
||||
{
|
||||
intersections.add(intersection);
|
||||
}
|
||||
}
|
||||
it.next();
|
||||
}
|
||||
|
||||
return intersections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the points in a path according to a method.
|
||||
*
|
||||
* @param it The iterator of the path to change the points on.
|
||||
* @param method The method to use to transform the points. Takes a float[2] array with x and y coordinates as parameter.
|
||||
* @return The transformed path.
|
||||
*/
|
||||
public static GeneralPath transformPath(PathIterator it, Consumer<float[]> method)
|
||||
{
|
||||
GeneralPath path = new GeneralPath();
|
||||
float[] coords = new float[2];
|
||||
while (!it.isDone())
|
||||
{
|
||||
int type = it.currentSegment(coords);
|
||||
if (type == PathIterator.SEG_MOVETO)
|
||||
{
|
||||
method.accept(coords);
|
||||
path.moveTo(coords[0], coords[1]);
|
||||
}
|
||||
else if (type == PathIterator.SEG_LINETO)
|
||||
{
|
||||
method.accept(coords);
|
||||
path.lineTo(coords[0], coords[1]);
|
||||
}
|
||||
else if (type == PathIterator.SEG_CLOSE)
|
||||
{
|
||||
path.closePath();
|
||||
}
|
||||
it.next();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms the points in a path according to a method.
|
||||
*
|
||||
* @param path The path to change the points on.
|
||||
* @param method The method to use to transform the points. Takes a float[2] array with x and y coordinates as parameter.
|
||||
* @return The transformed path.
|
||||
*/
|
||||
public static GeneralPath transformPath(GeneralPath path, Consumer<float[]> method)
|
||||
{
|
||||
return transformPath(path.getPathIterator(new AffineTransform()), method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a line into smaller segments and appends the segments to a path.
|
||||
*
|
||||
* @param path The path to append lines to.
|
||||
* @param segmentLength The desired length to use for the segmented lines.
|
||||
* @param x1 X coordinate of the first endpoint of the line.
|
||||
* @param y1 Y coordinate of the first endpoint of the line.
|
||||
* @param x2 X coordinate of the second endpoint of the line.
|
||||
* @param y2 Y coordinate of the second endpoint of the line.
|
||||
*/
|
||||
private static void appendSegmentLines(GeneralPath path, float segmentLength,
|
||||
float x1, float y1, float x2, float y2)
|
||||
{
|
||||
float x = x1;
|
||||
float y = y1;
|
||||
float angle = (float)Math.atan2(y2 - y1, x2 - x1);
|
||||
float dx = (float)Math.cos(angle) * segmentLength;
|
||||
float dy = (float)Math.sin(angle) * segmentLength;
|
||||
float length = (float)Math.hypot(x2 - x1, y2 - y1);
|
||||
int steps = (int)((length - 1e-4) / segmentLength);
|
||||
for (int i = 0; i < steps; i++)
|
||||
{
|
||||
x += dx;
|
||||
y += dy;
|
||||
path.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a path into smaller segments.
|
||||
* For example, calling this on a path with a line of length 6, with desired
|
||||
* segment length of 2, would split the path into 3 consecutive lines of length 2.
|
||||
*
|
||||
* @param it The iterator of the path to modify.
|
||||
* @param segmentLength The desired length to use for the segments.
|
||||
* @return The modified path.
|
||||
*/
|
||||
public static GeneralPath splitIntoSegments(PathIterator it, float segmentLength)
|
||||
{
|
||||
GeneralPath newPath = new GeneralPath();
|
||||
float[] prevCoords = new float[2];
|
||||
float[] coords = new float[2];
|
||||
float[] startCoords = new float[2];
|
||||
while (!it.isDone())
|
||||
{
|
||||
int type = it.currentSegment(coords);
|
||||
if (type == PathIterator.SEG_MOVETO)
|
||||
{
|
||||
startCoords[0] = coords[0];
|
||||
startCoords[1] = coords[1];
|
||||
newPath.moveTo(coords[0], coords[1]);
|
||||
prevCoords[0] = coords[0];
|
||||
prevCoords[1] = coords[1];
|
||||
}
|
||||
else if (type == PathIterator.SEG_LINETO)
|
||||
{
|
||||
appendSegmentLines(newPath, segmentLength, prevCoords[0], prevCoords[1], coords[0], coords[1]);
|
||||
newPath.lineTo(coords[0], coords[1]);
|
||||
prevCoords[0] = coords[0];
|
||||
prevCoords[1] = coords[1];
|
||||
}
|
||||
else if (type == PathIterator.SEG_CLOSE)
|
||||
{
|
||||
appendSegmentLines(newPath, segmentLength, coords[0], coords[1], startCoords[0], startCoords[1]);
|
||||
newPath.closePath();
|
||||
}
|
||||
it.next();
|
||||
}
|
||||
|
||||
return newPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a path into smaller segments.
|
||||
* For example, calling this on a path with a line of length 6, with desired
|
||||
* segment length of 2, would split the path into 3 consecutive lines of length 2.
|
||||
*
|
||||
* @param path The path to modify.
|
||||
* @param segmentLength The desired length to use for the segments.
|
||||
* @return The modified path.
|
||||
*/
|
||||
public static GeneralPath splitIntoSegments(GeneralPath path, float segmentLength)
|
||||
{
|
||||
return splitIntoSegments(path.getPathIterator(new AffineTransform()), segmentLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes lines from a path according to a method.
|
||||
*
|
||||
* @param it The iterator of the path to filter.
|
||||
* @param method The method to use to decide which lines to remove. Takes two float[2] arrays with x and y coordinates of the endpoints of the line. Lines for which the predicate returns false are removed.
|
||||
* @return The filtered path.
|
||||
*/
|
||||
public static GeneralPath filterPath(PathIterator it, BiPredicate<float[], float[]> method)
|
||||
{
|
||||
GeneralPath newPath = new GeneralPath();
|
||||
float[] prevCoords = new float[2];
|
||||
float[] coords = new float[2];
|
||||
float[] start = new float[2];
|
||||
boolean shouldMoveNext = false;
|
||||
while (!it.isDone())
|
||||
{
|
||||
int type = it.currentSegment(coords);
|
||||
if (type == PathIterator.SEG_MOVETO)
|
||||
{
|
||||
start[0] = coords[0];
|
||||
start[1] = coords[1];
|
||||
prevCoords[0] = coords[0];
|
||||
prevCoords[1] = coords[1];
|
||||
shouldMoveNext = true;
|
||||
}
|
||||
else if (type == PathIterator.SEG_LINETO)
|
||||
{
|
||||
if (method.test(prevCoords, coords))
|
||||
{
|
||||
if (shouldMoveNext)
|
||||
{
|
||||
newPath.moveTo(prevCoords[0], prevCoords[1]);
|
||||
shouldMoveNext = false;
|
||||
}
|
||||
newPath.lineTo(coords[0], coords[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldMoveNext = true;
|
||||
}
|
||||
prevCoords[0] = coords[0];
|
||||
prevCoords[1] = coords[1];
|
||||
}
|
||||
else if (type == PathIterator.SEG_CLOSE)
|
||||
{
|
||||
if (shouldMoveNext)
|
||||
{
|
||||
newPath.moveTo(prevCoords[0], prevCoords[1]);
|
||||
}
|
||||
if (method.test(prevCoords, start))
|
||||
{
|
||||
newPath.lineTo(start[0], start[1]);
|
||||
}
|
||||
shouldMoveNext = false;
|
||||
}
|
||||
it.next();
|
||||
}
|
||||
|
||||
return newPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes lines from a path according to a method.
|
||||
*
|
||||
* @param path The path to filter.
|
||||
* @param method The method to use to decide which lines to remove. Takes two float[2] arrays with x and y coordinates of the endpoints of the line. Lines for which the predicate returns false are removed.
|
||||
* @return The filtered path.
|
||||
*/
|
||||
public static GeneralPath filterPath(GeneralPath path, BiPredicate<float[], float[]> method)
|
||||
{
|
||||
return filterPath(path.getPathIterator(new AffineTransform()), method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes lines from a path that lie outside the clipping area and cuts
|
||||
* lines intersecting with the clipping area so the resulting lines
|
||||
* lie within the clipping area.
|
||||
*
|
||||
* @param it The iterator of the path to clip.
|
||||
* @param shape The clipping area to clip with.
|
||||
* @return The clipped path.
|
||||
*/
|
||||
public static GeneralPath clipPath(PathIterator it, Shape shape)
|
||||
{
|
||||
GeneralPath newPath = new GeneralPath();
|
||||
float[] prevCoords = new float[2];
|
||||
float[] coords = new float[2];
|
||||
float[] start = new float[2];
|
||||
float[] nextMove = new float[2];
|
||||
boolean shouldMove = false;
|
||||
boolean wasInside = false;
|
||||
while (!it.isDone())
|
||||
{
|
||||
int type = it.currentSegment(coords);
|
||||
if (type == PathIterator.SEG_MOVETO)
|
||||
{
|
||||
start[0] = coords[0];
|
||||
start[1] = coords[1];
|
||||
wasInside = shape.contains(coords[0], coords[1]);
|
||||
if (wasInside)
|
||||
{
|
||||
nextMove[0] = coords[0];
|
||||
nextMove[1] = coords[1];
|
||||
shouldMove = true;
|
||||
}
|
||||
prevCoords[0] = coords[0];
|
||||
prevCoords[1] = coords[1];
|
||||
}
|
||||
else if (type == PathIterator.SEG_LINETO || type == PathIterator.SEG_CLOSE)
|
||||
{
|
||||
if (type == PathIterator.SEG_CLOSE)
|
||||
{
|
||||
coords[0] = start[0];
|
||||
coords[1] = start[1];
|
||||
}
|
||||
|
||||
List<Point2D.Float> intersections = intersectionPoints(shape, prevCoords[0], prevCoords[1], coords[0], coords[1]);
|
||||
intersections.sort((a, b) ->
|
||||
{
|
||||
double diff = a.distance(prevCoords[0], prevCoords[1]) - b.distance(prevCoords[0], prevCoords[1]);
|
||||
if (diff < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (diff > 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
for (Point2D.Float intersection : intersections)
|
||||
{
|
||||
if (wasInside)
|
||||
{
|
||||
if (shouldMove)
|
||||
{
|
||||
newPath.moveTo(nextMove[0], nextMove[1]);
|
||||
shouldMove = false;
|
||||
}
|
||||
newPath.lineTo(intersection.getX(), intersection.getY());
|
||||
}
|
||||
else
|
||||
{
|
||||
nextMove[0] = intersection.x;
|
||||
nextMove[1] = intersection.y;
|
||||
shouldMove = true;
|
||||
}
|
||||
wasInside = !wasInside;
|
||||
prevCoords[0] = intersection.x;
|
||||
prevCoords[1] = intersection.y;
|
||||
}
|
||||
|
||||
wasInside = shape.contains(coords[0], coords[1]);
|
||||
if (wasInside)
|
||||
{
|
||||
if (shouldMove)
|
||||
{
|
||||
newPath.moveTo(nextMove[0], nextMove[1]);
|
||||
shouldMove = false;
|
||||
}
|
||||
newPath.lineTo(coords[0], coords[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
nextMove[0] = coords[0];
|
||||
nextMove[1] = coords[1];
|
||||
shouldMove = true;
|
||||
}
|
||||
|
||||
prevCoords[0] = coords[0];
|
||||
prevCoords[1] = coords[1];
|
||||
}
|
||||
it.next();
|
||||
}
|
||||
return newPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes lines from a path that lie outside the clipping area and cuts
|
||||
* lines intersecting with the clipping area so the resulting lines
|
||||
* lie within the clipping area.
|
||||
*
|
||||
* @param path The path to clip.
|
||||
* @param shape The clipping area to clip with.
|
||||
* @return The clipped path.
|
||||
*/
|
||||
public static GeneralPath clipPath(GeneralPath path, Shape shape)
|
||||
{
|
||||
return clipPath(path.getPathIterator(new AffineTransform()), shape);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user