Slayer update (#161)

* Slayer update

Stripped model outliner
Slayer points varbit
Seperate superior color
Slayer estimate task completion
Slayer kc fix
NPCID vhere needed

* Tests

* Ignore AWT-EventQueue-0 in tests

* Fix componnent creation threading error
This commit is contained in:
sdburns1998
2019-05-02 00:00:42 +02:00
committed by Tyler Bochard
parent 92f677727d
commit fb307cf235
28 changed files with 4970 additions and 589 deletions

View File

@@ -433,6 +433,11 @@ public enum Varbits
*/
ACCOUNT_TYPE(1777),
/**
* Varbit used for Slayer reward points
*/
SLAYER_REWARD_POINTS(4068),
/**
* The varbit that stores the oxygen percentage for player
*/

View File

@@ -54,12 +54,14 @@ import net.runelite.client.game.ClanManager;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.LootManager;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.graphics.ModelOutlineRenderer;
import net.runelite.client.menus.MenuManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginInstantiationException;
import net.runelite.client.plugins.PluginManager;
import net.runelite.client.plugins.config.ConfigPanel;
import net.runelite.client.rs.ClientUpdateCheckMode;
import net.runelite.client.task.Scheduler;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.DrawManager;
import net.runelite.client.ui.RuneLiteSplashScreen;
@@ -166,6 +168,12 @@ public class RuneLite
@Nullable
private Client client;
@Inject
private Provider<ModelOutlineRenderer> modelOutlineRenderer;
@Inject
private Scheduler scheduler;
public static void main(String[] args) throws Exception
{
Locale.setDefault(Locale.ENGLISH);
@@ -337,6 +345,12 @@ public class RuneLite
// Start plugins
pluginManager.startCorePlugins();
// Register additional schedulers
if (this.client != null)
{
scheduler.registerObject(modelOutlineRenderer.get());
}
}
public void shutdown()

View File

@@ -0,0 +1,973 @@
/*
* 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.graphics;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import net.runelite.api.Client;
import net.runelite.api.MainBufferProvider;
import net.runelite.api.Model;
import net.runelite.api.NPC;
import net.runelite.api.NPCComposition;
import net.runelite.api.Perspective;
import net.runelite.api.Player;
import net.runelite.api.coords.LocalPoint;
import net.runelite.client.task.Schedule;
@Singleton
public class ModelOutlineRenderer
{
/*
* This class doesn't really "need" static variables, but they are
* static for performance reasons. Arrays are kept outside methods
* to avoid frequent big allocations. Arrays should mostly be seen
* as ArrayLists. The size of them is increased whenever they need
* to become bigger.
*/
private final Client client;
private boolean isReset;
private boolean usedSinceLastCheck;
// Dimensions of the underlying image
private int imageWidth;
private int imageHeight;
// Boundaries for the current rasterization
private int clipX1;
private int clipY1;
private int clipX2;
private int clipY2;
// Pixel points that would be rendered to
private int[] visited;
private int currentVisitedNumber = 0;
// Transformed vertex positions
private int[] projectedVerticesX;
private int[] projectedVerticesY;
private boolean[] projectedVerticesRenderable;
// An array of pixel points to raster onto the image. These are checked against
// clip boundaries and the visited array to prevent drawing on top of the model
// and outside the scene area. They are grouped per distance to the closest pixel
// drawn on the model.
private int[][] outlinePixels;
private int[] outlinePixelsLengths; // outlinePixelsLength[i] is the used length of outlinePixels[i]
private int outlineArrayWidth;
// A list of pixel distances ordered from shortest to longest distance for
// each outline width. These are calculated once upon first usage and then
// stored here to prevent reevaluation.
private List<List<PixelDistanceAlpha>> precomputedDistancePriorities;
@Inject
private ModelOutlineRenderer(Client client)
{
this.client = client;
reset();
}
@Schedule(period = 5, unit = ChronoUnit.SECONDS)
public void checkUsage()
{
if (!isReset && !usedSinceLastCheck)
{
// Reset memory allocated when the rasterizer becomes inactive
reset();
}
usedSinceLastCheck = false;
}
/**
* Reset memory used by the rasterizer
*/
private void reset()
{
visited = new int[0];
projectedVerticesX = new int[0];
projectedVerticesY = new int[0];
projectedVerticesRenderable = new boolean[0];
outlinePixels = new int[0][];
outlinePixelsLengths = new int[0];
precomputedDistancePriorities = new ArrayList<>(0);
isReset = true;
}
/**
* Calculate the next power of two of a value
*
* @param value The value to find the next power of two of
* @return Returns the next power of two
*/
private static int nextPowerOfTwo(int value)
{
value--;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
value++;
return value;
}
/**
* Determine if a triangle goes counter clockwise
*
* @return Returns true if the triangle goes counter clockwise and should be culled, otherwise false
*/
private static boolean cullFace(int x1, int y1, int x2, int y2, int x3, int y3)
{
return
(y2 - y1) * (x3 - x2) -
(x2 - x1) * (y3 - y2) < 0;
}
/**
* Gets the list of pixel distances ordered by distance from closest pixel for a specific outline width.
*
* @param outlineWidth The outline width
* @return Returns the list of pixel distances
*/
private List<PixelDistanceAlpha> getPriorityList(int outlineWidth)
{
while (precomputedDistancePriorities.size() <= outlineWidth)
{
precomputedDistancePriorities.add(null);
}
// Grab the cached outline width if we have one
if (precomputedDistancePriorities.get(outlineWidth) != null)
{
return precomputedDistancePriorities.get(outlineWidth);
}
List<PixelDistanceAlpha> ps = new ArrayList<>();
for (int x = 0; x <= outlineWidth; x++)
{
for (int y = 0; y <= outlineWidth; y++)
{
if (x == 0 && y == 0)
{
continue;
}
double dist = Math.sqrt(x * x + y * y);
if (dist > outlineWidth)
{
continue;
}
int outerAlpha = outlineWidth == 1 ? 255 // For preventing division by 0
: (int) (255 * (dist - 1) / (outlineWidth - 1));
ps.add(new PixelDistanceAlpha(outerAlpha, x + y * outlineArrayWidth));
}
}
ps.sort(Comparator.comparingDouble(PixelDistanceAlpha::getOuterAlpha));
precomputedDistancePriorities.set(outlineWidth, ps);
return ps;
}
/**
* Checks that the size of outlinePixels is big enough to hold a specific
* amount of elements. This is used to reduce the amount of if checks needed
* when adding elements to outlinePixels.
*
* @param distArrayPos The position in the array
* @param additionalMinimumSize The additional minimum size required
*/
private void ensureMinimumOutlineQueueSize(int distArrayPos, int additionalMinimumSize)
{
int minimumSize = outlinePixelsLengths[distArrayPos] + additionalMinimumSize;
while (outlinePixels[distArrayPos].length < minimumSize)
{
int[] newArr = new int[nextPowerOfTwo(minimumSize)];
System.arraycopy(outlinePixels[distArrayPos], 0, newArr, 0,
outlinePixels[distArrayPos].length);
outlinePixels[distArrayPos] = newArr;
}
}
/**
* Resets the visited flag for a specific amount of pixels
*
* @param pixelAmount The amount of pixels to reset
*/
private void resetVisited(int pixelAmount)
{
// The visited array is essentially a boolean array, but by
// making it an int array and checking if visited[i] == currentVisitedNumber
// and changing currentVisitedNumber for every new outline, we can essentially
// reset the whole array without having to iterate over every element
if (visited.length < pixelAmount)
{
visited = new int[nextPowerOfTwo(pixelAmount)];
currentVisitedNumber = 0;
}
currentVisitedNumber++;
}
/**
* Resets the pixels that are queued for outlining
*
* @param outlineWidth The width of the outline to reset pixels for
*/
private void resetOutline(int outlineWidth)
{
outlineArrayWidth = outlineWidth + 2;
int arraySizes = outlineArrayWidth * outlineArrayWidth;
if (outlinePixels.length < arraySizes)
{
outlinePixels = new int[arraySizes][];
outlinePixelsLengths = new int[arraySizes];
for (int i = 0; i < arraySizes; i++)
{
outlinePixels[i] = new int[4];
}
}
else
{
for (int i = 0; i < arraySizes; i++)
{
outlinePixelsLengths[i] = 0;
}
}
}
/**
* Simulates a horizontal line rasterization and adds the pixels to the left
* and to the right to the outline queue if they are within the clip area.
*
* @param pixelPos The pixel position in the line where x == 0
* @param x1 The starting x position
* @param x2 The ending x position
*/
private void simulateHorizontalLineRasterizationForOutline(
int pixelPos, int x1, int x2)
{
if (x2 > clipX2)
{
x2 = clipX2;
}
if (x1 < clipX1)
{
x1 = clipX1;
}
if (x1 >= x2)
{
return;
}
// Queue the pixel positions to the left and to the right of the line
ensureMinimumOutlineQueueSize(1, 2);
if (x2 < clipX2)
{
outlinePixels[1][outlinePixelsLengths[1]++] = pixelPos + x2;
}
if (x1 > clipX1)
{
outlinePixels[1][outlinePixelsLengths[1]++] = pixelPos + x1 - 1;
}
// Divide by 4 to account for loop unrolling
int xDist = x2 - x1 >> 2;
pixelPos += x1;
// This loop could run over 100m times per second without loop unrolling in some cases,
// so unrolling it can give a noticeable performance boost.
while (xDist-- > 0)
{
visited[pixelPos++] = currentVisitedNumber;
visited[pixelPos++] = currentVisitedNumber;
visited[pixelPos++] = currentVisitedNumber;
visited[pixelPos++] = currentVisitedNumber;
}
// Draw up to 3 more pixels if there were any left
xDist = (x2 - x1) & 3;
while (xDist-- > 0)
{
visited[pixelPos++] = currentVisitedNumber;
}
}
/**
* Queues the pixel positions above and below two horizontal lines, excluding those
* where the x positions of the lines intersect.
*
* @param pixelPos The pixel position at x == 0 of the second line
* @param x1 The starting x position of the first line
* @param x2 The ending x position of the first line
* @param x3 The starting x position of the second line
* @param x4 The ending x position of the second line
*/
private void outlineAroundHorizontalLine(
int pixelPos, int x1, int x2, int x3, int x4)
{
if (x1 < clipX1)
{
x1 = clipX1;
}
if (x2 < clipX1)
{
x2 = clipX1;
}
if (x3 < clipX1)
{
x3 = clipX1;
}
if (x4 < clipX1)
{
x4 = clipX1;
}
if (x1 > clipX2)
{
x1 = clipX2;
}
if (x2 > clipX2)
{
x2 = clipX2;
}
if (x3 > clipX2)
{
x3 = clipX2;
}
if (x4 > clipX2)
{
x4 = clipX2;
}
if (x1 < x3)
{
ensureMinimumOutlineQueueSize(outlineArrayWidth, x3 - x1);
for (int x = x1; x < x3; x++)
{
outlinePixels[outlineArrayWidth][outlinePixelsLengths[outlineArrayWidth]++] = pixelPos - imageWidth + x;
}
}
else
{
ensureMinimumOutlineQueueSize(outlineArrayWidth, x1 - x3);
for (int x = x3; x < x1; x++)
{
outlinePixels[outlineArrayWidth][outlinePixelsLengths[outlineArrayWidth]++] = pixelPos + x;
}
}
if (x2 < x4)
{
ensureMinimumOutlineQueueSize(outlineArrayWidth, x4 - x2);
for (int x = x2; x < x4; x++)
{
outlinePixels[outlineArrayWidth][outlinePixelsLengths[outlineArrayWidth]++] = pixelPos + x;
}
}
else
{
ensureMinimumOutlineQueueSize(outlineArrayWidth, x2 - x4);
for (int x = x4; x < x2; x++)
{
outlinePixels[outlineArrayWidth][outlinePixelsLengths[outlineArrayWidth]++] = pixelPos - imageWidth + x;
}
}
}
/**
* Simulates rasterization of a triangle and adds every pixel outside the triangle
* to the outline queue.
*
* @param x1 The x position of the first vertex in the triangle
* @param y1 The y position of the first vertex in the triangle
* @param x2 The x position of the second vertex in the triangle
* @param y2 The y position of the second vertex in the triangle
* @param x3 The x position of the third vertex in the triangle
* @param y3 The y position of the third vertex in the triangle
*/
private void simulateTriangleRasterizationForOutline(
int x1, int y1, int x2, int y2, int x3, int y3)
{
// Swap vertices so y1 <= y2 <= y3 using bubble sort
if (y1 > y2)
{
int yp = y1;
int xp = x1;
y1 = y2;
y2 = yp;
x1 = x2;
x2 = xp;
}
if (y2 > y3)
{
int yp = y2;
int xp = x2;
y2 = y3;
y3 = yp;
x2 = x3;
x3 = xp;
}
if (y1 > y2)
{
int yp = y1;
int xp = x1;
y1 = y2;
y2 = yp;
x1 = x2;
x2 = xp;
}
if (y1 > clipY2)
{
// All points are outside clip boundaries
return;
}
int slope1 = 0;
if (y1 != y2)
{
slope1 = (x2 - x1 << 14) / (y2 - y1);
}
int slope2 = 0;
if (y3 != y2)
{
slope2 = (x3 - x2 << 14) / (y3 - y2);
}
int slope3 = 0;
if (y1 != y3)
{
slope3 = (x1 - x3 << 14) / (y1 - y3);
}
if (y2 > clipY2)
{
y2 = clipY2;
}
if (y3 > clipY2)
{
y3 = clipY2;
}
if (y1 == y3 || y3 < 0)
{
return;
}
x1 <<= 14;
x2 <<= 14;
x3 = x1;
if (y1 < 0)
{
x3 -= y1 * slope3;
x1 -= y1 * slope1;
y1 = 0;
}
if (y2 < 0)
{
x2 -= slope2 * y2;
y2 = 0;
}
int pixelPos = y1 * imageWidth;
int currX1;
int currX2;
if (y1 != y2 && slope3 < slope1 || y1 == y2 && slope3 > slope2)
{
int height1 = y2 - y1;
int height2 = y3 - y2;
int prevX1;
int prevX2;
if (height1 <= 0)
{
prevX1 = x3 >> 14;
prevX2 = x2 >> 14;
}
else
{
prevX1 = x3 >> 14;
prevX2 = x1 >> 14;
}
outlineAroundHorizontalLine(pixelPos, prevX1, prevX2, prevX2, prevX2);
while (height1-- > 0)
{
currX1 = x3 >> 14;
currX2 = x1 >> 14;
outlineAroundHorizontalLine(pixelPos, currX1, currX2, prevX1, prevX2);
simulateHorizontalLineRasterizationForOutline(pixelPos, currX1, currX2);
x3 += slope3;
x1 += slope1;
pixelPos += imageWidth;
prevX1 = currX1;
prevX2 = currX2;
}
while (height2-- > 0)
{
currX1 = x3 >> 14;
currX2 = x2 >> 14;
outlineAroundHorizontalLine(pixelPos, currX1, currX2, prevX1, prevX2);
simulateHorizontalLineRasterizationForOutline(pixelPos, currX1, currX2);
x3 += slope3;
x2 += slope2;
pixelPos += imageWidth;
prevX1 = currX1;
prevX2 = currX2;
}
outlineAroundHorizontalLine(pixelPos, prevX1, prevX1, prevX1, prevX2);
}
else
{
int height1 = y2 - y1;
int height2 = y3 - y2;
int prevX1;
int prevX2;
if (height1 <= 0)
{
prevX1 = x2 >> 14;
prevX2 = x3 >> 14;
}
else
{
prevX1 = x1 >> 14;
prevX2 = x3 >> 14;
}
outlineAroundHorizontalLine(pixelPos, prevX1, prevX2, prevX2, prevX2);
while (height1-- > 0)
{
currX1 = x1 >> 14;
currX2 = x3 >> 14;
outlineAroundHorizontalLine(pixelPos, currX1, currX2, prevX1, prevX2);
simulateHorizontalLineRasterizationForOutline(pixelPos, currX1, currX2);
x1 += slope1;
x3 += slope3;
pixelPos += imageWidth;
prevX1 = currX1;
prevX2 = currX2;
}
while (height2-- > 0)
{
currX1 = x2 >> 14;
currX2 = x3 >> 14;
outlineAroundHorizontalLine(pixelPos, currX1, currX2, prevX1, prevX2);
simulateHorizontalLineRasterizationForOutline(pixelPos, currX1, currX2);
x3 += slope3;
x2 += slope2;
pixelPos += imageWidth;
prevX1 = currX1;
prevX2 = currX2;
}
outlineAroundHorizontalLine(pixelPos, prevX1, prevX1, prevX1, prevX2);
}
}
/**
* Translates the vertices 3D points to the screen canvas 2D points
*
* @param localX The local x position of the vertices
* @param localY The local y position of the vertices
* @param localZ The local z position of the vertices
* @param vertexOrientation The orientation of the vertices
* @return Returns true if any of them are inside the clip area, otherwise false
*/
private boolean projectVertices(Model model,
final int localX, final int localY, final int localZ, final int vertexOrientation)
{
final int cameraX = client.getCameraX();
final int cameraY = client.getCameraY();
final int cameraZ = client.getCameraZ();
final int cameraYaw = client.getCameraYaw();
final int cameraPitch = client.getCameraPitch();
final int scale = client.getScale();
final int orientationSin = Perspective.SINE[vertexOrientation];
final int orientationCos = Perspective.COSINE[vertexOrientation];
final int pitchSin = Perspective.SINE[cameraPitch];
final int pitchCos = Perspective.COSINE[cameraPitch];
final int yawSin = Perspective.SINE[cameraYaw];
final int yawCos = Perspective.COSINE[cameraYaw];
final int vertexCount = model.getVerticesCount();
final int[] verticesX = model.getVerticesX();
final int[] verticesY = model.getVerticesY();
final int[] verticesZ = model.getVerticesZ();
boolean anyVisible = false;
// Make sure the arrays are big enough
while (projectedVerticesX.length < vertexCount)
{
int newSize = nextPowerOfTwo(vertexCount);
projectedVerticesX = new int[newSize];
projectedVerticesY = new int[newSize];
projectedVerticesRenderable = new boolean[newSize];
}
for (int i = 0; i < vertexCount; i++)
{
int vx = verticesX[i];
int vy = verticesZ[i];
int vz = verticesY[i];
int vh; // Value holder
// Rotate based on orientation
vh = vx * orientationCos + vy * orientationSin >> 16;
vy = vy * orientationCos - vx * orientationSin >> 16;
vx = vh;
// Translate to local coords
vx += localX;
vy += localY;
vz += localZ;
// Translate to camera
vx -= cameraX;
vy -= cameraY;
vz -= cameraZ;
// Transform to canvas
vh = vx * yawCos + vy * yawSin >> 16;
vy = vy * yawCos - vx * yawSin >> 16;
vx = vh;
vh = vz * pitchCos - vy * pitchSin >> 16;
vz = vz * pitchSin + vy * pitchCos >> 16;
vy = vh;
if (vz >= 50)
{
projectedVerticesX[i] = (clipX1 + clipX2) / 2 + vx * scale / vz;
projectedVerticesY[i] = (clipY1 + clipY2) / 2 + vy * scale / vz;
projectedVerticesRenderable[i] = true;
anyVisible |=
projectedVerticesX[i] >= clipX1 && projectedVerticesX[i] < clipX2 &&
projectedVerticesY[i] >= clipY1 && projectedVerticesY[i] < clipY2;
}
else
{
projectedVerticesRenderable[i] = false;
}
}
return anyVisible;
}
/**
* Simulate rendering of the model and puts every pixel of the wireframe of
* the non-culled and non-transparent faces into the outline pixel queue.
*/
private void simulateModelRasterizationForOutline(Model model)
{
final int triangleCount = model.getTrianglesCount();
final int[] indices1 = model.getTrianglesX();
final int[] indices2 = model.getTrianglesY();
final int[] indices3 = model.getTrianglesZ();
final byte[] triangleTransparencies = model.getTriangleTransparencies();
for (int i = 0; i < triangleCount; i++)
{
if (projectedVerticesRenderable[indices1[i]] &&
projectedVerticesRenderable[indices2[i]] &&
projectedVerticesRenderable[indices3[i]] &&
// 254 and 255 counts as fully transparent
(triangleTransparencies == null || (triangleTransparencies[i] & 255) < 254))
{
final int index1 = indices1[i];
final int index2 = indices2[i];
final int index3 = indices3[i];
final int v1x = projectedVerticesX[index1];
final int v1y = projectedVerticesY[index1];
final int v2x = projectedVerticesX[index2];
final int v2y = projectedVerticesY[index2];
final int v3x = projectedVerticesX[index3];
final int v3y = projectedVerticesY[index3];
if (!cullFace(v1x, v1y, v2x, v2y, v3x, v3y))
{
simulateTriangleRasterizationForOutline(
v1x, v1y, v2x, v2y, v3x, v3y);
}
}
}
}
/**
* Draws an outline of the pixels in the outline queue to an image
*
* @param image The image to draw the outline to
* @param outlineWidth The width of the outline
* @param innerColor The color of the pixels of the outline closest to the model
* @param outerColor The color of the pixels of the outline furthest away from the model
*/
private void renderOutline(BufferedImage image, int outlineWidth,
Color innerColor, Color outerColor)
{
int[] imageData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
List<PixelDistanceAlpha> ps = getPriorityList(outlineWidth);
for (PixelDistanceAlpha p : ps)
{
int color;
int alpha;
if (outlineWidth == 1)
{
color =
((innerColor.getRed() + outerColor.getRed()) << 15) |
((innerColor.getGreen() + outerColor.getGreen() << 7)) |
((innerColor.getBlue() + outerColor.getBlue() >> 1));
alpha = (innerColor.getAlpha() + outerColor.getAlpha()) >> 1;
}
else
{
int outerAlpha = p.getOuterAlpha();
int innerAlpha = 255 - outerAlpha;
int innerAlphaFraction = (innerAlpha * innerColor.getAlpha()) / 255;
int outerAlphaFraction = (outerAlpha * outerColor.getAlpha()) / 255;
alpha = innerAlphaFraction + outerAlphaFraction;
if (alpha != 0)
{
color =
((innerColor.getRed() * innerAlphaFraction +
outerColor.getRed() * outerAlphaFraction) / alpha << 16) |
((innerColor.getGreen() * innerAlphaFraction +
outerColor.getGreen() * outerAlphaFraction) / alpha << 8) |
((innerColor.getBlue() * innerAlphaFraction +
outerColor.getBlue() * outerAlphaFraction) / alpha);
}
else
{
color = 0;
}
}
final int distArrayPos = p.getDistArrayPos();
final int nextDistArrayPosY = distArrayPos + outlineArrayWidth;
final int nextDistArrayPosX = distArrayPos + 1;
ensureMinimumOutlineQueueSize(nextDistArrayPosX, outlinePixelsLengths[distArrayPos] * 2);
ensureMinimumOutlineQueueSize(nextDistArrayPosY, outlinePixelsLengths[distArrayPos] * 2);
// The following 3 branches do the same thing, but when the requirements are simple,
// there are less checks needed which can give a performance boost.
if (alpha == 255)
{
if (outlineWidth == 1)
{
for (int i2 = 0; i2 < outlinePixelsLengths[distArrayPos]; i2++)
{
int pixelPos = outlinePixels[distArrayPos][i2];
int x = pixelPos % imageWidth;
int y = pixelPos / imageWidth;
if (x < clipX1 || x >= clipX2 ||
y < clipY1 || y >= clipY2 ||
visited[pixelPos] == currentVisitedNumber)
{
continue;
}
imageData[pixelPos] = color;
}
}
else
{
for (int i2 = 0; i2 < outlinePixelsLengths[distArrayPos]; i2++)
{
int pixelPos = outlinePixels[distArrayPos][i2];
int x = pixelPos % imageWidth;
int y = pixelPos / imageWidth;
if (x < clipX1 || x >= clipX2 ||
y < clipY1 || y >= clipY2 ||
visited[pixelPos] == currentVisitedNumber)
{
continue;
}
visited[pixelPos] = currentVisitedNumber;
imageData[pixelPos] = color;
if (pixelPos % imageWidth != 0)
{
outlinePixels[nextDistArrayPosX][outlinePixelsLengths[nextDistArrayPosX]++] = pixelPos - 1;
}
if ((pixelPos + 1) % imageWidth != 0)
{
outlinePixels[nextDistArrayPosX][outlinePixelsLengths[nextDistArrayPosX]++] = pixelPos + 1;
}
outlinePixels[nextDistArrayPosY][outlinePixelsLengths[nextDistArrayPosY]++] = pixelPos - imageWidth;
outlinePixels[nextDistArrayPosY][outlinePixelsLengths[nextDistArrayPosY]++] = pixelPos + imageWidth;
}
}
}
else
{
for (int i2 = 0; i2 < outlinePixelsLengths[distArrayPos]; i2++)
{
int pixelPos = outlinePixels[distArrayPos][i2];
int x = pixelPos % imageWidth;
int y = pixelPos / imageWidth;
if (x < clipX1 || x >= clipX2 ||
y < clipY1 || y >= clipY2 ||
visited[pixelPos] == currentVisitedNumber)
{
continue;
}
visited[pixelPos] = currentVisitedNumber;
imageData[pixelPos] =
((((color & 0xFF0000) * alpha + (imageData[pixelPos] & 0xFF0000) * (255 - alpha)) / 255) & 0xFF0000) +
((((color & 0xFF00) * alpha + (imageData[pixelPos] & 0xFF00) * (255 - alpha)) / 255) & 0xFF00) +
((((color & 0xFF) * alpha + (imageData[pixelPos] & 0xFF) * (255 - alpha)) / 255) & 0xFF);
if (pixelPos % imageWidth != 0)
{
outlinePixels[nextDistArrayPosX][outlinePixelsLengths[nextDistArrayPosX]++] = pixelPos - 1;
}
if ((pixelPos + 1) % imageWidth != 0)
{
outlinePixels[nextDistArrayPosX][outlinePixelsLengths[nextDistArrayPosX]++] = pixelPos + 1;
}
outlinePixels[nextDistArrayPosY][outlinePixelsLengths[nextDistArrayPosY]++] = pixelPos - imageWidth;
outlinePixels[nextDistArrayPosY][outlinePixelsLengths[nextDistArrayPosY]++] = pixelPos + imageWidth;
}
}
}
}
/**
* Draws an outline around a model to an image
*
* @param localX The local x position of the model
* @param localY The local y position of the model
* @param localZ The local z position of the model
* @param orientation The orientation of the model
* @param outlineWidth The width of the outline
* @param innerColor The color of the pixels of the outline closest to the model
* @param outerColor The color of the pixels of the outline furthest away from the model
*/
private void drawModelOutline(Model model,
int localX, int localY, int localZ, int orientation,
int outlineWidth, Color innerColor, Color outerColor)
{
if (outlineWidth <= 0)
{
return;
}
isReset = false;
usedSinceLastCheck = true;
MainBufferProvider bufferProvider = (MainBufferProvider) client.getBufferProvider();
BufferedImage image = (BufferedImage) bufferProvider.getImage();
clipX1 = client.getViewportXOffset();
clipY1 = client.getViewportYOffset();
clipX2 = client.getViewportWidth() + clipX1;
clipY2 = client.getViewportHeight() + clipY1;
imageWidth = image.getWidth();
imageHeight = image.getHeight();
final int pixelAmount = imageWidth * imageHeight;
resetVisited(pixelAmount);
resetOutline(outlineWidth);
if (!projectVertices(model,
localX, localY, localZ, orientation))
{
// No vertex of the model is visible on the screen, so we can
// assume there are no parts of the model to outline.
return;
}
simulateModelRasterizationForOutline(model);
renderOutline(image, outlineWidth, innerColor, outerColor);
}
public void drawOutline(NPC npc, int outlineWidth, Color color)
{
drawOutline(npc, outlineWidth, color, color);
}
public void drawOutline(NPC npc, int outlineWidth,
Color innerColor, Color outerColor)
{
int size = 1;
NPCComposition composition = npc.getTransformedComposition();
if (composition != null)
{
size = composition.getSize();
}
LocalPoint lp = npc.getLocalLocation();
if (lp != null)
{
// NPCs z position are calculated based on the tile height of the northeastern tile
final int northEastX = lp.getX() + Perspective.LOCAL_TILE_SIZE * (size - 1) / 2;
final int northEastY = lp.getY() + Perspective.LOCAL_TILE_SIZE * (size - 1) / 2;
final LocalPoint northEastLp = new LocalPoint(northEastX, northEastY);
drawModelOutline(npc.getModel(), lp.getX(), lp.getY(),
Perspective.getTileHeight(client, northEastLp, client.getPlane()),
npc.getOrientation(), outlineWidth, innerColor, outerColor);
}
}
public void drawOutline(Player player, int outlineWidth, Color color)
{
drawOutline(player, outlineWidth, color, color);
}
public void drawOutline(Player player, int outlineWidth,
Color innerColor, Color outerColor)
{
LocalPoint lp = player.getLocalLocation();
if (lp != null)
{
drawModelOutline(player.getModel(), lp.getX(), lp.getY(),
Perspective.getTileHeight(client, lp, client.getPlane()),
player.getOrientation(), outlineWidth, innerColor, outerColor);
}
}
}

