From 84885f754688368b5b41988760564be526a3e10f Mon Sep 17 00:00:00 2001 From: Tomas Slusny Date: Tue, 21 Apr 2020 22:32:08 +0200 Subject: [PATCH 01/27] Add option to copy chat message contents to clipboard Signed-off-by: Tomas Slusny --- .../net/runelite/api/widgets/WidgetID.java | 2 + .../net/runelite/api/widgets/WidgetInfo.java | 2 + .../chathistory/ChatHistoryConfig.java | 11 +++ .../chathistory/ChatHistoryPlugin.java | 82 +++++++++++++++++++ 4 files changed, 97 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index 79a9ee8765..8b93e7a4dc 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -475,6 +475,8 @@ public class WidgetID static final int MESSAGES = 55; static final int TRANSPARENT_BACKGROUND_LINES = 56; static final int INPUT = 57; + static final int MESSAGE_LINES = 58; + static final int FIRST_MESSAGE = 59; } static class Prayer diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index b54e93c5e4..59cb4f4160 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -362,6 +362,8 @@ public enum WidgetInfo CHATBOX_INPUT(WidgetID.CHATBOX_GROUP_ID, WidgetID.Chatbox.INPUT), CHATBOX_TRANSPARENT_BACKGROUND(WidgetID.CHATBOX_GROUP_ID, WidgetID.Chatbox.TRANSPARENT_BACKGROUND), CHATBOX_TRANSPARENT_LINES(WidgetID.CHATBOX_GROUP_ID, WidgetID.Chatbox.TRANSPARENT_BACKGROUND_LINES), + CHATBOX_MESSAGE_LINES(WidgetID.CHATBOX_GROUP_ID, WidgetID.Chatbox.MESSAGE_LINES), + CHATBOX_FIRST_MESSAGE(WidgetID.CHATBOX_GROUP_ID, WidgetID.Chatbox.FIRST_MESSAGE), BA_HEAL_WAVE_TEXT(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.CURRENT_WAVE), BA_HEAL_CALL_TEXT(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.TO_CALL), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryConfig.java index e30629c742..cf558c3759 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryConfig.java @@ -52,4 +52,15 @@ public interface ChatHistoryConfig extends Config { return true; } + + @ConfigItem( + keyName = "copyToClipboard", + name = "Copy to clipboard", + description = "Add option on chat messages to copy them to clipboard", + position = 2 + ) + default boolean copyToClipboard() + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryPlugin.java index 77fbdd957f..bd0a95747d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chathistory/ChatHistoryPlugin.java @@ -24,8 +24,11 @@ */ package net.runelite.client.plugins.chathistory; +import com.google.common.base.Strings; import com.google.common.collect.EvictingQueue; import com.google.inject.Provides; +import java.awt.Toolkit; +import java.awt.datatransfer.StringSelection; import java.awt.event.KeyEvent; import java.util.ArrayDeque; import java.util.Deque; @@ -34,12 +37,19 @@ import java.util.Queue; import javax.inject.Inject; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; +import net.runelite.api.MenuAction; +import net.runelite.api.MenuEntry; import net.runelite.api.ScriptID; import net.runelite.api.VarClientInt; import net.runelite.api.VarClientStr; import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.MenuOpened; import net.runelite.api.events.MenuOptionClicked; import net.runelite.api.vars.InputType; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import static net.runelite.api.widgets.WidgetInfo.TO_CHILD; +import static net.runelite.api.widgets.WidgetInfo.TO_GROUP; import net.runelite.client.callback.ClientThread; import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.chat.QueuedMessage; @@ -50,6 +60,7 @@ import net.runelite.client.input.KeyManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.util.Text; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; @PluginDescriptor( @@ -62,12 +73,15 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener private static final String WELCOME_MESSAGE = "Welcome to Old School RuneScape"; private static final String CLEAR_HISTORY = "Clear history"; private static final String CLEAR_PRIVATE = "Private:"; + private static final String COPY_TO_CLIPBOARD = "Copy to clipboard"; private static final int CYCLE_HOTKEY = KeyEvent.VK_TAB; private static final int FRIENDS_MAX_SIZE = 5; private Queue messageQueue; private Deque friends; + private String currentMessage = null; + @Inject private Client client; @@ -104,6 +118,7 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener messageQueue = null; friends.clear(); friends = null; + currentMessage = null; keyManager.unregisterKeyListener(this); } @@ -167,6 +182,68 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener } } + @Subscribe + public void onMenuOpened(MenuOpened event) + { + if (event.getMenuEntries().length < 2 || !config.copyToClipboard()) + { + return; + } + + // Use second entry as first one can be walk here with transparent chatbox + final MenuEntry entry = event.getMenuEntries()[event.getMenuEntries().length - 2]; + + if (entry.getType() != MenuAction.CC_OP_LOW_PRIORITY.getId()) + { + return; + } + + final int groupId = TO_GROUP(entry.getParam1()); + final int childId = TO_CHILD(entry.getParam1()); + + if (groupId != WidgetInfo.CHATBOX.getGroupId()) + { + return; + } + + final Widget widget = client.getWidget(groupId, childId); + final Widget parent = widget.getParent(); + + if (WidgetInfo.CHATBOX_MESSAGE_LINES.getId() != parent.getId()) + { + return; + } + + // Get child id of first chat message static child so we can substract this offset to link to dynamic child + // later + final int first = WidgetInfo.CHATBOX_FIRST_MESSAGE.getChildId(); + + // Convert current message static widget id to dynamic widget id of message node with message contents + // When message is right clicked, we are actually right clicking static widget that contains only sender. + // The actual message contents are stored in dynamic widgets that follow same order as static widgets. + // Every first dynamic widget is message sender and every second one is message contents. + final int dynamicChildId = (childId - first) * 2 + 1; + + // Extract and store message contents when menu is opened because dynamic children can change while right click + // menu is open and dynamicChildId will be outdated + final Widget messageContents = parent.getChild(dynamicChildId); + if (messageContents == null) + { + return; + } + + currentMessage = messageContents.getText(); + + final MenuEntry menuEntry = new MenuEntry(); + menuEntry.setOption(COPY_TO_CLIPBOARD); + menuEntry.setTarget(entry.getTarget()); + menuEntry.setType(MenuAction.RUNELITE.getId()); + menuEntry.setParam0(entry.getParam0()); + menuEntry.setParam1(entry.getParam1()); + menuEntry.setIdentifier(entry.getIdentifier()); + client.setMenuEntries(ArrayUtils.insert(1, client.getMenuEntries(), menuEntry)); + } + @Subscribe public void onMenuOptionClicked(MenuOptionClicked event) { @@ -185,6 +262,11 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener messageQueue.removeIf(e -> e.getType() == ChatMessageType.PUBLICCHAT || e.getType() == ChatMessageType.MODCHAT); } } + else if (COPY_TO_CLIPBOARD.equals(menuOption) && !Strings.isNullOrEmpty(currentMessage)) + { + final StringSelection stringSelection = new StringSelection(Text.removeTags(currentMessage)); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(stringSelection, null); + } } /** From f95c5af4e4acb3dea94700bf5e305a5a22a07b1c Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 29 Apr 2020 18:20:15 -0400 Subject: [PATCH 02/27] client: update to jogl 2.4.0-rc-20200429 This also drops linux i586 support as I do not have any 32bit machines, and they are uncommon now anyway. --- runelite-client/pom.xml | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index d2108f4f81..578bd6e952 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -37,7 +37,7 @@ 4.1.0 - 2.3.2 + 2.4.0-rc-20200429 true @@ -92,71 +92,57 @@ 1.2 - org.jogamp.jogl + net.runelite.jogl jogl-all ${jogl.version} - org.jogamp.jogl + net.runelite.jogl jogl-all ${jogl.version} natives-windows-amd64 runtime - org.jogamp.jogl + net.runelite.jogl jogl-all ${jogl.version} natives-windows-i586 runtime - org.jogamp.jogl + net.runelite.jogl jogl-all ${jogl.version} natives-linux-amd64 runtime - org.jogamp.jogl - jogl-all - ${jogl.version} - natives-linux-i586 - runtime - - - org.jogamp.gluegen + net.runelite.gluegen gluegen-rt ${jogl.version} - org.jogamp.gluegen + net.runelite.gluegen gluegen-rt ${jogl.version} natives-windows-amd64 runtime - org.jogamp.gluegen + net.runelite.gluegen gluegen-rt ${jogl.version} natives-windows-i586 runtime - org.jogamp.gluegen + net.runelite.gluegen gluegen-rt ${jogl.version} natives-linux-amd64 runtime - - org.jogamp.gluegen - gluegen-rt - ${jogl.version} - natives-linux-i586 - runtime - net.runelite archive-patcher @@ -315,13 +301,13 @@ - org.jogamp.jogl:* + net.runelite.jogl:* ** - org.jogamp.gluegen:* + net.runelite.gluegen:* ** From 0e6f9f9aeca8ebb46b40780dbfeca24a5682f457 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 29 Apr 2020 18:24:57 -0400 Subject: [PATCH 03/27] gpu: add support for osx Compute shaders must be forced off due to macos not supporting opengl 4.3 --- runelite-client/pom.xml | 14 + .../client/plugins/gpu/GpuPlugin.java | 260 +++++++++++------- 2 files changed, 180 insertions(+), 94 deletions(-) diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 578bd6e952..527914a8b1 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -117,6 +117,13 @@ natives-linux-amd64 runtime + + net.runelite.jogl + jogl-all + ${jogl.version} + natives-macosx-universal + runtime + net.runelite.gluegen gluegen-rt @@ -143,6 +150,13 @@ natives-linux-amd64 runtime + + net.runelite.gluegen + gluegen-rt + ${jogl.version} + natives-macosx-universal + runtime + net.runelite archive-patcher diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index d7fc8f56d0..4e16ffd2e0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -35,6 +35,7 @@ import com.jogamp.opengl.GLContext; import com.jogamp.opengl.GLDrawable; import com.jogamp.opengl.GLDrawableFactory; import com.jogamp.opengl.GLException; +import com.jogamp.opengl.GLFBODrawable; import com.jogamp.opengl.GLProfile; import java.awt.Canvas; import java.awt.Dimension; @@ -51,6 +52,7 @@ import javax.inject.Inject; import javax.swing.SwingUtilities; import jogamp.nativewindow.SurfaceScaleUtils; import jogamp.nativewindow.jawt.x11.X11JAWTWindow; +import jogamp.nativewindow.macosx.OSXUtil; import jogamp.newt.awt.NewtFactoryAWT; import lombok.extern.slf4j.Slf4j; import net.runelite.api.BufferProvider; @@ -286,7 +288,8 @@ public class GpuPlugin extends Plugin implements DrawCallbacks return false; } - useComputeShaders = config.useComputeShaders(); + // OSX supports up to OpenGL 4.1, however 4.3 is required for compute shaders + useComputeShaders = config.useComputeShaders() && OSType.getOSType() != OSType.MacOS; canvas.setIgnoreRepaint(true); @@ -304,55 +307,73 @@ public class GpuPlugin extends Plugin implements DrawCallbacks GLProfile.initSingleton(); - GLProfile glProfile = GLProfile.get(GLProfile.GL4); - - GLCapabilities glCaps = new GLCapabilities(glProfile); - AWTGraphicsConfiguration config = AWTGraphicsConfiguration.create(canvas.getGraphicsConfiguration(), glCaps, glCaps); - - jawtWindow = NewtFactoryAWT.getNativeWindow(canvas, config); - canvas.setFocusable(true); - - GLDrawableFactory glDrawableFactory = GLDrawableFactory.getFactory(glProfile); - - glDrawable = glDrawableFactory.createGLDrawable(jawtWindow); - glDrawable.setRealized(true); - - glContext = glDrawable.createContext(null); - if (log.isDebugEnabled()) + invokeOnMainThread(() -> { - // Debug config on context needs to be set before .makeCurrent call - glContext.enableGLDebugMessage(true); - } + GLProfile glProfile = GLProfile.get(GLProfile.GL4); - int res = glContext.makeCurrent(); - if (res == GLContext.CONTEXT_NOT_CURRENT) - { - throw new GLException("Unable to make context current"); - } + GLCapabilities glCaps = new GLCapabilities(glProfile); + AWTGraphicsConfiguration config = AWTGraphicsConfiguration.create(canvas.getGraphicsConfiguration(), glCaps, glCaps); - // Surface needs to be unlocked on X11 window otherwise input is blocked - if (jawtWindow instanceof X11JAWTWindow && jawtWindow.getLock().isLocked()) - { - jawtWindow.unlockSurface(); - } + jawtWindow = NewtFactoryAWT.getNativeWindow(canvas, config); + canvas.setFocusable(true); - this.gl = glContext.getGL().getGL4(); - gl.setSwapInterval(0); + GLDrawableFactory glDrawableFactory = GLDrawableFactory.getFactory(glProfile); - if (log.isDebugEnabled()) - { - gl.glEnable(gl.GL_DEBUG_OUTPUT); + jawtWindow.lockSurface(); + try + { + glDrawable = glDrawableFactory.createGLDrawable(jawtWindow); + glDrawable.setRealized(true); - // Suppress warning messages which flood the log on NVIDIA systems. - gl.getContext().glDebugMessageControl(gl.GL_DEBUG_SOURCE_API, gl.GL_DEBUG_TYPE_OTHER, - gl.GL_DEBUG_SEVERITY_NOTIFICATION, 0, null, 0, false); - } + glContext = glDrawable.createContext(null); + if (log.isDebugEnabled()) + { + // Debug config on context needs to be set before .makeCurrent call + glContext.enableGLDebugMessage(true); + } + } + finally + { + jawtWindow.unlockSurface(); + } - initVao(); - initProgram(); - initInterfaceTexture(); - initUniformBuffer(); - initBuffers(); + int res = glContext.makeCurrent(); + if (res == GLContext.CONTEXT_NOT_CURRENT) + { + throw new GLException("Unable to make context current"); + } + + // Surface needs to be unlocked on X11 window otherwise input is blocked + if (jawtWindow instanceof X11JAWTWindow && jawtWindow.getLock().isLocked()) + { + jawtWindow.unlockSurface(); + } + + this.gl = glContext.getGL().getGL4(); + gl.setSwapInterval(0); + + if (log.isDebugEnabled()) + { + gl.glEnable(gl.GL_DEBUG_OUTPUT); + + // Suppress warning messages which flood the log on NVIDIA systems. + gl.getContext().glDebugMessageControl(gl.GL_DEBUG_SOURCE_API, gl.GL_DEBUG_TYPE_OTHER, + gl.GL_DEBUG_SEVERITY_NOTIFICATION, 0, null, 0, false); + } + + initVao(); + try + { + initProgram(); + } + catch (ShaderException ex) + { + throw new RuntimeException(ex); + } + initInterfaceTexture(); + initUniformBuffer(); + initBuffers(); + }); client.setDrawCallbacks(this); client.setGpu(true); @@ -408,41 +429,51 @@ public class GpuPlugin extends Plugin implements DrawCallbacks client.setGpu(false); client.setDrawCallbacks(null); - if (gl != null) + invokeOnMainThread(() -> { - if (textureArrayId != -1) + if (gl != null) { - textureManager.freeTextureArray(gl, textureArrayId); - textureArrayId = -1; + if (textureArrayId != -1) + { + textureManager.freeTextureArray(gl, textureArrayId); + textureArrayId = -1; + } + + if (uniformBufferId != -1) + { + GLUtil.glDeleteBuffer(gl, uniformBufferId); + uniformBufferId = -1; + } + + shutdownBuffers(); + shutdownInterfaceTexture(); + shutdownProgram(); + shutdownVao(); + shutdownAAFbo(); } - if (uniformBufferId != -1) + if (jawtWindow != null) { - GLUtil.glDeleteBuffer(gl, uniformBufferId); - uniformBufferId = -1; + if (!jawtWindow.getLock().isLocked()) + { + jawtWindow.lockSurface(); + } + + if (glContext != null) + { + glContext.destroy(); + } + + // this crashes on osx when the plugin is turned back on, don't know why + // we'll just leak the window... + if (OSType.getOSType() != OSType.MacOS) + { + NewtFactoryAWT.destroyNativeWindow(jawtWindow); + } } + }); - shutdownBuffers(); - shutdownInterfaceTexture(); - shutdownProgram(); - shutdownVao(); - shutdownAAFbo(); - } - - if (jawtWindow != null) - { - if (!jawtWindow.getLock().isLocked()) - { - jawtWindow.lockSurface(); - } - - if (glContext != null) - { - glContext.destroy(); - } - - NewtFactoryAWT.destroyNativeWindow(jawtWindow); - } + GLProfile.shutdown(); jawtWindow = null; gl = null; @@ -839,6 +870,42 @@ public class GpuPlugin extends Plugin implements DrawCallbacks @Override public void draw() + { + invokeOnMainThread(this::drawFrame); + } + + private void resize(int canvasWidth, int canvasHeight, int viewportWidth, int viewportHeight) + { + // If the viewport has changed, update the projection matrix + if (viewportWidth > 0 && viewportHeight > 0 && (viewportWidth != lastViewportWidth || viewportHeight != lastViewportHeight)) + { + lastViewportWidth = viewportWidth; + lastViewportHeight = viewportHeight; + createProjectionMatrix(0, viewportWidth, viewportHeight, 0, 0, Constants.SCENE_SIZE * Perspective.LOCAL_TILE_SIZE); + } + + if (canvasWidth != lastCanvasWidth || canvasHeight != lastCanvasHeight) + { + lastCanvasWidth = canvasWidth; + lastCanvasHeight = canvasHeight; + + gl.glBindTexture(gl.GL_TEXTURE_2D, interfaceTexture); + gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, canvasWidth, canvasHeight, 0, gl.GL_BGRA, gl.GL_UNSIGNED_INT_8_8_8_8_REV, null); + gl.glBindTexture(gl.GL_TEXTURE_2D, 0); + + if (OSType.getOSType() == OSType.MacOS && glDrawable instanceof GLFBODrawable) + { + // GLDrawables created with createGLDrawable() do not have a resize listener + // I don't know why this works with Windows/Linux, but on OSX + // it prevents JOGL from resizing its FBOs and underlying GL textures. So, + // we manually trigger a resize here. + GLFBODrawable glfboDrawable = (GLFBODrawable) glDrawable; + glfboDrawable.resetSize(gl); + } + } + } + + private void drawFrame() { if (jawtWindow.getAWTComponent() != client.getCanvas()) { @@ -862,13 +929,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks final int viewportHeight = client.getViewportHeight(); final int viewportWidth = client.getViewportWidth(); - // If the viewport has changed, update the projection matrix - if (viewportWidth > 0 && viewportHeight > 0 && (viewportWidth != lastViewportWidth || viewportHeight != lastViewportHeight)) - { - createProjectionMatrix(0, viewportWidth, viewportHeight, 0, 0, Constants.SCENE_SIZE * Perspective.LOCAL_TILE_SIZE); - lastViewportWidth = viewportWidth; - lastViewportHeight = viewportHeight; - } + resize(canvasWidth, canvasHeight, viewportWidth, viewportHeight); // Setup anti-aliasing final AntiAliasingMode antiAliasingMode = config.antiAliasingMode(); @@ -1176,16 +1237,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE_MINUS_SRC_ALPHA); gl.glBindTexture(gl.GL_TEXTURE_2D, interfaceTexture); - if (canvasWidth != lastCanvasWidth || canvasHeight != lastCanvasHeight) - { - gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0, gl.GL_BGRA, gl.GL_UNSIGNED_INT_8_8_8_8_REV, interfaceBuffer); - lastCanvasWidth = canvasWidth; - lastCanvasHeight = canvasHeight; - } - else - { - gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, 0, width, height, gl.GL_BGRA, gl.GL_UNSIGNED_INT_8_8_8_8_REV, interfaceBuffer); - } + gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, 0, width, height, gl.GL_BGRA, gl.GL_UNSIGNED_INT_8_8_8_8_REV, interfaceBuffer); // Use the texture bound in the first pass final UIScalingMode uiScalingMode = config.uiScalingMode(); @@ -1526,12 +1578,20 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private void glDpiAwareViewport(final int x, final int y, final int width, final int height) { - final AffineTransform t = ((Graphics2D) canvas.getGraphics()).getTransform(); - gl.glViewport( - getScaledValue(t.getScaleX(), x), - getScaledValue(t.getScaleY(), y), - getScaledValue(t.getScaleX(), width), - getScaledValue(t.getScaleY(), height)); + if (OSType.getOSType() == OSType.MacOS) + { + // JOGL seems to handle DPI scaling for us already + gl.glViewport(x, y, width, height); + } + else + { + final AffineTransform t = ((Graphics2D) canvas.getGraphics()).getTransform(); + gl.glViewport( + getScaledValue(t.getScaleX(), x), + getScaledValue(t.getScaleY(), y), + getScaledValue(t.getScaleX(), width), + getScaledValue(t.getScaleY(), height)); + } } private int getDrawDistance() @@ -1539,4 +1599,16 @@ public class GpuPlugin extends Plugin implements DrawCallbacks final int limit = useComputeShaders ? MAX_DISTANCE : DEFAULT_DISTANCE; return Ints.constrainToRange(config.drawDistance(), 0, limit); } + + private static void invokeOnMainThread(Runnable runnable) + { + if (OSType.getOSType() == OSType.MacOS) + { + OSXUtil.RunOnMainThread(true, false, runnable); + } + else + { + runnable.run(); + } + } } From 0b6dabd9a32583c6505be995843ff82827221bac Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 1 May 2020 18:12:51 -0400 Subject: [PATCH 04/27] travis: force tls 1.2 The openjdk 11 version travis uses does not correctly handle tls 1.3 session resumption --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index c33e2a950c..4aced04a0f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,9 @@ dist: xenial cache: directories: - $HOME/.m2 +env: + global: + - JAVA_TOOL_OPTIONS=-Dhttps.protocols=TLSv1.2 jdk: - openjdk8 - openjdk11 From 2836f89b9e8f971310922e8aee8eb111591fed99 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Thu, 23 Apr 2020 17:15:28 -0600 Subject: [PATCH 05/27] rl-api, rl-client: Remove core logger --- runelite-api/src/main/java/net/runelite/api/Client.java | 7 ------- .../src/main/java/net/runelite/client/RuneLiteModule.java | 6 ------ 2 files changed, 13 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index 5b654bb7b7..3937457d61 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -41,7 +41,6 @@ import net.runelite.api.vars.AccountType; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetConfig; import net.runelite.api.widgets.WidgetInfo; -import org.slf4j.Logger; /** * Represents the RuneScape client. @@ -57,12 +56,6 @@ public interface Client extends GameEngine void setDrawCallbacks(DrawCallbacks drawCallbacks); - /** - * Retrieve a global logger for the client. - * This is most useful for mixins which can't have their own. - */ - Logger getLogger(); - String getBuildID(); /** diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java index 0434f54abf..7f8dd55ddb 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java @@ -52,8 +52,6 @@ import net.runelite.client.util.ExecutorServiceExceptionLogger; import net.runelite.http.api.RuneLiteAPI; import okhttp3.Cache; import okhttp3.OkHttpClient; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class RuneLiteModule extends AbstractModule { @@ -97,10 +95,6 @@ public class RuneLiteModule extends AbstractModule bind(EventBus.class) .annotatedWith(Names.named("Deferred EventBus")) .to(DeferredEventBus.class); - - bind(Logger.class) - .annotatedWith(Names.named("Core Logger")) - .toInstance(LoggerFactory.getLogger(RuneLite.class)); } @Provides From 7bec4999fd1015c43d3c8b5095982ed3c56efe8f Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Fri, 31 Jan 2020 03:13:17 -0800 Subject: [PATCH 06/27] loottracker - Reword syncPanel config description --- .../client/plugins/loottracker/LootTrackerConfig.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java index 77c31694e8..5ab78b1591 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java @@ -82,9 +82,9 @@ public interface LootTrackerConfig extends Config @ConfigItem( keyName = "syncPanel", name = "Synchronize panel contents", - description = "Synchronize your local loot tracker with your online (requires being logged in). This means" + - " that panel is filled with portion of your remote data on startup and deleting data in panel deletes them" + - " also on server." + description = "Synchronize your local loot tracker with your server data (requires being logged in).
" + + " This means the panel is filled with portions of your remote data on startup
" + + " and deleting data in the panel also deletes it on the server." ) default boolean syncPanel() { From 08b49d78dab0a070159383c89aaef5c46bbfacc7 Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Fri, 31 Jan 2020 03:54:31 -0800 Subject: [PATCH 07/27] loot tracker - add ability to ignore loot events --- .../plugins/loottracker/LootTrackerBox.java | 15 +++++- .../loottracker/LootTrackerConfig.java | 17 +++++++ .../plugins/loottracker/LootTrackerPanel.java | 46 +++++++++++++------ .../loottracker/LootTrackerPlugin.java | 26 +++++++++++ 4 files changed, 88 insertions(+), 16 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java index 6e7d08cc97..015bd8ef83 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java @@ -91,7 +91,9 @@ class LootTrackerBox extends JPanel final boolean hideIgnoredItems, final LootTrackerPriceType priceType, final boolean showPriceType, - final BiConsumer onItemToggle) + final BiConsumer onItemToggle, + final BiConsumer onEventToggle, + final boolean eventIgnored) { this.id = id; this.lootRecordType = lootRecordType; @@ -106,7 +108,7 @@ class LootTrackerBox extends JPanel logTitle.setLayout(new BoxLayout(logTitle, BoxLayout.X_AXIS)); logTitle.setBorder(new EmptyBorder(7, 7, 7, 7)); - logTitle.setBackground(ColorScheme.DARKER_GRAY_COLOR.darker()); + logTitle.setBackground(eventIgnored ? ColorScheme.DARKER_GRAY_HOVER_COLOR : ColorScheme.DARKER_GRAY_COLOR.darker()); JLabel titleLabel = new JLabel(); titleLabel.setText(Text.removeTags(id)); @@ -135,6 +137,15 @@ class LootTrackerBox extends JPanel add(logTitle, BorderLayout.NORTH); add(itemContainer, BorderLayout.CENTER); + + // Create popup menu for ignoring the loot event + final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + this.setComponentPopupMenu(popupMenu); + + final JMenuItem toggle = new JMenuItem(eventIgnored ? "Include group" : "Ignore group"); + toggle.addActionListener(e -> onEventToggle.accept(id, !eventIgnored)); + popupMenu.add(toggle); } /** diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java index 5ab78b1591..a8917ebfe2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java @@ -90,4 +90,21 @@ public interface LootTrackerConfig extends Config { return true; } + + @ConfigItem( + keyName = "ignoredEvents", + name = "Ignored groups", + description = "Configures which loot groups should be excluded from the panel UI" + ) + default String getIgnoredEvents() + { + return ""; + } + + @ConfigItem( + keyName = "ignoredEvents", + name = "", + description = "" + ) + void setIgnoredEvents(String key); } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java index 69bd47032e..9550230422 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java @@ -35,6 +35,7 @@ import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.Predicate; import javax.swing.BorderFactory; @@ -355,6 +356,11 @@ class LootTrackerPanel extends PluginPanel final LootTrackerRecord record = new LootTrackerRecord(eventName, subTitle, type, items, 1); sessionRecords.add(record); + if (hideIgnoredItems && plugin.isEventIgnored(eventName)) + { + return; + } + LootTrackerBox box = buildBox(record); if (box != null) { @@ -450,15 +456,12 @@ class LootTrackerPanel extends PluginPanel } else { - int start = 0; - if (sessionRecords.size() > MAX_LOOT_BOXES) - { - start = sessionRecords.size() - MAX_LOOT_BOXES; - } - for (int i = start; i < sessionRecords.size(); i++) - { - buildBox(sessionRecords.get(i)); - } + sessionRecords.stream() + .sorted(Collections.reverseOrder()) + // filter records prior to limiting so that it is limited to the correct amount + .filter(r -> !hideIgnoredItems || !plugin.isEventIgnored(r.getTitle())) + .limit(MAX_LOOT_BOXES) + .forEach(this::buildBox); } boxes.forEach(LootTrackerBox::rebuild); @@ -480,6 +483,12 @@ class LootTrackerPanel extends PluginPanel return null; } + final boolean isIgnored = plugin.isEventIgnored(record.getTitle()); + if (hideIgnoredItems && isIgnored) + { + return null; + } + // Group all similar loot together if (groupLoot) { @@ -500,13 +509,17 @@ class LootTrackerPanel extends PluginPanel // Create box final LootTrackerBox box = new LootTrackerBox(itemManager, record.getTitle(), record.getType(), record.getSubTitle(), - hideIgnoredItems, config.priceType(), config.showPriceType(), plugin::toggleItem); + hideIgnoredItems, config.priceType(), config.showPriceType(), plugin::toggleItem, plugin::toggleEvent, isIgnored); box.addKill(record); - // Create popup menu - final JPopupMenu popupMenu = new JPopupMenu(); - popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); - box.setComponentPopupMenu(popupMenu); + // Use the existing popup menu or create a new one + JPopupMenu popupMenu = box.getComponentPopupMenu(); + if (popupMenu == null) + { + popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + box.setComponentPopupMenu(popupMenu); + } // Create collapse event box.addMouseListener(new MouseAdapter() @@ -593,6 +606,11 @@ class LootTrackerPanel extends PluginPanel continue; } + if (hideIgnoredItems && plugin.isEventIgnored(record.getTitle())) + { + continue; + } + int present = record.getItems().length; for (LootTrackerItem item : record.getItems()) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index e10427eec3..f6f473e092 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -43,6 +43,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -199,6 +200,7 @@ public class LootTrackerPlugin extends Plugin private String lastPickpocketTarget; private List ignoredItems = new ArrayList<>(); + private List ignoredEvents = new ArrayList<>(); private Multiset inventorySnapshot; @@ -268,6 +270,7 @@ public class LootTrackerPlugin extends Plugin if (event.getGroup().equals("loottracker")) { ignoredItems = Text.fromCSV(config.getIgnoredItems()); + ignoredEvents = Text.fromCSV(config.getIgnoredEvents()); SwingUtilities.invokeLater(panel::updateIgnoredRecords); } } @@ -276,6 +279,7 @@ public class LootTrackerPlugin extends Plugin protected void startUp() throws Exception { ignoredItems = Text.fromCSV(config.getIgnoredItems()); + ignoredEvents = Text.fromCSV(config.getIgnoredEvents()); panel = new LootTrackerPanel(this, itemManager, config); spriteManager.getSpriteAsync(SpriteID.TAB_INVENTORY, 0, panel::loadHeaderIcon); @@ -750,6 +754,28 @@ public class LootTrackerPlugin extends Plugin return ignoredItems.contains(name); } + void toggleEvent(String name, boolean ignore) + { + final Set ignoredSet = new LinkedHashSet<>(ignoredEvents); + + if (ignore) + { + ignoredSet.add(name); + } + else + { + ignoredSet.remove(name); + } + + config.setIgnoredEvents(Text.toCSV(ignoredSet)); + // the config changed will update the panel + } + + boolean isEventIgnored(String name) + { + return ignoredEvents.contains(name); + } + private LootTrackerItem buildLootTrackerItem(int itemId, int quantity) { final ItemComposition itemComposition = itemManager.getItemComposition(itemId); From 95481bbbd1f6da42a790116857ba3d1d70481175 Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Wed, 22 Apr 2020 10:30:00 -0700 Subject: [PATCH 08/27] loot tracker - Fix ignoredItems config value ordering --- .../runelite/client/plugins/loottracker/LootTrackerPlugin.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index f6f473e092..2a4afec80a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -42,7 +42,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -734,7 +733,7 @@ public class LootTrackerPlugin extends Plugin void toggleItem(String name, boolean ignore) { - final Set ignoredItemSet = new HashSet<>(ignoredItems); + final Set ignoredItemSet = new LinkedHashSet<>(ignoredItems); if (ignore) { From 3cb2f68725c1e33b1f130f067032b9dc56137a41 Mon Sep 17 00:00:00 2001 From: Anthony Alves Date: Mon, 20 Apr 2020 20:33:48 -0400 Subject: [PATCH 09/27] xp-tracker: add configurable labels to XpInfoBox This commit allows users to change the XpInfoBox to which ever xp stat is available in the config menu. This also adds TTL in the form of hh:mm:ss to the info box --- .../client/plugins/xptracker/XpInfoBox.java | 49 ++++++++----- .../plugins/xptracker/XpPanelLabel.java | 71 +++++++++++++++++++ .../plugins/xptracker/XpSnapshotSingle.java | 1 + .../plugins/xptracker/XpStateSingle.java | 47 +++++++++--- .../plugins/xptracker/XpTrackerConfig.java | 46 ++++++++++++ 5 files changed, 188 insertions(+), 26 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanelLabel.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java index 53bad6208a..d064cc63d3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2018, Adam * Copyright (c) 2018, Psikoi + * Copyright (c) 2020, Anthony * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -95,10 +96,10 @@ class XpInfoBox extends JPanel private final ProgressBar progressBar = new ProgressBar(); - private final JLabel expGained = new JLabel(); - private final JLabel expHour = new JLabel(); - private final JLabel expLeft = new JLabel(); - private final JLabel actionsLeft = new JLabel(); + private final JLabel topLeftStat = new JLabel(); + private final JLabel bottomLeftStat = new JLabel(); + private final JLabel topRightStat = new JLabel(); + private final JLabel bottomRightStat = new JLabel(); private final JMenuItem pauseSkill = new JMenuItem("Pause"); private final JMenuItem canvasItem = new JMenuItem(ADD_STATE); @@ -184,15 +185,16 @@ class XpInfoBox extends JPanel statsPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); statsPanel.setBorder(new EmptyBorder(9, 2, 9, 2)); - expGained.setFont(FontManager.getRunescapeSmallFont()); - expHour.setFont(FontManager.getRunescapeSmallFont()); - expLeft.setFont(FontManager.getRunescapeSmallFont()); - actionsLeft.setFont(FontManager.getRunescapeSmallFont()); - statsPanel.add(expGained); - statsPanel.add(expLeft); - statsPanel.add(expHour); - statsPanel.add(actionsLeft); + topLeftStat.setFont(FontManager.getRunescapeSmallFont()); + bottomLeftStat.setFont(FontManager.getRunescapeSmallFont()); + topRightStat.setFont(FontManager.getRunescapeSmallFont()); + bottomRightStat.setFont(FontManager.getRunescapeSmallFont()); + + statsPanel.add(topLeftStat); // top left + statsPanel.add(topRightStat); // top right + statsPanel.add(bottomLeftStat); // bottom left + statsPanel.add(bottomRightStat); // bottom right headerPanel.add(skillIcon, BorderLayout.WEST); headerPanel.add(statsPanel, BorderLayout.CENTER); @@ -243,11 +245,6 @@ class XpInfoBox extends JPanel paused = skillPaused; - // Update information labels - expGained.setText(htmlLabel("XP Gained: ", xpSnapshotSingle.getXpGainedInSession())); - expLeft.setText(htmlLabel("XP Left: ", xpSnapshotSingle.getXpRemainingToGoal())); - actionsLeft.setText(htmlLabel(xpSnapshotSingle.getActionType().getLabel() + ": ", xpSnapshotSingle.getActionsRemainingToGoal())); - // Update progress bar progressBar.setValue((int) xpSnapshotSingle.getSkillProgressToGoal()); progressBar.setCenterLabel(TWO_DECIMAL_FORMAT.format(xpSnapshotSingle.getSkillProgressToGoal()) + "%"); @@ -304,13 +301,29 @@ class XpInfoBox extends JPanel pauseSkill.setText("Pause"); } + // Update information labels // Update exp per hour separately, every time (not only when there's an update) - expHour.setText(htmlLabel("XP/Hour: ", xpSnapshotSingle.getXpPerHour())); + topLeftStat.setText(htmlLabel(xpTrackerConfig.xpPanelLabel1(), xpSnapshotSingle)); + topRightStat.setText(htmlLabel(xpTrackerConfig.xpPanelLabel2(), xpSnapshotSingle)); + bottomLeftStat.setText(htmlLabel(xpTrackerConfig.xpPanelLabel3(), xpSnapshotSingle)); + bottomRightStat.setText(htmlLabel(xpTrackerConfig.xpPanelLabel4(), xpSnapshotSingle)); + } + + static String htmlLabel(XpPanelLabel panelLabel, XpSnapshotSingle xpSnapshotSingle) + { + String key = panelLabel.getActionKey(xpSnapshotSingle) + ": "; + String value = panelLabel.getValueFunc().apply(xpSnapshotSingle); + return htmlLabel(key, value); } static String htmlLabel(String key, int value) { String valueStr = QuantityFormatter.quantityToRSDecimalStack(value, true); + return htmlLabel(key, valueStr); + } + + static String htmlLabel(String key, String valueStr) + { return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanelLabel.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanelLabel.java new file mode 100644 index 0000000000..d17c31872e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanelLabel.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020, Anthony + * 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.xptracker; + +import java.util.function.Function; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.client.util.QuantityFormatter; + +@Getter +@AllArgsConstructor +public enum XpPanelLabel +{ + TIME_TO_LEVEL("TTL", XpSnapshotSingle::getTimeTillGoalShort), + + XP_GAINED("XP Gained", snap -> format(snap.getXpGainedInSession())), + XP_HOUR("XP/hr", snap -> format(snap.getXpPerHour())), + XP_LEFT("XP Left", snap -> format(snap.getXpRemainingToGoal())), + + ACTIONS_LEFT("Actions", snap -> format(snap.getActionsRemainingToGoal())), + ACTIONS_HOUR("Actions/hr", snap -> format(snap.getActionsPerHour())), + ACTIONS_DONE("Actions Done", snap -> format(snap.getActionsInSession())), + ; + + private final String key; + private final Function valueFunc; + + /** + * Get the action key label based on if the Action type is an xp drop or kill + * + * @param snapshot + * @return + */ + public String getActionKey(XpSnapshotSingle snapshot) + { + String actionKey = key; + if (snapshot.getActionType() == XpActionType.ACTOR_HEALTH) + { + return actionKey.replace("Action", "Kill"); + } + + return actionKey; + } + + private static String format(int val) + { + return QuantityFormatter.quantityToRSDecimalStack(val, true); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpSnapshotSingle.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpSnapshotSingle.java index bb7f8a5ef7..6767ab6066 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpSnapshotSingle.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpSnapshotSingle.java @@ -44,4 +44,5 @@ class XpSnapshotSingle private int actionsRemainingToGoal; private int actionsPerHour; private String timeTillGoal; + private String timeTillGoalShort; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpStateSingle.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpStateSingle.java index 608accc239..82182ae54c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpStateSingle.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpStateSingle.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2017, Cameron * Copyright (c) 2018, Levi + * Copyright (c) 2020, Anthony * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -128,24 +129,31 @@ class XpStateSingle return (xpGained / xpGoal) * 100; } - private String getTimeTillLevel() + private long getSecondsTillLevel() { + // Java 8 doesn't have good duration / period objects to represent spans of time that can be formatted + // Rather than importing another dependency like joda time (which is practically built into java 10) + // below will be a custom formatter that handles spans larger than 1 day long seconds = getTimeElapsedInSeconds(); if (seconds <= 0 || xpGained <= 0) { - // Infinity symbol - return "\u221e"; + return -1; } // formula is xpRemaining / xpPerSecond // xpPerSecond being xpGained / seconds // This can be simplified so division is only done once and we can work in whole numbers! - long remainingSeconds = (getXpRemaining() * seconds) / xpGained; + return (getXpRemaining() * seconds) / xpGained; + } - // Java 8 doesn't have good duration / period objects to represent spans of time that can be formatted - // Rather than importing another dependency like joda time (which is practically built into java 10) - // below will be a custom formatter that handles spans larger than 1 day + private String getTimeTillLevel() + { + long remainingSeconds = getSecondsTillLevel(); + if (remainingSeconds < 0) + { + return "\u221e"; + } long durationDays = remainingSeconds / (24 * 60 * 60); long durationHours = (remainingSeconds % (24 * 60 * 60)) / (60 * 60); @@ -160,7 +168,29 @@ class XpStateSingle { return String.format("1 day %02d:%02d:%02d", durationHours, durationMinutes, durationSeconds); } - else if (durationHours > 0) + + // durationDays = 0 if we got here. + // return time remaining in hh:mm:ss or mm:ss format + return getTimeTillLevelShort(); + } + + /** + * Get time to level in `hh:mm:ss` or `mm:ss` format, + * where `hh` can be > 24. + * @return + */ + private String getTimeTillLevelShort() + { + long remainingSeconds = getSecondsTillLevel(); + if (remainingSeconds < 0) + { + return "\u221e"; + } + + long durationHours = remainingSeconds / (60 * 60); + long durationMinutes = (remainingSeconds % (60 * 60)) / 60; + long durationSeconds = remainingSeconds % 60; + if (durationHours > 0) { return String.format("%02d:%02d:%02d", durationHours, durationMinutes, durationSeconds); } @@ -269,6 +299,7 @@ class XpStateSingle .actionsRemainingToGoal(getActionsRemaining()) .actionsPerHour(getActionsHr()) .timeTillGoal(getTimeTillLevel()) + .timeTillGoalShort(getTimeTillLevelShort()) .startGoalXp(startLevelExp) .endGoalXp(endLevelExp) .build(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java index 7a4ccf8406..4ebeb2a075 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Levi + * Copyright (c) 2020, Anthony * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -126,4 +127,49 @@ public interface XpTrackerConfig extends Config { return OnScreenDisplayModeBottom.XP_HOUR; } + + @ConfigItem( + position = 7, + keyName = "xpPanelLabel1", + name = "Top-left XP info label", + description = "Configures the information displayed in the top-left of XP info box" + ) + default XpPanelLabel xpPanelLabel1() + { + return XpPanelLabel.XP_GAINED; + } + + @ConfigItem( + position = 8, + keyName = "xpPanelLabel2", + name = "Top-right XP info label", + description = "Configures the information displayed in the top-right of XP info box" + ) + + default XpPanelLabel xpPanelLabel2() + { + return XpPanelLabel.XP_LEFT; + } + + @ConfigItem( + position = 9, + keyName = "xpPanelLabel3", + name = "Bottom-left XP info label", + description = "Configures the information displayed in the bottom-left of XP info box" + ) + default XpPanelLabel xpPanelLabel3() + { + return XpPanelLabel.XP_HOUR; + } + + @ConfigItem( + position = 10, + keyName = "xpPanelLabel4", + name = "Bottom-right XP info label", + description = "Configures the information displayed in the bottom-right of XP info box" + ) + default XpPanelLabel xpPanelLabel4() + { + return XpPanelLabel.ACTIONS_LEFT; + } } From 240b14453f018ddc7bf85ebd0ad68bd03391f899 Mon Sep 17 00:00:00 2001 From: Anthony Alves Date: Mon, 20 Apr 2020 20:36:48 -0400 Subject: [PATCH 10/27] xp-tracker: add more configurable labels to canvas overlay --- .../plugins/xptracker/XpInfoBoxOverlay.java | 50 +++---------------- .../plugins/xptracker/XpTrackerConfig.java | 25 ++-------- 2 files changed, 12 insertions(+), 63 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBoxOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBoxOverlay.java index ec9ab50b71..95f9beefbd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBoxOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBoxOverlay.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Jasper Ketelaar + * Copyright (c) 2020, Anthony * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,7 +47,6 @@ import net.runelite.client.ui.overlay.components.LineComponent; import net.runelite.client.ui.overlay.components.PanelComponent; import net.runelite.client.ui.overlay.components.ProgressBarComponent; import net.runelite.client.ui.overlay.components.SplitComponent; -import net.runelite.client.util.QuantityFormatter; class XpInfoBoxOverlay extends OverlayPanel { @@ -91,54 +91,20 @@ class XpInfoBoxOverlay extends OverlayPanel final XpSnapshotSingle snapshot = plugin.getSkillSnapshot(skill); - final String leftStr; - final int rightNum; - - switch (config.onScreenDisplayMode()) - { - case ACTIONS_DONE: - leftStr = snapshot.getActionType().getLabel() + " Done"; - rightNum = snapshot.getActionsInSession(); - break; - case ACTIONS_LEFT: - leftStr = snapshot.getActionType().getLabel() + " Left"; - rightNum = snapshot.getActionsRemainingToGoal(); - break; - case XP_LEFT: - leftStr = "XP Left"; - rightNum = snapshot.getXpRemainingToGoal(); - break; - case XP_GAINED: - default: - leftStr = "XP Gained"; - rightNum = snapshot.getXpGainedInSession(); - break; - } + final String leftStr = config.onScreenDisplayMode().getActionKey(snapshot); + final String rightNum = config.onScreenDisplayMode().getValueFunc().apply(snapshot); final LineComponent xpLine = LineComponent.builder() .left(leftStr + ":") - .right(QuantityFormatter.quantityToRSDecimalStack(rightNum, true)) + .right(rightNum) .build(); - final String bottemLeftStr; - final int bottomRightNum; - - switch (config.onScreenDisplayModeBottom()) - { - case ACTIONS_HOUR: - bottemLeftStr = snapshot.getActionType().getLabel() + "/Hour"; - bottomRightNum = snapshot.getActionsPerHour(); - break; - case XP_HOUR: - default: - bottemLeftStr = "XP/Hour"; - bottomRightNum = snapshot.getXpPerHour(); - break; - } + final String bottomLeftStr = config.onScreenDisplayModeBottom().getActionKey(snapshot); + final String bottomRightNum = config.onScreenDisplayModeBottom().getValueFunc().apply(snapshot); final LineComponent xpLineBottom = LineComponent.builder() - .left(bottemLeftStr + ":") - .right(QuantityFormatter.quantityToRSDecimalStack(bottomRightNum, true)) + .left(bottomLeftStr + ":") + .right(bottomRightNum) .build(); final SplitComponent xpSplit = SplitComponent.builder() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java index 4ebeb2a075..f2655bb127 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java @@ -25,7 +25,6 @@ */ package net.runelite.client.plugins.xptracker; -import lombok.AllArgsConstructor; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; @@ -34,22 +33,6 @@ import net.runelite.client.config.Units; @ConfigGroup("xpTracker") public interface XpTrackerConfig extends Config { - @AllArgsConstructor - enum OnScreenDisplayMode - { - XP_GAINED, - XP_LEFT, - ACTIONS_DONE, - ACTIONS_LEFT - } - - @AllArgsConstructor - enum OnScreenDisplayModeBottom - { - XP_HOUR, - ACTIONS_HOUR, - } - @ConfigItem( position = 0, keyName = "hideMaxed", @@ -112,9 +95,9 @@ public interface XpTrackerConfig extends Config name = "On-screen tracker display mode (top)", description = "Configures the information displayed in the first line of on-screen XP overlays" ) - default OnScreenDisplayMode onScreenDisplayMode() + default XpPanelLabel onScreenDisplayMode() { - return OnScreenDisplayMode.XP_GAINED; + return XpPanelLabel.XP_GAINED; } @ConfigItem( @@ -123,9 +106,9 @@ public interface XpTrackerConfig extends Config name = "On-screen tracker display mode (bottom)", description = "Configures the information displayed in the second line of on-screen XP overlays" ) - default OnScreenDisplayModeBottom onScreenDisplayModeBottom() + default XpPanelLabel onScreenDisplayModeBottom() { - return OnScreenDisplayModeBottom.XP_HOUR; + return XpPanelLabel.XP_HOUR; } @ConfigItem( From cb4542469b3b43985df1f6346e1fbf3962172741 Mon Sep 17 00:00:00 2001 From: Anthony Alves Date: Mon, 20 Apr 2020 20:52:32 -0400 Subject: [PATCH 11/27] xp-tracker: add option to show ttl or percentage til goal --- .../client/plugins/xptracker/XpInfoBox.java | 4 +- .../plugins/xptracker/XpProgressBarLabel.java | 43 +++++++++++++++++++ .../plugins/xptracker/XpTrackerConfig.java | 11 +++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpProgressBarLabel.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java index d064cc63d3..cbc0be4520 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java @@ -61,7 +61,7 @@ import net.runelite.client.util.QuantityFormatter; class XpInfoBox extends JPanel { - private static final DecimalFormat TWO_DECIMAL_FORMAT = new DecimalFormat("0.00"); + static final DecimalFormat TWO_DECIMAL_FORMAT = new DecimalFormat("0.00"); static { @@ -247,7 +247,7 @@ class XpInfoBox extends JPanel // Update progress bar progressBar.setValue((int) xpSnapshotSingle.getSkillProgressToGoal()); - progressBar.setCenterLabel(TWO_DECIMAL_FORMAT.format(xpSnapshotSingle.getSkillProgressToGoal()) + "%"); + progressBar.setCenterLabel(xpTrackerConfig.progressBarLabel().getValueFunc().apply(xpSnapshotSingle)); progressBar.setLeftLabel("Lvl. " + xpSnapshotSingle.getStartLevel()); progressBar.setRightLabel(xpSnapshotSingle.getEndGoalXp() == Experience.MAX_SKILL_XP ? "200M" diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpProgressBarLabel.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpProgressBarLabel.java new file mode 100644 index 0000000000..bdcbb91fc3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpProgressBarLabel.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020, Anthony + * 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.xptracker; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.function.Function; + +import static net.runelite.client.plugins.xptracker.XpInfoBox.TWO_DECIMAL_FORMAT; + +@Getter +@AllArgsConstructor +public enum XpProgressBarLabel +{ + PERCENTAGE((snap) -> TWO_DECIMAL_FORMAT.format(snap.getSkillProgressToGoal()) + "%"), + TIME_TO_LEVEL(XpSnapshotSingle::getTimeTillGoal), + ; + + private final Function valueFunc; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java index f2655bb127..cc41bb5585 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java @@ -155,4 +155,15 @@ public interface XpTrackerConfig extends Config { return XpPanelLabel.ACTIONS_LEFT; } + + @ConfigItem( + position = 11, + keyName = "progressBarLabel", + name = "Progress bar label", + description = "Configures the info box progress bar to show Time to goal or percentage complete" + ) + default XpProgressBarLabel progressBarLabel() + { + return XpProgressBarLabel.PERCENTAGE; + } } From e5a7f9af73880e1e51bf39124a1d9cea9ac7c65a Mon Sep 17 00:00:00 2001 From: Anthony Alves Date: Fri, 1 May 2020 12:44:17 -0400 Subject: [PATCH 12/27] loot-tracker: make clue scroll drops stackable --- .../loottracker/LootTrackerMapping.java | 92 ++++++++++++ .../loottracker/LootTrackerPlugin.java | 6 +- .../loottracker/LootTrackerPluginTest.java | 136 ++++++++++++++++++ 3 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerMapping.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerMapping.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerMapping.java new file mode 100644 index 0000000000..27b08ced63 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerMapping.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020, Anthony + * 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.loottracker; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; +import lombok.Getter; +import net.runelite.api.ItemComposition; +import net.runelite.api.ItemID; +import net.runelite.client.game.ItemManager; +import net.runelite.client.game.ItemStack; + +@Getter +public enum LootTrackerMapping +{ + CLUE_SCROLL_BEGINNER("Clue scroll (beginner)", ItemID.CLUE_SCROLL_BEGINNER), + CLUE_SCROLL_EASY("Clue scroll (easy)", ItemID.CLUE_SCROLL_EASY), + CLUE_SCROLL_MEDIUM("Clue scroll (medium)", ItemID.CLUE_SCROLL_MEDIUM), + CLUE_SCROLL_HARD("Clue scroll (hard)", ItemID.CLUE_SCROLL_HARD), + CLUE_SCROLL_ELITE("Clue scroll (elite)", ItemID.CLUE_SCROLL_ELITE), + CLUE_SCROLL_MASTER("Clue scroll (master)", ItemID.CLUE_SCROLL_MASTER); + + private final String name; + private final int baseId; + + LootTrackerMapping(String name, int baseId) + { + this.name = name; + this.baseId = baseId; + } + + private static final ImmutableMap MAPPINGS; + + static + { + ImmutableMap.Builder map = ImmutableMap.builder(); + for (LootTrackerMapping mapping : values()) + { + map.put(mapping.name, mapping); + } + MAPPINGS = map.build(); + } + + public static int map(int itemId, ItemManager itemManager) + { + ItemComposition itemComp = itemManager.getItemComposition(itemId); + if (itemComp == null || Strings.isNullOrEmpty(itemComp.getName())) + { + return itemId; + } + + if (!MAPPINGS.containsKey(itemComp.getName())) + { + return itemId; + } + + return MAPPINGS.get(itemComp.getName()).baseId; + } + + public static ItemStack map(ItemStack item, ItemManager itemManager) + { + int baseId = map(item.getId(), itemManager); + if (baseId == item.getId()) + { + return item; + } + + return new ItemStack(baseId, item.getQuantity(), item.getLocation()); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index 2a4afec80a..27a9f82029 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -207,12 +207,14 @@ public class LootTrackerPlugin extends Plugin private LootTrackerClient lootTrackerClient; private final List queuedLoots = new ArrayList<>(); - private static Collection stack(Collection items) + @VisibleForTesting + Collection stack(Collection items) { final List list = new ArrayList<>(); - for (final ItemStack item : items) + for (ItemStack item : items) { + item = LootTrackerMapping.map(item, itemManager); int quantity = 0; for (final ItemStack i : list) { diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java index 2f30c315fc..235b8cf634 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java @@ -32,11 +32,14 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import javax.inject.Inject; +import lombok.AllArgsConstructor; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; +import net.runelite.api.ItemComposition; import net.runelite.api.ItemID; import net.runelite.api.IterableHashTable; import net.runelite.api.MessageNode; @@ -52,6 +55,7 @@ import net.runelite.http.api.item.ItemPrice; import net.runelite.http.api.loottracker.LootRecordType; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -64,6 +68,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; @RunWith(MockitoJUnitRunner.class) public class LootTrackerPluginTest @@ -145,6 +150,137 @@ public class LootTrackerPluginTest assertEquals(LootRecordType.EVENT, lootTrackerPlugin.lootRecordType); } + private static ItemStack is(int id, int q) + { + return new ItemStack(id, q, null); + } + + @Test + public void testClueStacks() + { + String beg = "Clue scroll (beginner)"; + String easy = "Clue scroll (easy)"; + String med = "Clue scroll (medium)"; + String hard = "Clue scroll (hard)"; + String elite = "Clue scroll (elite)"; + String master = "Clue scroll (master)"; + Map idsToName = ImmutableMap.builder() + .put(ItemID.CLUE_SCROLL_BEGINNER, beg) + .put(ItemID.CLUE_SCROLL_EASY, easy) + .put(ItemID.CLUE_SCROLL_EASY_2719, easy) + .put(ItemID.CLUE_SCROLL_EASY_23153, easy) + .put(ItemID.CLUE_SCROLL_MEDIUM, med) + .put(ItemID.CLUE_SCROLL_MEDIUM_3599, med) + .put(ItemID.CLUE_SCROLL_MEDIUM_2817, med) + .put(ItemID.CLUE_SCROLL_MEDIUM_3602, med) + .put(ItemID.CLUE_SCROLL_MEDIUM_12045, med) + .put(ItemID.CLUE_SCROLL_MEDIUM_12065, med) + .put(ItemID.CLUE_SCROLL_HARD, hard) + .put(ItemID.CLUE_SCROLL_HARD_3520, hard) + .put(ItemID.CLUE_SCROLL_HARD_3550, hard) + .put(ItemID.CLUE_SCROLL_HARD_23045, hard) + .put(ItemID.CLUE_SCROLL_ELITE, elite) + .put(ItemID.CLUE_SCROLL_ELITE_19783, elite) + .put(ItemID.CLUE_SCROLL_ELITE_21524, elite) + .put(ItemID.CLUE_SCROLL_ELITE_12096, elite) + .put(ItemID.CLUE_SCROLL_MASTER, master) + .put(ItemID.RUNE_PLATEBODY, "Rune platebody") + .put(ItemID.AMETHYST_ARROW, "Amethyst arrow") + .put(ItemID.GRACEFUL_HOOD_13579, "Graceful hood") + .put(ItemID.RUNITE_ORE, "Runite ore") + .put(ItemID.RUNITE_ORE + 1, "Runite ore") + .put(0, "null") + .build(); + + @AllArgsConstructor + class Case + { + private final List drops; + private final List expected; + } + + Case[] cases = { + new Case( + Arrays.asList( + is(ItemID.CLUE_SCROLL_MEDIUM, 1), + is(ItemID.CLUE_SCROLL_MEDIUM_3602, 1)), + Collections.singletonList( + is(ItemID.CLUE_SCROLL_MEDIUM, 2)) + ), + new Case( + Arrays.asList( + // graceful isn't a drop, but it is an item w/ variations that we're not tracking. + is(ItemID.GRACEFUL_HOOD_13579, 1), + is(ItemID.RUNE_PLATEBODY, 1), + is(ItemID.AMETHYST_ARROW, 125)), + Arrays.asList( + is(ItemID.GRACEFUL_HOOD_13579, 1), + is(ItemID.RUNE_PLATEBODY, 1), + is(ItemID.AMETHYST_ARROW, 125)) + ), + new Case( + Arrays.asList( + is(ItemID.CLUE_SCROLL_BEGINNER, 1), + is(ItemID.CLUE_SCROLL_ELITE_19783, 1), + is(ItemID.CLUE_SCROLL_MEDIUM_12045, 1), + is(ItemID.CLUE_SCROLL_MEDIUM_12065, 1), + is(ItemID.RUNITE_ORE, 25), + is(ItemID.RUNITE_ORE + 1, 10)), // noted rune ore + Arrays.asList( + is(ItemID.CLUE_SCROLL_BEGINNER, 1), + is(ItemID.CLUE_SCROLL_ELITE, 1), + is(ItemID.CLUE_SCROLL_MEDIUM, 2), + is(ItemID.RUNITE_ORE, 25), + is(ItemID.RUNITE_ORE + 1, 10)) // noted rune ore + ), + new Case( + Arrays.asList( + is(ItemID.CLUE_SCROLL_BEGINNER, 1), + is(ItemID.CLUE_SCROLL_BEGINNER, 1), + is(ItemID.CLUE_SCROLL_EASY, 1), + is(ItemID.CLUE_SCROLL_EASY_2719, 1), + is(ItemID.CLUE_SCROLL_EASY_23153, 1), + is(ItemID.CLUE_SCROLL_MEDIUM, 1), + is(ItemID.CLUE_SCROLL_MEDIUM_12065, 1), + is(ItemID.CLUE_SCROLL_MEDIUM_2817, 1), + is(ItemID.CLUE_SCROLL_HARD, 1), + is(ItemID.CLUE_SCROLL_HARD_3550, 1), + is(ItemID.CLUE_SCROLL_HARD_23045, 1), + is(ItemID.CLUE_SCROLL_ELITE, 1), + is(ItemID.CLUE_SCROLL_ELITE_21524, 1), + is(ItemID.CLUE_SCROLL_ELITE_12096, 1), + is(ItemID.CLUE_SCROLL_MASTER, 1), + is(ItemID.CLUE_SCROLL_MASTER, 1)), + Arrays.asList( + is(ItemID.CLUE_SCROLL_BEGINNER, 2), + is(ItemID.CLUE_SCROLL_EASY, 3), + is(ItemID.CLUE_SCROLL_MEDIUM, 3), + is(ItemID.CLUE_SCROLL_HARD, 3), + is(ItemID.CLUE_SCROLL_ELITE, 3), + is(ItemID.CLUE_SCROLL_MASTER, 2)) + ), + }; + + for (int i = 0; i < cases.length; i++) + { + Case tc = cases[i]; + when(itemManager.getItemComposition(anyInt())).thenAnswer((Answer) invocationOnMock -> + { + int itemId = invocationOnMock.getArgument(0); + if (!idsToName.containsKey(itemId)) + { + fail("item id not in names map: " + itemId); + } + + ItemComposition c = mock(ItemComposition.class); + when(c.getName()).thenReturn(idsToName.get(itemId)); + return c; + }); + + assertEquals("Test case: " + (i + 1), tc.expected, lootTrackerPlugin.stack(tc.drops)); + } + } + @Test public void testHerbiboarHerbSack() { From 7bea9f9f3b271b5e9c59250c7785974f9ac14204 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Wed, 22 Apr 2020 13:15:43 -0700 Subject: [PATCH 13/27] timers: Remove color matching for teleblock messages When playing using the resizable viewport and the transparent chatbox, teleblock messages are sent using a different color than when using the standard chatbox. This commit removes that color matching entirely from messages indicating that a teleblock timer has been applied or removed. --- .../client/plugins/timers/TimersPlugin.java | 10 +++---- .../plugins/timers/TimersPluginTest.java | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java index 34fd1de61a..71d3b67e95 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java @@ -104,12 +104,12 @@ public class TimersPlugin extends Plugin private static final String STAMINA_EXPIRED_MESSAGE = "Your stamina potion has expired."; private static final String SUPER_ANTIFIRE_DRINK_MESSAGE = "You drink some of your super antifire potion"; private static final String SUPER_ANTIFIRE_EXPIRED_MESSAGE = "Your super antifire potion has expired."; - private static final String KILLED_TELEBLOCK_OPPONENT_TEXT = "Your Tele Block has been removed because you killed "; + private static final String KILLED_TELEBLOCK_OPPONENT_TEXT = "Your Tele Block has been removed because you killed "; private static final String PRAYER_ENHANCE_EXPIRED = "Your prayer enhance effect has worn off."; - private static final Pattern DEADMAN_HALF_TELEBLOCK_PATTERN = Pattern.compile("A Tele Block spell has been cast on you by (.+)\\. It will expire in 1 minute, 15 seconds\\."); - private static final Pattern FULL_TELEBLOCK_PATTERN = Pattern.compile("A Tele Block spell has been cast on you by (.+)\\. It will expire in 5 minutes\\."); - private static final Pattern HALF_TELEBLOCK_PATTERN = Pattern.compile("A Tele Block spell has been cast on you by (.+)\\. It will expire in 2 minutes, 30 seconds\\."); + private static final Pattern DEADMAN_HALF_TELEBLOCK_PATTERN = Pattern.compile("A Tele Block spell has been cast on you by (.+)\\. It will expire in 1 minute, 15 seconds\\."); + private static final Pattern FULL_TELEBLOCK_PATTERN = Pattern.compile("A Tele Block spell has been cast on you by (.+)\\. It will expire in 5 minutes\\."); + private static final Pattern HALF_TELEBLOCK_PATTERN = Pattern.compile("A Tele Block spell has been cast on you by (.+)\\. It will expire in 2 minutes, 30 seconds\\."); private static final Pattern DIVINE_POTION_PATTERN = Pattern.compile("You drink some of your divine (.+) potion\\."); private static final int VENOM_VALUE_CUTOFF = -40; // Antivenom < -40 <= Antipoison < 0 private static final int POISON_TICK_LENGTH = 30; @@ -528,7 +528,7 @@ public class TimersPlugin extends Plugin { createGameTimer(DMM_HALFTB); } - else if (event.getMessage().startsWith(KILLED_TELEBLOCK_OPPONENT_TEXT)) + else if (event.getMessage().contains(KILLED_TELEBLOCK_OPPONENT_TEXT)) { removeTbTimers(); } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java index 3531a82c29..ea51b20747 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java @@ -42,7 +42,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import static org.mockito.ArgumentMatchers.any; import org.mockito.Mock; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; @@ -53,6 +55,8 @@ public class TimersPluginTest private static final String DMM_HALF_TELEBLOCK_MESSAGE = "A Tele Block spell has been cast on you by Runelite. It will expire in 1 minute, 15 seconds."; private static final String FULL_TELEBLOCK_MESSAGE = "A Tele Block spell has been cast on you by Runelite. It will expire in 5 minutes."; private static final String HALF_TELEBLOCK_MESSAGE = "A Tele Block spell has been cast on you by Runelite. It will expire in 2 minutes, 30 seconds."; + private static final String TRANSPARENT_CHATBOX_FULL_TELEBLOCK_MESSAGE = "A Tele Block spell has been cast on you by Alexsuperfly. It will expire in 5 minutes."; + private static final String TRANSPARENT_CHATBOX_TELEBLOCK_REMOVED_MESSAGE = "Your Tele Block has been removed because you killed Alexsuperfly."; @Inject private TimersPlugin timersPlugin; @@ -162,4 +166,27 @@ public class TimersPluginTest TimerTimer infoBox = (TimerTimer) captor.getValue(); assertEquals(GameTimer.DIVINE_BATTLEMAGE, infoBox.getTimer()); } + + @Test + public void testTransparentChatboxTb() + { + when(timersConfig.showTeleblock()).thenReturn(true); + ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", TRANSPARENT_CHATBOX_FULL_TELEBLOCK_MESSAGE, "", 0); + timersPlugin.onChatMessage(chatMessage); + + ArgumentCaptor captor = ArgumentCaptor.forClass(InfoBox.class); + verify(infoBoxManager).addInfoBox(captor.capture()); + TimerTimer infoBox = (TimerTimer) captor.getValue(); + assertEquals(GameTimer.FULLTB, infoBox.getTimer()); + } + + @Test + public void testTransparentChatboxTbRemoved() + { + when(timersConfig.showTeleblock()).thenReturn(true); + ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", TRANSPARENT_CHATBOX_TELEBLOCK_REMOVED_MESSAGE, "", 0); + timersPlugin.onChatMessage(chatMessage); + + verify(infoBoxManager, atLeastOnce()).removeIf(any()); + } } \ No newline at end of file From 137fa0e3cf90779359b9094e83cc374495f18c42 Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Thu, 5 Mar 2020 07:00:51 +0000 Subject: [PATCH 14/27] Revert "npc aggro timer: Add option to hide hint overlay (#8610)" This reverts commit 1242a508bc2cf00b37f61c73d685aeadc98e1e71. --- .../plugins/npcunaggroarea/NpcAggroAreaConfig.java | 13 +------------ .../NpcAggroAreaNotWorkingOverlay.java | 6 ++---- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaConfig.java index b13901a4cc..8f101f90c2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaConfig.java @@ -93,22 +93,11 @@ public interface NpcAggroAreaConfig extends Config return Color.YELLOW; } - @ConfigItem( - keyName = "npcUnaggroShowNotWorkingOverlay", - name = "Hide overlay hint", - description = "Hide overlay hint if plugin is enabled in unsupported area", - position = 6 - ) - default boolean hideOverlayHint() - { - return false; - } - @ConfigItem( keyName = "notifyExpire", name = "Notify Expiration", description = "Send a notifcation when the unaggressive timer expires", - position = 7 + position = 6 ) default boolean notifyExpire() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaNotWorkingOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaNotWorkingOverlay.java index 5c6caed46c..33e058f686 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaNotWorkingOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaNotWorkingOverlay.java @@ -35,13 +35,11 @@ import net.runelite.client.ui.overlay.components.LineComponent; class NpcAggroAreaNotWorkingOverlay extends OverlayPanel { private final NpcAggroAreaPlugin plugin; - private final NpcAggroAreaConfig config; @Inject - private NpcAggroAreaNotWorkingOverlay(NpcAggroAreaPlugin plugin, NpcAggroAreaConfig config) + private NpcAggroAreaNotWorkingOverlay(NpcAggroAreaPlugin plugin) { this.plugin = plugin; - this.config = config; panelComponent.getChildren().add(LineComponent.builder() .left("Unaggressive NPC timers will start working when you teleport far away or enter a dungeon.") @@ -55,7 +53,7 @@ class NpcAggroAreaNotWorkingOverlay extends OverlayPanel @Override public Dimension render(Graphics2D graphics) { - if (!plugin.isActive() || plugin.getSafeCenters()[1] != null || config.hideOverlayHint()) + if (!plugin.isActive() || plugin.getSafeCenters()[1] != null) { return null; } From db684c98d9554214844f6140457727e04efbfbc3 Mon Sep 17 00:00:00 2001 From: Dock Date: Sat, 2 May 2020 13:43:21 -0400 Subject: [PATCH 15/27] client session manager: fix shutdown race deleting a null session --- .../main/java/net/runelite/client/ClientSessionManager.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ClientSessionManager.java b/runelite-client/src/main/java/net/runelite/client/ClientSessionManager.java index a7d412b513..5009bd509d 100644 --- a/runelite-client/src/main/java/net/runelite/client/ClientSessionManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ClientSessionManager.java @@ -81,7 +81,11 @@ public class ClientSessionManager { try { - sessionClient.delete(sessionId); + UUID localUuid = sessionId; + if (localUuid != null) + { + sessionClient.delete(localUuid); + } } catch (IOException ex) { From d8bf99820e1d92f9bc65738c8e1f56c39e9469c6 Mon Sep 17 00:00:00 2001 From: Arman S Date: Sat, 2 May 2020 14:48:31 -0400 Subject: [PATCH 16/27] worldmap: Add dungeon names (#11419) --- .../plugins/worldmap/DungeonLocation.java | 221 ++++++++++++++++++ .../client/plugins/worldmap/DungeonPoint.java | 38 +++ .../plugins/worldmap/WorldMapConfig.java | 11 + .../plugins/worldmap/WorldMapPlugin.java | 12 +- 4 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonLocation.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonPoint.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonLocation.java new file mode 100644 index 0000000000..13e4c59233 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonLocation.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2020, Arman S + * 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 HOLDER 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.worldmap; + +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum DungeonLocation +{ + ABANDONED_MINE("Abandoned Mine", new WorldPoint(3439, 3232, 0)), + ABANDONED_MINE_SECRET("Abandoned Mine (secret entrance)", new WorldPoint(3452, 3244, 0)), + ANCIENT_CAVERN("Ancient Cavern", new WorldPoint(2511, 3508, 0)), + APE_ATOLL("Ape Atoll Dungeon", new WorldPoint(2762, 2703, 0)), + ARDOUGNE_CASTLE("Ardougne Castle basement", new WorldPoint(2569, 3296, 0)), + ARDOUGNE_RAT_PITS("Ardougne Rat Pits", new WorldPoint(2560, 3320, 0)), + ARDOUGNE_SEWERS_N("Ardougne Sewers", new WorldPoint(2631, 3294, 0)), + ARDOUGNE_SEWERS_S("Ardougne Sewers", new WorldPoint(2586, 3235, 0)), + ARDOUGNE_SEWERS_W("Ardougne Sewers", new WorldPoint(2528, 3303, 0)), + ASGARDIA_ICE("Asgardia Ice Dungeon", new WorldPoint(3007, 3150, 0)), + BRIMHAVEN_AGILITY("Brimhaven Agility Arena", new WorldPoint(2808, 3194, 0)), + BRIMHAVEN_N("Brimhaven Dungeon", new WorldPoint(2743, 3154, 0)), + BRIMHAVEN_S("Brimhaven Dungeon", new WorldPoint(2759, 3062, 0)), + BRIMSTAILS_CAVE("Brimstail's Cave", new WorldPoint(2402, 3419, 0)), + CATACOMBS_OF_KOUREND("Catacombs of Kourend", new WorldPoint(1636, 3673, 0)), + CHAMBERS_OF_XERIC("Chambers of Xeric", new WorldPoint(1232, 3573, 0)), + CHAMPIONS_CHALLENGE("Champions' Challenge", new WorldPoint(3188, 3355, 0)), + CHAOS_DRUID_TOWER("Chaos Druid Tower dungeon", new WorldPoint(2561, 3356, 0)), + CHASM_OF_FIRE("Chasm of Fire", new WorldPoint(1432, 3670, 0)), + CLOCK_TOWER("Clock Tower Dungeon", new WorldPoint(2568, 3229, 0)), + CORPOREAL_BEAST("Corporeal Beast cave", new WorldPoint(3202, 3681, 0)), + CORSAIR_COVE_E("Corsair Cove Dungeon", new WorldPoint(2522, 2861, 0)), + CORSAIR_COVE_N("Corsair Cove Dungeon", new WorldPoint(2482, 2891, 0)), + CRABCLAW_CAVES("Crabclaw Caves", new WorldPoint(1643, 3449, 0)), + CRABCLAW_CAVES_TUNNEL("Crabclaw Caves Tunnel (quest)", new WorldPoint(1643, 3449, 0)), + CRANDOR("Crandor Dungeon", new WorldPoint(2833, 3256, 0)), + CRASH_ISLAND("Crash Island Dungeon", new WorldPoint(2920, 2721, 0)), + DEEP_WILDERNESS("Deep Wilderness Dungeon", new WorldPoint(3044, 3924, 0)), + DRAYNOR_MANOR_E("Draynor Manor basement", new WorldPoint(3114, 3357, 0)), + DRAYNOR_MANOR_W("Draynor Manor basement", new WorldPoint(3091, 3362, 0)), + DRAYNOR_SEWERS_NW("Draynor Sewers", new WorldPoint(3083, 3272, 0)), + DRAYNOR_SEWERS_SE("Draynor Sewers", new WorldPoint(3117, 3244, 0)), + DWARVEN_MINE("Dwarven Mine", new WorldPoint(3017, 3450, 0)), + DWARVEN_MINE_STAIRS("Dwarven Mine", new WorldPoint(3058, 3377, 0)), + ECTOFUNTUS("Ectofuntus dungeon", new WorldPoint(3651, 3519, 0)), + EDGEVILLE("Edgeville Dungeon", new WorldPoint(3096, 3469, 0)), + EDGEVILLE_SHED("Edgeville Dungeon", new WorldPoint(3115, 3452, 0)), + EDGEVILLE_WILDERNESS("Edgeville Dungeon", new WorldPoint(3087, 3571, 0)), + ENTRANA("Entrana Dungeon", new WorldPoint(2819, 3374, 0)), + ETCETERIA("Miscellania and Etceteria Dungeon", new WorldPoint(2619, 3865, 0)), + FALADOR_MOLE_NW("Mole Hole", new WorldPoint(2985, 3387, 0)), + FALADOR_MOLE_SE("Mole Hole", new WorldPoint(2997, 3376, 0)), + FORSAKEN_TOWER("Forsaken Tower basement", new WorldPoint(1381, 3825, 0)), + FORTHOS_E("Forthos Dungeon", new WorldPoint(1701, 3574, 0)), + FORTHOS_W("Forthos Dungeon", new WorldPoint(1669, 3567, 0)), + FREMENNIK_SLAYER("Fremennik Slayer Dungeon", new WorldPoint(2796, 3615, 0)), + GOBLIN_CAVE("Goblin Cave", new WorldPoint(2622, 3393, 0)), + GOD_WARS("God Wars Dungeon", new WorldPoint(2917, 3747, 0)), + GRAND_TREE("Grand Tree Tunnels", new WorldPoint(2464, 3496, 0)), + GAUNTLET("The Gauntlet", new WorldPoint(3227, 6116, 0)), + HAM_HIDEOUT("H.A.M. Hideout", new WorldPoint(3164, 3252, 0)), + HEROES_GUILD("Heroes' Guild mine", new WorldPoint(2891, 3507, 0)), + HESPORI("Hespori Patch", new WorldPoint(1231, 3729, 0)), + ICE_QUEEN("Ice Queen's Lair", new WorldPoint(2846, 3516, 0)), + ICE_QUEEN_E("Ice Queen's Lair", new WorldPoint(2856, 3519, 0)), + ICE_QUEEN_W("Ice Queen's Lair", new WorldPoint(2822, 3510, 0)), + ICE_TROLL_E("Ice Troll Caves", new WorldPoint(2400, 3889, 0)), + ICE_TROLL_W("Ice Troll Caves", new WorldPoint(2315, 3894, 0)), + IORWERTH("Iorwerth Dungeon", new WorldPoint(3224, 6044, 0)), + IORWERTH_CAMP_CAVE("Iorwerth Camp cave", new WorldPoint(2200, 3262, 0)), + IORWERTH_CAMP_CAVE_PRIF("Iorwerth Camp cave", new WorldPoint(3224, 6014, 0)), + JOGRE("Jogre Dungeon", new WorldPoint(2824, 3118, 0)), + JORMUNGANDS_PRISON("Jormungand's Prison", new WorldPoint(2464, 4012, 0)), + KALPHITE_CAVE("Kalphite Cave", new WorldPoint(3319, 3122, 0)), + KALPHITE_LAIR("Kalphite Lair", new WorldPoint(3226, 3108, 0)), + KARAMJA_VOLCANO("Karamja Dungeon", new WorldPoint(2855, 3168, 0)), + KARUULM_SLAYER("Karuulm Slayer Dungeon", new WorldPoint(1308, 3807, 0)), + KING_BLACK_DRAGON("King Black Dragon Lair", new WorldPoint(3016, 3849, 0)), + KELDAGRIM("Keldagrim Entrance", new WorldPoint(2730, 3713, 0)), + KELDAGRIM_ENTRANCE_MOUNTAIN("Keldagrim Entrance", new WorldPoint(2795, 3718, 0)), + KRAKEN_BOSS("Kraken (boss)", new WorldPoint(2279, 10017, 0)), + KRAKEN_COVE("Kraken Cove", new WorldPoint(2277, 3611, 0)), + LAVA_MAZE("Lava Maze Dungeon", new WorldPoint(3068, 3856, 0)), + LEGENDS_GUILD("Legends' Guild dungeon", new WorldPoint(2723, 3375, 0)), + LIGHTHOUSE("Lighthouse basement", new WorldPoint(2508, 3644, 0)), + LIZARDMAN_CAVES("Lizardman Caves", new WorldPoint(1306, 3574, 0)), + LIZARDMAN_TEMPLE_E("Lizardman Temple", new WorldPoint(1329, 3669, 0)), + LIZARDMAN_TEMPLE_N("Lizardman Temple", new WorldPoint(1311, 3686, 0)), + LIZARDMAN_TEMPLE_S("Lizardman Temple", new WorldPoint(1313, 3663, 0)), + LIZARDMAN_TEMPLE_W("Lizardman Temple", new WorldPoint(1291, 3657, 0)), + LUMBRIDGE_CASTLE("Lumbridge Castle cellar", new WorldPoint(3208, 3218, 0)), + LUMBRIDGE_SWAMP("Lumbridge Swamp Caves", new WorldPoint(3168, 3172, 0)), + LUNAR_ISLE("Lunar Isle Mine", new WorldPoint(2141, 3944, 0)), + MAGE_ARENA("Mage Arena Bank", new WorldPoint(3089, 3956, 0)), + MELZARS_MAZE("Melzar's Maze basement", new WorldPoint(2923, 3250, 0)), + MINING_GUILD("Mining Guild basement", new WorldPoint(3018, 3339, 0)), + MISCELLANIA("Miscellania and Etceteria Dungeon", new WorldPoint(2508, 3846, 0)), + MOS_LE_HARMLESS("Mos Le'Harmless Cave", new WorldPoint(3747, 2973, 0)), + MOS_LE_HARMLESS_ISLAND_E("Mos Le'Harmless Cave", new WorldPoint(3829, 3062, 0)), + MOS_LE_HARMLESS_ISLAND_W("Mos Le'Harmless Cave", new WorldPoint(3814, 3062, 0)), + MOTHERLODE_MINE("Motherlode Mine", new WorldPoint(3059, 9764, 0)), + MOTHERLODE_MINE_GUILD("Motherlode Mine", new WorldPoint(3055, 9744, 0)), + MYREQUE_HIDEOUT_CANAFIS("Myreque Hideout", new WorldPoint(3494, 3464, 0)), + MYREQUE_HIDEOUT_SWAMP("Myreque Hideout", new WorldPoint(3508, 3448, 0)), + MYTH_GUILD("Myths' Guild dungeon", new WorldPoint(2456, 2847, 0)), + MYTH_GUILD_WRATH("Myths' Guild dungeon", new WorldPoint(2444, 2819, 0)), + OBSERVATORY_NE("Observatory Dungeon", new WorldPoint(2458, 3186, 0)), + OBSERVATORY_SW("Observatory Dungeon", new WorldPoint(2436, 3163, 0)), + OGRE_ENCLAVE("Ogre Enclave", new WorldPoint(2505, 3039, 0)), + OURANIA_CAVE("Ourania Cave", new WorldPoint(2451, 3231, 0)), + PATERDOMUS("Paterdomus basement", new WorldPoint(3404, 3506, 0)), + PORT_PHASMATYS_BREWERY("Port Phasmatys Brewery", new WorldPoint(3679, 3498, 0)), + PORT_SARIM_RAT_PITS("Port Sarim Rat Pits", new WorldPoint(3017, 3232, 0)), + QUIDAMORTEM_CAVE("Quidamortem Cave", new WorldPoint(1213, 3559, 0)), + RED_CHIN_HUNTING("Red chinchompa hunting ground", new WorldPoint(2525, 2894, 0)), + REVENANT_CAVES_N("Revenant Caves", new WorldPoint(3124, 3832, 0)), + REVENANT_CAVES_S("Revenant Caves", new WorldPoint(3074, 3655, 0)), + RIVER_ELID("River Elid Dungeon", new WorldPoint(3370, 3132, 0)), + RIVER_KELDA("River Kelda", new WorldPoint(2835, 10112, 0)), + SALT_MINE("Salt Mine", new WorldPoint(2866, 3941, 0)), + SCAVID_CAVES_N("Scavid Caves", new WorldPoint(2521, 3070, 0)), + SCAVID_CAVES_NE("Scavid Caves", new WorldPoint(2540, 3054, 0)), + SCAVID_CAVES_NEE("Scavid Caves", new WorldPoint(2552, 3054, 0)), + SCAVID_CAVES_E("Scavid Caves", new WorldPoint(2551, 3035, 0)), + SCAVID_CAVES_ISLAND_E("Scavid Caves (island)", new WorldPoint(2574, 3028, 0)), + SCAVID_CAVES_ISLAND_W("Scavid Caves (island)", new WorldPoint(2500, 2991, 0)), + SCAVID_CAVES_S("Scavid Caves", new WorldPoint(2528, 3014, 0)), + SCAVID_CAVES_SEE("Scavid Caves", new WorldPoint(2561, 3025, 0)), + SCORPIA_E("Scorpia cave", new WorldPoint(3243, 3949, 0)), + SCORPIA_N("Scorpia cave", new WorldPoint(3230, 3952, 0)), + SCORPIA_S("Scorpia cave", new WorldPoint(3231, 3936, 0)), + SHADE_CATACOMBS("Shade Catacombs", new WorldPoint(3484, 3321, 0)), + SHAYZIEN_CRYPTS("Shayzien Crypts", new WorldPoint(1482, 3549, 0)), + SHILO_VILLAGE_MINE("Shilo Village mine", new WorldPoint(2823, 3001, 0)), + SISTERHOOD_SANCTUARY("Sisterhood Sanctuary", new WorldPoint(3727, 3300, 0)), + SISTERHOOD_SANCTUARY_LAB("Sisterhood Sanctuary", new WorldPoint(3724, 3356, 0)), + SLAYER_TOWER("Slayer Tower basement", new WorldPoint(3416, 3535, 0)), + SLEPE_BASEMENT("Slepe basement", new WorldPoint(3718, 3307, 0)), + SMOKE_DEVIL("Smoke Devil Dungeon", new WorldPoint(2411, 3061, 0)), + SMOKE_DEVIL_BOSS("Thermonuclear Smoke Devil (boss)", new WorldPoint(2377, 9452, 0)), + SMOKE_DUNGEON("Smoke Dungeon", new WorldPoint(3309, 2962, 0)), + SOPHANEM("Sophanem Dungeon", new WorldPoint(3314, 2797, 0)), + STRONGHOLD_OF_SECURITY("Stronghold of Security", new WorldPoint(3080, 3420, 0)), + STRONGHOLD_SLAYER("Stronghold Slayer Dungeon", new WorldPoint(2427, 3424, 0)), + TAVERLY("Taverly Dungeon", new WorldPoint(2883, 3397, 0)), + TAVERLY_ISLAND("Taverly Dungeon", new WorldPoint(2841, 3424, 0)), + TEMPLE_OF_IKOV("Temple of Ikov", new WorldPoint(2676, 3404, 0)), + THEATRE_OF_BLOOD("Theatre of Blood", new WorldPoint(3676, 3219, 0)), + TOLNAS_RIFT("Tolna's rift", new WorldPoint(3308, 3450, 0)), + TOWER_OF_LIFE("Tower of Life dungeon", new WorldPoint(2649, 3213, 0)), + TRAHAEARN_MINE("Trahaearn Mine", new WorldPoint(3270, 6049, 0)), + TREE_GNOME_VILLAGE("Tree Gnome Village Dungeon", new WorldPoint(2532, 3155, 0)), + TROLL_STRONGHOLD("Troll Stronghold", new WorldPoint(2838, 3690, 0)), + TROLL_STRONGHOLD_ROOF("Troll Stronghold", new WorldPoint(2830, 3677, 0)), + TROLL_STRONGHOLD_MOUNTAIN("Troll Stronghold", new WorldPoint(2826, 3647, 0)), + TUTORIAL_ISLAND_E("Tutorial Island mine", new WorldPoint(3110, 3126, 0)), + TUTORIAL_ISLAND_W("Tutorial Island mine", new WorldPoint(3087, 3119, 0)), + TZHAAR_CITY("TzHaar City (Mor Ul Rek)", new WorldPoint(2862, 9572, 0)), + UNDERGROUND_PASS_KANDARIN("Underground Pass", new WorldPoint(2312, 3215, 0)), + UNDERGROUND_PASS_TIRANNWN("Underground Pass", new WorldPoint(2433, 3315, 0)), + UNDERGROUND_PASS_TIRANNWN_PRIF("Underground Pass", new WorldPoint(3336, 5967, 0)), + UNDERWATER("Underwater", new WorldPoint(3765, 3898, 0)), + UZER("Ruins of Uzer basement", new WorldPoint(3492, 3090, 0)), + VARROCK_SEWERS("Varrock Sewers", new WorldPoint(3236, 3458, 0)), + VARROCK_SEWERS_ZOO("Varrock Sewers", new WorldPoint(3229, 3504, 0)), + VOLCANIC_MINE("Volcanic Mine", new WorldPoint(3815, 3808, 0)), + VTAM_CORP("VTAM Corporation", new WorldPoint(3243, 3383, 0)), + WARRENS("The Warrens", new WorldPoint(1812, 3745, 0)), + WARRIORS_GUILD("Warriors' Guild basement", new WorldPoint(2832, 3542, 0)), + WATERBIRTH("Waterbirth Dungeon", new WorldPoint(2520, 3740, 0)), + WATERBIRTH_MOUNTAIN("Waterbirth Dungeon", new WorldPoint(2542, 3741, 0)), + WATERBIRTH_SUBLEVELS("Waterbirth Dungeon (sub-levels)", new WorldPoint(2545, 10143, 0)), + WEISS_HOLE("Hole (shortcut to boat)", new WorldPoint(2853, 3944, 0)), + WEREWOLF_AGILITY("Werewolf Agility Course", new WorldPoint(3542, 3461, 0)), + WHITE_WOLF_TUNNEL_E("White Wolf Tunnel", new WorldPoint(2876, 3480, 0)), + WHITE_WOLF_TUNNEL_W("White Wolf Tunnel", new WorldPoint(2819, 3484, 0)), + WILDERNESS_AGILITY("Wilderness Agility Course Dungeon", new WorldPoint(3004, 3963, 0)), + WILDERNESS_GOD_WARS("Wilderness God Wars Dungeon", new WorldPoint(3016, 3739, 0)), + WITCHAVEN("Witchaven Dungeon", new WorldPoint(2695, 3283, 0)), + WIZARDS_GUILD("Wizards' Guild basement", new WorldPoint(2593, 3085, 0)), + WIZARDS_TOWER("Wizards' Tower basement", new WorldPoint(3103, 3162, 0)), + WOODCUTTING_GUILD("Woodcutting Guild dungeon", new WorldPoint(1603, 3508, 0)), + WYVERN_CAVE("Wyvern Cave", new WorldPoint(3745, 3779, 0)), + WYVERN_CAVE_TASK("Wyvern Cave (task only)", new WorldPoint(3677, 3854, 0)), + YANILLE_AGILITY("Yanille Agility Dungeon", new WorldPoint(2568, 3122, 0)), + YANILLE_AGILITY_CITY("Yanille Agility Dungeon", new WorldPoint(2603, 3078, 0)), + ZALCANO("Zalcano's Prison", new WorldPoint(3280, 6059, 0)), + ZOGRE_CAVE("Zogre cave", new WorldPoint(2484, 3043, 0)); + + private final String tooltip; + private final WorldPoint location; + + DungeonLocation(String tooltip, WorldPoint location) + { + this.tooltip = tooltip; + this.location = location; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonPoint.java new file mode 100644 index 0000000000..9d50ec322f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonPoint.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020, Arman S + * 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 HOLDER 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.worldmap; + +import java.awt.image.BufferedImage; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; + +class DungeonPoint extends WorldMapPoint +{ + DungeonPoint(DungeonLocation data, BufferedImage icon) + { + super(data.getLocation(), icon); + setTooltip(data.getTooltip()); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapConfig.java index 50d5312d82..1b2103d992 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapConfig.java @@ -240,4 +240,15 @@ public interface WorldMapConfig extends Config { return true; } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_DUNGEON_TOOLTIPS, + name = "Show dungeon tooltips", + description = "Indicates the names of dungeons", + position = 20 + ) + default boolean dungeonTooltips() + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java index b8e8293ad7..4865401cb1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java @@ -50,7 +50,7 @@ import net.runelite.client.util.ImageUtil; @PluginDescriptor( name = "World Map", description = "Enhance the world map to display additional information", - tags = {"agility", "fairy", "farming", "rings", "teleports"} + tags = {"agility", "dungeon", "fairy", "farming", "rings", "teleports"} ) public class WorldMapPlugin extends Plugin { @@ -82,6 +82,7 @@ public class WorldMapPlugin extends Plugin static final String CONFIG_KEY_TRANSPORATION_TELEPORT_TOOLTIPS = "transportationTooltips"; static final String CONFIG_KEY_RUNECRAFTING_ALTAR_ICON = "runecraftingAltarIcon"; static final String CONFIG_KEY_MINING_SITE_TOOLTIPS = "miningSiteTooltips"; + static final String CONFIG_KEY_DUNGEON_TOOLTIPS = "dungeonTooltips"; static { @@ -159,6 +160,7 @@ public class WorldMapPlugin extends Plugin worldMapPointManager.removeIf(FarmingPatchPoint.class::isInstance); worldMapPointManager.removeIf(RareTreePoint.class::isInstance); worldMapPointManager.removeIf(RunecraftingAltarPoint.class::isInstance); + worldMapPointManager.removeIf(DungeonPoint.class::isInstance); agilityLevel = 0; woodcuttingLevel = 0; } @@ -328,6 +330,14 @@ public class WorldMapPlugin extends Plugin .map(value -> new MiningSitePoint(value, value.isIconRequired() ? MINING_SITE_ICON : BLANK_ICON)) .forEach(worldMapPointManager::add); } + + worldMapPointManager.removeIf(DungeonPoint.class::isInstance); + if (config.dungeonTooltips()) + { + Arrays.stream(DungeonLocation.values()) + .map(value -> new DungeonPoint(value, BLANK_ICON)) + .forEach(worldMapPointManager::add); + } } private void updateQuestStartPointIcons() From d99a426e0d1631bd77b756175960e1ba450f5266 Mon Sep 17 00:00:00 2001 From: raynerrebello Date: Sun, 3 May 2020 03:27:40 +0800 Subject: [PATCH 17/27] poh: Add new POH overlay icons for Weiss and Troll Stronghold portals (#11360) --- .../net/runelite/client/plugins/poh/PohIcons.java | 3 ++- .../runelite/client/plugins/poh/PohOverlay.java | 2 +- .../net/runelite/client/plugins/poh/trollheim.png | Bin 746 -> 0 bytes .../client/plugins/poh/trollstronghold.png | Bin 0 -> 417 bytes .../net/runelite/client/plugins/poh/weiss.png | Bin 0 -> 423 bytes 5 files changed, 3 insertions(+), 2 deletions(-) delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/poh/trollheim.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/poh/trollstronghold.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/poh/weiss.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohIcons.java b/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohIcons.java index 370f8061a5..d9ebe6754b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohIcons.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohIcons.java @@ -49,10 +49,11 @@ public enum PohIcons ANNAKARL("annakarl", ANNAKARL_PORTAL, ANNAKARL_PORTAL_29349, ANNAKARL_PORTAL_29357), KOUREND("kourend", KOUREND_PORTAL, KOUREND_PORTAL_29353, KOUREND_PORTAL_29361), MARIM("marim", MARIM_PORTAL, MARIM_PORTAL_29352, MARIM_PORTAL_29360), - TROLLSTRONGHOLD("trollheim", TROLL_STRONGHOLD_PORTAL, TROLL_STRONGHOLD_PORTAL_33180, TROLL_STRONGHOLD_PORTAL_33181), + TROLLSTRONGHOLD("trollstronghold", TROLL_STRONGHOLD_PORTAL, TROLL_STRONGHOLD_PORTAL_33180, TROLL_STRONGHOLD_PORTAL_33181), GHORROCK("ghorrock", GHORROCK_PORTAL, GHORROCK_PORTAL_33436, GHORROCK_PORTAL_33439), CARRALLANGAR("carrallangar", CARRALLANGAR_PORTAL, CARRALLANGAR_PORTAL_33437, CARRALLANGAR_PORTAL_33440), CATHERBY("catherby", CATHERBY_PORTAL, CATHERBY_PORTAL_33435, CATHERBY_PORTAL_33438), + WEISS("weiss", WEISS_PORTAL, WEISS_PORTAL_37593, WEISS_PORTAL_37605), ALTAR("altar", ALTAR_13179, ALTAR_13180, ALTAR_13181, ALTAR_13182, ALTAR_13183, ALTAR_13184, ALTAR_13185, ALTAR_13186, ALTAR_13187, ALTAR_13188, ALTAR_13189, ALTAR_13190, ALTAR_13191, ALTAR_13192, ALTAR_13193, ALTAR_13194, diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohOverlay.java index 73d970a5a2..b9870ff737 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/poh/PohOverlay.java @@ -44,7 +44,7 @@ public class PohOverlay extends Overlay { PohIcons.LUMBRIDGE, PohIcons.FALADOR, PohIcons.VARROCK, PohIcons.CAMELOT, PohIcons.ARDOUGNE, PohIcons.YANILLE, PohIcons.LUNARISLE, PohIcons.WATERBIRTH, PohIcons.FISHINGGUILD, - PohIcons.SENNTISTEN, PohIcons.KHARYLL, PohIcons.ANNAKARL, PohIcons.KOUREND, PohIcons.MARIM, PohIcons.TROLLSTRONGHOLD, PohIcons.CARRALLANGAR, PohIcons.CATHERBY, PohIcons.GHORROCK + PohIcons.SENNTISTEN, PohIcons.KHARYLL, PohIcons.ANNAKARL, PohIcons.KOUREND, PohIcons.MARIM, PohIcons.TROLLSTRONGHOLD, PohIcons.CARRALLANGAR, PohIcons.CATHERBY, PohIcons.WEISS, PohIcons.GHORROCK }; diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/poh/trollheim.png b/runelite-client/src/main/resources/net/runelite/client/plugins/poh/trollheim.png deleted file mode 100644 index 31ffe765fba2810a59b09e319421f5072ddc3ad7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 746 zcmeAS@N?(olHy`uVBq!ia0vp^{245bDP46hOx7_4S6Fo+k-*%fF5l-LsB z6XFV_;lRi!7{Zp5Gm@6p_4Tb^wd()!<^P3+<)=*f=H_0nsN|-g;3O)lylByXU!Q(y zNspf9|6Ps$8yc?I*yKCgP4e;G=;^uM!Xnzvu1HMOuD0yIx6g*h&z{}B_fTHmc=ek9 zDk|PRE&n~e<^nBx_3F|8|NmF4_^+hoR$ly-lV46-Tf@s|o}r;;eZ~Kf(49K^`fopc zcCnw_*Y-a!XuFJ*XK%~@?&kjwB)?Ap>Sr$r@(X5AIA6cNf4{QWW+|W$W0JSKi&pTy zEAxOH&H|6fVg?3oVGw3ym^DWND7e_u#W95Ada8e>xRZm(TtTO4lX4=3)l#Gd|Nj3k zx>#TK`)%v*cX=BmC)_Ho=4z5JGtrk}nRjoky0X`%qL38N6prek^S500XQY2Msdu<$ zpnG@UtzD^7IxJHjSJesJJ9OZCOgqy>Tfd+$C*(AIA62vL*i$iW#VM^2yY4y4HNCUe zIGR2bukGF)-X6Uu{?Bj5{U0v~*H5zdo1Yo;+5E5B-~Zog8Q*RcJhJx{a~IILNhPik zCCT}@xv6<23`zM#DXB#YNySzM3Q5UU$_8MdPypf=0Qsq4ppXjUry}H&v#m@G3{nh? z5>3(!(+pG0O)SlgO$?HgO)bnUEX|VA3=Lc6UjSOhpbD}rC9x#cD!C{XNHG{07+UBW xSOQUqfw`5bsg;3&wt)eV)Z*`+grXrgKP5A*61RrvAY(m{6`rnsF6*2UngG{>`=kH> diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/poh/trollstronghold.png b/runelite-client/src/main/resources/net/runelite/client/plugins/poh/trollstronghold.png new file mode 100644 index 0000000000000000000000000000000000000000..aa53b1b879ff49507b8cf371a77bd8c05c4c20b2 GIT binary patch literal 417 zcmV;S0bc%zP)gm&mc;xphY*g9GyVle3!FtUi( zG+LEXx8jCebq%ey_qG*-|MWcPIlptB^PKYtl$D^A+7zM0OS4q~B9RlVA5Q03ZdC|U z1Ph85^7tk=IyhF@vO%%|bd|uh%~|{#!z=}6RQ9Z2_J(>Y$QvBMRBNDT$X>|>UAe3P zR=b(6jKXvL4S=0+2#;?Pin8lUD2+05hUs}P?R^alx;j*6DtKYlM__9Xfb{bRpK^vz z`HSA6X6hPE#BW|yu&U;fsVYYS1O-sFv z?*1mkxP(P#=JxyvF)6VhK2*WEJ{KNsdEHN^)s9}M!e+JbF3Tju6x+LddFZFt`I^Vw zZUBt3fxG)8m(d4K&#t(*j**Co1XmW(>emS?r{W3wuh}lv-+t9M4TXW(g2URf00000 LNkvXXu0mjfwZgr6 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/poh/weiss.png b/runelite-client/src/main/resources/net/runelite/client/plugins/poh/weiss.png new file mode 100644 index 0000000000000000000000000000000000000000..940d0fed922584ff48b1a1e95968b94e6cfcca1f GIT binary patch literal 423 zcmV;Y0a*TtP)A!ZjK!Tn$wFqD6e$+ULM)~Rk<5&RK?n<9rCCTUloieRN|uyjuh^N{`5&Zc z2xT=3`HH#Ibl93$KmsL zfhkFPA=&Jj8S?cF5Ixw>NRmn*a)IOXi+IC(49zbY{AlDTeZqWlsADdS)mcUD@Jy}{ zM7F8`#bpjOO+!%>078ey_YQ#Lmw+d9e|@ilN2h>~AdU!@uXOZ(4ADi!omtE{BuiqC)N;A2_|d#_MiK zL#bJ(VSc)}y|arD0*esj2|Wj{y}4p-btMxvSN~1=7xq7ITd%+VsxRfdmTCK% RVio`Z002ovPDHLkV1hXRyF&l~ literal 0 HcmV?d00001 From baf4f58768ef98e180457846c35b74135bb229ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20N=C3=B6nnig?= Date: Tue, 28 Apr 2020 17:47:37 +0200 Subject: [PATCH 18/27] grounditems: update menu highlight logic to match overlay logic When the GE price is 0 (untradeable items) the GroundItemPlugin used the store price, not the HA price (which is the store price multiplied by 0.6). For non-stackable untradeable items the relevant code didn't handle the quantities correctly, e.g. dropping two raw lava eels returned a quantity of 1 and a wrong price, which lead to the wrong color Co-authored-by: Adam --- .../plugins/grounditems/GroundItem.java | 1 + .../grounditems/GroundItemsPlugin.java | 46 ++++++------------- 2 files changed, 14 insertions(+), 33 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java index f130a55bc8..0b926d30e0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java @@ -50,6 +50,7 @@ class GroundItem private LootType lootType; @Nullable private Instant spawnTime; + private boolean stackable; int getHaPrice() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java index 157a16d98d..2ee7db3816 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java @@ -54,12 +54,9 @@ import net.runelite.api.Constants; import net.runelite.api.GameState; import net.runelite.api.ItemComposition; import net.runelite.api.ItemID; -import net.runelite.api.ItemLayer; import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; -import net.runelite.api.Node; import net.runelite.api.Player; -import net.runelite.api.Scene; import net.runelite.api.Tile; import net.runelite.api.TileItem; import net.runelite.api.coords.WorldPoint; @@ -406,6 +403,7 @@ public class GroundItemsPlugin extends Plugin .tradeable(itemComposition.isTradeable()) .lootType(dropped ? LootType.DROPPED : LootType.UNKNOWN) .spawnTime(Instant.now()) + .stackable(itemComposition.isStackable()) .build(); @@ -482,40 +480,22 @@ public class GroundItemsPlugin extends Plugin return; } - int itemId = event.getIdentifier(); - Scene scene = client.getScene(); - Tile tile = scene.getTiles()[client.getPlane()][event.getActionParam0()][event.getActionParam1()]; - ItemLayer itemLayer = tile.getItemLayer(); - - if (itemLayer == null) - { - return; - } + final int itemId = event.getIdentifier(); + final int sceneX = event.getActionParam0(); + final int sceneY = event.getActionParam1(); MenuEntry[] menuEntries = client.getMenuEntries(); MenuEntry lastEntry = menuEntries[menuEntries.length - 1]; - int quantity = 1; - Node current = itemLayer.getBottom(); + final WorldPoint worldPoint = WorldPoint.fromScene(client, sceneX, sceneY, client.getPlane()); + GroundItem.GroundItemKey groundItemKey = new GroundItem.GroundItemKey(itemId, worldPoint); + GroundItem groundItem = collectedGroundItems.get(groundItemKey); + int quantity = groundItem.getQuantity(); - while (current instanceof TileItem) - { - TileItem item = (TileItem) current; - if (item.getId() == itemId) - { - quantity = item.getQuantity(); - } - current = current.getNext(); - } - - final ItemComposition itemComposition = itemManager.getItemComposition(itemId); - final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemComposition.getId(); - final int itemPrice = itemManager.getItemPrice(realItemId); - final int price = itemPrice <= 0 ? itemComposition.getPrice() : itemPrice; - final int haPrice = Math.round(itemComposition.getPrice() * Constants.HIGH_ALCHEMY_MULTIPLIER) * quantity; - final int gePrice = quantity * price; - final Color hidden = getHidden(new NamedQuantity(itemComposition.getName(), quantity), gePrice, haPrice, itemComposition.isTradeable()); - final Color highlighted = getHighlighted(new NamedQuantity(itemComposition.getName(), quantity), gePrice, haPrice); + final int gePrice = groundItem.getGePrice(); + final int haPrice = groundItem.getHaPrice(); + final Color hidden = getHidden(new NamedQuantity(groundItem.getName(), quantity), gePrice, haPrice, groundItem.isTradeable()); + final Color highlighted = getHighlighted(new NamedQuantity(groundItem.getName(), quantity), gePrice, haPrice); final Color color = getItemColor(highlighted, hidden); final boolean canBeRecolored = highlighted != null || (hidden != null && config.recolorMenuHiddenItems()); @@ -549,7 +529,7 @@ public class GroundItemsPlugin extends Plugin } } - if (config.showMenuItemQuantities() && itemComposition.isStackable() && quantity > 1) + if (config.showMenuItemQuantities() && groundItem.isStackable() && quantity > 1) { lastEntry.setTarget(lastEntry.getTarget() + " (" + quantity + ")"); } From cbff13a6a02c721e684d5fd2372f7c283592bab4 Mon Sep 17 00:00:00 2001 From: Atroxide Date: Sat, 2 May 2020 17:44:24 -0700 Subject: [PATCH 19/27] game: Center Fossil Island agility shortcut icons --- .../main/java/net/runelite/client/game/AgilityShortcut.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java b/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java index 110407efc6..b8fd8ec89a 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java +++ b/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java @@ -179,8 +179,8 @@ public enum AgilityShortcut TAVERLEY_DUNGEON_PIPE_BLUE_DRAGON(70, "Pipe Squeeze", new WorldPoint(2886, 9798, 0), OBSTACLE_PIPE_16509), TAVERLEY_DUNGEON_ROCKS_NORTH(70, "Rocks", new WorldPoint(2887, 9823, 0), ROCKS, ROCKS_14106), TAVERLEY_DUNGEON_ROCKS_SOUTH(70, "Rocks", new WorldPoint(2887, 9631, 0), ROCKS, ROCKS_14106), - FOSSIL_ISLAND_HARDWOOD_NORTH(70, "Hole" , new WorldPoint(3713, 3827, 0), HOLE_31481, HOLE_31482), - FOSSIL_ISLAND_HARDWOOD_SOUTH(70, "Hole" , new WorldPoint(3715, 3817, 0), HOLE_31481, HOLE_31482), + FOSSIL_ISLAND_HARDWOOD_NORTH(70, "Hole" , new WorldPoint(3712, 3828, 0), HOLE_31481, HOLE_31482), + FOSSIL_ISLAND_HARDWOOD_SOUTH(70, "Hole" , new WorldPoint(3714, 3816, 0), HOLE_31481, HOLE_31482), AL_KHARID_WINDOW(70, "Window", new WorldPoint(3293, 3158, 0), BROKEN_WALL_33344, BIG_WINDOW), GWD_SARADOMIN_ROPE_NORTH(70, "Rope Descent", new WorldPoint(2912, 5300, 0), NULL_26371), GWD_SARADOMIN_ROPE_SOUTH(70, "Rope Descent", new WorldPoint(2951, 5267, 0), NULL_26375), From cfc014280940e900c90629970ff2f66293305070 Mon Sep 17 00:00:00 2001 From: Atroxide Date: Sat, 2 May 2020 17:45:02 -0700 Subject: [PATCH 20/27] worldmap: Center Otto's Grotto tree icon --- .../net/runelite/client/plugins/worldmap/RareTreeLocation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreeLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreeLocation.java index 452eef5bc7..addcda5af7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreeLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/RareTreeLocation.java @@ -147,7 +147,7 @@ enum RareTreeLocation new WorldPoint(2360, 3471, 0), new WorldPoint(2434, 3432, 0), new WorldPoint(2493, 3396, 0), - new WorldPoint(2494, 3490, 0), + new WorldPoint(2495, 3490, 0), new WorldPoint(2733, 3334, 0), new WorldPoint(2757, 3431, 0), new WorldPoint(2714, 3460, 0), From 04dedfd1c3b063e1d3cdaf92ce35dde894406bbf Mon Sep 17 00:00:00 2001 From: Sam Rosenthal <39362114+SammRosenthal@users.noreply.github.com> Date: Sat, 2 May 2020 19:35:53 -0700 Subject: [PATCH 21/27] clues: Add Viggora cryptic clue item requirement description --- .../runelite/client/plugins/cluescrolls/clues/CrypticClue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java index dd327a795a..7ee9b762fa 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java @@ -267,7 +267,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc new CrypticClue("In a town where everyone has perfect vision, seek some locked drawers in a house that sits opposite a workshop.", "Chicken", DRAWERS_25766, new WorldPoint(2709, 3478, 0), "The Seers' Village house south of the Elemental Workshop entrance. Kill any Chicken to obtain a key."), new CrypticClue("The treasure is buried in a small building full of bones. Here is a hint: it's not near a graveyard.", new WorldPoint(3356, 3507, 0), "In the western building near the Limestone quarry east of Varrock. Dig south of the box of bones in the smaller building."), new CrypticClue("Search the crates in East Ardougne's general store.", CRATE_357, new WorldPoint(2615, 3291, 0), "Located south of the Ardougne church."), - new CrypticClue("Come brave adventurer, your sense is on fire. If you talk to me, it's an old god you desire.", "Viggora", null, "Speak to Viggora"), + new CrypticClue("Come brave adventurer, your sense is on fire. If you talk to me, it's an old god you desire.", "Viggora", null, "Speak to Viggora while wearing a ring of visibility and a Ghostspeak amulet."), new CrypticClue("2 musical birds. Dig in front of the spinning light.", new WorldPoint(2671, 10396, 0), "Dig in front of the spinning light in Ping and Pong's room inside the Iceberg"), new CrypticClue("Search the wheelbarrow in Rimmington mine.", WHEELBARROW_9625, new WorldPoint(2978, 3239, 0), "The Rimmington mining site is located north of Rimmington."), new CrypticClue("Belladonna, my dear. If only I had gloves, then I could hold you at last.", "Tool Leprechaun", new WorldPoint(3088, 3357, 0), "Talk to Tool Leprechaun at Draynor Manor"), From 1da967ff42c5cf052d3ac78c8a041145514cfdfc Mon Sep 17 00:00:00 2001 From: Sam Rosenthal <39362114+SammRosenthal@users.noreply.github.com> Date: Sat, 2 May 2020 19:50:37 -0700 Subject: [PATCH 22/27] ItemMapping: Add Blade of Saeldor (c) --- .../src/main/java/net/runelite/client/game/ItemMapping.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java b/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java index c0dc440d2e..2613326a5a 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java +++ b/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java @@ -239,7 +239,7 @@ public enum ItemMapping ITEM_CRYSTAL_AXE(DRAGON_AXE, CRYSTAL_AXE, CRYSTAL_AXE_INACTIVE), ITEM_CRYSTAL_HARPOON(DRAGON_HARPOON, CRYSTAL_HARPOON, CRYSTAL_HARPOON_INACTIVE), ITEM_CRYSTAL_PICKAXE(DRAGON_PICKAXE, CRYSTAL_PICKAXE, CRYSTAL_PICKAXE_INACTIVE), - ITEM_BLADE_OF_SAELDOR(BLADE_OF_SAELDOR_INACTIVE, BLADE_OF_SAELDOR), + ITEM_BLADE_OF_SAELDOR(BLADE_OF_SAELDOR_INACTIVE, BLADE_OF_SAELDOR, BLADE_OF_SAELDOR_C), ITEM_CRYSTAL_BOW(CRYSTAL_WEAPON_SEED, CRYSTAL_BOW, CRYSTAL_BOW_24123, CRYSTAL_BOW_INACTIVE), ITEM_CRYSTAL_HALBERD(CRYSTAL_WEAPON_SEED, CRYSTAL_HALBERD, CRYSTAL_HALBERD_24125, CRYSTAL_HALBERD_INACTIVE), ITEM_CRYSTAL_SHIELD(CRYSTAL_WEAPON_SEED, CRYSTAL_SHIELD, CRYSTAL_SHIELD_24127, CRYSTAL_SHIELD_INACTIVE), From 0a249a0ba3638509cb4c3bc91ab6f375689bf4fd Mon Sep 17 00:00:00 2001 From: melkypie <5113962+melkypie@users.noreply.github.com> Date: Mon, 4 May 2020 18:49:40 +0300 Subject: [PATCH 23/27] worldmap: add hunter area tooltips --- .../plugins/worldmap/HunterAreaLocation.java | 136 ++++++++++++++++++ .../plugins/worldmap/HunterAreaPoint.java | 38 +++++ .../plugins/worldmap/WorldMapConfig.java | 11 ++ .../plugins/worldmap/WorldMapPlugin.java | 9 ++ 4 files changed, 194 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaLocation.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaPoint.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaLocation.java new file mode 100644 index 0000000000..3f9ee919b8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaLocation.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2020, melky + * 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 HOLDER 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.worldmap; + +import com.google.common.base.Joiner; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.coords.WorldPoint; + +@Getter +enum HunterAreaLocation +{ + BONEYARD_HUNTER_AREA(new WorldPoint(3294, 3673, 0), HunterCreature.BLACK_SALAMANDER), + CANIFIS_HUNTER_AREA1(new WorldPoint(3553, 3438, 0), HunterCreature.SWAMP_LIZARD), + CANIFIS_HUNTER_AREA2(new WorldPoint(3535, 3445, 0), HunterCreature.SWAMP_LIZARD), + FALCONRY(new WorldPoint(2379, 3599, 0), HunterCreature.SPOTTED_KEBBIT, HunterCreature.DARK_KEBBIT, + HunterCreature.DASHING_KEBBIT, HunterCreature.GREATER_SIREN), + FELDIP_HUNTER_AREA(new WorldPoint(2557, 2912, 0), HunterCreature.CRIMSON_SWIFT, HunterCreature.FELDIP_WEASEL, + HunterCreature.TROPICAL_WAGTAIL, HunterCreature.SPINED_LARUPIA, HunterCreature.BARB_TAILED_KEBBIT, + HunterCreature.BLACK_WARLOCK, HunterCreature.CARNIVOROUS_CHINCHOMPA), + FOSSIL_ISLAND1(new WorldPoint(3693, 3800, 0), HunterCreature.HERBIBOAR), + FOSSIL_ISLAND2(new WorldPoint(3701, 3809, 0), HunterCreature.HERBIBOAR), + FOSSIL_ISLAND3(new WorldPoint(3703, 3829, 0), HunterCreature.HERBIBOAR), + FOSSIL_ISLAND4(new WorldPoint(3749, 3850, 0), HunterCreature.HERBIBOAR), + FOSSIL_ISLAND5(new WorldPoint(3684, 3870, 0), HunterCreature.HERBIBOAR), + FOSSIL_ISLAND_UNDERWATER(new WorldPoint(3743, 10295, 0), HunterCreature.FISH_SHOAL), + GWENITH_HUNTER_AREA_OUTSIDE(new WorldPoint(2269, 3408, 0), HunterCreature.CARNIVOROUS_CHINCHOMPA), + GWENITH_HUNTER_AREA_INSIDE(new WorldPoint(3293, 6160, 0), HunterCreature.CARNIVOROUS_CHINCHOMPA), + KARAMJA_HUNTER_AREA(new WorldPoint(2786, 3001, 0), HunterCreature.HORNED_GRAAHK), + KEBOS_SWAMP(new WorldPoint(1184, 3595, 0), HunterCreature.CRIMSON_SWIFT), + KOUREND_WOODLAND_CENTER(new WorldPoint(1512, 3478, 0), HunterCreature.RUBY_HARVEST), + KOUREND_WOODLAND_NORTH_WEST(new WorldPoint(1481, 3504, 0), HunterCreature.CHINCHOMPA), + KOUREND_WOODLAND_SOUTH(new WorldPoint(1556, 3436, 0), HunterCreature.COPPER_LONGTAIL), + LAKE_MOLCH(new WorldPoint(1363, 3632, 0), HunterCreature.BLUEGILL, HunterCreature.COMMON_TENCH, + HunterCreature.MOTTLED_EEL, HunterCreature.GREATER_SIREN), + OURANIA_HUNTER_AREA_EAST(new WorldPoint(2447, 3219, 0), HunterCreature.RED_SALAMANDER), + OURANIA_HUNTER_AREA_SOUTH(new WorldPoint(2475, 3240, 0), HunterCreature.RED_SALAMANDER), + PISCATORIS_HUNTER_AREA(new WorldPoint(2335, 3584, 0), HunterCreature.COMMON_KEBBIT, HunterCreature.COPPER_LONGTAIL, + HunterCreature.RUBY_HARVEST, HunterCreature.WILD_KEBBIT, HunterCreature.FERRET, HunterCreature.WHITE_RABBIT, + HunterCreature.PRICKLY_KEBBIT, HunterCreature.RAZOR_BACKED_KEBBIT, HunterCreature.CHINCHOMPA), + RELLEKA_HUNTER_AREA(new WorldPoint(2719, 3780, 0), HunterCreature.POLAR_KEBBIT, HunterCreature.CERULEAN_TWITCH, + HunterCreature.SAPPHIRE_GLACIALIS, HunterCreature.SNOWY_KNIGHT, + HunterCreature.SABRE_TOOTHED_KEBBIT, HunterCreature.SABRE_TOOTHED_KYATT), + SLEPE_NORTH(new WorldPoint(3677, 3405, 0), HunterCreature.SWAMP_LIZARD), + UZER_HUNTER_AREA(new WorldPoint(3401, 3104, 0), HunterCreature.GOLDEN_WARBLER, HunterCreature.DESERT_DEVIL, + HunterCreature.ORANGE_SALAMANDER), + WILDERNESS(new WorldPoint(3142, 3771, 0), HunterCreature.BLACK_CHINCHOMPA), + ; + + private final WorldPoint location; + private final String tooltip; + + HunterAreaLocation(WorldPoint location, HunterCreature... creatures) + { + this.location = location; + this.tooltip = Joiner.on("
").join(creatures); + } + + @AllArgsConstructor + private enum HunterCreature + { + BARB_TAILED_KEBBIT("Barb-tailed kebbit", 33), + BLACK_CHINCHOMPA("Black chinchompa", 73), + BLACK_SALAMANDER("Black salamander", 67), + BLACK_WARLOCK("Black warlock", 45), + BLUEGILL("Bluegill", 35), + CARNIVOROUS_CHINCHOMPA("Carnivorous chinchompa", 63), + CERULEAN_TWITCH("Cerulean twitch", 11), + CHINCHOMPA("Chinchompa", 53), + COMMON_KEBBIT("Common kebbit", 3), + COMMON_TENCH("Common tench", 51), + COPPER_LONGTAIL("Copper longtail", 9), + CRIMSON_SWIFT("Crimson swift", 1), + DARK_KEBBIT("Dark kebbit", 57), + DASHING_KEBBIT("Dashing kebbit", 69), + DESERT_DEVIL("Desert devil", 13), + FELDIP_WEASEL("Feldip Weasel", 7), + FERRET("Ferret", 27), + FISH_SHOAL("Fish shoal", 44), + GOLDEN_WARBLER("Golden warbler", 5), + GREATER_SIREN("Greater siren", 87), + HERBIBOAR("Herbiboar", 80), + HORNED_GRAAHK("Horned graahk", 41), + // IMP and MANIACAL_MONKEY do not have a specific hunter area + MOTTLED_EEL("Mottled eel", 68), + ORANGE_SALAMANDER("Orange salamander", 47), + POLAR_KEBBIT("Polar kebbit", 1), + PRICKLY_KEBBIT("Prickly kebbit", 37), + RAZOR_BACKED_KEBBIT("Razor-backed kebbit", 49), + RED_SALAMANDER("Red salamander", 59), + RUBY_HARVEST("Ruby harvest", 15), + SABRE_TOOTHED_KEBBIT("Sabre-toothed kebbit", 51), + SABRE_TOOTHED_KYATT("Sabre-toothed kyatt", 55), + SAPPHIRE_GLACIALIS("Sapphire glacialis", 25), + SNOWY_KNIGHT("Snowy knight", 35), + SPINED_LARUPIA("Spined larupia", 31), + SPOTTED_KEBBIT("Spotted kebbit", 43), + SWAMP_LIZARD("Swamp lizard", 29), + TROPICAL_WAGTAIL("Tropical wagtail", 19), + WHITE_RABBIT("White rabbit", 27), + WILD_KEBBIT("Wild kebbit", 23), + ; + + private String name; + private int level; + + @Override + public String toString() + { + return name + " (" + level + ")"; + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaPoint.java new file mode 100644 index 0000000000..2725f3de11 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaPoint.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020, melky + * 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 HOLDER 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.worldmap; + +import java.awt.image.BufferedImage; +import net.runelite.client.ui.overlay.worldmap.WorldMapPoint; + +class HunterAreaPoint extends WorldMapPoint +{ + HunterAreaPoint(HunterAreaLocation data, BufferedImage icon) + { + super(data.getLocation(), icon); + setTooltip(data.getTooltip()); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapConfig.java index 1b2103d992..25a1065b56 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapConfig.java @@ -251,4 +251,15 @@ public interface WorldMapConfig extends Config { return true; } + + @ConfigItem( + keyName = WorldMapPlugin.CONFIG_KEY_HUNTER_AREA_TOOLTIPS, + name = "Show hunter area tooltips", + description = "Indicates the creatures inside a hunting area", + position = 21 + ) + default boolean hunterAreaTooltips() + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java index 4865401cb1..1eaaf89619 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/WorldMapPlugin.java @@ -83,6 +83,7 @@ public class WorldMapPlugin extends Plugin static final String CONFIG_KEY_RUNECRAFTING_ALTAR_ICON = "runecraftingAltarIcon"; static final String CONFIG_KEY_MINING_SITE_TOOLTIPS = "miningSiteTooltips"; static final String CONFIG_KEY_DUNGEON_TOOLTIPS = "dungeonTooltips"; + static final String CONFIG_KEY_HUNTER_AREA_TOOLTIPS = "hunterAreaTooltips"; static { @@ -338,6 +339,14 @@ public class WorldMapPlugin extends Plugin .map(value -> new DungeonPoint(value, BLANK_ICON)) .forEach(worldMapPointManager::add); } + + worldMapPointManager.removeIf(HunterAreaPoint.class::isInstance); + if (config.hunterAreaTooltips()) + { + Arrays.stream(HunterAreaLocation.values()) + .map(value -> new HunterAreaPoint(value, BLANK_ICON)) + .forEach(worldMapPointManager::add); + } } private void updateQuestStartPointIcons() From a791223fb44341856b8e048cd2c2d0afc01cfd26 Mon Sep 17 00:00:00 2001 From: ivanfeli <1448157+ivanfeli@users.noreply.github.com> Date: Mon, 4 May 2020 14:13:09 -0400 Subject: [PATCH 24/27] item stats: Fix Castle Wars bracelet bandage heal amount The amount a bandage heals while wearing a castle wars bracelet is increased by 50%, not the amount of a player's health. Hence, the actual amount healed is 15%. --- .../client/plugins/itemstats/special/CastleWarsBandage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/CastleWarsBandage.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/CastleWarsBandage.java index 7da51c034a..19a1a63cb5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/CastleWarsBandage.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/CastleWarsBandage.java @@ -48,7 +48,7 @@ public class CastleWarsBandage implements Effect ); private static final double BASE_HP_PERC = .10; - private static final double BRACELET_HP_PERC = .50; + private static final double BRACELET_HP_PERC = .15; @Override public StatsChanges calculate(Client client) From 8eb57c5a92ee05df47dbdc9d530f3c2da77f0bd4 Mon Sep 17 00:00:00 2001 From: Rangvaldr <47463740+rangvaldr@users.noreply.github.com> Date: Mon, 4 May 2020 14:20:24 -0400 Subject: [PATCH 25/27] discord: Add missing Iorwerth Dungeon region (#11451) --- .../runelite/client/plugins/discord/DiscordGameEventType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java index d99f4217d2..55d19919b7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java @@ -184,7 +184,7 @@ enum DiscordGameEventType DUNGEON_GOBLIN_CAVE("Goblin Cave", DiscordAreaType.DUNGEONS, 10393), DUNGEON_GRAND_TREE_TUNNELS("Grand Tree Tunnels", DiscordAreaType.DUNGEONS, 9882), DUNGEON_HAM("H.A.M Dungeon", DiscordAreaType.DUNGEONS, 12694, 10321), - DUNGEON_IORWERTH("Iorwerth Dungeon", DiscordAreaType.DUNGEONS, 12738, 12993, 12994), + DUNGEON_IORWERTH("Iorwerth Dungeon", DiscordAreaType.DUNGEONS, 12737, 12738, 12993, 12994), DUNGEON_JATIZSO_MINES("Jatizso Mines", DiscordAreaType.DUNGEONS, 9631), DUNGEON_JIGGIG_BURIAL_TOMB("Jiggig Burial Tomb", DiscordAreaType.DUNGEONS, 9875, 9874), DUNGEON_JOGRE("Jogre Dungeon", DiscordAreaType.DUNGEONS, 11412), From c7a155d09c5f3f267c5bc84b71bf00285baba6ec Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 3 May 2020 13:33:54 -0400 Subject: [PATCH 26/27] loot tracker: cleanup clue scroll stacking and test Most of this test was superfluous and continiously retesting the same logic --- .../loottracker/LootTrackerMapping.java | 31 ++-- .../loottracker/LootTrackerPluginTest.java | 145 +++--------------- 2 files changed, 28 insertions(+), 148 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerMapping.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerMapping.java index 27b08ced63..3c948aa24a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerMapping.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerMapping.java @@ -26,14 +26,14 @@ package net.runelite.client.plugins.loottracker; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; -import lombok.Getter; +import lombok.AllArgsConstructor; import net.runelite.api.ItemComposition; import net.runelite.api.ItemID; import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemStack; -@Getter -public enum LootTrackerMapping +@AllArgsConstructor +enum LootTrackerMapping { CLUE_SCROLL_BEGINNER("Clue scroll (beginner)", ItemID.CLUE_SCROLL_BEGINNER), CLUE_SCROLL_EASY("Clue scroll (easy)", ItemID.CLUE_SCROLL_EASY), @@ -45,41 +45,30 @@ public enum LootTrackerMapping private final String name; private final int baseId; - LootTrackerMapping(String name, int baseId) - { - this.name = name; - this.baseId = baseId; - } - - private static final ImmutableMap MAPPINGS; + private static final ImmutableMap MAPPINGS; static { - ImmutableMap.Builder map = ImmutableMap.builder(); + ImmutableMap.Builder map = ImmutableMap.builder(); for (LootTrackerMapping mapping : values()) { - map.put(mapping.name, mapping); + map.put(mapping.name, mapping.baseId); } MAPPINGS = map.build(); } - public static int map(int itemId, ItemManager itemManager) + private static int map(int itemId, ItemManager itemManager) { ItemComposition itemComp = itemManager.getItemComposition(itemId); - if (itemComp == null || Strings.isNullOrEmpty(itemComp.getName())) + if (Strings.isNullOrEmpty(itemComp.getName())) { return itemId; } - if (!MAPPINGS.containsKey(itemComp.getName())) - { - return itemId; - } - - return MAPPINGS.get(itemComp.getName()).baseId; + return MAPPINGS.getOrDefault(itemComp.getName(), itemId); } - public static ItemStack map(ItemStack item, ItemManager itemManager) + static ItemStack map(ItemStack item, ItemManager itemManager) { int baseId = map(item.getId(), itemManager); if (baseId == item.getId()) diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java index 235b8cf634..dc05bbabf2 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java @@ -32,11 +32,9 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; -import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import javax.inject.Inject; -import lombok.AllArgsConstructor; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.ItemComposition; @@ -55,7 +53,6 @@ import net.runelite.http.api.item.ItemPrice; import net.runelite.http.api.loottracker.LootRecordType; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -68,7 +65,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; @RunWith(MockitoJUnitRunner.class) public class LootTrackerPluginTest @@ -150,135 +146,30 @@ public class LootTrackerPluginTest assertEquals(LootRecordType.EVENT, lootTrackerPlugin.lootRecordType); } - private static ItemStack is(int id, int q) + private static ItemComposition mockItem(String name) { - return new ItemStack(id, q, null); + ItemComposition itemComposition = mock(ItemComposition.class); + when(itemComposition.getName()).thenReturn(name); + return itemComposition; } @Test public void testClueStacks() { - String beg = "Clue scroll (beginner)"; - String easy = "Clue scroll (easy)"; - String med = "Clue scroll (medium)"; - String hard = "Clue scroll (hard)"; - String elite = "Clue scroll (elite)"; - String master = "Clue scroll (master)"; - Map idsToName = ImmutableMap.builder() - .put(ItemID.CLUE_SCROLL_BEGINNER, beg) - .put(ItemID.CLUE_SCROLL_EASY, easy) - .put(ItemID.CLUE_SCROLL_EASY_2719, easy) - .put(ItemID.CLUE_SCROLL_EASY_23153, easy) - .put(ItemID.CLUE_SCROLL_MEDIUM, med) - .put(ItemID.CLUE_SCROLL_MEDIUM_3599, med) - .put(ItemID.CLUE_SCROLL_MEDIUM_2817, med) - .put(ItemID.CLUE_SCROLL_MEDIUM_3602, med) - .put(ItemID.CLUE_SCROLL_MEDIUM_12045, med) - .put(ItemID.CLUE_SCROLL_MEDIUM_12065, med) - .put(ItemID.CLUE_SCROLL_HARD, hard) - .put(ItemID.CLUE_SCROLL_HARD_3520, hard) - .put(ItemID.CLUE_SCROLL_HARD_3550, hard) - .put(ItemID.CLUE_SCROLL_HARD_23045, hard) - .put(ItemID.CLUE_SCROLL_ELITE, elite) - .put(ItemID.CLUE_SCROLL_ELITE_19783, elite) - .put(ItemID.CLUE_SCROLL_ELITE_21524, elite) - .put(ItemID.CLUE_SCROLL_ELITE_12096, elite) - .put(ItemID.CLUE_SCROLL_MASTER, master) - .put(ItemID.RUNE_PLATEBODY, "Rune platebody") - .put(ItemID.AMETHYST_ARROW, "Amethyst arrow") - .put(ItemID.GRACEFUL_HOOD_13579, "Graceful hood") - .put(ItemID.RUNITE_ORE, "Runite ore") - .put(ItemID.RUNITE_ORE + 1, "Runite ore") - .put(0, "null") - .build(); - - @AllArgsConstructor - class Case - { - private final List drops; - private final List expected; - } - - Case[] cases = { - new Case( - Arrays.asList( - is(ItemID.CLUE_SCROLL_MEDIUM, 1), - is(ItemID.CLUE_SCROLL_MEDIUM_3602, 1)), - Collections.singletonList( - is(ItemID.CLUE_SCROLL_MEDIUM, 2)) - ), - new Case( - Arrays.asList( - // graceful isn't a drop, but it is an item w/ variations that we're not tracking. - is(ItemID.GRACEFUL_HOOD_13579, 1), - is(ItemID.RUNE_PLATEBODY, 1), - is(ItemID.AMETHYST_ARROW, 125)), - Arrays.asList( - is(ItemID.GRACEFUL_HOOD_13579, 1), - is(ItemID.RUNE_PLATEBODY, 1), - is(ItemID.AMETHYST_ARROW, 125)) - ), - new Case( - Arrays.asList( - is(ItemID.CLUE_SCROLL_BEGINNER, 1), - is(ItemID.CLUE_SCROLL_ELITE_19783, 1), - is(ItemID.CLUE_SCROLL_MEDIUM_12045, 1), - is(ItemID.CLUE_SCROLL_MEDIUM_12065, 1), - is(ItemID.RUNITE_ORE, 25), - is(ItemID.RUNITE_ORE + 1, 10)), // noted rune ore - Arrays.asList( - is(ItemID.CLUE_SCROLL_BEGINNER, 1), - is(ItemID.CLUE_SCROLL_ELITE, 1), - is(ItemID.CLUE_SCROLL_MEDIUM, 2), - is(ItemID.RUNITE_ORE, 25), - is(ItemID.RUNITE_ORE + 1, 10)) // noted rune ore - ), - new Case( - Arrays.asList( - is(ItemID.CLUE_SCROLL_BEGINNER, 1), - is(ItemID.CLUE_SCROLL_BEGINNER, 1), - is(ItemID.CLUE_SCROLL_EASY, 1), - is(ItemID.CLUE_SCROLL_EASY_2719, 1), - is(ItemID.CLUE_SCROLL_EASY_23153, 1), - is(ItemID.CLUE_SCROLL_MEDIUM, 1), - is(ItemID.CLUE_SCROLL_MEDIUM_12065, 1), - is(ItemID.CLUE_SCROLL_MEDIUM_2817, 1), - is(ItemID.CLUE_SCROLL_HARD, 1), - is(ItemID.CLUE_SCROLL_HARD_3550, 1), - is(ItemID.CLUE_SCROLL_HARD_23045, 1), - is(ItemID.CLUE_SCROLL_ELITE, 1), - is(ItemID.CLUE_SCROLL_ELITE_21524, 1), - is(ItemID.CLUE_SCROLL_ELITE_12096, 1), - is(ItemID.CLUE_SCROLL_MASTER, 1), - is(ItemID.CLUE_SCROLL_MASTER, 1)), - Arrays.asList( - is(ItemID.CLUE_SCROLL_BEGINNER, 2), - is(ItemID.CLUE_SCROLL_EASY, 3), - is(ItemID.CLUE_SCROLL_MEDIUM, 3), - is(ItemID.CLUE_SCROLL_HARD, 3), - is(ItemID.CLUE_SCROLL_ELITE, 3), - is(ItemID.CLUE_SCROLL_MASTER, 2)) - ), - }; - - for (int i = 0; i < cases.length; i++) - { - Case tc = cases[i]; - when(itemManager.getItemComposition(anyInt())).thenAnswer((Answer) invocationOnMock -> - { - int itemId = invocationOnMock.getArgument(0); - if (!idsToName.containsKey(itemId)) - { - fail("item id not in names map: " + itemId); - } - - ItemComposition c = mock(ItemComposition.class); - when(c.getName()).thenReturn(idsToName.get(itemId)); - return c; - }); - - assertEquals("Test case: " + (i + 1), tc.expected, lootTrackerPlugin.stack(tc.drops)); - } + when(itemManager.getItemComposition(ItemID.CLUE_SCROLL_MEDIUM)).thenAnswer(invocation -> mockItem("Clue scroll (medium)")); + when(itemManager.getItemComposition(ItemID.CLUE_SCROLL_MEDIUM_3602)).thenAnswer(invocation -> mockItem("Clue scroll (medium)")); + when(itemManager.getItemComposition(ItemID.GRACEFUL_HOOD_13579)).thenAnswer(invocation -> mockItem("Graceful hood")); + Collection stack = lootTrackerPlugin.stack( + Arrays.asList( + new ItemStack(ItemID.CLUE_SCROLL_MEDIUM, 1, null), + new ItemStack(ItemID.CLUE_SCROLL_MEDIUM_3602, 1, null), + new ItemStack(ItemID.GRACEFUL_HOOD_13579, 1, null) + ) + ); + assertEquals(Arrays.asList( + new ItemStack(ItemID.CLUE_SCROLL_MEDIUM, 2, null), + new ItemStack(ItemID.GRACEFUL_HOOD_13579, 1, null) + ), stack); } @Test From 26ffd1be33157ad7e798c8675969a6b5da454203 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 2 May 2020 20:31:03 -0400 Subject: [PATCH 27/27] key remapper: consume key typed events for remapped keys Since all of the keys we remap to (f/arrow/escape keys) do not have associated characters, consume key typed events for the characters associated to the original key press event. This fixes entering bank pins with fkey remapping on both entering your pin and changing your inventory tab, due to the client processing the tab change on key press (which is remapped) and the bank plugin processing the pin input on key typed. --- .../keyremapping/KeyRemappingListener.java | 125 +++++++++++------- .../KeyRemappingListenerTest.java | 17 +++ 2 files changed, 91 insertions(+), 51 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java index 6ca245af1a..3c7ce4be79 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/keyremapping/KeyRemappingListener.java @@ -28,7 +28,9 @@ package net.runelite.client.plugins.keyremapping; import com.google.common.base.Strings; import java.awt.event.KeyEvent; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import javax.inject.Inject; import net.runelite.api.Client; import net.runelite.api.GameState; @@ -51,10 +53,16 @@ class KeyRemappingListener implements KeyListener private ClientThread clientThread; private final Map modified = new HashMap<>(); + private final Set blockedChars = new HashSet<>(); @Override public void keyTyped(KeyEvent e) { + char keyChar = e.getKeyChar(); + if (keyChar != KeyEvent.CHAR_UNDEFINED && blockedChars.contains(keyChar)) + { + e.consume(); + } } @Override @@ -67,27 +75,25 @@ class KeyRemappingListener implements KeyListener if (!plugin.isTyping()) { + int mappedKeyCode = KeyEvent.VK_UNDEFINED; + if (config.cameraRemap()) { if (config.up().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_UP); - e.setKeyCode(KeyEvent.VK_UP); + mappedKeyCode = KeyEvent.VK_UP; } else if (config.down().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_DOWN); - e.setKeyCode(KeyEvent.VK_DOWN); + mappedKeyCode = KeyEvent.VK_DOWN; } else if (config.left().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_LEFT); - e.setKeyCode(KeyEvent.VK_LEFT); + mappedKeyCode = KeyEvent.VK_LEFT; } else if (config.right().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_RIGHT); - e.setKeyCode(KeyEvent.VK_RIGHT); + mappedKeyCode = KeyEvent.VK_RIGHT; } } @@ -98,68 +104,70 @@ class KeyRemappingListener implements KeyListener { if (config.f1().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_F1); - e.setKeyCode(KeyEvent.VK_F1); + mappedKeyCode = KeyEvent.VK_F1; } else if (config.f2().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_F2); - e.setKeyCode(KeyEvent.VK_F2); + mappedKeyCode = KeyEvent.VK_F2; } else if (config.f3().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_F3); - e.setKeyCode(KeyEvent.VK_F3); + mappedKeyCode = KeyEvent.VK_F3; } else if (config.f4().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_F4); - e.setKeyCode(KeyEvent.VK_F4); + mappedKeyCode = KeyEvent.VK_F4; } else if (config.f5().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_F5); - e.setKeyCode(KeyEvent.VK_F5); + mappedKeyCode = KeyEvent.VK_F5; } else if (config.f6().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_F6); - e.setKeyCode(KeyEvent.VK_F6); + mappedKeyCode = KeyEvent.VK_F6; } else if (config.f7().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_F7); - e.setKeyCode(KeyEvent.VK_F7); + mappedKeyCode = KeyEvent.VK_F7; } else if (config.f8().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_F8); - e.setKeyCode(KeyEvent.VK_F8); + mappedKeyCode = KeyEvent.VK_F8; } else if (config.f9().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_F9); - e.setKeyCode(KeyEvent.VK_F9); + mappedKeyCode = KeyEvent.VK_F9; } else if (config.f10().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_F10); - e.setKeyCode(KeyEvent.VK_F10); + mappedKeyCode = KeyEvent.VK_F10; } else if (config.f11().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_F11); - e.setKeyCode(KeyEvent.VK_F11); + mappedKeyCode = KeyEvent.VK_F11; } else if (config.f12().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_F12); - e.setKeyCode(KeyEvent.VK_F12); + mappedKeyCode = KeyEvent.VK_F12; } else if (config.esc().matches(e)) { - modified.put(e.getKeyCode(), KeyEvent.VK_ESCAPE); - e.setKeyCode(KeyEvent.VK_ESCAPE); + mappedKeyCode = KeyEvent.VK_ESCAPE; + } + } + + if (mappedKeyCode != KeyEvent.VK_UNDEFINED) + { + final char keyChar = e.getKeyChar(); + modified.put(e.getKeyCode(), mappedKeyCode); + e.setKeyCode(mappedKeyCode); + // arrow keys and fkeys do not have a character + e.setKeyChar(KeyEvent.CHAR_UNDEFINED); + if (keyChar != KeyEvent.CHAR_UNDEFINED) + { + // If this key event has a valid key char then a key typed event may be received next, + // we must block it + blockedChars.add(keyChar); } } @@ -209,6 +217,12 @@ class KeyRemappingListener implements KeyListener @Override public void keyReleased(KeyEvent e) { + final char keyChar = e.getKeyChar(); + if (keyChar != KeyEvent.CHAR_UNDEFINED) + { + blockedChars.remove(keyChar); + } + if (client.getGameState() == GameState.LOGIN_SCREEN) { return; @@ -218,23 +232,25 @@ class KeyRemappingListener implements KeyListener { modified.remove(e.getKeyCode()); + int mappedKeyCode = KeyEvent.VK_UNDEFINED; + if (config.cameraRemap()) { if (config.up().matches(e)) { - e.setKeyCode(KeyEvent.VK_UP); + mappedKeyCode = KeyEvent.VK_UP; } else if (config.down().matches(e)) { - e.setKeyCode(KeyEvent.VK_DOWN); + mappedKeyCode = KeyEvent.VK_DOWN; } else if (config.left().matches(e)) { - e.setKeyCode(KeyEvent.VK_LEFT); + mappedKeyCode = KeyEvent.VK_LEFT; } else if (config.right().matches(e)) { - e.setKeyCode(KeyEvent.VK_RIGHT); + mappedKeyCode = KeyEvent.VK_RIGHT; } } @@ -242,57 +258,63 @@ class KeyRemappingListener implements KeyListener { if (config.f1().matches(e)) { - e.setKeyCode(KeyEvent.VK_F1); + mappedKeyCode = KeyEvent.VK_F1; } else if (config.f2().matches(e)) { - e.setKeyCode(KeyEvent.VK_F2); + mappedKeyCode = KeyEvent.VK_F2; } else if (config.f3().matches(e)) { - e.setKeyCode(KeyEvent.VK_F3); + mappedKeyCode = KeyEvent.VK_F3; } else if (config.f4().matches(e)) { - e.setKeyCode(KeyEvent.VK_F4); + mappedKeyCode = KeyEvent.VK_F4; } else if (config.f5().matches(e)) { - e.setKeyCode(KeyEvent.VK_F5); + mappedKeyCode = KeyEvent.VK_F5; } else if (config.f6().matches(e)) { - e.setKeyCode(KeyEvent.VK_F6); + mappedKeyCode = KeyEvent.VK_F6; } else if (config.f7().matches(e)) { - e.setKeyCode(KeyEvent.VK_F7); + mappedKeyCode = KeyEvent.VK_F7; } else if (config.f8().matches(e)) { - e.setKeyCode(KeyEvent.VK_F8); + mappedKeyCode = KeyEvent.VK_F8; } else if (config.f9().matches(e)) { - e.setKeyCode(KeyEvent.VK_F9); + mappedKeyCode = KeyEvent.VK_F9; } else if (config.f10().matches(e)) { - e.setKeyCode(KeyEvent.VK_F10); + mappedKeyCode = KeyEvent.VK_F10; } else if (config.f11().matches(e)) { - e.setKeyCode(KeyEvent.VK_F11); + mappedKeyCode = KeyEvent.VK_F11; } else if (config.f12().matches(e)) { - e.setKeyCode(KeyEvent.VK_F12); + mappedKeyCode = KeyEvent.VK_F12; } else if (config.esc().matches(e)) { - e.setKeyCode(KeyEvent.VK_ESCAPE); + mappedKeyCode = KeyEvent.VK_ESCAPE; } } + + if (mappedKeyCode != KeyEvent.VK_UNDEFINED) + { + e.setKeyCode(mappedKeyCode); + e.setKeyChar(KeyEvent.CHAR_UNDEFINED); + } } else { @@ -302,6 +324,7 @@ class KeyRemappingListener implements KeyListener { modified.remove(e.getKeyCode()); e.setKeyCode(m); + e.setKeyChar(KeyEvent.CHAR_UNDEFINED); } } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/keyremapping/KeyRemappingListenerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/keyremapping/KeyRemappingListenerTest.java index 867413420e..036f53e303 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/keyremapping/KeyRemappingListenerTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/keyremapping/KeyRemappingListenerTest.java @@ -36,6 +36,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -77,11 +78,26 @@ public class KeyRemappingListenerTest when(keyRemappingConfig.right()).thenReturn(new ModifierlessKeybind(KeyEvent.VK_D, 0)); when(keyRemappingPlugin.chatboxFocused()).thenReturn(true); + KeyEvent event = mock(KeyEvent.class); + when(event.getKeyChar()).thenReturn('d'); when(event.getKeyCode()).thenReturn(KeyEvent.VK_D); when(event.getExtendedKeyCode()).thenReturn(KeyEvent.VK_D); // for keybind matches() + keyRemappingListener.keyPressed(event); + verify(event).setKeyCode(KeyEvent.VK_RIGHT); + verify(event).setKeyChar(KeyEvent.CHAR_UNDEFINED); + + // now the key listener has remapped d->right, it should consume the key type + // event for d + event = mock(KeyEvent.class); + when(event.getKeyChar()).thenReturn('d'); + lenient().when(event.getKeyCode()).thenReturn(KeyEvent.VK_UNDEFINED); + + keyRemappingListener.keyTyped(event); + + verify(event).consume(); // with the plugin now in typing mode, previously pressed and remapped keys should still be mapped // on key release regardless @@ -90,5 +106,6 @@ public class KeyRemappingListenerTest when(event.getKeyCode()).thenReturn(KeyEvent.VK_D); keyRemappingListener.keyReleased(event); verify(event).setKeyCode(KeyEvent.VK_RIGHT); + verify(event).setKeyChar(KeyEvent.CHAR_UNDEFINED); } }