Merge pull request #1548 from WooxSolo/demonic-gorilla-plugin
Add demonic gorilla plugin
This commit is contained in:
@@ -28,6 +28,7 @@ import java.awt.Graphics2D;
|
||||
import java.awt.Polygon;
|
||||
import java.awt.image.BufferedImage;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.coords.WorldArea;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
|
||||
public interface Actor extends Renderable
|
||||
@@ -75,4 +76,6 @@ public interface Actor extends Renderable
|
||||
int getLogicalHeight();
|
||||
|
||||
Polygon getConvexHull();
|
||||
|
||||
WorldArea getWorldArea();
|
||||
}
|
||||
|
||||
@@ -113,6 +113,12 @@ public final class AnimationID
|
||||
public static final int LOOKING_INTO = 832;
|
||||
public static final int DIG = 830;
|
||||
public static final int VENGEANCE_OTHER = 4411;
|
||||
public static final int DEMONIC_GORILLA_MAGIC_ATTACK = 7225;
|
||||
public static final int DEMONIC_GORILLA_MELEE_ATTACK = 7226;
|
||||
public static final int DEMONIC_GORILLA_RANGED_ATTACK = 7227;
|
||||
public static final int DEMONIC_GORILLA_AOE_ATTACK = 7228;
|
||||
public static final int DEMONIC_GORILLA_PRAYER_SWITCH = 7228;
|
||||
public static final int DEMONIC_GORILLA_DEFEND = 7224;
|
||||
|
||||
// NPC animations
|
||||
public static final int TZTOK_JAD_MAGIC_ATTACK = 2656;
|
||||
|
||||
@@ -413,4 +413,6 @@ public interface Client extends GameEngine
|
||||
void setAttackersHidden(boolean state);
|
||||
|
||||
void setProjectilesHidden(boolean state);
|
||||
|
||||
CollisionData[] getCollisionMaps();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface CollisionData
|
||||
{
|
||||
int[][] getFlags();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public final class CollisionDataFlag
|
||||
{
|
||||
public static final int BLOCK_MOVEMENT_NORTH_WEST = 0x1;
|
||||
public static final int BLOCK_MOVEMENT_NORTH = 0x2;
|
||||
public static final int BLOCK_MOVEMENT_NORTH_EAST = 0x4;
|
||||
public static final int BLOCK_MOVEMENT_EAST = 0x8;
|
||||
public static final int BLOCK_MOVEMENT_SOUTH_EAST = 0x10;
|
||||
public static final int BLOCK_MOVEMENT_SOUTH = 0x20;
|
||||
public static final int BLOCK_MOVEMENT_SOUTH_WEST = 0x40;
|
||||
public static final int BLOCK_MOVEMENT_WEST = 0x80;
|
||||
|
||||
public static final int BLOCK_MOVEMENT_OBJECT = 0x100;
|
||||
public static final int BLOCK_MOVEMENT_FLOOR_DECORATION = 0x40000;
|
||||
public static final int BLOCK_MOVEMENT_FLOOR = 0x200000; // Eg. water
|
||||
public static final int BLOCK_MOVEMENT_FULL =
|
||||
BLOCK_MOVEMENT_OBJECT |
|
||||
BLOCK_MOVEMENT_FLOOR_DECORATION |
|
||||
BLOCK_MOVEMENT_FLOOR;
|
||||
|
||||
public static final int BLOCK_LINE_OF_SIGHT_NORTH = BLOCK_MOVEMENT_NORTH << 9; // 0x400
|
||||
public static final int BLOCK_LINE_OF_SIGHT_EAST = BLOCK_MOVEMENT_EAST << 9; // 0x1000
|
||||
public static final int BLOCK_LINE_OF_SIGHT_SOUTH = BLOCK_MOVEMENT_SOUTH << 9; // 0x4000
|
||||
public static final int BLOCK_LINE_OF_SIGHT_WEST = BLOCK_MOVEMENT_WEST << 9; // 0x10000
|
||||
public static final int BLOCK_LINE_OF_SIGHT_FULL = 0x20000;
|
||||
}
|
||||
70
runelite-api/src/main/java/net/runelite/api/Hitsplat.java
Normal file
70
runelite-api/src/main/java/net/runelite/api/Hitsplat.java
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class Hitsplat
|
||||
{
|
||||
public enum HitsplatType
|
||||
{
|
||||
BLOCK, // Blue
|
||||
DAMAGE, // Red
|
||||
POISON, // Green
|
||||
VENOM, // Darkgreen
|
||||
DISEASE, // Yellow
|
||||
HEAL; // Purple
|
||||
|
||||
public static HitsplatType fromInteger(int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case 0: return BLOCK;
|
||||
case 1: return DAMAGE;
|
||||
case 2: return POISON;
|
||||
case 4: return DISEASE;
|
||||
case 5: return VENOM;
|
||||
case 6: return HEAL;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
private HitsplatType hitsplatType;
|
||||
|
||||
@Getter
|
||||
private int amount;
|
||||
|
||||
@Getter
|
||||
private int disappearsOnGameCycle;
|
||||
|
||||
public Hitsplat(HitsplatType hitsplatType, int amount, int disappearsOnGameCycle)
|
||||
{
|
||||
this.hitsplatType = hitsplatType;
|
||||
this.amount = amount;
|
||||
this.disappearsOnGameCycle = disappearsOnGameCycle;
|
||||
}
|
||||
}
|
||||
@@ -41,8 +41,12 @@ public interface NPCComposition
|
||||
int getId();
|
||||
|
||||
int getCombatLevel();
|
||||
|
||||
|
||||
int[] getConfigs();
|
||||
|
||||
NPCComposition transform();
|
||||
|
||||
int getSize();
|
||||
|
||||
int getOverheadIcon();
|
||||
}
|
||||
|
||||
@@ -40,4 +40,6 @@ public interface Player extends Actor
|
||||
boolean isClanMember();
|
||||
|
||||
boolean isFriend();
|
||||
|
||||
int getOverheadIcon();
|
||||
}
|
||||
|
||||
@@ -60,6 +60,10 @@ public class ProjectileID
|
||||
|
||||
public static final int WINTERTODT_SNOW_FALL_AOE = 501;
|
||||
|
||||
public static final int DEMONIC_GORILLA_RANGED = 1302;
|
||||
public static final int DEMONIC_GORILLA_MAGIC = 1304;
|
||||
public static final int DEMONIC_GORILLA_BOULDER = 856;
|
||||
|
||||
/**
|
||||
* missing: marble gargoyle, superior dark beast
|
||||
*/
|
||||
|
||||
@@ -55,4 +55,6 @@ public interface Tile
|
||||
LocalPoint getLocalLocation();
|
||||
|
||||
int getPlane();
|
||||
|
||||
boolean hasLineOfSightTo(Tile other);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,630 @@
|
||||
/*
|
||||
* 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.coords;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.CollisionData;
|
||||
import net.runelite.api.CollisionDataFlag;
|
||||
import net.runelite.api.Constants;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.Tile;
|
||||
|
||||
public class WorldArea
|
||||
{
|
||||
/**
|
||||
* The western most point of the area
|
||||
*/
|
||||
@Getter
|
||||
private int x;
|
||||
|
||||
/**
|
||||
* The southern most point of the area
|
||||
*/
|
||||
@Getter
|
||||
private int y;
|
||||
|
||||
/**
|
||||
* The width of the area
|
||||
*/
|
||||
@Getter
|
||||
private int width;
|
||||
|
||||
/**
|
||||
* The height of the area
|
||||
*/
|
||||
@Getter
|
||||
private int height;
|
||||
|
||||
/**
|
||||
* The plane the area is on
|
||||
*/
|
||||
@Getter
|
||||
private int plane;
|
||||
|
||||
public WorldArea(int x, int y, int width, int height, int plane)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.plane = plane;
|
||||
}
|
||||
|
||||
public WorldArea(WorldPoint location, int width, int height)
|
||||
{
|
||||
this.x = location.getX();
|
||||
this.y = location.getY();
|
||||
this.plane = location.getPlane();
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shortest distance to another WorldArea for both x and y axis
|
||||
* @param other The WorldArea to get the distance to
|
||||
* @return Returns a Point with the shortest distance
|
||||
*/
|
||||
private Point getAxisDistances(WorldArea other)
|
||||
{
|
||||
Point p1 = this.getComparisonPoint(other);
|
||||
Point p2 = other.getComparisonPoint(this);
|
||||
return new Point(Math.abs(p1.getX() - p2.getX()), Math.abs(p1.getY() - p2.getY()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shortest distance to another WorldArea
|
||||
*
|
||||
* @param other The other area
|
||||
* @return Returns the distance
|
||||
*/
|
||||
public int distanceTo(WorldArea other)
|
||||
{
|
||||
if (this.getPlane() != other.getPlane())
|
||||
{
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
Point distances = getAxisDistances(other);
|
||||
return Math.max(distances.getX(), distances.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shortest distance to another WorldPoint
|
||||
*
|
||||
* @param other The other worldpoint
|
||||
* @return Returns the distance
|
||||
*/
|
||||
public int distanceTo(WorldPoint other)
|
||||
{
|
||||
return distanceTo(new WorldArea(other, 1, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this WorldArea is within melee distance of another WorldArea
|
||||
*
|
||||
* @param other The other world area to compare with
|
||||
* @return Returns true if it is in melee distance
|
||||
*/
|
||||
public boolean isInMeleeDistance(WorldArea other)
|
||||
{
|
||||
if (other == null || this.getPlane() != other.getPlane())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Point distances = getAxisDistances(other);
|
||||
return distances.getX() + distances.getY() == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this WorldArea is within melee distance of another WorldPoint
|
||||
*
|
||||
* @param other The world pint to compare with
|
||||
* @return Returns true if it is in melee distance
|
||||
*/
|
||||
public boolean isInMeleeDistance(WorldPoint other)
|
||||
{
|
||||
return isInMeleeDistance(new WorldArea(other, 1, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a WorldArea intersects with another WorldArea
|
||||
*
|
||||
* @param other The other WorldArea to compare with
|
||||
* @return Returns true if the areas intersect
|
||||
*/
|
||||
public boolean intersectsWith(WorldArea other)
|
||||
{
|
||||
if (this.getPlane() != other.getPlane())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Point distances = getAxisDistances(other);
|
||||
return distances.getX() + distances.getY() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the area can travel in one of the 8 directions
|
||||
* by using the standard collision detection algorithm.
|
||||
* Note that this method does not consider other actors as
|
||||
* a collision, but most non-boss NPCs do check for collision
|
||||
* with some actors.
|
||||
*
|
||||
* @param client The client to test in
|
||||
* @param dx The x direction to test against
|
||||
* @param dy The y direction to test against
|
||||
* @return Returns true if it's possible to travel in specified direction
|
||||
*/
|
||||
public boolean canTravelInDirection(Client client, int dx, int dy)
|
||||
{
|
||||
return canTravelInDirection(client, dx, dy, x -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the area can travel in one of the 8 directions
|
||||
* by using the standard collision detection algorithm.
|
||||
* Note that this method does not consider other actors as
|
||||
* a collision, but most non-boss NPCs do check for collision
|
||||
* with some actors.
|
||||
*
|
||||
* @param client The client to test in
|
||||
* @param dx The x direction to test against
|
||||
* @param dy The y direction to test against
|
||||
* @param extraCondition Additional check for if movement is allowed through specific
|
||||
* tiles, which may be used if movement should be disabled through other actors
|
||||
* @return Returns true if it's possible to travel in specified direction
|
||||
*/
|
||||
public boolean canTravelInDirection(Client client, int dx, int dy,
|
||||
Predicate<? super WorldPoint> extraCondition)
|
||||
{
|
||||
dx = Integer.signum(dx);
|
||||
dy = Integer.signum(dy);
|
||||
|
||||
if (dx == 0 && dy == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
LocalPoint lp = LocalPoint.fromWorld(client, x, y);
|
||||
|
||||
int startX = lp.getRegionX() + dx;
|
||||
int startY = lp.getRegionY() + dy;
|
||||
int checkX = startX + (dx > 0 ? width - 1 : 0);
|
||||
int checkY = startY + (dy > 0 ? height - 1 : 0);
|
||||
int endX = startX + width - 1;
|
||||
int endY = startY + height - 1;
|
||||
|
||||
int xFlags = CollisionDataFlag.BLOCK_MOVEMENT_FULL;
|
||||
int yFlags = CollisionDataFlag.BLOCK_MOVEMENT_FULL;
|
||||
int xyFlags = CollisionDataFlag.BLOCK_MOVEMENT_FULL;
|
||||
int xWallFlagsSouth = CollisionDataFlag.BLOCK_MOVEMENT_FULL;
|
||||
int xWallFlagsNorth = CollisionDataFlag.BLOCK_MOVEMENT_FULL;
|
||||
int yWallFlagsWest = CollisionDataFlag.BLOCK_MOVEMENT_FULL;
|
||||
int yWallFlagsEast = CollisionDataFlag.BLOCK_MOVEMENT_FULL;
|
||||
|
||||
if (dx < 0)
|
||||
{
|
||||
xFlags |= CollisionDataFlag.BLOCK_MOVEMENT_EAST;
|
||||
xWallFlagsSouth |= CollisionDataFlag.BLOCK_MOVEMENT_SOUTH |
|
||||
CollisionDataFlag.BLOCK_MOVEMENT_SOUTH_EAST;
|
||||
xWallFlagsNorth |= CollisionDataFlag.BLOCK_MOVEMENT_NORTH |
|
||||
CollisionDataFlag.BLOCK_MOVEMENT_NORTH_EAST;
|
||||
}
|
||||
if (dx > 0)
|
||||
{
|
||||
xFlags |= CollisionDataFlag.BLOCK_MOVEMENT_WEST;
|
||||
xWallFlagsSouth |= CollisionDataFlag.BLOCK_MOVEMENT_SOUTH |
|
||||
CollisionDataFlag.BLOCK_MOVEMENT_SOUTH_WEST;
|
||||
xWallFlagsNorth |= CollisionDataFlag.BLOCK_MOVEMENT_NORTH |
|
||||
CollisionDataFlag.BLOCK_MOVEMENT_NORTH_WEST;
|
||||
}
|
||||
if (dy < 0)
|
||||
{
|
||||
yFlags |= CollisionDataFlag.BLOCK_MOVEMENT_NORTH;
|
||||
yWallFlagsWest |= CollisionDataFlag.BLOCK_MOVEMENT_WEST |
|
||||
CollisionDataFlag.BLOCK_MOVEMENT_NORTH_WEST;
|
||||
yWallFlagsEast |= CollisionDataFlag.BLOCK_MOVEMENT_EAST |
|
||||
CollisionDataFlag.BLOCK_MOVEMENT_NORTH_EAST;
|
||||
}
|
||||
if (dy > 0)
|
||||
{
|
||||
yFlags |= CollisionDataFlag.BLOCK_MOVEMENT_SOUTH;
|
||||
yWallFlagsWest |= CollisionDataFlag.BLOCK_MOVEMENT_WEST |
|
||||
CollisionDataFlag.BLOCK_MOVEMENT_SOUTH_WEST;
|
||||
yWallFlagsEast |= CollisionDataFlag.BLOCK_MOVEMENT_EAST |
|
||||
CollisionDataFlag.BLOCK_MOVEMENT_SOUTH_EAST;
|
||||
}
|
||||
if (dx < 0 && dy < 0)
|
||||
{
|
||||
xyFlags |= CollisionDataFlag.BLOCK_MOVEMENT_NORTH_EAST;
|
||||
}
|
||||
if (dx < 0 && dy > 0)
|
||||
{
|
||||
xyFlags |= CollisionDataFlag.BLOCK_MOVEMENT_SOUTH_EAST;
|
||||
}
|
||||
if (dx > 0 && dy < 0)
|
||||
{
|
||||
xyFlags |= CollisionDataFlag.BLOCK_MOVEMENT_NORTH_WEST;
|
||||
}
|
||||
if (dx > 0 && dy > 0)
|
||||
{
|
||||
xyFlags |= CollisionDataFlag.BLOCK_MOVEMENT_SOUTH_WEST;
|
||||
}
|
||||
|
||||
CollisionData[] collisionData = client.getCollisionMaps();
|
||||
int[][] collisionDataFlags = collisionData[plane].getFlags();
|
||||
|
||||
if (dx != 0)
|
||||
{
|
||||
// Check that the area doesn't bypass a wall
|
||||
for (int y = startY; y <= endY; y++)
|
||||
{
|
||||
if ((collisionDataFlags[checkX][y] & xFlags) != 0 ||
|
||||
!extraCondition.test(WorldPoint.fromRegion(client, checkX, y, plane)))
|
||||
{
|
||||
// Collision while attempting to travel along the x axis
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the new area tiles don't contain a wall
|
||||
for (int y = startY + 1; y <= endY; y++)
|
||||
{
|
||||
if ((collisionDataFlags[checkX][y] & xWallFlagsSouth) != 0)
|
||||
{
|
||||
// The new area tiles contains a wall
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int y = endY - 1; y >= startY; y--)
|
||||
{
|
||||
if ((collisionDataFlags[checkX][y] & xWallFlagsNorth) != 0)
|
||||
{
|
||||
// The new area tiles contains a wall
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dy != 0)
|
||||
{
|
||||
// Check that the area tiles don't bypass a wall
|
||||
for (int x = startX; x <= endX; x++)
|
||||
{
|
||||
if ((collisionDataFlags[x][checkY] & yFlags) != 0 ||
|
||||
!extraCondition.test(WorldPoint.fromRegion(client, x, checkY, client.getPlane())))
|
||||
{
|
||||
// Collision while attempting to travel along the y axis
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the new area tiles don't contain a wall
|
||||
for (int x = startX + 1; x <= endX; x++)
|
||||
{
|
||||
if ((collisionDataFlags[x][checkY] & yWallFlagsWest) != 0)
|
||||
{
|
||||
// The new area tiles contains a wall
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int x = endX - 1; x >= startX; x--)
|
||||
{
|
||||
if ((collisionDataFlags[x][checkY] & yWallFlagsEast) != 0)
|
||||
{
|
||||
// The new area tiles contains a wall
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dx != 0 && dy != 0)
|
||||
{
|
||||
if ((collisionDataFlags[checkX][checkY] & xyFlags) != 0 ||
|
||||
!extraCondition.test(WorldPoint.fromRegion(client, checkX, checkY, client.getPlane())))
|
||||
{
|
||||
// Collision while attempting to travel diagonally
|
||||
return false;
|
||||
}
|
||||
|
||||
// When the areas edge size is 1 and it attempts to travel
|
||||
// diagonally, a collision check is done for respective
|
||||
// x and y axis as well.
|
||||
if (width == 1)
|
||||
{
|
||||
if ((collisionDataFlags[checkX][checkY - dy] & xFlags) != 0 &&
|
||||
extraCondition.test(WorldPoint.fromRegion(client, checkX, startY, client.getPlane())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (height == 1)
|
||||
{
|
||||
if ((collisionDataFlags[checkX - dx][checkY] & yFlags) != 0 &&
|
||||
extraCondition.test(WorldPoint.fromRegion(client, startX, checkY, client.getPlane())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Point within this WorldArea which is the closest to another WorldArea
|
||||
*
|
||||
* @param other The other WorldArea to compare to
|
||||
* @return Returns the closest Point
|
||||
*/
|
||||
private Point getComparisonPoint(WorldArea other)
|
||||
{
|
||||
int x, y;
|
||||
if (other.x <= this.x)
|
||||
{
|
||||
x = this.x;
|
||||
}
|
||||
else if (other.x >= this.x + this.width - 1)
|
||||
{
|
||||
x = this.x + this.width - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = other.x;
|
||||
}
|
||||
if (other.y <= this.y)
|
||||
{
|
||||
y = this.y;
|
||||
}
|
||||
else if (other.y >= this.y + this.height - 1)
|
||||
{
|
||||
y = this.y + this.height - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
y = other.y;
|
||||
}
|
||||
return new Point(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the next area that will be occupied if this area
|
||||
* attempts to move toward it by using the normal NPC travelling
|
||||
* pattern.
|
||||
*
|
||||
* @param client The client to calculate with
|
||||
* @param target The target area
|
||||
* @param stopAtMeleeDistance Determine if it should stop at melee distance to the target
|
||||
* @return Returns the next occupied area
|
||||
*/
|
||||
public WorldArea calculateNextTravellingPoint(Client client, WorldArea target,
|
||||
boolean stopAtMeleeDistance)
|
||||
{
|
||||
return calculateNextTravellingPoint(client, target, stopAtMeleeDistance, x -> true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the next area that will be occupied if this area
|
||||
* attempts to move toward it by using the normal NPC travelling
|
||||
* pattern.
|
||||
*
|
||||
* @param client The client to calculate with
|
||||
* @param target The target area
|
||||
* @param stopAtMeleeDistance Determine if it should stop at melee distance to the target
|
||||
* @param extraCondition Additional check for if movement is allowed through specific
|
||||
* tiles, which may be used if movement should be disabled through other actors
|
||||
* @return Returns the next occupied area
|
||||
*/
|
||||
public WorldArea calculateNextTravellingPoint(Client client, WorldArea target,
|
||||
boolean stopAtMeleeDistance, Predicate<? super WorldPoint> extraCondition)
|
||||
{
|
||||
if (plane != target.getPlane())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.intersectsWith(target))
|
||||
{
|
||||
if (stopAtMeleeDistance)
|
||||
{
|
||||
// Movement is unpredictable when the NPC and actor stand on top of each other
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
int dx = target.x - this.x;
|
||||
int dy = target.y - this.y;
|
||||
Point axisDistances = getAxisDistances(target);
|
||||
if (stopAtMeleeDistance && axisDistances.getX() + axisDistances.getY() == 1)
|
||||
{
|
||||
// NPC is in melee distance of target, so no movement is done
|
||||
return this;
|
||||
}
|
||||
|
||||
LocalPoint lp = LocalPoint.fromWorld(client, x, y);
|
||||
if (lp == null ||
|
||||
lp.getRegionX() + dx < 0 || lp.getRegionX() + dy >= Constants.REGION_SIZE ||
|
||||
lp.getRegionY() + dx < 0 || lp.getRegionY() + dy >= Constants.REGION_SIZE)
|
||||
{
|
||||
// NPC is travelling out of region, so collision data isn't available
|
||||
return null;
|
||||
}
|
||||
|
||||
int dxSig = Integer.signum(dx);
|
||||
int dySig = Integer.signum(dy);
|
||||
if (stopAtMeleeDistance && axisDistances.getX() == 1 && axisDistances.getY() == 1)
|
||||
{
|
||||
// When it needs to stop at melee distance, it will only attempt
|
||||
// to travel along the x axis when it is standing diagonally
|
||||
// from the target
|
||||
if (this.canTravelInDirection(client, dxSig, 0, extraCondition))
|
||||
{
|
||||
return new WorldArea(x + dxSig, y, width, height, plane);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.canTravelInDirection(client, dxSig, dySig, extraCondition))
|
||||
{
|
||||
return new WorldArea(x + dxSig, y + dySig, width, height, plane);
|
||||
}
|
||||
else if (dx != 0 && this.canTravelInDirection(client, dxSig, 0, extraCondition))
|
||||
{
|
||||
return new WorldArea(x + dxSig, y, width, height, plane);
|
||||
}
|
||||
else if (dy != 0 && Math.max(Math.abs(dx), Math.abs(dy)) > 1 &&
|
||||
this.canTravelInDirection(client, 0, dy, extraCondition))
|
||||
{
|
||||
// Note that NPCs don't attempts to travel along the y-axis
|
||||
// if the target is <= 1 tile distance away
|
||||
return new WorldArea(x, y + dySig, width, height, plane);
|
||||
}
|
||||
}
|
||||
|
||||
// The NPC is stuck
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this WorldArea has line of sight to another WorldArea.
|
||||
* Note that the reverse isn't necessarily true, meaning this can return true
|
||||
* while the other WorldArea does not have line of sight to this WorldArea.
|
||||
*
|
||||
* @param client The client to compare in
|
||||
* @param other The other WorldArea to compare with
|
||||
* @return Returns true if this WorldArea has line of sight to the other
|
||||
*/
|
||||
public boolean hasLineOfSightTo(Client client, WorldArea other)
|
||||
{
|
||||
if (plane != other.getPlane())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LocalPoint sourceLp = LocalPoint.fromWorld(client, x, y);
|
||||
LocalPoint targetLp = LocalPoint.fromWorld(client, other.getX(), other.getY());
|
||||
if (sourceLp == null || targetLp == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int thisX = sourceLp.getRegionX();
|
||||
int thisY = sourceLp.getRegionY();
|
||||
int otherX = targetLp.getRegionX();
|
||||
int otherY = targetLp.getRegionY();
|
||||
|
||||
int cmpThisX, cmpThisY, cmpOtherX, cmpOtherY;
|
||||
|
||||
// Determine which position to compare with for this NPC
|
||||
if (otherX <= thisX)
|
||||
{
|
||||
cmpThisX = thisX;
|
||||
}
|
||||
else if (otherX >= thisX + width - 1)
|
||||
{
|
||||
cmpThisX = thisX + width - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmpThisX = otherX;
|
||||
}
|
||||
if (otherY <= thisY)
|
||||
{
|
||||
cmpThisY = thisY;
|
||||
}
|
||||
else if (otherY >= thisY + height - 1)
|
||||
{
|
||||
cmpThisY = thisY + height - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmpThisY = otherY;
|
||||
}
|
||||
|
||||
// Determine which position to compare for the other actor
|
||||
if (thisX <= otherX)
|
||||
{
|
||||
cmpOtherX = otherX;
|
||||
}
|
||||
else if (thisX >= otherX + other.getWidth() - 1)
|
||||
{
|
||||
cmpOtherX = otherX + other.getWidth() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmpOtherX = thisX;
|
||||
}
|
||||
if (thisY <= otherY)
|
||||
{
|
||||
cmpOtherY = otherY;
|
||||
}
|
||||
else if (thisY >= otherY + other.getHeight() - 1)
|
||||
{
|
||||
cmpOtherY = otherY + other.getHeight() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmpOtherY = thisY;
|
||||
}
|
||||
|
||||
Tile[][][] tiles = client.getRegion().getTiles();
|
||||
Tile sourceTile = tiles[plane][cmpThisX][cmpThisY];
|
||||
Tile targetTile = tiles[other.getPlane()][cmpOtherX][cmpOtherY];
|
||||
if (sourceTile == null || targetTile == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return sourceTile.hasLineOfSightTo(targetTile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this WorldArea has line of sight to another WorldArea.
|
||||
* Note that the reverse isn't necessarily true, meaning this can return true
|
||||
* while the other WorldArea does not have line of sight to this WorldArea.
|
||||
*
|
||||
* @param client The client to compare in
|
||||
* @param other The other WorldPoint to compare with
|
||||
* @return Returns true if this WorldPoint has line of sight to the WorldPoint
|
||||
*/
|
||||
public boolean hasLineOfSightTo(Client client, WorldPoint other)
|
||||
{
|
||||
return hasLineOfSightTo(client, new WorldArea(other, 1, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the southwestern most point of this WorldArea
|
||||
*
|
||||
* @return Returns the southwestern most WorldPoint in the area
|
||||
*/
|
||||
public WorldPoint toWorldPoint()
|
||||
{
|
||||
return new WorldPoint(x, y, plane);
|
||||
}
|
||||
}
|
||||
@@ -119,6 +119,17 @@ public class WorldPoint
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the shortest distance from this point to a WorldArea
|
||||
*
|
||||
* @param other The WorldArea to find the distance to
|
||||
* @return Returns the shortest distance
|
||||
*/
|
||||
public int distanceTo(WorldArea other)
|
||||
{
|
||||
return new WorldArea(this, 1, 1).distanceTo(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the distance from this point to another point. Returns Integer.MAX_VALUE if other is on
|
||||
* a different plane.
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.events;
|
||||
|
||||
import lombok.Data;
|
||||
import net.runelite.api.Actor;
|
||||
import net.runelite.api.Hitsplat;
|
||||
|
||||
@Data
|
||||
public class HitsplatApplied
|
||||
{
|
||||
private Actor actor;
|
||||
private Hitsplat hitsplat;
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import java.awt.RenderingHints;
|
||||
import net.runelite.api.Actor;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Hitsplat;
|
||||
import net.runelite.api.ItemComposition;
|
||||
import net.runelite.api.KeyFocusListener;
|
||||
import net.runelite.api.MainBufferProvider;
|
||||
@@ -55,6 +56,7 @@ import net.runelite.api.events.ActorDeath;
|
||||
import net.runelite.api.events.ChatMessage;
|
||||
import net.runelite.api.events.FocusChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.HitsplatApplied;
|
||||
import net.runelite.api.events.MenuOpened;
|
||||
import net.runelite.api.events.MenuOptionClicked;
|
||||
import net.runelite.api.events.PostItemComposition;
|
||||
@@ -455,4 +457,29 @@ public class Hooks
|
||||
event.setMenuEntries(client.getMenuEntries());
|
||||
eventBus.post(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a hitsplat has been processed on an actor.
|
||||
* Note that this event runs even if the hitsplat didn't show up,
|
||||
* i.e. the actor already had 4 visible hitsplats.
|
||||
*
|
||||
* @param actor The actor the hitsplat was applied to
|
||||
* @param type The hitsplat type (i.e. color)
|
||||
* @param value The value of the hitsplat (i.e. how high the hit was)
|
||||
* @param var3
|
||||
* @param var4
|
||||
* @param gameCycle The gamecycle the hitsplat was applied on
|
||||
* @param duration The amount of gamecycles the hitsplat will last for
|
||||
*/
|
||||
public static void onActorHitsplat(Actor actor, int type, int value, int var3, int var4,
|
||||
int gameCycle, int duration)
|
||||
{
|
||||
Hitsplat hitsplat = new Hitsplat(Hitsplat.HitsplatType.fromInteger(type), value,
|
||||
gameCycle + duration);
|
||||
|
||||
HitsplatApplied event = new HitsplatApplied();
|
||||
event.setActor(actor);
|
||||
event.setHitsplat(hitsplat);
|
||||
eventBus.post(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.client.plugins.demonicgorilla;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.runelite.api.Actor;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.NPCComposition;
|
||||
import net.runelite.api.coords.WorldArea;
|
||||
|
||||
public class DemonicGorilla
|
||||
{
|
||||
static final int MAX_ATTACK_RANGE = 10; // Needs <= 10 tiles to reach target
|
||||
static final int ATTACK_RATE = 5; // 5 ticks between each attack
|
||||
static final int ATTACKS_PER_SWITCH = 3; // 3 unsuccessful attacks per style switch
|
||||
|
||||
static final int PROJECTILE_MAGIC_SPEED = 8; // Travels 8 tiles per tick
|
||||
static final int PROJECTILE_RANGED_SPEED = 6; // Travels 6 tiles per tick
|
||||
static final int PROJECTILE_MAGIC_DELAY = 12; // Requires an extra 12 tiles
|
||||
static final int PROJECTILE_RANGED_DELAY = 9; // Requires an extra 9 tiles
|
||||
|
||||
public static final AttackStyle[] ALL_REGULAR_ATTACK_STYLES =
|
||||
{
|
||||
AttackStyle.MELEE,
|
||||
AttackStyle.RANGED,
|
||||
AttackStyle.MAGIC
|
||||
};
|
||||
|
||||
enum AttackStyle
|
||||
{
|
||||
MAGIC,
|
||||
RANGED,
|
||||
MELEE,
|
||||
BOULDER
|
||||
}
|
||||
|
||||
@Getter
|
||||
private NPC npc;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private List<AttackStyle> nextPosibleAttackStyles;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private int attacksUntilSwitch;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private int nextAttackTick;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private int lastTickAnimation;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private WorldArea lastWorldArea;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean initiatedCombat;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private Actor lastTickInteracting;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean takenDamageRecently;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private int recentProjectileId;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean changedPrayerThisTick;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean changedAttackStyleThisTick;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean changedAttackStyleLastTick;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private int lastTickOverheadIcon;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private int disabledMeleeMovementForTicks;
|
||||
|
||||
public DemonicGorilla(NPC npc)
|
||||
{
|
||||
this.npc = npc;
|
||||
this.nextPosibleAttackStyles = Arrays.asList(ALL_REGULAR_ATTACK_STYLES);
|
||||
this.nextAttackTick = -100;
|
||||
this.attacksUntilSwitch = ATTACKS_PER_SWITCH;
|
||||
this.recentProjectileId = -1;
|
||||
this.lastTickOverheadIcon = -1;
|
||||
}
|
||||
|
||||
public int getOverheadIcon()
|
||||
{
|
||||
NPCComposition composition = this.npc.getComposition();
|
||||
if (composition != null)
|
||||
{
|
||||
return composition.getOverheadIcon();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.client.plugins.demonicgorilla;
|
||||
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.geom.Arc2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Perspective;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.client.game.SkillIconManager;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
|
||||
public class DemonicGorillaOverlay extends Overlay
|
||||
{
|
||||
private static final Color COLOR_ICON_BACKGROUND = new Color(0, 0, 0, 128);
|
||||
private static final Color COLOR_ICON_BORDER = new Color(0, 0, 0, 255);
|
||||
private static final Color COLOR_ICON_BORDER_FILL = new Color(219, 175, 0, 255);
|
||||
private static final int OVERLAY_ICON_DISTANCE = 50;
|
||||
private static final int OVERLAY_ICON_MARGIN = 8;
|
||||
|
||||
private Client client;
|
||||
private DemonicGorillaPlugin plugin;
|
||||
|
||||
@Inject
|
||||
private SkillIconManager iconManager;
|
||||
|
||||
@Inject
|
||||
public DemonicGorillaOverlay(Client client, DemonicGorillaPlugin plugin)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_SCENE);
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
private BufferedImage getIcon(DemonicGorilla.AttackStyle attackStyle)
|
||||
{
|
||||
switch (attackStyle)
|
||||
{
|
||||
case MELEE: return iconManager.getSkillImage(Skill.ATTACK);
|
||||
case RANGED: return iconManager.getSkillImage(Skill.RANGED);
|
||||
case MAGIC: return iconManager.getSkillImage(Skill.MAGIC);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
Player player = client.getLocalPlayer();
|
||||
|
||||
for (DemonicGorilla gorilla : plugin.getGorillas().values())
|
||||
{
|
||||
if (gorilla.getNpc().getInteracting() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
LocalPoint lp = gorilla.getNpc().getLocalLocation();
|
||||
if (lp != null)
|
||||
{
|
||||
Point point = Perspective.worldToCanvas(client, lp.getX(), lp.getY(), client.getPlane(),
|
||||
gorilla.getNpc().getLogicalHeight() + 16);
|
||||
if (point != null)
|
||||
{
|
||||
List<DemonicGorilla.AttackStyle> attackStyles = gorilla.getNextPosibleAttackStyles();
|
||||
List<BufferedImage> icons = new ArrayList<>();
|
||||
int totalWidth = (attackStyles.size() - 1) * OVERLAY_ICON_MARGIN;
|
||||
for (DemonicGorilla.AttackStyle attackStyle : attackStyles)
|
||||
{
|
||||
BufferedImage icon = getIcon(attackStyle);
|
||||
icons.add(icon);
|
||||
totalWidth += icon.getWidth();
|
||||
}
|
||||
|
||||
int bgPadding = 4;
|
||||
int currentPosX = 0;
|
||||
for (BufferedImage icon : icons)
|
||||
{
|
||||
graphics.setStroke(new BasicStroke(2));
|
||||
graphics.setColor(COLOR_ICON_BACKGROUND);
|
||||
graphics.fillOval(
|
||||
point.getX() - totalWidth / 2 + currentPosX - bgPadding,
|
||||
point.getY() - icon.getHeight() / 2 - OVERLAY_ICON_DISTANCE - bgPadding,
|
||||
icon.getWidth() + bgPadding * 2,
|
||||
icon.getHeight() + bgPadding * 2);
|
||||
|
||||
graphics.setColor(COLOR_ICON_BORDER);
|
||||
graphics.drawOval(
|
||||
point.getX() - totalWidth / 2 + currentPosX - bgPadding,
|
||||
point.getY() - icon.getHeight() / 2 - OVERLAY_ICON_DISTANCE - bgPadding,
|
||||
icon.getWidth() + bgPadding * 2,
|
||||
icon.getHeight() + bgPadding * 2);
|
||||
|
||||
graphics.drawImage(
|
||||
icon,
|
||||
point.getX() - totalWidth / 2 + currentPosX,
|
||||
point.getY() - icon.getHeight() / 2 - OVERLAY_ICON_DISTANCE,
|
||||
null);
|
||||
|
||||
graphics.setColor(COLOR_ICON_BORDER_FILL);
|
||||
Arc2D.Double arc = new Arc2D.Double(
|
||||
point.getX() - totalWidth / 2 + currentPosX - bgPadding,
|
||||
point.getY() - icon.getHeight() / 2 - OVERLAY_ICON_DISTANCE - bgPadding,
|
||||
icon.getWidth() + bgPadding * 2,
|
||||
icon.getHeight() + bgPadding * 2,
|
||||
90.0,
|
||||
-360.0 * (DemonicGorilla.ATTACKS_PER_SWITCH -
|
||||
gorilla.getAttacksUntilSwitch()) / DemonicGorilla.ATTACKS_PER_SWITCH,
|
||||
Arc2D.OPEN);
|
||||
graphics.draw(arc);
|
||||
|
||||
currentPosX += icon.getWidth() + OVERLAY_ICON_MARGIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,695 @@
|
||||
/*
|
||||
* 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.client.plugins.demonicgorilla;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.AnimationID;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.Hitsplat;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.NpcID;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.Projectile;
|
||||
import net.runelite.api.ProjectileID;
|
||||
import net.runelite.api.coords.WorldArea;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.HitsplatApplied;
|
||||
import net.runelite.api.events.NpcDespawned;
|
||||
import net.runelite.api.events.NpcSpawned;
|
||||
import net.runelite.api.events.PlayerDespawned;
|
||||
import net.runelite.api.events.PlayerSpawned;
|
||||
import net.runelite.api.events.ProjectileMoved;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Demonic gorillas"
|
||||
)
|
||||
@Slf4j
|
||||
public class DemonicGorillaPlugin extends Plugin
|
||||
{
|
||||
static final int OVERHEAD_ICON_MELEE = 0;
|
||||
static final int OVERHEAD_ICON_RANGED = 1;
|
||||
static final int OVERHEAD_ICON_MAGIC = 2;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private DemonicGorillaOverlay overlay;
|
||||
|
||||
@Getter
|
||||
private Map<NPC, DemonicGorilla> gorillas;
|
||||
|
||||
private int tickCounter;
|
||||
|
||||
private List<WorldPoint> recentBoulders;
|
||||
|
||||
private List<PendingGorillaAttack> pendingAttacks;
|
||||
|
||||
private Map<Player, MemorizedPlayer> memorizedPlayers;
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
tickCounter = 0;
|
||||
gorillas = new HashMap<>();
|
||||
recentBoulders = new ArrayList<>();
|
||||
pendingAttacks = new ArrayList<>();
|
||||
memorizedPlayers = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
gorillas = null;
|
||||
recentBoulders = null;
|
||||
pendingAttacks = null;
|
||||
memorizedPlayers = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Overlay getOverlay()
|
||||
{
|
||||
return overlay;
|
||||
}
|
||||
|
||||
private void clear()
|
||||
{
|
||||
recentBoulders.clear();
|
||||
pendingAttacks.clear();
|
||||
gorillas.clear();
|
||||
memorizedPlayers.clear();
|
||||
}
|
||||
|
||||
private void resetPlayers()
|
||||
{
|
||||
memorizedPlayers.clear();
|
||||
for (Player player : client.getPlayers())
|
||||
{
|
||||
memorizedPlayers.put(player, new MemorizedPlayer(player));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isNpcGorilla(int npcId)
|
||||
{
|
||||
return npcId == NpcID.DEMONIC_GORILLA ||
|
||||
npcId == NpcID.DEMONIC_GORILLA_7145 ||
|
||||
npcId == NpcID.DEMONIC_GORILLA_7146 ||
|
||||
npcId == NpcID.DEMONIC_GORILLA_7147 ||
|
||||
npcId == NpcID.DEMONIC_GORILLA_7148 ||
|
||||
npcId == NpcID.DEMONIC_GORILLA_7149;
|
||||
}
|
||||
|
||||
private void checkGorillaAttackStyleSwitch(DemonicGorilla gorilla,
|
||||
final DemonicGorilla.AttackStyle... protectedStyles)
|
||||
{
|
||||
if (gorilla.getAttacksUntilSwitch() <= 0 ||
|
||||
gorilla.getNextPosibleAttackStyles().size() == 0)
|
||||
{
|
||||
gorilla.setNextPosibleAttackStyles(Arrays
|
||||
.stream(DemonicGorilla.ALL_REGULAR_ATTACK_STYLES)
|
||||
.filter(x -> !Arrays.stream(protectedStyles).anyMatch(y -> x == y))
|
||||
.collect(Collectors.toList()));
|
||||
gorilla.setAttacksUntilSwitch(DemonicGorilla.ATTACKS_PER_SWITCH);
|
||||
gorilla.setChangedAttackStyleThisTick(true);
|
||||
}
|
||||
}
|
||||
|
||||
private DemonicGorilla.AttackStyle getProtectedStyle(Player player)
|
||||
{
|
||||
if (player.getOverheadIcon() == OVERHEAD_ICON_MELEE)
|
||||
{
|
||||
return DemonicGorilla.AttackStyle.MELEE;
|
||||
}
|
||||
else if (player.getOverheadIcon() == OVERHEAD_ICON_RANGED)
|
||||
{
|
||||
return DemonicGorilla.AttackStyle.RANGED;
|
||||
}
|
||||
else if (player.getOverheadIcon() == OVERHEAD_ICON_MAGIC)
|
||||
{
|
||||
return DemonicGorilla.AttackStyle.MAGIC;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void onGorillaAttack(DemonicGorilla gorilla, final DemonicGorilla.AttackStyle attackStyle)
|
||||
{
|
||||
gorilla.setInitiatedCombat(true);
|
||||
|
||||
Player target = (Player)gorilla.getNpc().getInteracting();
|
||||
|
||||
DemonicGorilla.AttackStyle protectedStyle = null;
|
||||
if (target != null)
|
||||
{
|
||||
protectedStyle = getProtectedStyle(target);
|
||||
}
|
||||
boolean correctPrayer =
|
||||
target == null || // If player is out of memory, assume prayer was correct
|
||||
attackStyle == protectedStyle;
|
||||
|
||||
if (attackStyle == DemonicGorilla.AttackStyle.BOULDER)
|
||||
{
|
||||
// The gorilla can't throw boulders when it's meleeing
|
||||
gorilla.setNextPosibleAttackStyles(gorilla
|
||||
.getNextPosibleAttackStyles()
|
||||
.stream()
|
||||
.filter(x -> x != DemonicGorilla.AttackStyle.MELEE)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (correctPrayer)
|
||||
{
|
||||
gorilla.setAttacksUntilSwitch(gorilla.getAttacksUntilSwitch() - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're not sure if the attack will hit a 0 or not,
|
||||
// so we don't know if we should decrease the counter or not,
|
||||
// so we keep track of the attack here until the damage splat
|
||||
// has appeared on the player.
|
||||
|
||||
int damagesOnTick = tickCounter;
|
||||
if (attackStyle == DemonicGorilla.AttackStyle.MAGIC)
|
||||
{
|
||||
MemorizedPlayer mp = memorizedPlayers.get(target);
|
||||
WorldArea lastPlayerArea = mp.getLastWorldArea();
|
||||
if (lastPlayerArea != null)
|
||||
{
|
||||
int dist = gorilla.getNpc().getWorldArea().distanceTo(lastPlayerArea);
|
||||
damagesOnTick += (dist + DemonicGorilla.PROJECTILE_MAGIC_DELAY) /
|
||||
DemonicGorilla.PROJECTILE_MAGIC_SPEED;
|
||||
}
|
||||
}
|
||||
else if (attackStyle == DemonicGorilla.AttackStyle.RANGED)
|
||||
{
|
||||
MemorizedPlayer mp = memorizedPlayers.get(target);
|
||||
WorldArea lastPlayerArea = mp.getLastWorldArea();
|
||||
if (lastPlayerArea != null)
|
||||
{
|
||||
int dist = gorilla.getNpc().getWorldArea().distanceTo(lastPlayerArea);
|
||||
damagesOnTick += (dist + DemonicGorilla.PROJECTILE_RANGED_DELAY) /
|
||||
DemonicGorilla.PROJECTILE_RANGED_SPEED;
|
||||
}
|
||||
}
|
||||
pendingAttacks.add(new PendingGorillaAttack(gorilla, attackStyle, target, damagesOnTick));
|
||||
}
|
||||
|
||||
gorilla.setNextPosibleAttackStyles(gorilla
|
||||
.getNextPosibleAttackStyles()
|
||||
.stream()
|
||||
.filter(x -> x == attackStyle)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
if (gorilla.getNextPosibleAttackStyles().size() == 0)
|
||||
{
|
||||
// Sometimes the gorilla can switch attack style before it's supposed to
|
||||
// if someone was fighting it earlier and then left, so we just
|
||||
// reset the counter in that case.
|
||||
|
||||
gorilla.setNextPosibleAttackStyles(Arrays
|
||||
.stream(DemonicGorilla.ALL_REGULAR_ATTACK_STYLES)
|
||||
.filter(x -> x == attackStyle)
|
||||
.collect(Collectors.toList()));
|
||||
gorilla.setAttacksUntilSwitch(DemonicGorilla.ATTACKS_PER_SWITCH -
|
||||
(correctPrayer ? 1 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
checkGorillaAttackStyleSwitch(gorilla, protectedStyle);
|
||||
|
||||
gorilla.setNextAttackTick(tickCounter + DemonicGorilla.ATTACK_RATE);
|
||||
}
|
||||
|
||||
private void checkGorillaAttacks()
|
||||
{
|
||||
for (DemonicGorilla gorilla : gorillas.values())
|
||||
{
|
||||
Player interacting = (Player)gorilla.getNpc().getInteracting();
|
||||
MemorizedPlayer mp = memorizedPlayers.get(interacting);
|
||||
|
||||
if (gorilla.getLastTickInteracting() != null && interacting == null)
|
||||
{
|
||||
gorilla.setInitiatedCombat(false);
|
||||
}
|
||||
else if (mp != null && mp.getLastWorldArea() != null &&
|
||||
!gorilla.isInitiatedCombat() &&
|
||||
tickCounter < gorilla.getNextAttackTick() &&
|
||||
gorilla.getNpc().getWorldArea().isInMeleeDistance(mp.getLastWorldArea()))
|
||||
{
|
||||
gorilla.setInitiatedCombat(true);
|
||||
gorilla.setNextAttackTick(tickCounter + 1);
|
||||
}
|
||||
|
||||
int animationId = gorilla.getNpc().getAnimation();
|
||||
|
||||
if (gorilla.isTakenDamageRecently() &&
|
||||
tickCounter >= gorilla.getNextAttackTick() + 4)
|
||||
{
|
||||
// The gorilla was flinched, so its next attack gets delayed
|
||||
gorilla.setNextAttackTick(tickCounter + DemonicGorilla.ATTACK_RATE / 2);
|
||||
gorilla.setInitiatedCombat(true);
|
||||
|
||||
if (mp != null && mp.getLastWorldArea() != null &&
|
||||
!gorilla.getNpc().getWorldArea().isInMeleeDistance(mp.getLastWorldArea()) &&
|
||||
!gorilla.getNpc().getWorldArea().intersectsWith(mp.getLastWorldArea()))
|
||||
{
|
||||
// Gorillas stop meleeing when they get flinched
|
||||
// and the target isn't in melee distance
|
||||
gorilla.setNextPosibleAttackStyles(gorilla
|
||||
.getNextPosibleAttackStyles()
|
||||
.stream()
|
||||
.filter(x -> x != DemonicGorilla.AttackStyle.MELEE)
|
||||
.collect(Collectors.toList()));
|
||||
checkGorillaAttackStyleSwitch(gorilla, DemonicGorilla.AttackStyle.MELEE,
|
||||
getProtectedStyle(interacting));
|
||||
}
|
||||
}
|
||||
else if (animationId != gorilla.getLastTickAnimation())
|
||||
{
|
||||
if (animationId == AnimationID.DEMONIC_GORILLA_MELEE_ATTACK)
|
||||
{
|
||||
onGorillaAttack(gorilla, DemonicGorilla.AttackStyle.MELEE);
|
||||
}
|
||||
else if (animationId == AnimationID.DEMONIC_GORILLA_MAGIC_ATTACK)
|
||||
{
|
||||
onGorillaAttack(gorilla, DemonicGorilla.AttackStyle.MAGIC);
|
||||
}
|
||||
else if (animationId == AnimationID.DEMONIC_GORILLA_RANGED_ATTACK)
|
||||
{
|
||||
onGorillaAttack(gorilla, DemonicGorilla.AttackStyle.RANGED);
|
||||
}
|
||||
else if (animationId == AnimationID.DEMONIC_GORILLA_AOE_ATTACK && interacting != null)
|
||||
{
|
||||
// Note that AoE animation is the same as prayer switch animation
|
||||
// so we need to check if the prayer was switched or not.
|
||||
// It also does this animation when it spawns, so
|
||||
// we need the interacting != null check.
|
||||
|
||||
if (gorilla.getOverheadIcon() == gorilla.getLastTickOverheadIcon())
|
||||
{
|
||||
// Confirmed, the gorilla used the AoE attack
|
||||
onGorillaAttack(gorilla, DemonicGorilla.AttackStyle.BOULDER);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tickCounter >= gorilla.getNextAttackTick())
|
||||
{
|
||||
gorilla.setChangedPrayerThisTick(true);
|
||||
|
||||
// This part is more complicated because the gorilla may have
|
||||
// used an attack, but the prayer switch animation takes
|
||||
// priority over normal attack animations.
|
||||
|
||||
int projectileId = gorilla.getRecentProjectileId();
|
||||
if (projectileId == ProjectileID.DEMONIC_GORILLA_MAGIC)
|
||||
{
|
||||
onGorillaAttack(gorilla, DemonicGorilla.AttackStyle.MAGIC);
|
||||
}
|
||||
else if (projectileId == ProjectileID.DEMONIC_GORILLA_RANGED)
|
||||
{
|
||||
onGorillaAttack(gorilla, DemonicGorilla.AttackStyle.RANGED);
|
||||
}
|
||||
else if (mp != null)
|
||||
{
|
||||
WorldArea lastPlayerArea = mp.getLastWorldArea();
|
||||
if (lastPlayerArea != null &&
|
||||
interacting != null && recentBoulders.stream()
|
||||
.anyMatch(x -> x.distanceTo(lastPlayerArea) == 0))
|
||||
{
|
||||
// A boulder started falling on the gorillas target,
|
||||
// so we assume it was the gorilla who shot it
|
||||
onGorillaAttack(gorilla, DemonicGorilla.AttackStyle.BOULDER);
|
||||
}
|
||||
else if (mp.getRecentHitsplats().size() > 0)
|
||||
{
|
||||
// It wasn't any of the three other attacks,
|
||||
// but the player took damage, so we assume
|
||||
// it's a melee attack
|
||||
onGorillaAttack(gorilla, DemonicGorilla.AttackStyle.MELEE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The next attack tick is always delayed if the
|
||||
// gorilla switched prayer
|
||||
gorilla.setNextAttackTick(tickCounter + DemonicGorilla.ATTACK_RATE);
|
||||
gorilla.setChangedPrayerThisTick(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gorilla.getDisabledMeleeMovementForTicks() > 0)
|
||||
{
|
||||
gorilla.setDisabledMeleeMovementForTicks(gorilla.getDisabledMeleeMovementForTicks() - 1);
|
||||
}
|
||||
else if (gorilla.isInitiatedCombat() &&
|
||||
gorilla.getNpc().getInteracting() != null &&
|
||||
!gorilla.isChangedAttackStyleThisTick() &&
|
||||
gorilla.getNextPosibleAttackStyles().size() >= 2 &&
|
||||
gorilla.getNextPosibleAttackStyles().stream()
|
||||
.anyMatch(x -> x == DemonicGorilla.AttackStyle.MELEE))
|
||||
{
|
||||
// If melee is a possibility, we can check if the gorilla
|
||||
// is or isn't moving toward the player to determine if
|
||||
// it is actually attempting to melee or not.
|
||||
// We only run this check if the gorilla is in combat
|
||||
// because otherwise it attempts to travel to melee
|
||||
// distance before attacking its target.
|
||||
|
||||
if (mp != null && mp.getLastWorldArea() != null && gorilla.getLastWorldArea() != null)
|
||||
{
|
||||
WorldArea predictedNewArea = gorilla.getLastWorldArea().calculateNextTravellingPoint(
|
||||
client, mp.getLastWorldArea(), true, x ->
|
||||
{
|
||||
// Gorillas can't normally walk through other gorillas
|
||||
// or other players
|
||||
final WorldArea area1 = new WorldArea(x, 1, 1);
|
||||
return area1 != null &&
|
||||
!gorillas.values().stream().anyMatch(y ->
|
||||
{
|
||||
if (y == gorilla)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
final WorldArea area2 =
|
||||
y.getNpc().getIndex() < gorilla.getNpc().getIndex() ?
|
||||
y.getNpc().getWorldArea() : y.getLastWorldArea();
|
||||
return area2 != null && area1.intersectsWith(area2);
|
||||
}) &&
|
||||
!memorizedPlayers.values().stream().anyMatch(y ->
|
||||
{
|
||||
final WorldArea area2 = y.getLastWorldArea();
|
||||
return area2 != null && area1.intersectsWith(area2);
|
||||
});
|
||||
|
||||
// There is a special case where if a player walked through
|
||||
// a gorilla, or a player walked through another player,
|
||||
// the tiles that were walked through becomes
|
||||
// walkable, but I didn't feel like it's necessary to handle
|
||||
// that special case as it should rarely happen.
|
||||
});
|
||||
if (predictedNewArea != null)
|
||||
{
|
||||
int distance = gorilla.getNpc().getWorldArea().distanceTo(mp.getLastWorldArea());
|
||||
WorldPoint predictedMovement = predictedNewArea.toWorldPoint();
|
||||
if (distance <= DemonicGorilla.MAX_ATTACK_RANGE &&
|
||||
mp != null &&
|
||||
mp.getLastWorldArea().hasLineOfSightTo(client, gorilla.getLastWorldArea()))
|
||||
{
|
||||
if (predictedMovement.distanceTo(gorilla.getLastWorldArea().toWorldPoint()) != 0)
|
||||
{
|
||||
if (predictedMovement.distanceTo(gorilla.getNpc().getWorldLocation()) == 0)
|
||||
{
|
||||
gorilla.setNextPosibleAttackStyles(gorilla
|
||||
.getNextPosibleAttackStyles()
|
||||
.stream()
|
||||
.filter(x -> x == DemonicGorilla.AttackStyle.MELEE)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
else
|
||||
{
|
||||
gorilla.setNextPosibleAttackStyles(gorilla
|
||||
.getNextPosibleAttackStyles()
|
||||
.stream()
|
||||
.filter(x -> x != DemonicGorilla.AttackStyle.MELEE)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
else if (tickCounter >= gorilla.getNextAttackTick() &&
|
||||
gorilla.getRecentProjectileId() == -1 &&
|
||||
!recentBoulders.stream().anyMatch(x -> x.distanceTo(mp.getLastWorldArea()) == 0))
|
||||
{
|
||||
gorilla.setNextPosibleAttackStyles(gorilla
|
||||
.getNextPosibleAttackStyles()
|
||||
.stream()
|
||||
.filter(x -> x == DemonicGorilla.AttackStyle.MELEE)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gorilla.isTakenDamageRecently())
|
||||
{
|
||||
gorilla.setInitiatedCombat(true);
|
||||
}
|
||||
|
||||
if (gorilla.getOverheadIcon() != gorilla.getLastTickOverheadIcon())
|
||||
{
|
||||
if (gorilla.isChangedAttackStyleLastTick() ||
|
||||
gorilla.isChangedAttackStyleThisTick())
|
||||
{
|
||||
// Apparently if it changes attack style and changes
|
||||
// prayer on the same tick or 1 tick apart, it won't
|
||||
// be able to move for the next 2 ticks if it attempts
|
||||
// to melee
|
||||
gorilla.setDisabledMeleeMovementForTicks(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it didn't change attack style lately,
|
||||
// it's only for the next 1 tick
|
||||
gorilla.setDisabledMeleeMovementForTicks(1);
|
||||
}
|
||||
}
|
||||
gorilla.setLastTickAnimation(gorilla.getNpc().getAnimation());
|
||||
gorilla.setLastWorldArea(gorilla.getNpc().getWorldArea());
|
||||
gorilla.setLastTickInteracting(gorilla.getNpc().getInteracting());
|
||||
gorilla.setTakenDamageRecently(false);
|
||||
gorilla.setChangedPrayerThisTick(false);
|
||||
gorilla.setChangedAttackStyleLastTick(gorilla.isChangedAttackStyleThisTick());
|
||||
gorilla.setChangedAttackStyleThisTick(false);
|
||||
gorilla.setLastTickOverheadIcon(gorilla.getOverheadIcon());
|
||||
gorilla.setRecentProjectileId(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProjectile(ProjectileMoved event)
|
||||
{
|
||||
Projectile projectile = event.getProjectile();
|
||||
int projectileId = projectile.getId();
|
||||
if (projectileId != ProjectileID.DEMONIC_GORILLA_RANGED &&
|
||||
projectileId != ProjectileID.DEMONIC_GORILLA_MAGIC &&
|
||||
projectileId != ProjectileID.DEMONIC_GORILLA_BOULDER)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The event fires once before the projectile starts moving,
|
||||
// and we only want to check each projectile once
|
||||
if (client.getGameCycle() >= projectile.getStartMovementCycle())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (projectileId == ProjectileID.DEMONIC_GORILLA_BOULDER)
|
||||
{
|
||||
recentBoulders.add(WorldPoint.fromLocal(client, event.getPosition()));
|
||||
}
|
||||
else if (projectileId == ProjectileID.DEMONIC_GORILLA_MAGIC ||
|
||||
projectileId == ProjectileID.DEMONIC_GORILLA_RANGED)
|
||||
{
|
||||
WorldPoint projectileSourcePosition = WorldPoint.fromLocal(
|
||||
client, projectile.getX1(), projectile.getY1(), client.getPlane());
|
||||
for (DemonicGorilla gorilla : gorillas.values())
|
||||
{
|
||||
if (gorilla.getNpc().getWorldLocation().distanceTo(projectileSourcePosition) == 0)
|
||||
{
|
||||
gorilla.setRecentProjectileId(projectile.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPendingAttacks()
|
||||
{
|
||||
Iterator<PendingGorillaAttack> it = pendingAttacks.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
PendingGorillaAttack attack = it.next();
|
||||
if (tickCounter >= attack.getFinishesOnTick())
|
||||
{
|
||||
boolean shouldDecreaseCounter = false;
|
||||
DemonicGorilla gorilla = attack.getAttacker();
|
||||
MemorizedPlayer target = memorizedPlayers.get(attack.getTarget());
|
||||
if (target == null)
|
||||
{
|
||||
// Player went out of memory, so assume the hit was a 0
|
||||
shouldDecreaseCounter = true;
|
||||
}
|
||||
else if (target.getRecentHitsplats().size() == 0)
|
||||
{
|
||||
// No hitsplats was applied. This may happen in some cases
|
||||
// where the player was out of memory while the
|
||||
// projectile was travelling. So we assume the hit was a 0.
|
||||
shouldDecreaseCounter = true;
|
||||
}
|
||||
else if (target.getRecentHitsplats().stream()
|
||||
.anyMatch(x -> x.getHitsplatType() == Hitsplat.HitsplatType.BLOCK))
|
||||
{
|
||||
// A blue hitsplat appeared, so we assume the gorilla hit a 0
|
||||
shouldDecreaseCounter = true;
|
||||
}
|
||||
|
||||
if (shouldDecreaseCounter)
|
||||
{
|
||||
gorilla.setAttacksUntilSwitch(gorilla.getAttacksUntilSwitch() - 1);
|
||||
checkGorillaAttackStyleSwitch(gorilla);
|
||||
}
|
||||
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePlayers()
|
||||
{
|
||||
for (MemorizedPlayer mp : memorizedPlayers.values())
|
||||
{
|
||||
mp.setLastWorldArea(mp.getPlayer().getWorldArea());
|
||||
mp.getRecentHitsplats().clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onHitsplat(HitsplatApplied event)
|
||||
{
|
||||
if (gorillas.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getActor() instanceof Player)
|
||||
{
|
||||
Player player = (Player)event.getActor();
|
||||
MemorizedPlayer mp = memorizedPlayers.get(player);
|
||||
if (mp != null)
|
||||
{
|
||||
mp.getRecentHitsplats().add(event.getHitsplat());
|
||||
}
|
||||
}
|
||||
else if (event.getActor() instanceof NPC)
|
||||
{
|
||||
DemonicGorilla gorilla = gorillas.get(event.getActor());
|
||||
Hitsplat.HitsplatType hitsplatType = event.getHitsplat().getHitsplatType();
|
||||
if (gorilla != null && (hitsplatType == Hitsplat.HitsplatType.BLOCK ||
|
||||
hitsplatType == Hitsplat.HitsplatType.DAMAGE))
|
||||
{
|
||||
gorilla.setTakenDamageRecently(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameState(GameStateChanged event)
|
||||
{
|
||||
GameState gs = event.getGameState();
|
||||
if (gs == GameState.LOGGING_IN ||
|
||||
gs == GameState.CONNECTION_LOST ||
|
||||
gs == GameState.HOPPING)
|
||||
{
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPlayerSpawned(PlayerSpawned event)
|
||||
{
|
||||
if (gorillas.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
memorizedPlayers.put(player, new MemorizedPlayer(player));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPlayerDespawned(PlayerDespawned event)
|
||||
{
|
||||
if (gorillas.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
memorizedPlayers.remove(event.getPlayer());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onNpcSpawned(NpcSpawned event)
|
||||
{
|
||||
NPC npc = event.getNpc();
|
||||
if (isNpcGorilla(npc.getId()))
|
||||
{
|
||||
if (gorillas.isEmpty())
|
||||
{
|
||||
// Players are not kept track of when there are no gorillas in
|
||||
// memory, so we need to add the players that were already in memory.
|
||||
resetPlayers();
|
||||
}
|
||||
|
||||
gorillas.put(npc, new DemonicGorilla(npc));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onNpcDespawned(NpcDespawned event)
|
||||
{
|
||||
if (gorillas.remove(event.getNpc()) != null && gorillas.isEmpty())
|
||||
{
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick event)
|
||||
{
|
||||
checkGorillaAttacks();
|
||||
checkPendingAttacks();
|
||||
updatePlayers();
|
||||
recentBoulders.clear();
|
||||
|
||||
tickCounter++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.client.plugins.demonicgorilla;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.runelite.api.Hitsplat;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.coords.WorldArea;
|
||||
|
||||
public class MemorizedPlayer
|
||||
{
|
||||
@Getter
|
||||
private Player player;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private WorldArea lastWorldArea;
|
||||
|
||||
@Getter
|
||||
private List<Hitsplat> recentHitsplats;
|
||||
|
||||
public MemorizedPlayer(Player player)
|
||||
{
|
||||
this.player = player;
|
||||
this.recentHitsplats = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.client.plugins.demonicgorilla;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.Player;
|
||||
|
||||
public class PendingGorillaAttack
|
||||
{
|
||||
@Getter
|
||||
private DemonicGorilla attacker;
|
||||
|
||||
@Getter
|
||||
private DemonicGorilla.AttackStyle attackStyle;
|
||||
|
||||
@Getter
|
||||
private Player target;
|
||||
|
||||
@Getter
|
||||
private int finishesOnTick;
|
||||
|
||||
public PendingGorillaAttack(DemonicGorilla attacker, DemonicGorilla.AttackStyle attackStyle,
|
||||
Player target, int finishesOnTick)
|
||||
{
|
||||
this.attacker = attacker;
|
||||
this.attackStyle = attackStyle;
|
||||
this.target = target;
|
||||
this.finishesOnTick = finishesOnTick;
|
||||
}
|
||||
}
|
||||
@@ -180,6 +180,22 @@ public class DevToolsPanel extends PluginPanel
|
||||
});
|
||||
container.add(mapSquaresBtn);
|
||||
|
||||
final JButton validMovementBtn = new JButton("Valid Moves");
|
||||
validMovementBtn.addActionListener(e ->
|
||||
{
|
||||
highlightButton(validMovementBtn);
|
||||
plugin.toggleValidMovement();
|
||||
});
|
||||
container.add(validMovementBtn);
|
||||
|
||||
final JButton lineOfSightBtn = new JButton("Line of Sight");
|
||||
lineOfSightBtn.addActionListener(e ->
|
||||
{
|
||||
highlightButton(lineOfSightBtn);
|
||||
plugin.toggleLineOfSight();
|
||||
});
|
||||
container.add(lineOfSightBtn);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ public class DevToolsPlugin extends Plugin
|
||||
private LocationOverlay locationOverlay;
|
||||
|
||||
@Inject
|
||||
private BorderOverlay borderOverlay;
|
||||
private SceneOverlay sceneOverlay;
|
||||
|
||||
@Inject
|
||||
private EventBus eventBus;
|
||||
@@ -84,6 +84,8 @@ public class DevToolsPlugin extends Plugin
|
||||
private boolean toggleLocation;
|
||||
private boolean toggleChunkBorders;
|
||||
private boolean toggleMapSquares;
|
||||
private boolean toggleValidMovement;
|
||||
private boolean toggleLineOfSight;
|
||||
|
||||
Widget currentWidget;
|
||||
int itemIndex = -1;
|
||||
@@ -129,7 +131,7 @@ public class DevToolsPlugin extends Plugin
|
||||
@Override
|
||||
public Collection<Overlay> getOverlays()
|
||||
{
|
||||
return Arrays.asList(overlay, locationOverlay, borderOverlay);
|
||||
return Arrays.asList(overlay, locationOverlay, sceneOverlay);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@@ -223,6 +225,16 @@ public class DevToolsPlugin extends Plugin
|
||||
toggleMapSquares = !toggleMapSquares;
|
||||
}
|
||||
|
||||
void toggleValidMovement()
|
||||
{
|
||||
toggleValidMovement = !toggleValidMovement;
|
||||
}
|
||||
|
||||
void toggleLineOfSight()
|
||||
{
|
||||
toggleLineOfSight = !toggleLineOfSight;
|
||||
}
|
||||
|
||||
boolean isTogglePlayers()
|
||||
{
|
||||
return togglePlayers;
|
||||
@@ -282,4 +294,14 @@ public class DevToolsPlugin extends Plugin
|
||||
{
|
||||
return toggleMapSquares;
|
||||
}
|
||||
|
||||
boolean isToggleValidMovement()
|
||||
{
|
||||
return toggleValidMovement;
|
||||
}
|
||||
|
||||
boolean isToggleLineOfSight()
|
||||
{
|
||||
return toggleLineOfSight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,33 +28,44 @@ import java.awt.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Polygon;
|
||||
import java.awt.geom.GeneralPath;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Actor;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.Perspective;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.coords.WorldArea;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
|
||||
public class BorderOverlay extends Overlay
|
||||
public class SceneOverlay extends Overlay
|
||||
{
|
||||
private static final Color MAP_SQUARE_COLOR = Color.GREEN;
|
||||
private static final Color CHUNK_BORDER_COLOR = Color.BLUE;
|
||||
private static final Color LOCAL_VALID_MOVEMENT_COLOR = new Color(141, 220, 26);
|
||||
private static final Color VALID_MOVEMENT_COLOR = new Color(73, 122, 18);
|
||||
private static final Color LINE_OF_SIGHT_COLOR = new Color(204, 42, 219);
|
||||
|
||||
private static final int LOCAL_TILE_SIZE = Perspective.LOCAL_TILE_SIZE;
|
||||
private static final int CHUNK_SIZE = 8;
|
||||
private static final int MAP_SQUARE_SIZE = CHUNK_SIZE * CHUNK_SIZE; // 64
|
||||
private static final int CULL_RANGE = 16;
|
||||
private static final int CULL_CHUNK_BORDERS_RANGE = 16;
|
||||
private static final int STROKE_WIDTH = 4;
|
||||
private static final int CULL_LINE_OF_SIGHT_RANGE = 10;
|
||||
|
||||
private final Client client;
|
||||
private final DevToolsPlugin plugin;
|
||||
|
||||
@Inject
|
||||
public BorderOverlay(Client client, DevToolsPlugin plugin)
|
||||
public SceneOverlay(Client client, DevToolsPlugin plugin)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_SCENE);
|
||||
@@ -75,16 +86,26 @@ public class BorderOverlay extends Overlay
|
||||
renderMapSquares(graphics);
|
||||
}
|
||||
|
||||
if (plugin.isToggleLineOfSight())
|
||||
{
|
||||
renderLineOfSight(graphics);
|
||||
}
|
||||
|
||||
if (plugin.isToggleValidMovement())
|
||||
{
|
||||
renderValidMovement(graphics);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void renderChunkBorders(Graphics2D graphics)
|
||||
{
|
||||
WorldPoint wp = client.getLocalPlayer().getWorldLocation();
|
||||
int startX = (wp.getX() - CULL_RANGE + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
|
||||
int startY = (wp.getY() - CULL_RANGE + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
|
||||
int endX = (wp.getX() + CULL_RANGE) / CHUNK_SIZE * CHUNK_SIZE;
|
||||
int endY = (wp.getY() + CULL_RANGE) / CHUNK_SIZE * CHUNK_SIZE;
|
||||
int startX = (wp.getX() - CULL_CHUNK_BORDERS_RANGE + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
|
||||
int startY = (wp.getY() - CULL_CHUNK_BORDERS_RANGE + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
|
||||
int endX = (wp.getX() + CULL_CHUNK_BORDERS_RANGE) / CHUNK_SIZE * CHUNK_SIZE;
|
||||
int endY = (wp.getY() + CULL_CHUNK_BORDERS_RANGE) / CHUNK_SIZE * CHUNK_SIZE;
|
||||
|
||||
graphics.setStroke(new BasicStroke(STROKE_WIDTH));
|
||||
graphics.setColor(CHUNK_BORDER_COLOR);
|
||||
@@ -92,8 +113,8 @@ public class BorderOverlay extends Overlay
|
||||
GeneralPath path = new GeneralPath();
|
||||
for (int x = startX; x <= endX; x += CHUNK_SIZE)
|
||||
{
|
||||
LocalPoint lp1 = LocalPoint.fromWorld(client, x, wp.getY() - CULL_RANGE);
|
||||
LocalPoint lp2 = LocalPoint.fromWorld(client, x, wp.getY() + CULL_RANGE);
|
||||
LocalPoint lp1 = LocalPoint.fromWorld(client, x, wp.getY() - CULL_CHUNK_BORDERS_RANGE);
|
||||
LocalPoint lp2 = LocalPoint.fromWorld(client, x, wp.getY() + CULL_CHUNK_BORDERS_RANGE);
|
||||
|
||||
boolean first = true;
|
||||
for (int y = lp1.getY(); y <= lp2.getY(); y += LOCAL_TILE_SIZE)
|
||||
@@ -118,8 +139,8 @@ public class BorderOverlay extends Overlay
|
||||
}
|
||||
for (int y = startY; y <= endY; y += CHUNK_SIZE)
|
||||
{
|
||||
LocalPoint lp1 = LocalPoint.fromWorld(client, wp.getX() - CULL_RANGE, y);
|
||||
LocalPoint lp2 = LocalPoint.fromWorld(client, wp.getX() + CULL_RANGE, y);
|
||||
LocalPoint lp1 = LocalPoint.fromWorld(client, wp.getX() - CULL_CHUNK_BORDERS_RANGE, y);
|
||||
LocalPoint lp2 = LocalPoint.fromWorld(client, wp.getX() + CULL_CHUNK_BORDERS_RANGE, y);
|
||||
|
||||
boolean first = true;
|
||||
for (int x = lp1.getX(); x <= lp2.getX(); x += LOCAL_TILE_SIZE)
|
||||
@@ -148,10 +169,10 @@ public class BorderOverlay extends Overlay
|
||||
private void renderMapSquares(Graphics2D graphics)
|
||||
{
|
||||
WorldPoint wp = client.getLocalPlayer().getWorldLocation();
|
||||
int startX = (wp.getX() - CULL_RANGE + MAP_SQUARE_SIZE - 1) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE;
|
||||
int startY = (wp.getY() - CULL_RANGE + MAP_SQUARE_SIZE - 1) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE;
|
||||
int endX = (wp.getX() + CULL_RANGE) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE;
|
||||
int endY = (wp.getY() + CULL_RANGE) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE;
|
||||
int startX = (wp.getX() - CULL_CHUNK_BORDERS_RANGE + MAP_SQUARE_SIZE - 1) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE;
|
||||
int startY = (wp.getY() - CULL_CHUNK_BORDERS_RANGE + MAP_SQUARE_SIZE - 1) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE;
|
||||
int endX = (wp.getX() + CULL_CHUNK_BORDERS_RANGE) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE;
|
||||
int endY = (wp.getY() + CULL_CHUNK_BORDERS_RANGE) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE;
|
||||
|
||||
graphics.setStroke(new BasicStroke(STROKE_WIDTH));
|
||||
graphics.setColor(MAP_SQUARE_COLOR);
|
||||
@@ -159,8 +180,8 @@ public class BorderOverlay extends Overlay
|
||||
GeneralPath path = new GeneralPath();
|
||||
for (int x = startX; x <= endX; x += MAP_SQUARE_SIZE)
|
||||
{
|
||||
LocalPoint lp1 = LocalPoint.fromWorld(client, x, wp.getY() - CULL_RANGE);
|
||||
LocalPoint lp2 = LocalPoint.fromWorld(client, x, wp.getY() + CULL_RANGE);
|
||||
LocalPoint lp1 = LocalPoint.fromWorld(client, x, wp.getY() - CULL_CHUNK_BORDERS_RANGE);
|
||||
LocalPoint lp2 = LocalPoint.fromWorld(client, x, wp.getY() + CULL_CHUNK_BORDERS_RANGE);
|
||||
|
||||
boolean first = true;
|
||||
for (int y = lp1.getY(); y <= lp2.getY(); y += LOCAL_TILE_SIZE)
|
||||
@@ -185,8 +206,8 @@ public class BorderOverlay extends Overlay
|
||||
}
|
||||
for (int y = startY; y <= endY; y += MAP_SQUARE_SIZE)
|
||||
{
|
||||
LocalPoint lp1 = LocalPoint.fromWorld(client, wp.getX() - CULL_RANGE, y);
|
||||
LocalPoint lp2 = LocalPoint.fromWorld(client, wp.getX() + CULL_RANGE, y);
|
||||
LocalPoint lp1 = LocalPoint.fromWorld(client, wp.getX() - CULL_CHUNK_BORDERS_RANGE, y);
|
||||
LocalPoint lp2 = LocalPoint.fromWorld(client, wp.getX() + CULL_CHUNK_BORDERS_RANGE, y);
|
||||
|
||||
boolean first = true;
|
||||
for (int x = lp1.getX(); x <= lp2.getX(); x += LOCAL_TILE_SIZE)
|
||||
@@ -212,4 +233,125 @@ public class BorderOverlay extends Overlay
|
||||
graphics.draw(path);
|
||||
}
|
||||
|
||||
private void renderTileIfValidForMovement(Graphics2D graphics, Actor actor, int dx, int dy)
|
||||
{
|
||||
WorldArea area = actor.getWorldArea();
|
||||
if (area == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (area.canTravelInDirection(client, dx, dy))
|
||||
{
|
||||
LocalPoint lp = actor.getLocalLocation();
|
||||
if (lp == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lp = new LocalPoint(
|
||||
lp.getX() + dx * Perspective.LOCAL_TILE_SIZE + dx * Perspective.LOCAL_TILE_SIZE * (area.getWidth() - 1) / 2,
|
||||
lp.getY() + dy * Perspective.LOCAL_TILE_SIZE + dy * Perspective.LOCAL_TILE_SIZE * (area.getHeight() - 1) / 2);
|
||||
if (lp == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Polygon poly = Perspective.getCanvasTilePoly(client, lp);
|
||||
if (poly == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (actor == client.getLocalPlayer())
|
||||
{
|
||||
OverlayUtil.renderPolygon(graphics, poly, LOCAL_VALID_MOVEMENT_COLOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
OverlayUtil.renderPolygon(graphics, poly, VALID_MOVEMENT_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderValidMovement(Graphics2D graphics)
|
||||
{
|
||||
Player player = client.getLocalPlayer();
|
||||
List<NPC> npcs = client.getNpcs();
|
||||
for (NPC npc : npcs)
|
||||
{
|
||||
if (player.getInteracting() != npc && npc.getInteracting() != player)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (int dx = -1; dx <= 1; dx++)
|
||||
{
|
||||
for (int dy = -1; dy <= 1; dy++)
|
||||
{
|
||||
if (dx == 0 && dy == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
renderTileIfValidForMovement(graphics, npc, dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int dx = -1; dx <= 1; dx++)
|
||||
{
|
||||
for (int dy = -1; dy <= 1; dy++)
|
||||
{
|
||||
if (dx == 0 && dy == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
renderTileIfValidForMovement(graphics, player, dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderTileIfHasLineOfSight(Graphics2D graphics, WorldArea start, int targetX, int targetY)
|
||||
{
|
||||
WorldPoint targetLocation = new WorldPoint(targetX, targetY, start.getPlane());
|
||||
if (targetLocation == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Running the line of sight algorithm 100 times per frame doesn't
|
||||
// seem to use much CPU time, however rendering 100 tiles does
|
||||
if (start.hasLineOfSightTo(client, targetLocation))
|
||||
{
|
||||
LocalPoint lp = LocalPoint.fromWorld(client, targetLocation);
|
||||
if (lp == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Polygon poly = Perspective.getCanvasTilePoly(client, lp);
|
||||
if (poly == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OverlayUtil.renderPolygon(graphics, poly, LINE_OF_SIGHT_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderLineOfSight(Graphics2D graphics)
|
||||
{
|
||||
WorldArea area = client.getLocalPlayer().getWorldArea();
|
||||
for (int x = area.getX() - CULL_LINE_OF_SIGHT_RANGE; x <= area.getX() + CULL_LINE_OF_SIGHT_RANGE; x++)
|
||||
{
|
||||
for (int y = area.getY() - CULL_LINE_OF_SIGHT_RANGE; y <= area.getY() + CULL_LINE_OF_SIGHT_RANGE; y++)
|
||||
{
|
||||
if (x == area.getX() && y == area.getY())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
renderTileIfHasLineOfSight(graphics, area, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,11 +29,13 @@ import java.awt.Polygon;
|
||||
import java.awt.image.BufferedImage;
|
||||
import net.runelite.api.Actor;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.NPCComposition;
|
||||
import net.runelite.api.Perspective;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.SpritePixels;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.coords.WorldArea;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.events.AnimationChanged;
|
||||
import net.runelite.api.events.GraphicChanged;
|
||||
@@ -204,4 +206,25 @@ public abstract class RSActorMixin implements RSActor
|
||||
}
|
||||
return model.getConvexHull(getX(), getY(), getOrientation());
|
||||
}
|
||||
|
||||
@Inject
|
||||
@Override
|
||||
public WorldArea getWorldArea()
|
||||
{
|
||||
int size = 1;
|
||||
if (this instanceof NPC)
|
||||
{
|
||||
NPCComposition composition = ((NPC)this).getComposition();
|
||||
if (composition != null && composition.getConfigs() != null)
|
||||
{
|
||||
composition = composition.transform();
|
||||
}
|
||||
if (composition != null)
|
||||
{
|
||||
size = composition.getSize();
|
||||
}
|
||||
}
|
||||
|
||||
return new WorldArea(this.getWorldLocation(), size, size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,13 @@
|
||||
package net.runelite.mixins;
|
||||
|
||||
import net.runelite.api.Actor;
|
||||
import net.runelite.api.CollisionDataFlag;
|
||||
import net.runelite.api.DecorativeObject;
|
||||
import net.runelite.api.GameObject;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.GroundObject;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.Tile;
|
||||
import net.runelite.api.WallObject;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
@@ -52,6 +54,7 @@ import net.runelite.api.mixins.Mixin;
|
||||
import net.runelite.api.mixins.Shadow;
|
||||
import static net.runelite.client.callback.Hooks.eventBus;
|
||||
import net.runelite.rs.api.RSClient;
|
||||
import net.runelite.rs.api.RSCollisionData;
|
||||
import net.runelite.rs.api.RSGameObject;
|
||||
import net.runelite.rs.api.RSTile;
|
||||
|
||||
@@ -272,4 +275,115 @@ public abstract class RSTileMixin implements RSTile
|
||||
ItemLayerChanged itemLayerChanged = new ItemLayerChanged(this);
|
||||
eventBus.post(itemLayerChanged);
|
||||
}
|
||||
|
||||
@Inject
|
||||
@Override
|
||||
public boolean hasLineOfSightTo(Tile other)
|
||||
{
|
||||
// Thanks to Henke for this method :)
|
||||
|
||||
if (this.getPlane() != other.getPlane())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RSCollisionData[] collisionData = client.getCollisionMaps();
|
||||
int z = this.getPlane();
|
||||
int[][] collisionDataFlags = collisionData[z].getFlags();
|
||||
|
||||
Point p1 = this.getRegionLocation();
|
||||
Point p2 = other.getRegionLocation();
|
||||
if (p1.getX() == p2.getX() && p1.getY() == p2.getY())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int dx = p2.getX() - p1.getX();
|
||||
int dy = p2.getY() - p1.getY();
|
||||
int dxAbs = Math.abs(dx);
|
||||
int dyAbs = Math.abs(dy);
|
||||
|
||||
int xFlags = CollisionDataFlag.BLOCK_LINE_OF_SIGHT_FULL;
|
||||
int yFlags = CollisionDataFlag.BLOCK_LINE_OF_SIGHT_FULL;
|
||||
if (dx < 0)
|
||||
{
|
||||
xFlags |= CollisionDataFlag.BLOCK_LINE_OF_SIGHT_EAST;
|
||||
}
|
||||
else
|
||||
{
|
||||
xFlags |= CollisionDataFlag.BLOCK_LINE_OF_SIGHT_WEST;
|
||||
}
|
||||
if (dy < 0)
|
||||
{
|
||||
yFlags |= CollisionDataFlag.BLOCK_LINE_OF_SIGHT_NORTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
yFlags |= CollisionDataFlag.BLOCK_LINE_OF_SIGHT_SOUTH;
|
||||
}
|
||||
|
||||
if (dxAbs > dyAbs)
|
||||
{
|
||||
int x = p1.getX();
|
||||
int yBig = p1.getY() << 16; // The y position is represented as a bigger number to handle rounding
|
||||
int slope = (dy << 16) / dxAbs;
|
||||
yBig += 0x8000; // Add half of a tile
|
||||
if (dy < 0)
|
||||
{
|
||||
yBig--; // For correct rounding
|
||||
}
|
||||
int direction = dx < 0 ? -1 : 1;
|
||||
|
||||
while (x != p2.getX())
|
||||
{
|
||||
x += direction;
|
||||
int y = yBig >>> 16;
|
||||
if ((collisionDataFlags[x][y] & xFlags) != 0)
|
||||
{
|
||||
// Collision while traveling on the x axis
|
||||
return false;
|
||||
}
|
||||
yBig += slope;
|
||||
int nextY = yBig >>> 16;
|
||||
if (nextY != y && (collisionDataFlags[x][nextY] & yFlags) != 0)
|
||||
{
|
||||
// Collision while traveling on the y axis
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int y = p1.getY();
|
||||
int xBig = p1.getX() << 16; // The x position is represented as a bigger number to handle rounding
|
||||
int slope = (dx << 16) / dyAbs;
|
||||
xBig += 0x8000; // Add half of a tile
|
||||
if (dx < 0)
|
||||
{
|
||||
xBig--; // For correct rounding
|
||||
}
|
||||
int direction = dy < 0 ? -1 : 1;
|
||||
|
||||
while (y != p2.getY())
|
||||
{
|
||||
y += direction;
|
||||
int x = xBig >>> 16;
|
||||
if ((collisionDataFlags[x][y] & yFlags) != 0)
|
||||
{
|
||||
// Collision while traveling on the y axis
|
||||
return false;
|
||||
}
|
||||
xBig += slope;
|
||||
int nextX = xBig >>> 16;
|
||||
if (nextX != x && (collisionDataFlags[nextX][y] & xFlags) != 0)
|
||||
{
|
||||
// Collision while traveling on the x axis
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No collision
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,4 +92,13 @@ public interface RSActor extends RSRenderable, Actor
|
||||
|
||||
@Import("spotAnimFrameCycle")
|
||||
int getSpotAnimFrameCycle();
|
||||
|
||||
@Import("hitsplatValues")
|
||||
int[] getHitsplatValues();
|
||||
|
||||
@Import("hitsplatTypes")
|
||||
int[] getHitsplatTypes();
|
||||
|
||||
@Import("hitsplatCycles")
|
||||
int[] getHitsplatCycles();
|
||||
}
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
*/
|
||||
package net.runelite.rs.api;
|
||||
|
||||
import net.runelite.api.CollisionData;
|
||||
import net.runelite.mapping.Import;
|
||||
|
||||
public interface RSCollisionData
|
||||
public interface RSCollisionData extends CollisionData
|
||||
{
|
||||
@Import("flags")
|
||||
int[][] getFlags();
|
||||
|
||||
@@ -68,4 +68,12 @@ public interface RSNPCComposition extends NPCComposition
|
||||
@Import("transform")
|
||||
@Override
|
||||
RSNPCComposition transform();
|
||||
|
||||
@Import("size")
|
||||
@Override
|
||||
int getSize();
|
||||
|
||||
@Import("headIcon")
|
||||
@Override
|
||||
int getOverheadIcon();
|
||||
}
|
||||
|
||||
@@ -57,4 +57,8 @@ public interface RSPlayer extends RSActor, Player
|
||||
@Import("isFriend")
|
||||
@Override
|
||||
boolean isFriend();
|
||||
|
||||
@Import("overheadIcon")
|
||||
@Override
|
||||
int getOverheadIcon();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user