View File

@@ -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.client.graphics;
import lombok.RequiredArgsConstructor;
import lombok.Value;
@Value
@RequiredArgsConstructor
class PixelDistanceAlpha
{
private final int outerAlpha;
private final int distArrayPos;
}

View File

@@ -40,7 +40,7 @@ public interface NpcIndicatorsConfig extends Config
)
default RenderStyle renderStyle()
{
return RenderStyle.HULL;
return RenderStyle.THIN_OUTLINE;
}
@ConfigItem(

View File

@@ -42,6 +42,7 @@ import net.runelite.api.Perspective;
import net.runelite.api.Point;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.graphics.ModelOutlineRenderer;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
@@ -50,6 +51,8 @@ import net.runelite.client.util.Text;
public class NpcSceneOverlay extends Overlay
{
private static final Color TRANSPARENT = new Color(0, 0, 0, 0);
// Anything but white text is quite hard to see since it is drawn on
// a dark background
private static final Color TEXT_COLOR = Color.WHITE;
@@ -67,13 +70,15 @@ public class NpcSceneOverlay extends Overlay
private final Client client;
private final NpcIndicatorsConfig config;
private final NpcIndicatorsPlugin plugin;
private final ModelOutlineRenderer modelOutliner;
@Inject
NpcSceneOverlay(Client client, NpcIndicatorsConfig config, NpcIndicatorsPlugin plugin)
NpcSceneOverlay(Client client, NpcIndicatorsConfig config, NpcIndicatorsPlugin plugin, ModelOutlineRenderer modelOutliner)
{
this.client = client;
this.config = config;
this.plugin = plugin;
this.modelOutliner = modelOutliner;
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
}
@@ -173,6 +178,22 @@ public class NpcSceneOverlay extends Overlay
renderPoly(graphics, color, objectClickbox);
break;
case THIN_OUTLINE:
modelOutliner.drawOutline(actor, 1, color);
break;
case OUTLINE:
modelOutliner.drawOutline(actor, 2, color);
break;
case THIN_GLOW:
modelOutliner.drawOutline(actor, 4, color, TRANSPARENT);
break;
case GLOW:
modelOutliner.drawOutline(actor, 8, color, TRANSPARENT);
break;
}
if (config.drawNames())

View File

@@ -29,7 +29,11 @@ public enum RenderStyle
OFF("Off"),
TILE("Tile"),
HULL("Hull"),
SOUTH_WEST_TILE("South West Tile");
SOUTH_WEST_TILE("South West Tile"),
THIN_OUTLINE("Thin outline"),
OUTLINE("Outline"),
THIN_GLOW("Thin glow"),
GLOW("Glow");
private final String name;

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2018, Davis Cook <daviscook447@gmail.com>
* 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.slayer;
import java.util.ArrayList;
import java.util.List;
public class KnapsackSolver
{
private List<Integer> reconstructItemsInSack(int [] [] sackMatrix, List<Integer> items, int i, int w)
{
if (i == 0)
{
return new ArrayList<>();
}
if (sackMatrix[i][w] > sackMatrix[i - 1][w])
{
List<Integer> list = reconstructItemsInSack(sackMatrix, items,
i - 1, w - items.get(i - 1));
list.add(items.get(i - 1));
return list;
}
else
{
return reconstructItemsInSack(sackMatrix, items, i - 1, w);
}
}
public int howMuchFitsInSack(List<Integer> items, int maxWeight)
{
int itemCount = items.size();
int[] [] sackMatrix = new int[itemCount + 1] [maxWeight + 1];
for (int i = 1; i <= itemCount; i++)
{
for (int j = 0; j <= maxWeight; j++)
{
if (items.get(i - 1) > j)
{
sackMatrix[i] [j] = sackMatrix[i - 1] [j];
}
else
{
sackMatrix[i] [j] = Math.max(
sackMatrix[i - 1] [j],
sackMatrix[i - 1] [j - items.get(i - 1)] + items.get(i - 1)
);
}
}
}
return reconstructItemsInSack(sackMatrix, items, itemCount, maxWeight).size();
}
}

View File

@@ -0,0 +1,47 @@
package net.runelite.client.plugins.slayer;
import lombok.Getter;
import net.runelite.api.NPC;
public class NPCPresence
{
private static final int FADE_TIMER_START = 20;
@Getter
private String name;
@Getter
private int combatLevel;
private int fadeTimer;
private NPCPresence(String name, int combatLevel)
{
this.name = name;
this.combatLevel = combatLevel;
this.fadeTimer = FADE_TIMER_START;
}
@Override
public String toString()
{
return name + "[" + combatLevel + "]";
}
public boolean shouldExist()
{
return fadeTimer > 0;
}
public void tickExistence()
{
fadeTimer--;
}
public static NPCPresence buildPresence(NPC npc)
{
return new NPCPresence(npc.getName(), npc.getCombatLevel());
}
}

View File

@@ -0,0 +1,26 @@
package net.runelite.client.plugins.slayer;
public enum RenderStyle
{
TILE("Tile"),
HULL("Hull"),
SOUTH_WEST_TILE("South West Tile"),
THIN_OUTLINE("Thin outline"),
OUTLINE("Outline"),
THIN_GLOW("Thin glow"),
GLOW("Glow"),
TRUE_LOCATIONS("True Location");
private final String name;
RenderStyle(String name)
{
this.name = name;
}
@Override
public String toString()
{
return name;
}
}

View File

@@ -90,7 +90,18 @@ public interface SlayerConfig extends Config
}
@ConfigItem(
position = 6,
position = 7,
keyName = "highlightStyle",
name = "Highlight Style",
description = "Highlight setting"
)
default RenderStyle renderStyle()
{
return RenderStyle.THIN_OUTLINE;
}
@ConfigItem(
position = 7,
keyName = "targetColor",
name = "Target Color",
description = "Color of the highlighted targets"
@@ -101,7 +112,7 @@ public interface SlayerConfig extends Config
}
@ConfigItem(
position = 7,
position = 8,
keyName = "superiorColor",
name = "Superior Color",
description = "Color of the highlighted superior slayer creatures"
@@ -112,7 +123,29 @@ public interface SlayerConfig extends Config
}
@ConfigItem(
position = 8,
position = 9,
keyName = "drawNames",
name = "Draw names above NPC",
description = "Configures whether or not NPC names should be drawn above the NPC"
)
default boolean drawNames()
{
return false;
}
@ConfigItem(
position = 10,
keyName = "drawMinimapNames",
name = "Draw names on minimap",
description = "Configures whether or not NPC names should be drawn on the minimap"
)
default boolean drawMinimapNames()
{
return false;
}
@ConfigItem(
position = 12,
keyName = "weaknessPrompt",
name = "Show Monster Weakness",
description = "Show an overlay on a monster when it is weak enough to finish off (Only Lizards, Gargoyles & Rockslugs)"
@@ -123,7 +156,7 @@ public interface SlayerConfig extends Config
}
@ConfigItem(
position = 8,
position = 13,
keyName = "taskCommand",
name = "Task Command",
description = "Configures whether the slayer task command is enabled<br> !task"
@@ -180,6 +213,7 @@ public interface SlayerConfig extends Config
{
return -1;
}
@ConfigItem(
keyName = "initialAmount",
name = "",
@@ -205,6 +239,24 @@ public interface SlayerConfig extends Config
)
void taskLocation(String key);
@ConfigItem(
keyName = "lastCertainAmount",
name = "",
description = "",
hidden = true
)
default int lastCertainAmount()
{
return -1;
}
@ConfigItem(
keyName = "lastCertainAmount",
name = "",
description = ""
)
void lastCertainAmount(int lastCertainAmount);
@ConfigItem(
keyName = "streak",
name = "",
@@ -222,58 +274,4 @@ public interface SlayerConfig extends Config
description = ""
)
void streak(int streak);
@ConfigItem(
keyName = "points",
name = "",
description = "",
hidden = true
)
default int points()
{
return -1;
}
@ConfigItem(
keyName = "points",
name = "",
description = ""
)
void points(int points);
@ConfigItem(
keyName = "expeditious",
name = "",
description = "",
hidden = true
)
default int expeditious()
{
return -1;
}
@ConfigItem(
keyName = "expeditious",
name = "",
description = ""
)
void expeditious(int expeditious);
@ConfigItem(
keyName = "slaughter",
name = "",
description = "",
hidden = true
)
default int slaughter()
{
return -1;
}
@ConfigItem(
keyName = "slaughter",
name = "",
description = ""
)
void slaughter(int slaughter);
}

View File

@@ -104,32 +104,23 @@ class SlayerOverlay extends WidgetItemOverlay
return;
}
int amount = plugin.getAmount();
if (amount <= 0)
if (plugin.getCurrentTask() == null)
{
return;
}
int slaughterCount = plugin.getSlaughterChargeCount();
int expeditiousCount = plugin.getExpeditiousChargeCount();
int amount = plugin.getCurrentTask().getAmount();
if (amount <= 0)
{
return;
}
graphics.setFont(FontManager.getRunescapeSmallFont());
final Rectangle bounds = itemWidget.getCanvasBounds();
final TextComponent textComponent = new TextComponent();
switch (itemId)
{
case ItemID.EXPEDITIOUS_BRACELET:
textComponent.setText(String.valueOf(expeditiousCount));
break;
case ItemID.BRACELET_OF_SLAUGHTER:
textComponent.setText(String.valueOf(slaughterCount));
break;
default:
textComponent.setText(String.valueOf(amount));
break;
}
textComponent.setText(String.valueOf(amount));
// Draw the counter in the bottom left for equipment, and top left for jewelry
textComponent.setPosition(new Point(bounds.x, bounds.y + (SLAYER_JEWELRY.contains(itemId)

View File

@@ -0,0 +1,368 @@
package net.runelite.client.plugins.slayer;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.PluginErrorPanel;
import net.runelite.client.util.ColorUtil;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.StackFormatter;
public class SlayerTaskPanel extends PluginPanel
{
private static final long MILLIS_PER_SECOND = 1000;
private static final long SECONDS_PER_MINUTE = 60;
private static final long MINUTES_PER_HOUR = 60;
// Templates
private static final String HTML_LABEL_TEMPLATE =
"<html><body style='color:%s'>%s<span style='color:white'>%s</span></body></html>";
private static final String HTML_TIME_LABEL_TEMPLATE =
"<html><body style='color:%s'>%s<span style='color:white'>%02d:%02d:%02d</span></body></html>";
private static final ImageIcon PAUSE, PAUSE_FADED, PAUSE_HOVER;
private static final ImageIcon PLAY, PLAY_FADED, PLAY_HOVER;
// TODO: set some kind of maximum for the amount of tasks to be tracked in a session
private static final int MAX_TASK_BOXES = 50;
// When there are no tasks, display this
private final PluginErrorPanel errorPanel = new PluginErrorPanel();
// Handle task boxes
private final JPanel tasksContainer = new JPanel();
// Handle overall slayer session data
private final JPanel overallPanel = new JPanel();
private final JLabel overallKillsLabel = new JLabel();
private final JLabel overallTimeLabel = new JLabel();
private final JLabel overallIcon = new JLabel();
// Actions
private final JPanel actionsContainer = new JPanel();
private final JLabel playBtn = new JLabel();
private final JLabel pauseBtn = new JLabel();
// Log tasks
private final List<TaskBox> tasks = new ArrayList<>();
private SlayerPlugin slayerPlugin;
static
{
final BufferedImage pauseImg = ImageUtil.getResourceStreamFromClass(SlayerPlugin.class, "pause_icon.png");
final BufferedImage playImg = ImageUtil.getResourceStreamFromClass(SlayerPlugin.class, "play_icon.png");
PAUSE = new ImageIcon(pauseImg);
PAUSE_FADED = new ImageIcon(ImageUtil.alphaOffset(pauseImg, -180));
PAUSE_HOVER = new ImageIcon(ImageUtil.alphaOffset(pauseImg, -220));
PLAY = new ImageIcon(playImg);
PLAY_FADED = new ImageIcon(ImageUtil.alphaOffset(playImg, -180));
PLAY_HOVER = new ImageIcon(ImageUtil.alphaOffset(playImg, -220));
}
public SlayerTaskPanel(SlayerPlugin slayerPlugin)
{
this.slayerPlugin = slayerPlugin;
setBorder(new EmptyBorder(6, 6, 6, 6));
setBackground(ColorScheme.DARK_GRAY_COLOR);
setLayout(new BorderLayout());
// Create layout panel for wrapping
final JPanel layoutPanel = new JPanel();
layoutPanel.setLayout(new BoxLayout(layoutPanel, BoxLayout.Y_AXIS));
add(layoutPanel, BorderLayout.NORTH);
actionsContainer.setLayout(new BorderLayout());
actionsContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
actionsContainer.setPreferredSize(new Dimension(0, 30));
actionsContainer.setBorder(new EmptyBorder(5, 5, 5, 10));
actionsContainer.setVisible(false);
final JPanel controlsPanel = new JPanel(new GridLayout(1, 2, 10, 0));
controlsPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
playBtn.setIcon(PLAY);
playBtn.setToolTipText("Resume the current slayer task");
playBtn.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent mouseEvent)
{
slayerPlugin.setPaused(false);
changePauseState(false);
}
@Override
public void mouseExited(MouseEvent mouseEvent)
{
boolean paused = true;
TaskData currentTask = slayerPlugin.getCurrentTask();
if (currentTask != null)
{
paused = currentTask.isPaused();
}
playBtn.setIcon(paused ? PLAY_FADED : PLAY);
}
@Override
public void mouseEntered(MouseEvent mouseEvent)
{
boolean paused = true;
TaskData currentTask = slayerPlugin.getCurrentTask();
if (currentTask != null)
{
paused = currentTask.isPaused();
}
playBtn.setIcon(paused ? PLAY_HOVER : PLAY);
}
});
pauseBtn.setIcon(PAUSE);
pauseBtn.setToolTipText("Pause the current slayer task");
pauseBtn.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent mouseEvent)
{
slayerPlugin.setPaused(true);
changePauseState(true);
}
@Override
public void mouseExited(MouseEvent mouseEvent)
{
boolean paused = true;
TaskData currentTask = slayerPlugin.getCurrentTask();
if (currentTask != null)
{
paused = currentTask.isPaused();
}
pauseBtn.setIcon(paused ? PAUSE : PAUSE_FADED);
}
@Override
public void mouseEntered(MouseEvent mouseEvent)
{
boolean paused = true;
TaskData currentTask = slayerPlugin.getCurrentTask();
if (currentTask != null)
{
paused = currentTask.isPaused();
}
pauseBtn.setIcon(paused ? PAUSE : PAUSE_HOVER);
}
});
controlsPanel.add(playBtn);
controlsPanel.add(pauseBtn);
actionsContainer.add(controlsPanel, BorderLayout.EAST);
changePauseState(true);
if (slayerPlugin.getCurrentTask() != null)
{
changePauseState(slayerPlugin.getCurrentTask().isPaused());
}
// Create panel that will contain overall data
overallPanel.setBorder(new EmptyBorder(8, 10, 8, 10));
overallPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
overallPanel.setLayout(new BorderLayout());
overallPanel.setVisible(false);
// Add contents
final JPanel overallInfo = new JPanel();
overallInfo.setBackground(ColorScheme.DARKER_GRAY_COLOR);
overallInfo.setLayout(new GridLayout(2, 1));
overallInfo.setBorder(new EmptyBorder(2, 10, 2, 0));
overallKillsLabel.setFont(FontManager.getRunescapeSmallFont());
overallTimeLabel.setFont(FontManager.getRunescapeSmallFont());
overallInfo.add(overallKillsLabel);
overallInfo.add(overallTimeLabel);
overallPanel.add(overallIcon, BorderLayout.WEST);
overallPanel.add(overallInfo, BorderLayout.CENTER);
tasksContainer.setLayout(new BoxLayout(tasksContainer, BoxLayout.Y_AXIS));
layoutPanel.add(actionsContainer);
layoutPanel.add(Box.createRigidArea(new Dimension(0, 5)));
layoutPanel.add(overallPanel);
layoutPanel.add(tasksContainer);
// Add error pane
errorPanel.setContent("Task trackers", "You have not received any slayer tasks yet.");
add(errorPanel);
}
void loadHeaderIcon(BufferedImage img)
{
overallIcon.setIcon(new ImageIcon(img));
}
private void changePauseState(boolean paused)
{
playBtn.setIcon(paused ? PLAY_FADED : PLAY);
pauseBtn.setIcon(paused ? PAUSE : PAUSE_FADED);
}
private void updateOverall()
{
int overallKills = 0;
long overallTime = 0;
for (TaskBox box : tasks)
{
overallKills += box.getTaskData().getElapsedKills();
overallTime += box.getTaskData().getElapsedTime();
}
overallKillsLabel.setText(htmlLabel("Total kills: ", overallKills));
overallTimeLabel.setText(htmlLabel("Total time: ", overallTime));
}
private static boolean isEmptyTask(TaskData taskData)
{
return (taskData.getTaskName() == null || taskData.getTaskName().equals("")) && taskData.getAmount() == 0 && taskData.getInitialAmount() == 0;
}
private void showMainView()
{
remove(errorPanel);
actionsContainer.setVisible(true);
overallPanel.setVisible(true);
}
private TaskBox buildBox(SlayerPlugin plugin, JPanel container, TaskData data)
{
TaskBox newBox = new TaskBox(plugin, container, data.toBuilder().build());
tasks.add(0, newBox);
showMainView();
return newBox;
}
private boolean stringsEqualIncludeNull(String str0, String str1)
{
if (str0 == null && str1 == null)
{
return true; // both are null
}
else if (str0 == null || str1 == null)
{
return false; // only 1 is null
}
else
{
// none are null so equals check is safe
return str0.equals(str1);
}
}
void updateCurrentTask(boolean updated, boolean paused, TaskData newData, boolean isNewAssignment)
{
// important case for if the current task is completed so the update will show the empty task
if (isEmptyTask(newData))
{
if (tasks.isEmpty()) // if there is no current task an empty task doesn't do anything
{
return;
}
TaskBox current = tasks.get(0);
// current task has ended even though it should still have 1 amount remaining b/c the ending chat message
// pops before the slayer xp drop so we need to force the remaining kc to zero and add the last kc to
// the elapsed kc
if (current.getTaskData().getAmount() != 0) // must check not equal zero b/c otherwise this would constantly tick
{
int amountDelta = current.getTaskData().getAmount();
current.getTaskData().setAmount(0);
current.getTaskData().setElapsedKills(current.getTaskData().getElapsedKills() + amountDelta);
// current task has ended so it should be paused
current.update(true, true, current.getTaskData());
updateOverall();
}
return;
}
if (tasks.isEmpty() || isNewAssignment)
{
// new task so append it to the front of the list
SwingUtilities.invokeLater(() -> {
TaskBox newBox = buildBox(slayerPlugin, tasksContainer, newData);
newBox.update(true, newData.isPaused(), newData);
});
return;
}
else
{
// if here there is a current task so check if the current task matches
// the update being sent
TaskBox current = tasks.get(0);
if (!stringsEqualIncludeNull(current.getTaskData().getTaskName(), newData.getTaskName()) ||
!stringsEqualIncludeNull(current.getTaskData().getTaskLocation(), newData.getTaskLocation()) ||
current.getTaskData().getInitialAmount() != newData.getInitialAmount())
{
// current task does not match the update being sent so the current task
// must have been outdated - this is necessarily true because if a true
// new task was sent it would have set the isNewAssignment flag
// so this previous task is invalid so delete it then add in the new actually
// correct task
SwingUtilities.invokeLater(() -> {
tasksContainer.remove(tasks.get(0));
tasks.remove(0);
TaskBox newBox = buildBox(slayerPlugin, tasksContainer, newData);
newBox.update(true, newData.isPaused(), newData);
});
return;
}
}
// not an empty assignment or a new assignment so just update the current assignment
TaskBox current = tasks.get(0);
current.update(updated, paused, newData);
// update the overall stats once this task stats are updated
updateOverall();
changePauseState(paused);
}
static String htmlLabel(String key, long timeMillis)
{
if (timeMillis == Long.MAX_VALUE)
{
String valueStr = "N/A";
return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR),
key, valueStr);
}
else
{
long seconds = timeMillis / MILLIS_PER_SECOND;
long minutes = seconds / SECONDS_PER_MINUTE;
seconds %= 60;
long hours = minutes / MINUTES_PER_HOUR;
minutes %= 60;
return String.format(HTML_TIME_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR),
key, (int) hours, (int) minutes, (int) seconds);
}
}
static String htmlLabel(String key, int value)
{
String valueStr = StackFormatter.quantityToRSDecimalStack(value);
return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR),
key, valueStr);
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 2018, Davis Cook <daviscook447@gmail.com>
* 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.slayer;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
public class SlayerXpDropLookup
{
private Map<String, List<Double>> xpMap;
// floating point math equality
private static final double EPSILON = 1e-6;
void loadXpJson()
{
final InputStream xpFile = getClass().getResourceAsStream("/slayer_xp.json");
Gson gson = new Gson();
xpMap = gson.fromJson(new InputStreamReader(xpFile), new TypeToken<Map<String, List<Double>>>()
{
}.getType());
}
/**
* Finds the xp for a given npc using the xp + combat level data provided
* from the JSON - since scrapping from the wiki isn't perfectly accurate
* we make some estimations
*
* precondition is that xpCombatLevel array is non-null - if it is null
* we can simply return -1 to indicate no slayer xp because this npc
* has no associated xpCombatLevel array
*
* 1. first check to see if anywhere in the xp + combat level data this
* creature name give slayer xp - if it doesn't just return -1 and
* be done with this - if it does give slayer xp then continue
* 2. now check to see if we can find the xp for this combat level where
* that xp is greater than 0. note that we don't just find the xp for
* this combat level - this is because for some monsters the wiki
* only has slayer xp data for some combat levels and has it unknown
* for the other combat levels. this way we only return the combat level
* related xp data for a monster if it is know
* 3. finally if the slayer xp data for the monster was unknown for the given
* level we estimate the slayer xp by using one of the slayer xps for a level
* that does have xp given
* 4. note that if a monster gives no slayer xp for any level it will return
* -1 so we don't accidentally misscount non-slayer targets dying as giving
* slayer xp
*
* @param npc the npc we are estimating slayer xp for
* @return our best guess for the slayer xp for this npc
*/
public double findXpForNpc(NPCPresence npc)
{
List<Double> xpCombatLevel = xpMap.get(npc.getName());
if (xpCombatLevel == null)
{
return -1;
}
boolean givesSlayerXp = false;
for (int i = 0; i < xpCombatLevel.size() - 1; i += 2)
{
if (xpCombatLevel.get(i) > 0)
{
givesSlayerXp = true;
}
}
if (!givesSlayerXp)
{
return -1;
}
boolean foundCombatLevel = false;
for (int i = 0; i < xpCombatLevel.size() - 1; i += 2)
{
if (Math.abs(xpCombatLevel.get(i + 1) - npc.getCombatLevel()) < EPSILON
&& xpCombatLevel.get(i) > 0)
{
foundCombatLevel = true;
}
}
if (foundCombatLevel)
{
for (int i = 0; i < xpCombatLevel.size() - 1; i += 2)
{
if (Math.abs(xpCombatLevel.get(i + 1) - npc.getCombatLevel()) < EPSILON)
{
return xpCombatLevel.get(i);
}
}
}
else
{
for (int i = 0; i < xpCombatLevel.size() - 1; i += 2)
{
if (xpCombatLevel.get(i) > 0)
{
return xpCombatLevel.get(i);
}
}
}
return -1;
}
public SlayerXpDropLookup()
{
loadXpJson();
}
}

View File

@@ -33,21 +33,37 @@ import java.awt.Graphics2D;
import java.awt.Polygon;
import java.util.List;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.NPCComposition;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.graphics.ModelOutlineRenderer;
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;
import net.runelite.client.util.Text;
public class TargetClickboxOverlay extends Overlay
{
private static final Color TRANSPARENT = new Color(0, 0, 0, 0);
private final Client client;
private final SlayerConfig config;
private final SlayerPlugin plugin;
private final ModelOutlineRenderer modelOutliner;
@Inject
TargetClickboxOverlay(SlayerConfig config, SlayerPlugin plugin)
TargetClickboxOverlay(Client client, SlayerConfig config, SlayerPlugin plugin,
ModelOutlineRenderer modelOutlineRenderer)
{
this.client = client;
this.config = config;
this.plugin = plugin;
this.modelOutliner = modelOutlineRenderer;
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
}
@@ -55,36 +71,104 @@ public class TargetClickboxOverlay extends Overlay
@Override
public Dimension render(Graphics2D graphics)
{
if (!config.highlightTargets())
if (config.highlightTargets())
{
return null;
}
List<NPC> targets = plugin.getHighlightedTargets();
for (NPC target : targets)
{
Color coloration = config.getTargetColor();
if (plugin.isSuperior(target.getName()))
List<NPC> targets = plugin.getHighlightedTargets();
for (NPC target : targets)
{
coloration = config.getSuperiorColor();
}
Color coloration = config.getTargetColor();
if (plugin.isSuperior(target.getName()))
{
coloration = config.getSuperiorColor();
}
renderTargetOverlay(graphics, target, coloration);
renderNpcOverlay(graphics, target, coloration);
}
}
return null;
}
private void renderTargetOverlay(Graphics2D graphics, NPC actor, Color color)
private void renderNpcOverlay(Graphics2D graphics, NPC actor, Color color)
{
Polygon objectClickbox = actor.getConvexHull();
if (objectClickbox != null)
switch (config.renderStyle())
{
case SOUTH_WEST_TILE:
LocalPoint lp1 = LocalPoint.fromWorld(client, actor.getWorldLocation());
Polygon tilePoly1 = Perspective.getCanvasTilePoly(client, lp1);
renderPoly(graphics, color, tilePoly1);
break;
case TILE:
int size = 1;
NPCComposition composition = actor.getTransformedComposition();
if (composition != null)
{
size = composition.getSize();
}
LocalPoint lp = actor.getLocalLocation();
Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, lp, size);
renderPoly(graphics, color, tilePoly);
break;
case HULL:
Polygon objectClickbox = actor.getConvexHull();
renderPoly(graphics, color, objectClickbox);
break;
case THIN_OUTLINE:
modelOutliner.drawOutline(actor, 1, color);
break;
case OUTLINE:
modelOutliner.drawOutline(actor, 2, color);
break;
case THIN_GLOW:
modelOutliner.drawOutline(actor, 4, color, TRANSPARENT);
break;
case GLOW:
modelOutliner.drawOutline(actor, 8, color, TRANSPARENT);
break;
case TRUE_LOCATIONS:
size = 1;
composition = actor.getTransformedComposition();
if (composition != null)
{
size = composition.getSize();
}
WorldPoint wp = actor.getWorldLocation();
lp = LocalPoint.fromWorld(client, wp);
tilePoly = Perspective.getCanvasTileAreaPoly(client, lp, size);
renderPoly(graphics, color, tilePoly);
break;
}
if (config.drawNames())
{
String npcName = Text.removeTags(actor.getName());
Point textLocation = actor.getCanvasTextLocation(graphics, npcName, actor.getLogicalHeight() + 40);
if (textLocation != null)
{
OverlayUtil.renderTextLocation(graphics, textLocation, npcName, color);
}
}
}
private static void renderPoly(Graphics2D graphics, Color color, Polygon polygon)
{
if (polygon != null)
{
graphics.setColor(color);
graphics.setStroke(new BasicStroke(2));
graphics.draw(objectClickbox);
graphics.draw(polygon);
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 20));
graphics.fill(objectClickbox);
graphics.fill(polygon);
}
}
}

View File

@@ -31,6 +31,7 @@ import java.awt.Dimension;
import java.awt.Graphics2D;
import java.util.List;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.Point;
import net.runelite.client.ui.overlay.Overlay;
@@ -40,12 +41,15 @@ import net.runelite.client.ui.overlay.OverlayUtil;
public class TargetMinimapOverlay extends Overlay
{
private final Client client;
private final SlayerConfig config;
private final SlayerPlugin plugin;
@Inject
TargetMinimapOverlay(SlayerConfig config, SlayerPlugin plugin)
TargetMinimapOverlay(Client client, SlayerConfig config, SlayerPlugin plugin)
{
this.client = client;
this.config = config;
this.plugin = plugin;
setPosition(OverlayPosition.DYNAMIC);
@@ -63,18 +67,29 @@ public class TargetMinimapOverlay extends Overlay
List<NPC> targets = plugin.getHighlightedTargets();
for (NPC target : targets)
{
renderTargetOverlay(graphics, target, config.getTargetColor());
Color coloration = config.getTargetColor();
if (plugin.isSuperior(target.getName()))
{
coloration = config.getSuperiorColor();
}
renderTargetOverlay(graphics, target, target.getName(), coloration);
}
return null;
}
private void renderTargetOverlay(Graphics2D graphics, NPC actor, Color color)
private void renderTargetOverlay(Graphics2D graphics, NPC actor, String name, Color color)
{
Point minimapLocation = actor.getMinimapLocation();
if (minimapLocation != null)
{
OverlayUtil.renderMinimapLocation(graphics, minimapLocation, color);
if (config.drawMinimapNames())
{
OverlayUtil.renderTextLocation(graphics, minimapLocation, name, color);
}
}
}
}

View File

@@ -27,7 +27,6 @@ package net.runelite.client.plugins.slayer;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.List;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.NPC;
@@ -65,35 +64,26 @@ class TargetWeaknessOverlay extends Overlay
@Override
public Dimension render(Graphics2D graphics)
{
final List<NPC> targets = plugin.getHighlightedTargets();
if (targets.isEmpty() || !config.weaknessPrompt())
if (!config.weaknessPrompt())
{
return null;
}
final Task curTask = Task.getTask(plugin.getTaskName());
if (curTask == null || curTask.getWeaknessThreshold() < 0 || curTask.getWeaknessItem() < 0)
final Task npcTask = plugin.getWeaknessTask();
if (npcTask == null)
{
return null;
}
final int threshold = curTask.getWeaknessThreshold();
final BufferedImage image = itemManager.getImage(curTask.getWeaknessItem());
final NPC npc = (NPC) client.getLocalPlayer().getInteracting();
final int threshold = npcTask.getWeaknessThreshold();
final BufferedImage image = itemManager.getImage(npcTask.getWeaknessItem());
final int currentHealth = calculateHealth(npc);
if (image == null)
if (currentHealth >= 0 && currentHealth <= threshold)
{
return null;
}
for (NPC target : targets)
{
final int currentHealth = calculateHealth(target);
if (currentHealth >= 0 && currentHealth <= threshold)
{
renderTargetItem(graphics, target, image);
}
renderTargetItem(graphics, npc, image);
}
return null;

View File

@@ -26,131 +26,191 @@
package net.runelite.client.plugins.slayer;
import com.google.common.base.Preconditions;
import java.util.ArrayList;
import static java.util.Arrays.asList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.Getter;
import net.runelite.api.ItemID;
import net.runelite.api.NpcID;
@Getter
enum Task
{
/*
* format for enum is that the name of the task is first
* second is the item id image we use to represent the task graphically
* third is the list of names of monsters that can be killed on task (note that he task name is already handled
* so that is why for a task like ankou there is no need to include this list)
* fourth is the list of ids of monsters that can be killed on task (main reason for this is weird cases like baby dragons and the elf mourner)
*/
//<editor-fold desc="Enums">
ABERRANT_SPECTRES("Aberrant spectres", ItemID.ABERRANT_SPECTRE, "Spectre"),
ABYSSAL_DEMONS("Abyssal demons", ItemID.ABYSSAL_DEMON),
ABERRANT_SPECTRES("Aberrant spectres", ItemID.ABERRANT_SPECTRE,
asList("Abhorrent spectre", "Deviant spectre", "Repugnant spectre"), Collections.emptyList()),
ABYSSAL_DEMONS("Abyssal demons", ItemID.ABYSSAL_DEMON,
asList("Ayssal Sire"), Collections.emptyList()),
ABYSSAL_SIRE("Abyssal Sire", ItemID.ABYSSAL_ORPHAN),
ADAMANT_DRAGONS("Adamant dragons", ItemID.ADAMANT_DRAGON_MASK),
ALCHEMICAL_HYDRA("Alchemical Hydra", ItemID.IKKLE_HYDRA),
ANKOU("Ankou", ItemID.ANKOU_MASK),
AVIANSIES("Aviansies", ItemID.ENSOULED_AVIANSIE_HEAD),
AVIANSIES("Aviansies", ItemID.ENSOULED_AVIANSIE_HEAD,
asList("Kree'arra"), Collections.emptyList()),
BANSHEES("Banshees", ItemID.BANSHEE),
BARROWS_BROTHERS("Barrows Brothers", ItemID.KARILS_COIF),
BARROWS_BROTHERS("Barrows Brothers", ItemID.KARILS_COIF,
asList("Ahrim the blighted", "Dharok the wretched", "Guthan the infested", "Karil the tainted", "Torag the corrupted", "Verac the defiled"), Collections.emptyList()),
BASILISKS("Basilisks", ItemID.BASILISK),
BATS("Bats", ItemID.GIRAL_BAT_2),
BEARS("Bears", ItemID.ENSOULED_BEAR_HEAD),
ENTS("Ents", ItemID.NICE_TREE, "Ent"),
LAVA_DRAGONS("Lava Dragons", ItemID.LAVA_SCALE, "Lava dragon"),
BIRDS("Birds", ItemID.FEATHER, "Chicken", "Rooster", "Terrorbird", "Seagull"),
BLACK_DEMONS("Black demons", ItemID.BLACK_DEMON_MASK),
BLACK_DRAGONS("Black dragons", ItemID.BLACK_DRAGON_MASK),
BEARS("Bears", ItemID.ENSOULED_BEAR_HEAD,
asList("Callisto"), Collections.emptyList()),
ENTS("Ents", ItemID.ENTS_ROOTS),
LAVA_DRAGONS("Lava Dragons", ItemID.LAVA_SCALE),
BANDITS("Bandits", ItemID.BANDIT),
BIRDS("Birds", ItemID.FEATHER,
asList("Chicken", "Rooster", "Terrorbird", "Seagull", "Chompy bird", "Jubbly bird", "Oomlie bird", "Vulture"), Collections.emptyList()),
BLACK_DEMONS("Black demons", ItemID.BLACK_DEMON_MASK,
asList("Demonic gorilla", "Balfrug kreeyath", "Skotizo"), Collections.emptyList()),
BLACK_DRAGONS("Black dragons", ItemID.BLACK_DRAGON_MASK,
Collections.emptyList(), asList(NpcID.BABY_DRAGON_1871, NpcID.BABY_DRAGON_1872, NpcID.BABY_DRAGON_7955)),
BLOODVELD("Bloodveld", ItemID.BLOODVELD),
BLUE_DRAGONS("Blue dragons", ItemID.BLUE_DRAGON_MASK),
BLUE_DRAGONS("Blue dragons", ItemID.BLUE_DRAGON_MASK,
asList("Vorkath"), asList(NpcID.BABY_DRAGON, NpcID.BABY_DRAGON_242, NpcID.BABY_DRAGON_243)),
BRINE_RATS("Brine rats", ItemID.BRINE_RAT),
BRONZE_DRAGONS("Bronze dragons", ItemID.BRONZE_DRAGON_MASK),
CALLISTO("Callisto", ItemID.CALLISTO_CUB),
CATABLEPON("Catablepon", ItemID.LEFT_SKULL_HALF),
CAVE_BUGS("Cave bugs", ItemID.SWAMP_CAVE_BUG),
CAVE_CRAWLERS("Cave crawlers", ItemID.CAVE_CRAWLER, "Chasm crawler"),
CAVE_HORRORS("Cave horrors", ItemID.CAVE_HORROR, "Cave abomination"),
CAVE_KRAKEN("Cave kraken", ItemID.CAVE_KRAKEN),
CAVE_CRAWLERS("Cave crawlers", ItemID.CAVE_CRAWLER,
asList("Chasm crawler"), Collections.emptyList()),
CAVE_HORRORS("Cave horrors", ItemID.CAVE_HORROR,
asList("Cave abomination"), Collections.emptyList()),
CAVE_KRAKEN("Cave kraken", ItemID.CAVE_KRAKEN,
asList("Kraken"), Collections.emptyList()),
CAVE_SLIMES("Cave slimes", ItemID.SWAMP_CAVE_SLIME),
CERBERUS("Cerberus", ItemID.HELLPUPPY),
CHAOS_DRUIDS("Chaos druids", ItemID.ELDER_CHAOS_HOOD),
CHAOS_ELEMENTAL("Chaos Elemental", ItemID.PET_CHAOS_ELEMENTAL),
CHAOS_FANATIC("Chaos Fanatic", ItemID.ANCIENT_STAFF),
COCKATRICE("Cockatrice", ItemID.COCKATRICE, "Cockathrice"),
COCKATRICE("Cockatrice", ItemID.COCKATRICE,
asList("Cockathrice"), Collections.emptyList()),
COWS("Cows", ItemID.COW_MASK),
CRAWLING_HANDS("Crawling hands", ItemID.CRAWLING_HAND, "Crushing hand"),
CRAZY_ARCHAEOLOGIST("Crazy Archaeologists", ItemID.FEDORA, "Crazy Archaeologist"),
CRAWLING_HANDS("Crawling hands", ItemID.CRAWLING_HAND,
asList("Crushing hand"), Collections.emptyList()),
CRAZY_ARCHAEOLOGIST("Crazy Archaeologist", ItemID.FEDORA),
CROCODILES("Crocodiles", ItemID.SWAMP_LIZARD),
DAGANNOTH("Dagannoth", ItemID.DAGANNOTH),
DAGANNOTH_KINGS("Dagannoth Kings", ItemID.PET_DAGANNOTH_PRIME),
DARK_BEASTS("Dark beasts", ItemID.DARK_BEAST, "Night beast"),
DARK_WARRIORS("Dark warriors", ItemID.BLACK_MED_HELM, "Dark warrior"),
DAGANNOTH("Dagannoth", ItemID.DAGANNOTH,
asList("Dagannoth Rex", "Dagannoth Prime", "Dagannoth Supreme"), Collections.emptyList()),
DAGANNOTH_KINGS("Dagannoth Kings", ItemID.PET_DAGANNOTH_PRIME,
asList("Dagannoth Rex", "Dagannoth Prime", "Dagannoth Supreme"), Collections.emptyList()),
DARK_BEASTS("Dark beasts", ItemID.DARK_BEAST,
asList("Night beast"), Collections.emptyList()),
DARK_WARRIORS("Dark warriors", ItemID.BLACK_MED_HELM),
DERANGED_ARCHAEOLOGIST("Deranged Archaeologist", ItemID.ARCHAEOLOGISTS_DIARY),
DESERT_LIZARDS("Desert lizards", ItemID.DESERT_LIZARD, 4, ItemID.ICE_COOLER, "Small lizard", "Lizard"),
DOGS("Dogs", ItemID.GUARD_DOG, "Jackal"),
DESERT_LIZARDS("Desert lizards", ItemID.DESERT_LIZARD,
asList("Small lizard", "Lizard"), Collections.emptyList(), 4, ItemID.ICE_COOLER),
DOGS("Dogs", ItemID.GUARD_DOG, asList("Jackal"), Collections.emptyList()),
DRAKES("Drakes", ItemID.DRAKE),
DUST_DEVILS("Dust devils", ItemID.DUST_DEVIL, "Choke devil"),
DWARVES("Dwarves", ItemID.DWARVEN_HELMET, "Dwarf"),
DUST_DEVILS("Dust devils", ItemID.DUST_DEVIL,
asList("Choke devil"), Collections.emptyList()),
DWARVES("Dwarves", ItemID.DWARVEN_HELMET,
asList("Dwarf", "Black guard"), Collections.emptyList()),
EARTH_WARRIORS("Earth warriors", ItemID.BRONZE_FULL_HELM_T),
ELVES("Elves", ItemID.ELF, "Elf"),
ELVES("Elves", ItemID.ELF,
asList("Elf"), asList(NpcID.MOURNER_5311)),
FEVER_SPIDERS("Fever spiders", ItemID.FEVER_SPIDER),
FIRE_GIANTS("Fire giants", ItemID.FIRE_BATTLESTAFF),
REVENANTS("Revenants", ItemID.BRACELET_OF_ETHEREUM, "Revenant imp", "Revenant goblin", "Revenant pyrefiend", "Revenant hobgoblin", "Revenant cyclops", "Revenant hellhound", "Revenant demon", "Revenant ork", "Revenant dark beast", "Revenant knight", "Revenant dragon"),
FLESH_CRAWLERS("Fleshcrawlers", ItemID.ENSOULED_SCORPION_HEAD, "Flesh crawler"),
FOSSIL_ISLAND_WYVERNS("Fossil island wyverns", ItemID.FOSSIL_ISLAND_WYVERN, "Ancient wyvern", "Long-tailed wyvern", "Spitting wyvern", "Taloned wyvern"),
GARGOYLES("Gargoyles", ItemID.GARGOYLE, 9, ItemID.ROCK_HAMMER),
REVENANTS("Revenants", ItemID.REVENANT_ETHER,
asList("Revenant imp", "Revenant goblin", "Revenant pyrefiend", "Revenant hobgoblin", "Revenant cyclops", "Revenant hellhound", "Revenant demon", "Revenant ork", "Revenant dark beast", "Revenant knight", "Revenant dragon"), Collections.emptyList()),
FLESH_CRAWLERS("Flesh crawlers", ItemID.ENSOULED_SCORPION_HEAD),
FOSSIL_ISLAND_WYVERNS("Fossil island wyverns", ItemID.FOSSIL_ISLAND_WYVERN,
asList("Ancient wyvern", "Long-tailed wyvern", "Spitting wyvern", "Taloned wyvern"), Collections.emptyList()),
GARGOYLES("Gargoyles", ItemID.GARGOYLE,
asList("Dusk", "Dawn"), Collections.emptyList(), 9, ItemID.ROCK_HAMMER),
GENERAL_GRAARDOR("General Graardor", ItemID.PET_GENERAL_GRAARDOR),
GHOSTS("Ghosts", ItemID.GHOSTSPEAK_AMULET, "Tortured soul"),
GHOSTS("Ghosts", ItemID.GHOSTSPEAK_AMULET,
asList("Tortured soul"), Collections.emptyList()),
GIANT_MOLE("Giant Mole", ItemID.BABY_MOLE),
GHOULS("Ghouls", ItemID.ZOMBIE_HEAD),
GOBLINS("Goblins", ItemID.ENSOULED_GOBLIN_HEAD),
GREATER_DEMONS("Greater demons", ItemID.GREATER_DEMON_MASK),
GREEN_DRAGONS("Green dragons", ItemID.GREEN_DRAGON_MASK),
GROTESQUE_GUARDIANS("Grotesque Guardians", ItemID.MIDNIGHT, 0, ItemID.ROCK_HAMMER, "Dusk", "Dawn"),
GREATER_DEMONS("Greater demons", ItemID.GREATER_DEMON_MASK,
asList("K'ril Tsutsaroth", "Tstanon Karlak", "Skotizo"), Collections.emptyList()),
GREEN_DRAGONS("Green dragons", ItemID.GREEN_DRAGON_MASK,
Collections.emptyList(), asList(NpcID.BABY_DRAGON_5194, NpcID.BABY_DRAGON_5872, NpcID.BABY_DRAGON_5873)),
GROTESQUE_GUARDIANS("Grotesque Guardians", ItemID.MIDNIGHT,
asList("Dusk", "Dawn"), Collections.emptyList(), 0, ItemID.ROCK_HAMMER),
HARPIE_BUG_SWARMS("Harpie bug swarms", ItemID.SWARM),
HELLHOUNDS("Hellhounds", ItemID.HELLHOUND),
HILL_GIANTS("Hill giants", ItemID.ENSOULED_GIANT_HEAD),
HELLHOUNDS("Hellhounds", ItemID.HELLHOUND,
asList("Cerberus"), Collections.emptyList()),
HILL_GIANTS("Hill giants", ItemID.ENSOULED_GIANT_HEAD,
asList("Cyclops"), Collections.emptyList()),
HOBGOBLINS("Hobgoblins", ItemID.HOBGOBLIN_GUARD),
HYDRAS("Hydras", ItemID.HYDRA),
ICEFIENDS("Icefiends", ItemID.ICE_DIAMOND),
ICE_GIANTS("Ice giants", ItemID.ICE_DIAMOND),
ICE_WARRIORS("Ice warriors", ItemID.MITHRIL_FULL_HELM_T),
ICEFIENDS("Icefiends", ItemID.ICE_DIAMOND),
INFERNAL_MAGES("Infernal mages", ItemID.INFERNAL_MAGE, "Malevolent mage"),
INFERNAL_MAGES("Infernal mages", ItemID.INFERNAL_MAGE,
asList("Malevolent mage"), Collections.emptyList()),
IRON_DRAGONS("Iron dragons", ItemID.IRON_DRAGON_MASK),
JAD("TzTok-Jad", ItemID.TZREKJAD),
JELLIES("Jellies", ItemID.JELLY, "Jelly"),
JELLIES("Jellies", ItemID.JELLY,
asList("Jelly"), Collections.emptyList()),
JUNGLE_HORROR("Jungle horrors", ItemID.ENSOULED_HORROR_HEAD),
KALPHITE("Kalphite", ItemID.KALPHITE_SOLDIER),
MAMMOTHS("Mammoths", ItemID.ATTACKER_HORN, "Mammoth"),
KALPHITE_QUEEN("Kalphite Queen", ItemID.KALPHITE_PRINCESS),
KILLERWATTS("Killerwatts", ItemID.KILLERWATT),
KING_BLACK_DRAGON("King Black Dragon", ItemID.PRINCE_BLACK_DRAGON),
KRAKEN("Cave Kraken Boss", ItemID.PET_KRAKEN, "Kraken"),
KRAKEN("Cave Kraken Boss", ItemID.PET_KRAKEN,
asList("Kraken"), Collections.emptyList()),
KREEARRA("Kree'arra", ItemID.PET_KREEARRA),
KRIL_TSUTSAROTH("K'ril Tsutsaroth", ItemID.PET_KRIL_TSUTSAROTH),
KURASK("Kurask", ItemID.KURASK),
ROGUES("Rogues", ItemID.ROGUE_MASK, "Rogue"),
ROGUES("Rogues", ItemID.ROGUE_MASK, asList("Rogue"), Collections.emptyList()),
LESSER_DEMONS("Lesser demons", ItemID.LESSER_DEMON_MASK),
LIZARDMEN("Lizardmen", ItemID.LIZARDMAN_FANG, "Lizardman"),
MINIONS_OF_SCABARAS("Minions of scabaras", ItemID.GOLDEN_SCARAB, "Scarab swarm", "Locust rider", "Scarab mage"),
LIZARDMEN("Lizardmen", ItemID.LIZARDMAN_FANG,
asList("Lizardman"), Collections.emptyList()),
MAGIC_AXES("Magic axes", ItemID.IRON_BATTLEAXE),
MAMMOTHS("Mammoths", ItemID.ATTACKER_HORN,
asList("Mammoth"), Collections.emptyList()),
MINIONS_OF_SCABARAS("Minions of scabaras", ItemID.GOLDEN_SCARAB,
asList("Scarab swarm", "Locust rider", "Scarab mage"), Collections.emptyList()),
MINOTAURS("Minotaurs", ItemID.ENSOULED_MINOTAUR_HEAD),
MITHRIL_DRAGONS("Mithril dragons", ItemID.MITHRIL_DRAGON_MASK),
MOGRES("Mogres", ItemID.MOGRE),
MOLANISKS("Molanisks", ItemID.MOLANISK),
MONKEYS("Monkeys", ItemID.ENSOULED_MONKEY_HEAD),
MOSS_GIANTS("Moss giants", ItemID.HILL_GIANT_CLUB),
MUTATED_ZYGOMITES("Mutated zygomites", ItemID.MUTATED_ZYGOMITE, 7, ItemID.FUNGICIDE_SPRAY_0, "Zygomite", "Fungi"),
NECHRYAEL("Nechryael", ItemID.NECHRYAEL, "Nechryarch"),
OGRES("Ogres", ItemID.ENSOULED_OGRE_HEAD),
MOSS_GIANTS("Moss giants", ItemID.HILL_GIANT_CLUB,
asList("Bryophyta"), Collections.emptyList()),
MUTATED_ZYGOMITES("Mutated zygomites", ItemID.MUTATED_ZYGOMITE,
asList("Zygomite"), Collections.emptyList(), 7, ItemID.FUNGICIDE_SPRAY_0),
NECHRYAEL("Nechryael", ItemID.NECHRYAEL,
asList("Nechryarch"), Collections.emptyList()),
OGRES("Ogres", ItemID.ENSOULED_OGRE_HEAD,
asList("Enclave guard"), Collections.emptyList()),
OTHERWORLDLY_BEING("Otherworldly beings", ItemID.GHOSTLY_HOOD),
PYREFIENDS("Pyrefiends", ItemID.PYREFIEND, "Flaming pyrelord"),
PYREFIENDS("Pyrefiends", ItemID.PYREFIEND,
asList("Flaming pyrelord"), Collections.emptyList()),
RATS("Rats", ItemID.RATS_TAIL),
RED_DRAGONS("Red dragons", ItemID.BABY_RED_DRAGON),
RED_DRAGONS("Red dragons", ItemID.BABY_RED_DRAGON,
Collections.emptyList(), asList(NpcID.BABY_DRAGON_244, NpcID.BABY_DRAGON_245, NpcID.BABY_DRAGON_246)),
ROCKSLUGS("Rockslugs", ItemID.ROCKSLUG, 4, ItemID.BAG_OF_SALT),
RUNE_DRAGONS("Rune dragons", ItemID.RUNE_DRAGON_MASK),
SCORPIA("Scorpia", ItemID.SCORPIAS_OFFSPRING),
CHAOS_DRUIDS("Chaos druids", ItemID.ELDER_CHAOS_HOOD, "Elder Chaos druid", "Chaos druid"),
BANDITS("Bandits", ItemID.BANDIT, "Bandit"),
MAGIC_AXES("Magic axes", ItemID.IRON_BATTLEAXE, "Magic axe"),
SCORPIONS("Scorpions", ItemID.ENSOULED_SCORPION_HEAD),
SCORPIONS("Scorpions", ItemID.ENSOULED_SCORPION_HEAD,
asList("Scorpia"), Collections.emptyList()),
SEA_SNAKES("Sea snakes", ItemID.SNAKE_CORPSE),
SHADES("Shades", ItemID.SHADE_ROBE_TOP, "Loar Shadow", "Loar Shade", "Phrin Shadow", "Phrin Shade", "Riyl Shadow", "Riyl Shade", "Asyn Shadow", "Asyn Shade", "Fiyr Shadow", "Fiyr Shade"),
SHADES("Shades", ItemID.SHADE_ROBE_TOP,
asList("Loar Shadow", "Loar Shade", "Phrin Shadow", "Phrin Shade", "Riyl Shadow", "Riyl Shade", "Asyn Shadow", "Asyn Shade", "Fiyr Shadow", "Fiyr Shade"), Collections.emptyList()),
SHADOW_WARRIORS("Shadow warriors", ItemID.BLACK_FULL_HELM),
SKELETAL_WYVERNS("Skeletal wyverns", ItemID.SKELETAL_WYVERN),
SKELETONS("Skeletons", ItemID.SKELETON_GUARD),
SMOKE_DEVILS("Smoke devils", ItemID.SMOKE_DEVIL),
SPIDERS("Spiders", ItemID.HUGE_SPIDER),
SPIRITUAL_CREATURES("Spiritual creatures", ItemID.DRAGON_BOOTS, "Spiritual ranger", "Spiritual mage", "Spiritual warrior"),
SPIDERS("Spiders", ItemID.HUGE_SPIDER,
asList("Venenatis"), Collections.emptyList()),
SPIRITUAL_CREATURES("Spiritual creatures", ItemID.DRAGON_BOOTS,
asList("Spiritual ranger", "Spiritual mage", "Spiritual warrior"), Collections.emptyList()),
STEEL_DRAGONS("Steel dragons", ItemID.STEEL_DRAGON),
SULPHUR_LIZARDS("Sulphur Lizards", ItemID.SULPHUR_LIZARD),
SUQAHS("Suqahs", ItemID.SUQAH_TOOTH),
@@ -158,18 +218,23 @@ enum Task
THERMONUCLEAR_SMOKE_DEVIL("Thermonuclear Smoke Devil", ItemID.PET_SMOKE_DEVIL),
TROLLS("Trolls", ItemID.TROLL_GUARD),
TUROTH("Turoth", ItemID.TUROTH),
TZHAAR("Tzhaar", ItemID.ENSOULED_TZHAAR_HEAD),
VAMPIRES("Vampires", ItemID.STAKE),
TZHAAR("Tzhaar", ItemID.ENSOULED_TZHAAR_HEAD,
asList("Tz-"), Collections.emptyList(), false),
VAMPIRES("Vampires", ItemID.STAKE,
asList("Vampyre"), Collections.emptyList()),
VENENATIS("Venenatis", ItemID.VENENATIS_SPIDERLING),
VETION("Vet'ion", ItemID.VETION_JR),
VORKATH("Vorkath", ItemID.VORKI),
WALL_BEASTS("Wall beasts", ItemID.SWAMP_WALLBEAST),
WATERFIENDS("Waterfiends", ItemID.WATER_ORB),
WEREWOLVES("Werewolves", ItemID.WOLFBANE, "Werewolf"),
WOLVES("Wolves", ItemID.GREY_WOLF_FUR, "Wolf"),
WEREWOLVES("Werewolves", ItemID.WOLFBANE,
asList("Werewolf"), Collections.emptyList()),
WOLVES("Wolves", ItemID.GREY_WOLF_FUR,
asList("Wolf"), Collections.emptyList()),
WYRMS("Wyrms", ItemID.WYRM),
ZILYANA("Commander Zilyana", ItemID.PET_ZILYANA),
ZOMBIES("Zombies", ItemID.ZOMBIE_HEAD, "Undead"),
ZILYANA("Zilyana", ItemID.PET_ZILYANA),
ZOMBIES("Zombies", ItemID.ZOMBIE_HEAD,
asList("Undead"), Collections.emptyList()),
ZULRAH("Zulrah", ItemID.PET_SNAKELING),
ZUK("TzKal-Zuk", ItemID.TZREKZUK);
//</editor-fold>
@@ -178,7 +243,10 @@ enum Task
private final String name;
private final int itemSpriteId;
private final String[] targetNames;
private final List<String> targetNames;
private final List<Integer> npcIds;
private final boolean checkAsTokens;
private final int weaknessThreshold;
private final int weaknessItem;
@@ -190,7 +258,43 @@ enum Task
}
}
Task(String name, int itemSpriteId, String... targetNames)
Task(String name, int itemSpriteId)
{
Preconditions.checkArgument(itemSpriteId >= 0);
this.name = name;
this.itemSpriteId = itemSpriteId;
this.weaknessThreshold = -1;
this.weaknessItem = -1;
this.targetNames = new ArrayList<>();
this.npcIds = new ArrayList<>();
this.checkAsTokens = true;
}
Task(String name, int itemSpriteId, int weaknessThreshold, int weaknessItem)
{
Preconditions.checkArgument(itemSpriteId >= 0);
this.name = name;
this.itemSpriteId = itemSpriteId;
this.weaknessThreshold = weaknessThreshold;
this.weaknessItem = weaknessItem;
this.targetNames = new ArrayList<>();
this.npcIds = new ArrayList<>();
this.checkAsTokens = true;
}
Task(String name, int itemSpriteId, boolean checkAsTokens)
{
Preconditions.checkArgument(itemSpriteId >= 0);
this.name = name;
this.itemSpriteId = itemSpriteId;
this.weaknessThreshold = -1;
this.weaknessItem = -1;
this.targetNames = new ArrayList<>();
this.npcIds = new ArrayList<>();
this.checkAsTokens = checkAsTokens;
}
Task(String name, int itemSpriteId, List<String> targetNames, List<Integer> npcIds)
{
Preconditions.checkArgument(itemSpriteId >= 0);
this.name = name;
@@ -198,9 +302,11 @@ enum Task
this.weaknessThreshold = -1;
this.weaknessItem = -1;
this.targetNames = targetNames;
this.npcIds = npcIds;
this.checkAsTokens = true;
}
Task(String name, int itemSpriteId, int weaknessThreshold, int weaknessItem, String... targetNames)
Task(String name, int itemSpriteId, List<String> targetNames, List<Integer> npcIds, int weaknessThreshold, int weaknessItem)
{
Preconditions.checkArgument(itemSpriteId >= 0);
this.name = name;
@@ -208,6 +314,20 @@ enum Task
this.weaknessThreshold = weaknessThreshold;
this.weaknessItem = weaknessItem;
this.targetNames = targetNames;
this.npcIds = npcIds;
this.checkAsTokens = true;
}
Task(String name, int itemSpriteId, List<String> targetNames, List<Integer> npcIds, boolean checkAsTokens)
{
Preconditions.checkArgument(itemSpriteId >= 0);
this.name = name;
this.itemSpriteId = itemSpriteId;
this.weaknessThreshold = -1;
this.weaknessItem = -1;
this.targetNames = targetNames;
this.npcIds = npcIds;
this.checkAsTokens = checkAsTokens;
}
static Task getTask(String taskName)

View File

@@ -0,0 +1,304 @@
package net.runelite.client.plugins.slayer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.text.DecimalFormat;
import java.util.Collections;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.DynamicGridLayout;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.components.ProgressBar;
import net.runelite.client.util.StackFormatter;
class TaskBox extends JPanel
{
private static final long MILLIS_PER_SECOND = 1000;
private static final long SECONDS_PER_MINUTE = 60;
private static final long MINUTES_PER_HOUR = 60;
private static final long MILLIS_PER_HOUR = MILLIS_PER_SECOND * SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
private static final DecimalFormat TWO_DECIMAL_FORMAT = new DecimalFormat("0.00");
// Templates
private static final String HTML_TOOL_TIP_TEMPLATE =
"<html>%.1f Kills/hr<br/>" +
"%02d:%02d:%02d per kill</html>";
private static final String HTML_LABEL_TEMPLATE =
"<html><body style='color:white'>%s</body></html>";
private static final String HTML_TIME_LABEL_TEMPLATE =
"<html><body style='color:white'>%02d:%02d:%02d</span></body></html>";
// Instance members
private final JPanel panel;
@Getter(AccessLevel.PACKAGE)
private final TaskData taskData;
/* Contains the task icon and the stats panel */
private final JPanel headerPanel = new JPanel();
/* Contains the overall stats of the slayer task */
private final JPanel statsPanel = new JPanel();
private final ProgressBar progressBar = new ProgressBar();
private final JLabel elapsed = new JLabel("Elapsed:");
private final JLabel remaining = new JLabel("Remaining:");
private final JLabel duration = new JLabel("Time:");
private final JLabel currentDuration = new JLabel();
private final JLabel remainingDuration = new JLabel();
private final JLabel kills = new JLabel("Kills:");
private final JLabel currentKills = new JLabel();
private final JLabel remainingKills = new JLabel();
private final JLabel xp = new JLabel("XP:");
private final JLabel currentXp = new JLabel();
private final JLabel remainingXp = new JLabel();
private boolean paused = false;
TaskBox(SlayerPlugin slayerPlugin, JPanel panel, TaskData taskData)
{
this.panel = panel;
this.taskData = taskData;
setLayout(new BorderLayout());
setBorder(new EmptyBorder(5, 0, 0, 0));
/* This task's wrapping container */
JPanel container = new JPanel();
container.setLayout(new BorderLayout());
container.setBackground(ColorScheme.DARKER_GRAY_COLOR);
SwingUtilities.invokeLater(() -> {
BufferedImage taskImg = slayerPlugin.getImageForTask(Task.getTask(taskData.getTaskName()));
JLabel taskIcon = new JLabel(new ImageIcon(taskImg));
taskIcon.setHorizontalAlignment(SwingConstants.CENTER);
taskIcon.setVerticalAlignment(SwingConstants.CENTER);
taskIcon.setPreferredSize(new Dimension(35, 35));
statsPanel.setLayout(new DynamicGridLayout(4, 3));
statsPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
statsPanel.setBorder(new EmptyBorder(9, 2, 9, 2));
elapsed.setFont(FontManager.getRunescapeSmallFont());
remaining.setFont(FontManager.getRunescapeSmallFont());
duration.setFont(FontManager.getRunescapeSmallFont());
currentDuration.setFont(FontManager.getRunescapeSmallFont());
remainingDuration.setFont(FontManager.getRunescapeSmallFont());
kills.setFont(FontManager.getRunescapeSmallFont());
currentKills.setFont(FontManager.getRunescapeSmallFont());
remainingKills.setFont(FontManager.getRunescapeSmallFont());
xp.setFont(FontManager.getRunescapeSmallFont());
currentXp.setFont(FontManager.getRunescapeSmallFont());
remainingXp.setFont(FontManager.getRunescapeSmallFont());
statsPanel.add(new JLabel());
statsPanel.add(elapsed);
statsPanel.add(remaining);
statsPanel.add(duration);
statsPanel.add(currentDuration);
statsPanel.add(remainingDuration);
statsPanel.add(kills);
statsPanel.add(currentKills);
statsPanel.add(remainingKills);
statsPanel.add(xp);
statsPanel.add(currentXp);
statsPanel.add(remainingXp);
headerPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
headerPanel.setLayout(new BorderLayout());
headerPanel.add(statsPanel, BorderLayout.CENTER);
headerPanel.add(taskIcon, BorderLayout.WEST);
});
JPanel progressWrapper = new JPanel();
progressWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR);
progressWrapper.setLayout(new BorderLayout());
progressWrapper.setBorder(new EmptyBorder(0, 7, 7, 7));
progressBar.setMaximumValue(100);
progressBar.setBackground(new Color(61, 56, 49));
progressWrapper.add(progressBar, BorderLayout.NORTH);
final JPanel logTitle = new JPanel(new BorderLayout(5, 0));
logTitle.setBorder(new EmptyBorder(7, 7, 7, 7));
logTitle.setBackground(ColorScheme.DARKER_GRAY_COLOR.darker());
String taskName = taskData.getTaskName();
taskName = taskName.substring(0, 1).toUpperCase() + taskName.substring(1);
final JLabel titleLabel = new JLabel(taskName);
titleLabel.setFont(FontManager.getRunescapeSmallFont());
titleLabel.setForeground(Color.WHITE);
logTitle.add(titleLabel, BorderLayout.WEST);
final JLabel subTitleLabel = new JLabel("x " + taskData.getInitialAmount());
subTitleLabel.setFont(FontManager.getRunescapeSmallFont());
subTitleLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
logTitle.add(subTitleLabel, BorderLayout.CENTER);
if (taskData.getTaskLocation() != null && !taskData.getTaskLocation().equals(""))
{
final JLabel locationLabel = new JLabel(taskData.getTaskLocation());
locationLabel.setFont(FontManager.getRunescapeSmallFont());
locationLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
logTitle.add(locationLabel, BorderLayout.EAST);
}
container.add(logTitle, BorderLayout.NORTH);
container.add(headerPanel, BorderLayout.CENTER);
container.add(progressWrapper, BorderLayout.SOUTH);
add(container, BorderLayout.NORTH);
}
void update(boolean updated, boolean paused, TaskData newData)
{
SwingUtilities.invokeLater(() -> rebuildAsync(updated, paused, newData));
}
private void rebuildAsync(boolean updated, boolean taskPaused, TaskData newData)
{
if (updated)
{
if (getParent() != panel)
{
panel.add(this, 0);
panel.revalidate();
}
// Update data
taskData.setElapsedKills(newData.getElapsedKills());
taskData.setAmount(newData.getAmount());
taskData.setElapsedXp(newData.getElapsedXp());
// Update information labels
int kills = taskData.getInitialAmount() - taskData.getAmount();
currentKills.setText(htmlLabel(taskData.getElapsedKills()));
remainingKills.setText(htmlLabel(taskData.getAmount()));
currentXp.setText(htmlLabel(taskData.getElapsedXp()));
double xpPerKill = ((double) taskData.getElapsedXp()) / ((double) taskData.getElapsedKills());
double xpLeft = xpPerKill * taskData.getAmount();
remainingXp.setText(htmlLabel((int) xpLeft));
// Update progress bar
double percentComplete = ((double) kills) / ((double) taskData.getInitialAmount());
progressBar.setForeground(new Color(98, 70, 70));
progressBar.setValue((int) (percentComplete * 100));
progressBar.setCenterLabel(TWO_DECIMAL_FORMAT.format(percentComplete * 100) + "%");
progressBar.setLeftLabel("0 Kc");
progressBar.setRightLabel(taskData.getInitialAmount() + " Kc");
progressBar.setPositions(Collections.emptyList());
double killsPerMillis = ((double) taskData.getElapsedKills() - 1) / ((double) taskData.getElapsedTime());
if (killsPerMillis > 0)
{
double killsPerHour = killsPerMillis * MILLIS_PER_HOUR;
double millisPerKill = 1.0 / killsPerMillis;
long seconds = ((long) millisPerKill) / MILLIS_PER_SECOND;
long minutes = seconds / SECONDS_PER_MINUTE;
seconds %= 60;
long hours = minutes / MINUTES_PER_HOUR;
minutes %= 60;
progressBar.setToolTipText(String.format(
HTML_TOOL_TIP_TEMPLATE,
killsPerHour,
hours,
minutes,
seconds
));
}
if (taskData.getAmount() == 0 && taskData.getElapsedXp() == 0)
{
progressBar.setDimmedText("Skipped");
progressBar.setForeground(new Color(128, 0, 0));
}
else if ((taskData.getAmount() == 0 && taskData.getElapsedXp() > 0) || (int) (percentComplete * 100) >= 100)
{
progressBar.setDimmedText("Finished");
progressBar.setForeground(new Color(0, 128, 0));
}
else
{
progressBar.setDimmedText("Paused");
}
progressBar.setDimmed(taskPaused);
progressBar.repaint();
}
else if (!paused && taskPaused)
{
progressBar.setDimmedText("Paused");
progressBar.setDimmed(true);
progressBar.repaint();
paused = true;
}
else if (paused && !taskPaused)
{
progressBar.setDimmed(false);
progressBar.repaint();
paused = false;
}
// Update duration separately, every time (not only when there's an update)
taskData.setElapsedTime(newData.getElapsedTime());
currentDuration.setText(htmlLabel(taskData.getElapsedTime()));
remainingDuration.setText(htmlLabel(estimateRemainingTime(taskData)));
repaint();
}
private static long estimateRemainingTime(TaskData taskData)
{
int kills = taskData.getElapsedKills();
int killsInElapsedTime = kills - 1; // b/c time only elapses after 1st slayer drop
if (killsInElapsedTime < 1)
{
return Long.MAX_VALUE;
}
double timePerKill = ((double) taskData.getElapsedTime()) / ((double) killsInElapsedTime);
double remainingTime = timePerKill * taskData.getAmount();
return (long) remainingTime;
}
private static String htmlLabel(long timeMillis)
{
if (timeMillis == Long.MAX_VALUE)
{
String valueStr = "N/A";
return String.format(HTML_LABEL_TEMPLATE, valueStr);
}
else
{
long seconds = timeMillis / MILLIS_PER_SECOND;
long minutes = seconds / SECONDS_PER_MINUTE;
seconds %= 60;
long hours = minutes / MINUTES_PER_HOUR;
minutes %= 60;
return String.format(HTML_TIME_LABEL_TEMPLATE, (int) hours, (int) minutes, (int) seconds);
}
}
private static String htmlLabel(int value)
{
String valueStr = StackFormatter.quantityToRSDecimalStack(value);
return String.format(HTML_LABEL_TEMPLATE, valueStr);
}
}

View File

@@ -24,11 +24,10 @@
*/
package net.runelite.client.plugins.slayer;
import java.awt.image.BufferedImage;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.ui.overlay.infobox.Counter;
import java.awt.image.BufferedImage;
class TaskCounter extends Counter
{
TaskCounter(BufferedImage img, Plugin plugin, int amount)

View File

@@ -0,0 +1,27 @@
package net.runelite.client.plugins.slayer;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@Data
@AllArgsConstructor
@Builder(toBuilder=true)
public class TaskData
{
private long elapsedTime;
private int elapsedKills;
private int elapsedXp;
private int amount, initialAmount, lastCertainAmount;
private String taskLocation;
private String taskName;
private boolean paused;
public void tick(long delta)
{
if (!paused)
{
elapsedTime += delta;
}
}
}

View File

@@ -60,6 +60,33 @@ public class Scheduler
return Collections.unmodifiableList(scheduledMethods);
}
public void registerObject(Object obj)
{
for (Method method : obj.getClass().getMethods())
{
Schedule schedule = method.getAnnotation(Schedule.class);
if (schedule == null)
{
continue;
}
ScheduledMethod scheduledMethod = new ScheduledMethod(schedule, method, obj);
addScheduledMethod(scheduledMethod);
}
}
public void unregisterObject(Object obj)
{
for (ScheduledMethod sm : scheduledMethods)
{
if (sm.getObject() == obj)
{
removeScheduledMethod(sm);
break;
}
}
}
public void tick()
{
Instant now = Instant.now();

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

File diff suppressed because it is too large Load Diff

View File

@@ -35,9 +35,11 @@ import static net.runelite.api.ChatMessageType.GAMEMESSAGE;
import net.runelite.api.Client;
import net.runelite.api.MessageNode;
import net.runelite.api.Player;
import net.runelite.api.Varbits;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.VarbitChanged;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.Notifier;
@@ -73,7 +75,6 @@ public class SlayerPluginTest
private static final String TASK_CHECKSLAYERGEM = "You're assigned to kill Suqahs; only 211 more to go.";
private static final String TASK_CHECKSLAYERGEM_WILDERNESS = "You're assigned to kill Suqahs in the Wilderness; only 211 more to go.";
private static final String TASK_CHECKSLAYERGEM_KONAR = "You're assigned to kill Blue dragons in the Ogre Enclave; only 122 more to go.";
private static final String TASK_UPDATE_COMBAT_BRACELET = "You still need to kill 30 monsters to complete your current Slayer assignment";
private static final String TASK_BOSS_NEW = "Excellent. You're now assigned to kill Vet'ion 3 times.<br>Your reward point tally is 914.";
private static final String TASK_BOSS_NEW_THE = "Excellent. You're now assigned to kill the Chaos <br>Elemental 3 times. Your reward point tally is 914.";
@@ -92,24 +93,6 @@ public class SlayerPluginTest
private static final String SUPERIOR_MESSAGE = "A superior foe has appeared...";
private static final String BRACLET_SLAUGHTER = "Your bracelet of slaughter prevents your slayer count decreasing. It has 9 charges left.";
private static final String BRACLET_EXPEDITIOUS = "Your expeditious bracelet helps you progress your slayer task faster. It has 9 charges left.";
private static final String BRACLET_SLAUGHTER_V2 = "Your bracelet of slaughter prevents your slayer count decreasing. It has 1 charge left.";
private static final String BRACLET_EXPEDITIOUS_V2 = "Your expeditious bracelet helps you progress your slayer faster. It has 1 charge left.";
private static final String BRACLET_SLAUGHTER_V3 = "Your bracelet of slaughter prevents your slayer count decreasing. It then crumbles to dust.";
private static final String BRACLET_EXPEDITIOUS_V3 = "Your expeditious bracelet helps you progress your slayer faster. It then crumbles to dust.";
private static final String CHAT_BRACELET_SLAUGHTER_CHARGE = "Your bracelet of slaughter has 12 charges left.";
private static final String CHAT_BRACELET_EXPEDITIOUS_CHARGE = "Your expeditious bracelet has 12 charges left.";
private static final String CHAT_BRACELET_SLAUGHTER_CHARGE_ONE = "Your bracelet of slaughter has 1 charge left.";
private static final String CHAT_BRACELET_EXPEDITIOUS_CHARGE_ONE = "Your expeditious bracelet has 1 charge left.";
private static final String BREAK_SLAUGHTER = "The bracelet shatters. Your next bracelet of slaughter<br>will start afresh from 30 charges.";
private static final String BREAK_EXPEDITIOUS = "The bracelet shatters. Your next expeditious bracelet<br>will start afresh from 30 charges.";
@Mock
@Bind
Client client;
@@ -157,6 +140,10 @@ public class SlayerPluginTest
@Inject
SlayerPlugin slayerPlugin;
@Mock
@Bind
SlayerTaskPanel panel;
@Before
public void before()
{
@@ -171,8 +158,8 @@ public class SlayerPluginTest
when(client.getWidget(WidgetInfo.DIALOG_NPC_TEXT)).thenReturn(npcDialog);
slayerPlugin.onGameTick(new GameTick());
assertEquals("Suqahs", slayerPlugin.getTaskName());
assertEquals(231, slayerPlugin.getAmount());
assertEquals("Suqahs", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(231, slayerPlugin.getCurrentTask().getAmount());
}
@Test
@@ -183,9 +170,9 @@ public class SlayerPluginTest
when(client.getWidget(WidgetInfo.DIALOG_NPC_TEXT)).thenReturn(npcDialog);
slayerPlugin.onGameTick(new GameTick());
assertEquals("Wyrms", slayerPlugin.getTaskName());
assertEquals(147, slayerPlugin.getAmount());
assertEquals("Karuulm Slayer Dungeon", slayerPlugin.getTaskLocation());
assertEquals("Wyrms", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(147, slayerPlugin.getCurrentTask().getAmount());
assertEquals("Karuulm Slayer Dungeon", slayerPlugin.getCurrentTask().getTaskLocation());
}
@Test
@@ -196,9 +183,9 @@ public class SlayerPluginTest
when(client.getWidget(WidgetInfo.DIALOG_NPC_TEXT)).thenReturn(npcDialog);
slayerPlugin.onGameTick(new GameTick());
assertEquals("Hellhounds", slayerPlugin.getTaskName());
assertEquals(142, slayerPlugin.getAmount());
assertEquals("Witchhaven Dungeon", slayerPlugin.getTaskLocation());
assertEquals("Hellhounds", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(142, slayerPlugin.getCurrentTask().getAmount());
assertEquals("Witchhaven Dungeon", slayerPlugin.getCurrentTask().getTaskLocation());
}
@Test
@@ -209,8 +196,8 @@ public class SlayerPluginTest
when(client.getWidget(WidgetInfo.DIALOG_NPC_TEXT)).thenReturn(npcDialog);
slayerPlugin.onGameTick(new GameTick());
assertEquals("goblins", slayerPlugin.getTaskName());
assertEquals(17, slayerPlugin.getAmount());
assertEquals("goblins", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(17, slayerPlugin.getCurrentTask().getAmount());
}
@Test
@@ -221,8 +208,8 @@ public class SlayerPluginTest
when(client.getWidget(WidgetInfo.DIALOG_NPC_TEXT)).thenReturn(npcDialog);
slayerPlugin.onGameTick(new GameTick());
assertEquals("Suqahs", slayerPlugin.getTaskName());
assertEquals(211, slayerPlugin.getAmount());
assertEquals("Suqahs", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(211, slayerPlugin.getCurrentTask().getAmount());
}
@Test
@@ -233,8 +220,8 @@ public class SlayerPluginTest
when(client.getWidget(WidgetInfo.DIALOG_NPC_TEXT)).thenReturn(npcDialog);
slayerPlugin.onGameTick(new GameTick());
assertEquals("Vet'ion", slayerPlugin.getTaskName());
assertEquals(3, slayerPlugin.getAmount());
assertEquals("Vet'ion", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(3, slayerPlugin.getCurrentTask().getAmount());
assertEquals(914, slayerPlugin.getPoints());
}
@@ -246,8 +233,8 @@ public class SlayerPluginTest
when(client.getWidget(WidgetInfo.DIALOG_NPC_TEXT)).thenReturn(npcDialog);
slayerPlugin.onGameTick(new GameTick());
assertEquals("Chaos Elemental", slayerPlugin.getTaskName());
assertEquals(3, slayerPlugin.getAmount());
assertEquals("Chaos Elemental", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(3, slayerPlugin.getCurrentTask().getAmount());
assertEquals(914, slayerPlugin.getPoints());
}
@@ -257,8 +244,8 @@ public class SlayerPluginTest
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", TASK_NEW_FROM_PARTNER, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals("Dust Devils", slayerPlugin.getTaskName());
assertEquals(377, slayerPlugin.getAmount());
assertEquals("Dust Devils", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(377, slayerPlugin.getCurrentTask().getAmount());
}
@Test
@@ -266,8 +253,8 @@ public class SlayerPluginTest
{
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", TASK_CHECKSLAYERGEM, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals("Suqahs", slayerPlugin.getTaskName());
assertEquals(211, slayerPlugin.getAmount());
assertEquals("Suqahs", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(211, slayerPlugin.getCurrentTask().getAmount());
}
@Test
@@ -275,9 +262,9 @@ public class SlayerPluginTest
{
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", TASK_CHECKSLAYERGEM_WILDERNESS, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals("Suqahs", slayerPlugin.getTaskName());
assertEquals(211, slayerPlugin.getAmount());
assertEquals("Wilderness", slayerPlugin.getTaskLocation());
assertEquals("Suqahs", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(211, slayerPlugin.getCurrentTask().getAmount());
assertEquals("Wilderness", slayerPlugin.getCurrentTask().getTaskLocation());
}
@Test
@@ -286,9 +273,9 @@ public class SlayerPluginTest
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", TASK_CHECKSLAYERGEM_KONAR, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals("Blue dragons", slayerPlugin.getTaskName());
assertEquals(122, slayerPlugin.getAmount());
assertEquals("Ogre Enclave", slayerPlugin.getTaskLocation());
assertEquals("Blue dragons", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(122, slayerPlugin.getCurrentTask().getAmount());
assertEquals("Ogre Enclave", slayerPlugin.getCurrentTask().getTaskLocation());
}
@Test
@@ -299,23 +286,8 @@ public class SlayerPluginTest
when(client.getWidget(WidgetInfo.DIALOG_NPC_TEXT)).thenReturn(npcDialog);
slayerPlugin.onGameTick(new GameTick());
assertEquals("suqahs", slayerPlugin.getTaskName());
assertEquals(222, slayerPlugin.getAmount());
}
@Test
public void testRewardPointsWidget()
{
Widget rewardBar = mock(Widget.class);
Widget rewardBarText = mock(Widget.class);
Widget[] rewardBarChildren = new Widget[]{rewardBarText};
when(rewardBar.getDynamicChildren()).thenReturn(rewardBarChildren);
when(rewardBarText.getText()).thenReturn(REWARD_POINTS);
when(client.getWidget(WidgetInfo.SLAYER_REWARDS_TOPBAR)).thenReturn(rewardBar);
slayerPlugin.onGameTick(new GameTick());
assertEquals(17566, slayerPlugin.getPoints());
assertEquals("suqahs", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(222, slayerPlugin.getCurrentTask().getAmount());
}
@Test
@@ -325,8 +297,8 @@ public class SlayerPluginTest
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals(1, slayerPlugin.getStreak());
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
assertEquals("", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(0, slayerPlugin.getCurrentTask().getAmount());
}
@Test
@@ -336,58 +308,66 @@ public class SlayerPluginTest
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals(3, slayerPlugin.getStreak());
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
assertEquals("", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(0, slayerPlugin.getCurrentTask().getAmount());
}
@Test
public void testPoints()
{
when(client.getVar(Varbits.SLAYER_REWARD_POINTS)).thenReturn(18_000);
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_POINTS, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
VarbitChanged varbitChanged = new VarbitChanged();
slayerPlugin.onVarbitChanged(varbitChanged);
assertEquals(9, slayerPlugin.getStreak());
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
assertEquals("", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(0, slayerPlugin.getCurrentTask().getAmount());
assertEquals(18_000, slayerPlugin.getPoints());
}
@Test
public void testLargeStreak()
{
when(client.getVar(Varbits.SLAYER_REWARD_POINTS)).thenReturn(17_566_000);
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_LARGE_STREAK, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
VarbitChanged varbitChanged = new VarbitChanged();
slayerPlugin.onVarbitChanged(varbitChanged);
assertEquals(2465, slayerPlugin.getStreak());
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
assertEquals("", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(0, slayerPlugin.getCurrentTask().getAmount());
assertEquals(17_566_000, slayerPlugin.getPoints());
}
@Test
public void testComplete()
{
slayerPlugin.setTaskName("cows");
slayerPlugin.setAmount(42);
slayerPlugin.getCurrentTask().setTaskName("cows");
slayerPlugin.getCurrentTask().setAmount(42);
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_COMPLETE, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
assertEquals("", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(0, slayerPlugin.getCurrentTask().getAmount());
}
@Test
public void testCancelled()
{
slayerPlugin.setTaskName("cows");
slayerPlugin.setAmount(42);
slayerPlugin.getCurrentTask().setTaskName("cows");
slayerPlugin.getCurrentTask().setAmount(42);
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Perterter", TASK_CANCELED, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals("", slayerPlugin.getTaskName());
assertEquals(0, slayerPlugin.getAmount());
assertEquals("", slayerPlugin.getCurrentTask().getTaskName());
assertEquals(0, slayerPlugin.getCurrentTask().getAmount());
}
@Test
@@ -404,120 +384,6 @@ public class SlayerPluginTest
verifyNoMoreInteractions(notifier);
}
@Test
public void testBraceletSlaughter()
{
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", BRACLET_SLAUGHTER, null, 0);
slayerPlugin.setAmount(42);
slayerPlugin.setSlaughterChargeCount(10);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals(9, slayerPlugin.getSlaughterChargeCount());
assertEquals(43, slayerPlugin.getAmount());
chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", CHAT_BRACELET_SLAUGHTER_CHARGE, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals(12, slayerPlugin.getSlaughterChargeCount());
chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", CHAT_BRACELET_SLAUGHTER_CHARGE_ONE, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals(1, slayerPlugin.getSlaughterChargeCount());
slayerPlugin.setSlaughterChargeCount(1);
chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", BRACLET_SLAUGHTER_V3, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals(30, slayerPlugin.getSlaughterChargeCount());
Widget braceletBreakWidget = mock(Widget.class);
when(braceletBreakWidget.getText()).thenReturn(BREAK_SLAUGHTER);
when(client.getWidget(WidgetInfo.DIALOG_SPRITE_TEXT)).thenReturn(braceletBreakWidget);
slayerPlugin.setSlaughterChargeCount(-1);
slayerPlugin.onGameTick(new GameTick());
assertEquals(30, slayerPlugin.getSlaughterChargeCount());
chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", BRACLET_SLAUGHTER_V2, null, 0);
slayerPlugin.setAmount(42);
slayerPlugin.setSlaughterChargeCount(2);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals(1, slayerPlugin.getSlaughterChargeCount());
assertEquals(43, slayerPlugin.getAmount());
}
@Test
public void testBraceletExpeditious()
{
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", BRACLET_EXPEDITIOUS, null, 0);
slayerPlugin.setAmount(42);
slayerPlugin.setExpeditiousChargeCount(10);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals(41, slayerPlugin.getAmount());
assertEquals(9, slayerPlugin.getExpeditiousChargeCount());
chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", CHAT_BRACELET_EXPEDITIOUS_CHARGE, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals(12, slayerPlugin.getExpeditiousChargeCount());
chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", CHAT_BRACELET_EXPEDITIOUS_CHARGE_ONE, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals(1, slayerPlugin.getExpeditiousChargeCount());
slayerPlugin.setExpeditiousChargeCount(1);
chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", BRACLET_EXPEDITIOUS_V3, null, 0);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals(30, slayerPlugin.getExpeditiousChargeCount());
Widget braceletBreakWidget = mock(Widget.class);
when(braceletBreakWidget.getText()).thenReturn(BREAK_EXPEDITIOUS);
when(client.getWidget(WidgetInfo.DIALOG_SPRITE_TEXT)).thenReturn(braceletBreakWidget);
slayerPlugin.setExpeditiousChargeCount(-1);
slayerPlugin.onGameTick(new GameTick());
assertEquals(30, slayerPlugin.getExpeditiousChargeCount());
chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", BRACLET_EXPEDITIOUS_V2, null, 0);
slayerPlugin.setAmount(42);
slayerPlugin.setExpeditiousChargeCount(2);
slayerPlugin.onChatMessage(chatMessageEvent);
assertEquals(41, slayerPlugin.getAmount());
assertEquals(1, slayerPlugin.getExpeditiousChargeCount());
}
@Test
public void testCombatBraceletUpdate()
{
final Player player = mock(Player.class);
when(player.getLocalLocation()).thenReturn(new LocalPoint(0, 0));
when(client.getLocalPlayer()).thenReturn(player);
slayerPlugin.setTaskName("Suqahs");
slayerPlugin.setAmount(231);
ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", TASK_UPDATE_COMBAT_BRACELET, null, 0);
slayerPlugin.onChatMessage(chatMessage);
assertEquals("Suqahs", slayerPlugin.getTaskName());
slayerPlugin.killedOne();
assertEquals(30, slayerPlugin.getAmount());
}
@Test
public void testTaskLookup() throws IOException
{