From e47a79db13ae0d776a25d2a72b152f8f57fa37a7 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Fri, 4 Dec 2020 16:37:15 -0700 Subject: [PATCH 1/7] runelite-api: add param accessors --- .../main/java/net/runelite/api/Client.java | 12 ++++ .../net/runelite/api/ItemComposition.java | 2 +- .../net/runelite/api/IterableHashTable.java | 1 + .../java/net/runelite/api/NPCComposition.java | 2 +- .../net/runelite/api/ObjectComposition.java | 2 +- .../java/net/runelite/api/ParamHolder.java | 57 +++++++++++++++++++ .../main/java/net/runelite/api/ParamID.java | 32 +++++++++++ .../net/runelite/api/StructComposition.java | 38 +++++++++++++ .../main/java/net/runelite/api/StructID.java | 34 +++++++++++ .../api/events/PostStructComposition.java | 41 +++++++++++++ 10 files changed, 218 insertions(+), 3 deletions(-) create mode 100644 runelite-api/src/main/java/net/runelite/api/ParamHolder.java create mode 100644 runelite-api/src/main/java/net/runelite/api/ParamID.java create mode 100644 runelite-api/src/main/java/net/runelite/api/StructComposition.java create mode 100644 runelite-api/src/main/java/net/runelite/api/StructID.java create mode 100644 runelite-api/src/main/java/net/runelite/api/events/PostStructComposition.java 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 93d2c2ef81..7c75f6ace8 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -897,6 +897,18 @@ public interface Client extends GameEngine */ NPCComposition getNpcDefinition(int npcId); + /** + * Gets the {@link StructComposition} for a given struct ID + * + * @see StructID + */ + StructComposition getStructComposition(int structID); + + /** + * Gets the client's cache of in memory struct compositions + */ + NodeCache getStructCompositionCache(); + /** * Gets an array of all world areas * diff --git a/runelite-api/src/main/java/net/runelite/api/ItemComposition.java b/runelite-api/src/main/java/net/runelite/api/ItemComposition.java index 72f955e5ba..37407724cb 100644 --- a/runelite-api/src/main/java/net/runelite/api/ItemComposition.java +++ b/runelite-api/src/main/java/net/runelite/api/ItemComposition.java @@ -29,7 +29,7 @@ import javax.annotation.Nullable; /** * Represents the template of a specific item type. */ -public interface ItemComposition +public interface ItemComposition extends ParamHolder { /** * Gets the items name. diff --git a/runelite-api/src/main/java/net/runelite/api/IterableHashTable.java b/runelite-api/src/main/java/net/runelite/api/IterableHashTable.java index 48894f1863..016888cc80 100644 --- a/runelite-api/src/main/java/net/runelite/api/IterableHashTable.java +++ b/runelite-api/src/main/java/net/runelite/api/IterableHashTable.java @@ -27,4 +27,5 @@ package net.runelite.api; public interface IterableHashTable extends Iterable { T get(long hash); + void put(T node, long hash); } diff --git a/runelite-api/src/main/java/net/runelite/api/NPCComposition.java b/runelite-api/src/main/java/net/runelite/api/NPCComposition.java index d61ef4b670..faff1da7db 100644 --- a/runelite-api/src/main/java/net/runelite/api/NPCComposition.java +++ b/runelite-api/src/main/java/net/runelite/api/NPCComposition.java @@ -27,7 +27,7 @@ package net.runelite.api; /** * Information about a specific {@link NpcID} */ -public interface NPCComposition +public interface NPCComposition extends ParamHolder { /** * Gets the name of the NPC. diff --git a/runelite-api/src/main/java/net/runelite/api/ObjectComposition.java b/runelite-api/src/main/java/net/runelite/api/ObjectComposition.java index 43fa5aa88a..1d0357b78a 100644 --- a/runelite-api/src/main/java/net/runelite/api/ObjectComposition.java +++ b/runelite-api/src/main/java/net/runelite/api/ObjectComposition.java @@ -27,7 +27,7 @@ package net.runelite.api; /** * Information about a specific {@link ObjectID} */ -public interface ObjectComposition +public interface ObjectComposition extends ParamHolder { /** * Gets ID for the object. diff --git a/runelite-api/src/main/java/net/runelite/api/ParamHolder.java b/runelite-api/src/main/java/net/runelite/api/ParamHolder.java new file mode 100644 index 0000000000..694a9448f0 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/ParamHolder.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api; + +/** + * A composition that can hold `param` keys. This lets Jagex attach arbitrary constant + * data to certain items, objects, npcs, or structs for use in cs2 + * + * @see ParamID + */ +public interface ParamHolder +{ + IterableHashTable getParams(); + void setParams(IterableHashTable params); + + /** + * Gets the value of a given {@link ParamID}, or its default if it is unset + */ + int getIntValue(int paramID); + + /** + * Sets the value of a given {@link ParamID} + */ + void setValue(int paramID, int value); + + /** + * Gets the value of a given {@link ParamID}, or its default if it is unset + */ + String getStringValue(int paramID); + + /** + * Sets the value of a given {@link ParamID} + */ + void setValue(int paramID, String value); +} diff --git a/runelite-api/src/main/java/net/runelite/api/ParamID.java b/runelite-api/src/main/java/net/runelite/api/ParamID.java new file mode 100644 index 0000000000..0775cf9f26 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/ParamID.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api; + +/** + * @see ParamHolder + */ +public class ParamID +{ +} diff --git a/runelite-api/src/main/java/net/runelite/api/StructComposition.java b/runelite-api/src/main/java/net/runelite/api/StructComposition.java new file mode 100644 index 0000000000..b395a3beb2 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/StructComposition.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2020 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api; + +/** + * A config type dedicated to holding params. + * + * Historically items were often used for this before structs were made + * available, and there are many of these still around. + * + * @see ParamHolder + */ +public interface StructComposition extends ParamHolder +{ + int getId(); +} diff --git a/runelite-api/src/main/java/net/runelite/api/StructID.java b/runelite-api/src/main/java/net/runelite/api/StructID.java new file mode 100644 index 0000000000..dd3c148cd3 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/StructID.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.runelite.api; + +/** + * @see StructComposition + * @see Client#getStructComposition(int) + */ +public class StructID +{ +} diff --git a/runelite-api/src/main/java/net/runelite/api/events/PostStructComposition.java b/runelite-api/src/main/java/net/runelite/api/events/PostStructComposition.java new file mode 100644 index 0000000000..82238e94fd --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/PostStructComposition.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api.events; + +import lombok.Data; +import net.runelite.api.StructComposition; + +/** + * An event called after a new {@link StructComposition} is created and + * its data is initialized. + */ +@Data +public class PostStructComposition +{ + /** + * The newly created struct. + */ + private StructComposition structComposition; +} From 6056b92faf32ecc537785e1bd121df5c93f64918 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Fri, 4 Dec 2020 16:39:36 -0700 Subject: [PATCH 2/7] runelite-api: add various widget accessors --- .../main/java/net/runelite/api/Client.java | 14 +++++++++ .../java/net/runelite/api/widgets/Widget.java | 31 +++++++++++++++++++ 2 files changed, 45 insertions(+) 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 7c75f6ace8..3bf027952b 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -1156,6 +1156,20 @@ public interface Client extends GameEngine */ String[] getStringStack(); + /** + * Gets the cs2 vm's active widget + * + * This is used for all {@code cc_*} operations with a {@code 0} operand + */ + Widget getScriptActiveWidget(); + + /** + * Gets the cs2 vm's "dot" widget + * + * This is used for all {@code .cc_*} operations with a {@code 1} operand + */ + Widget getScriptDotWidget(); + /** * Checks whether a player is on the friends list. * diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java b/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java index 5b7cdec58d..e1d101d8ee 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/Widget.java @@ -972,4 +972,35 @@ public interface Widget * @param args A ScriptID, then the args for the script */ void setOnReleaseListener(Object ...args); + + /** + * Sets a script to be ran when a drag operation is finished on this widget + * + * @param args A ScriptID, then the args for the script + */ + void setOnDragCompleteListener(Object ...args); + + /** + * Sets a script to be ran when this widget moves due to a drag + * + * @param args A ScriptID, then the args for the script + */ + void setOnDragListener(Object ...args); + + /** + * Container this can be dragged in + */ + Widget getDragParent(); + + /** + * Container this can be dragged in + */ + void setDragParent(Widget dragParent); + + /** + * Sets a script to be ran when a varplayer changes + * + * @param args A ScriptID, then the args for the script + */ + void setOnVarTransmitListener(Object ...args); } From 54f953dc42ca5d9f31ab4c56138dd35597b639c6 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sun, 6 Dec 2020 19:20:05 -0700 Subject: [PATCH 3/7] runelite-api: allow running scripts with a widget source --- .../main/java/net/runelite/api/Client.java | 10 +++++++++ .../java/net/runelite/api/ScriptEvent.java | 21 +++++++++++++++---- 2 files changed, 27 insertions(+), 4 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 3bf027952b..f33514e899 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -1337,11 +1337,21 @@ public interface Client extends GameEngine * * This method must be ran on the client thread and is not reentrant * + * This method is shorthand for {@code client.createScriptEvent(args).run()} + * * @param args the script id, then any additional arguments to execute the script with * @see ScriptID */ void runScript(Object... args); + /** + * Creates a blank ScriptEvent for executing a ClientScript2 script + * + * @param args the script id, then any additional arguments to execute the script with + * @see ScriptID + */ + ScriptEvent createScriptEvent(Object ...args); + /** * Checks whether or not there is any active hint arrow. * diff --git a/runelite-api/src/main/java/net/runelite/api/ScriptEvent.java b/runelite-api/src/main/java/net/runelite/api/ScriptEvent.java index e97061f1f9..2436c9ac3b 100644 --- a/runelite-api/src/main/java/net/runelite/api/ScriptEvent.java +++ b/runelite-api/src/main/java/net/runelite/api/ScriptEvent.java @@ -40,13 +40,19 @@ public interface ScriptEvent String NAME = "event_opbase"; /** - * Gets the widget of the event. - * - * @return the widget - * @see net.runelite.api.widgets.Widget + * Gets the widget the {@link #WIDGET_ID} and {@link #WIDGET_INDEX} args + * are substituted with */ Widget getSource(); + /** + * Sets the widget the {@link #WIDGET_ID} and {@link #WIDGET_INDEX} args + * are substituted with. This is useful for running widget listeners + * + * @see Widget#getOnLoadListener() + */ + ScriptEvent setSource(Widget widget); + /** * Gets the menu index of the event * @@ -80,4 +86,11 @@ public interface ScriptEvent * @return */ int getTypedKeyChar(); + + /** + * Executes a cs2 script specified by this event + * + * This method must be ran on the client thread and is not reentrant + */ + void run(); } From 7840bb2b184b1fb08e8e03a4319a42b0e45ec0ae Mon Sep 17 00:00:00 2001 From: Max Weber Date: Thu, 3 Dec 2020 00:44:46 -0700 Subject: [PATCH 4/7] devtools: add dragParent to widget inspector --- .../client/plugins/devtools/WidgetField.java | 4 ++++ .../devtools/WidgetInfoTableModel.java | 1 + .../plugins/devtools/WidgetInspector.java | 21 +++++++++++++++++++ .../plugins/devtools/WidgetTreeNode.java | 21 +------------------ 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetField.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetField.java index a4b21cb94d..cc92dd9fd5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetField.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetField.java @@ -64,6 +64,10 @@ public class WidgetField { return value; } + if (value instanceof Widget) + { + return WidgetInspector.getWidgetIdentifier((Widget) value); + } return MessageFormatter.format("{}", value).getMessage(); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInfoTableModel.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInfoTableModel.java index a08323eba3..2b515e5fb4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInfoTableModel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInfoTableModel.java @@ -193,6 +193,7 @@ public class WidgetInfoTableModel extends AbstractTableModel out.add(new WidgetField<>("NoClickThrough", Widget::getNoClickThrough, Widget::setNoClickThrough, Boolean.class)); out.add(new WidgetField<>("NoScrollThrough", Widget::getNoScrollThrough, Widget::setNoScrollThrough, Boolean.class)); out.add(new WidgetField<>("TargetVerb", Widget::getTargetVerb, Widget::setTargetVerb, String.class)); + out.add(new WidgetField<>("DragParent", Widget::getDragParent)); return out; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInspector.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInspector.java index 7407711d58..2e99bc3a97 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInspector.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInspector.java @@ -59,6 +59,8 @@ import net.runelite.api.Client; import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; import net.runelite.api.SpriteID; +import static net.runelite.api.widgets.WidgetInfo.TO_CHILD; +import static net.runelite.api.widgets.WidgetInfo.TO_GROUP; import net.runelite.client.events.ConfigChanged; import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.events.MenuOptionClicked; @@ -578,4 +580,23 @@ class WidgetInspector extends JFrame return null; } + + public static String getWidgetIdentifier(Widget widget) + { + int id = widget.getId(); + String str = TO_GROUP(id) + "." + TO_CHILD(id); + + if (widget.getIndex() != -1) + { + str += "[" + widget.getIndex() + "]"; + } + + WidgetInfo info = WidgetInspector.getWidgetInfo(id); + if (info != null) + { + str += " " + info.name(); + } + + return str; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetTreeNode.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetTreeNode.java index 6b7526637a..c82a4d775e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetTreeNode.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetTreeNode.java @@ -26,9 +26,6 @@ package net.runelite.client.plugins.devtools; import javax.swing.tree.DefaultMutableTreeNode; 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; class WidgetTreeNode extends DefaultMutableTreeNode { @@ -48,22 +45,6 @@ class WidgetTreeNode extends DefaultMutableTreeNode @Override public String toString() { - Widget widget = getWidget(); - - int id = widget.getId(); - String str = type + " " + TO_GROUP(id) + "." + TO_CHILD(id); - - if (widget.getIndex() != -1) - { - str += "[" + widget.getIndex() + "]"; - } - - WidgetInfo info = WidgetInspector.getWidgetInfo(id); - if (info != null) - { - str += " " + info.name(); - } - - return str; + return type + " " + WidgetInspector.getWidgetIdentifier(getWidget()); } } From 07819bd9a479f449b4e322ea7056af07ae014d68 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Fri, 4 Dec 2020 09:06:42 -0700 Subject: [PATCH 5/7] rl-api, rl-client: add Client::getWidget(I) --- .../src/main/java/net/runelite/api/Client.java | 10 ++++++++++ .../net/runelite/client/plugins/bank/BankPlugin.java | 4 +--- .../client/plugins/devtools/WidgetInspector.java | 4 ++-- .../client/plugins/experiencedrop/XpDropPlugin.java | 4 +--- .../net/runelite/client/plugins/wiki/WikiPlugin.java | 2 +- 5 files changed, 15 insertions(+), 9 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 f33514e899..580b16b1f3 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -502,6 +502,16 @@ public interface Client extends GameEngine @Nullable Widget getWidget(int groupId, int childId); + /** + * Gets a widget by it's packed ID. + * + *

+ * Note: Use {@link #getWidget(WidgetInfo)} or {@link #getWidget(int, int)} for + * a more readable version of this method. + */ + @Nullable + Widget getWidget(int packedID); + /** * Gets an array containing the x-axis canvas positions * of all widgets. diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/bank/BankPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/bank/BankPlugin.java index e0e1efbd5c..5b8ea483d6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/bank/BankPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/bank/BankPlugin.java @@ -56,8 +56,6 @@ import net.runelite.api.widgets.JavaScriptCallback; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetID; 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.config.ConfigManager; import net.runelite.client.config.Keybind; @@ -226,7 +224,7 @@ public class BankPlugin extends Plugin final int compId = intStack[intStackSize - 2]; final int buttonId = intStack[intStackSize - 1]; - Widget button = client.getWidget(TO_GROUP(compId), TO_CHILD(compId)); + Widget button = client.getWidget(compId); Widget buttonRect = button.getChild(0); final Object[] onOpListener = buttonRect.getOnOpListener(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInspector.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInspector.java index 2e99bc3a97..986ebb996a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInspector.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/WidgetInspector.java @@ -564,7 +564,7 @@ class WidgetInspector extends JFrame { if (type == MenuAction.SPELL_CAST_ON_WIDGET.getId()) { - Widget w = client.getWidget(WidgetInfo.TO_GROUP(param1), WidgetInfo.TO_CHILD(param1)); + Widget w = client.getWidget(param1); if (param0 != -1) { w = w.getChild(param0); @@ -574,7 +574,7 @@ class WidgetInspector extends JFrame } else if (type == MenuAction.ITEM_USE_ON_WIDGET.getId()) { - Widget w = client.getWidget(WidgetInfo.TO_GROUP(param1), WidgetInfo.TO_CHILD(param1)); + Widget w = client.getWidget(param1); return w.getWidgetItem(param0); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropPlugin.java index 0c2497cf3b..c3b9523f21 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropPlugin.java @@ -43,8 +43,6 @@ import net.runelite.api.events.GameTick; import net.runelite.api.events.ScriptPreFired; import net.runelite.api.events.StatChanged; import net.runelite.api.widgets.Widget; -import static net.runelite.api.widgets.WidgetInfo.TO_CHILD; -import static net.runelite.api.widgets.WidgetInfo.TO_GROUP; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.plugins.Plugin; @@ -92,7 +90,7 @@ public class XpDropPlugin extends Plugin private void processXpDrop(int widgetId) { - final Widget xpdrop = client.getWidget(TO_GROUP(widgetId), TO_CHILD(widgetId)); + final Widget xpdrop = client.getWidget(widgetId); final Widget[] children = xpdrop.getChildren(); // child 0 is the xpdrop text, everything else are sprite ids for skills final Widget text = children[0]; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiPlugin.java index dce174eeee..2f0609bcfe 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/wiki/WikiPlugin.java @@ -345,7 +345,7 @@ public class WikiPlugin extends Plugin private Widget getWidget(int wid, int index) { - Widget w = client.getWidget(WidgetInfo.TO_GROUP(wid), WidgetInfo.TO_CHILD(wid)); + Widget w = client.getWidget(wid); if (index != -1) { w = w.getChild(index); From b5b9da1a416b7cb70a9e92687ac2f51d500c2032 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Thu, 3 Dec 2020 00:46:59 -0700 Subject: [PATCH 6/7] runelite-api: rename Options group to SettingsSide --- .../src/main/java/net/runelite/api/widgets/WidgetID.java | 5 +++-- .../src/main/java/net/runelite/api/widgets/WidgetInfo.java | 2 +- .../net/runelite/client/plugins/camera/CameraOverlay.java | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) 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 78346fbee4..7437371b70 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 @@ -147,7 +147,8 @@ public class WidgetID public static final int SEED_BOX_GROUP_ID = 128; public static final int SEED_VAULT_GROUP_ID = 631; public static final int EXPLORERS_RING_ALCH_GROUP_ID = 483; - public static final int OPTIONS_GROUP_ID = 116; + public static final int SETTINGS_SIDE_GROUP_ID = 116; + public static final int SETTINGS_GROUP_ID = 134; public static final int GWD_KC_GROUP_ID = 406; public static final int LMS_GROUP_ID = 333; public static final int LMS_INGAME_GROUP_ID = 328; @@ -880,7 +881,7 @@ public class WidgetID static final int INVENTORY = 7; } - static class Options + static class SettingsSide { static final int CAMERA_ZOOM_SLIDER_HANDLE = 55; } 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 9205b8d1e2..1aa7b4d15f 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 @@ -532,7 +532,7 @@ public enum WidgetInfo SEED_VAULT_ITEM_TEXT(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.ITEM_TEXT), SEED_VAULT_INVENTORY_ITEMS_CONTAINER(WidgetID.SEED_VAULT_INVENTORY_GROUP_ID, WidgetID.SeedVault.INVENTORY_ITEM_CONTAINER), - OPTIONS_CAMERA_ZOOM_SLIDER_HANDLE(WidgetID.OPTIONS_GROUP_ID, WidgetID.Options.CAMERA_ZOOM_SLIDER_HANDLE), + SETTINGS_SIDE_CAMERA_ZOOM_SLIDER_HANDLE(WidgetID.SETTINGS_SIDE_GROUP_ID, WidgetID.SettingsSide.CAMERA_ZOOM_SLIDER_HANDLE), ACHIEVEMENT_DIARY_CONTAINER(WidgetID.ACHIEVEMENT_DIARY_GROUP_ID, WidgetID.AchievementDiary.CONTAINER), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraOverlay.java index f379d60cb2..1616cdf1e5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraOverlay.java @@ -57,7 +57,7 @@ class CameraOverlay extends Overlay @Override public Dimension render(final Graphics2D graphics) { - final Widget slider = client.getWidget(WidgetInfo.OPTIONS_CAMERA_ZOOM_SLIDER_HANDLE); + final Widget slider = client.getWidget(WidgetInfo.SETTINGS_SIDE_CAMERA_ZOOM_SLIDER_HANDLE); final Point mousePos = client.getMouseCanvasPosition(); if (slider == null || slider.isHidden() || !slider.getBounds().contains(mousePos.getX(), mousePos.getY())) From 1076272d8d9ade5877cac63fdc47a756cab50fb8 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sat, 5 Dec 2020 03:44:59 -0700 Subject: [PATCH 7/7] music: re-add slider granularity removed in e60c25be994018f12a53ee35fe117f8a3fe60f45 --- .../main/java/net/runelite/api/ParamID.java | 16 + .../main/java/net/runelite/api/ScriptID.java | 19 +- .../main/java/net/runelite/api/SettingID.java | 35 ++ .../main/java/net/runelite/api/SpriteID.java | 4 + .../main/java/net/runelite/api/StructID.java | 3 + .../main/java/net/runelite/api/Varbits.java | 7 + .../net/runelite/api/widgets/WidgetID.java | 11 + .../net/runelite/api/widgets/WidgetInfo.java | 5 + .../client/plugins/music/MusicConfig.java | 13 + .../client/plugins/music/MusicPlugin.java | 534 ++++++++++++++++++ 10 files changed, 646 insertions(+), 1 deletion(-) create mode 100644 runelite-api/src/main/java/net/runelite/api/SettingID.java diff --git a/runelite-api/src/main/java/net/runelite/api/ParamID.java b/runelite-api/src/main/java/net/runelite/api/ParamID.java index 0775cf9f26..9be05d76ef 100644 --- a/runelite-api/src/main/java/net/runelite/api/ParamID.java +++ b/runelite-api/src/main/java/net/runelite/api/ParamID.java @@ -29,4 +29,20 @@ package net.runelite.api; */ public class ParamID { + /** + * @see SettingID + */ + public static final int SETTING_ID = 1077; + // defaults to 5 + // 1 is continuous + public static final int SETTING_SLIDER_STEPS = 1101; + public static final int SETTING_CUSTOM_TRANSMIT = 1085; + // defaults to true + // track is foreground + public static final int SETTING_FOREGROUND_CLICKZONE = 1105; + public static final int SETTING_SLIDER_CUSTOM_ONOP = 1106; + public static final int SETTING_SLIDER_CUSTOM_SETPOS = 1107; + public static final int SETTING_SLIDER_IS_DRAGGABLE = 1108; + public static final int SETTING_SLIDER_DEADZONE = 1109; + public static final int SETTING_SLIDER_DEADTIME = 1110; } diff --git a/runelite-api/src/main/java/net/runelite/api/ScriptID.java b/runelite-api/src/main/java/net/runelite/api/ScriptID.java index 7679476c49..b451aeb6cb 100644 --- a/runelite-api/src/main/java/net/runelite/api/ScriptID.java +++ b/runelite-api/src/main/java/net/runelite/api/ScriptID.java @@ -303,4 +303,21 @@ public final class ScriptID */ @ScriptArguments(integer = 18) public static final int BANKMAIN_SEARCH_TOGGLE = 281; -} + + /** + * Chooses the click handler for a {@link ParamID#SETTING_SLIDER_CUSTOM_ONOP} = 1 settings slider + * + * The active widget is set to the track created by {@link ParamID#SETTING_FOREGROUND_CLICKZONE} + *

    + *
  • int {@link ParamID#SETTING_ID}
  • + *
  • int (WidgetID) Slider handle ID
  • + *
  • int (widget index) Slider handle index
  • + *
  • int track width
  • + *
  • int y offset
  • + *
  • int x offset
  • + *
  • int (WidgetID) drag parent
  • + *
+ */ + @ScriptArguments(integer = 6) + public static final int SETTINGS_SLIDER_CHOOSE_ONOP = 3885; +} \ No newline at end of file diff --git a/runelite-api/src/main/java/net/runelite/api/SettingID.java b/runelite-api/src/main/java/net/runelite/api/SettingID.java new file mode 100644 index 0000000000..008b3259b4 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/SettingID.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api; + +/** + * @see ParamID#SETTING_ID + */ +public class SettingID +{ + public static final int MUSIC_VOLUME = 30; + public static final int EFFECT_VOLUME = 31; + public static final int AREA_VOLUME = 32; +} diff --git a/runelite-api/src/main/java/net/runelite/api/SpriteID.java b/runelite-api/src/main/java/net/runelite/api/SpriteID.java index ec1d72deff..d5bb718245 100644 --- a/runelite-api/src/main/java/net/runelite/api/SpriteID.java +++ b/runelite-api/src/main/java/net/runelite/api/SpriteID.java @@ -1591,4 +1591,8 @@ public final class SpriteID public static final int FRIENDS_CHAT_RANK_TRIPLE_CHEVRON_SERGEANT = 2831; public static final int FRIENDS_CHAT_RANK_DOUBLE_CHEVRON_CORPORAL = 2832; public static final int FRIENDS_CHAT_RANK_SINGLE_CHEVRON_RECRUIT = 2833; + + public static final int SETTINGS_SLIDER_HANDLE_BLUE = 2858; + public static final int SETTINGS_SLIDER_HANDLE_RED = 2859; + public static final int SETTINGS_SLIDER_HANDLE_GREEN = 2860; } diff --git a/runelite-api/src/main/java/net/runelite/api/StructID.java b/runelite-api/src/main/java/net/runelite/api/StructID.java index dd3c148cd3..e1dab81026 100644 --- a/runelite-api/src/main/java/net/runelite/api/StructID.java +++ b/runelite-api/src/main/java/net/runelite/api/StructID.java @@ -31,4 +31,7 @@ package net.runelite.api; */ public class StructID { + public static final int SETTINGS_MUSIC_VOLUME = 2753; + public static final int SETTINGS_EFFECT_VOLUME = 2754; + public static final int SETTINGS_AREA_VOLUME = 2755; } diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index b96a5994df..79d38b7e78 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -572,6 +572,13 @@ public enum Varbits LEAGUE_RELIC_5(10053), LEAGUE_RELIC_6(11696), + /** + * Muted volume restore values + */ + MUTED_MUSIC_VOLUME(9666), + MUTED_SOUND_EFFECT_VOLUME(9674), + MUTED_AREA_EFFECT_VOLUME(9675), + /** * Whether the Special Attack orb is disabled due to being in a PvP area * 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 7437371b70..5374988635 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 @@ -610,6 +610,7 @@ public class WidgetID static final int ROLE_SPRITE = 11; static final int ROLE = 12; } + static class HLR { static final int TEAMMATE1 = 18; @@ -617,6 +618,7 @@ public class WidgetID static final int TEAMMATE3 = 26; static final int TEAMMATE4 = 30; } + static final int CORRECT_STYLE = 3; static final int CURRENT_WAVE_WIDGET = 4; static final int CURRENT_WAVE = 5; @@ -839,6 +841,7 @@ public class WidgetID static final int MEMBERS_CONTAINER = 7; static final int MINIQUEST_CONTAINER = 8; } + static class Music { static final int CONTAINER = 0; @@ -884,6 +887,14 @@ public class WidgetID static class SettingsSide { static final int CAMERA_ZOOM_SLIDER_HANDLE = 55; + static final int MUSIC_SLIDER = 10; + static final int SOUND_EFFECT_SLIDER = 14; + static final int AREA_SOUND_SLIDER = 18; + } + + static class Settings + { + static final int INIT = 1; } static class AchievementDiary 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 1aa7b4d15f..70c8595b2b 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 @@ -533,6 +533,11 @@ public enum WidgetInfo SEED_VAULT_INVENTORY_ITEMS_CONTAINER(WidgetID.SEED_VAULT_INVENTORY_GROUP_ID, WidgetID.SeedVault.INVENTORY_ITEM_CONTAINER), SETTINGS_SIDE_CAMERA_ZOOM_SLIDER_HANDLE(WidgetID.SETTINGS_SIDE_GROUP_ID, WidgetID.SettingsSide.CAMERA_ZOOM_SLIDER_HANDLE), + SETTINGS_SIDE_MUSIC_SLIDER(WidgetID.SETTINGS_SIDE_GROUP_ID, WidgetID.SettingsSide.MUSIC_SLIDER), + SETTINGS_SIDE_SOUND_EFFECT_SLIDER(WidgetID.SETTINGS_SIDE_GROUP_ID, WidgetID.SettingsSide.SOUND_EFFECT_SLIDER), + SETTINGS_SIDE_AREA_SOUND_SLIDER(WidgetID.SETTINGS_SIDE_GROUP_ID, WidgetID.SettingsSide.AREA_SOUND_SLIDER), + + SETTINGS_INIT(WidgetID.SETTINGS_GROUP_ID, WidgetID.Settings.INIT), ACHIEVEMENT_DIARY_CONTAINER(WidgetID.ACHIEVEMENT_DIARY_GROUP_ID, WidgetID.AchievementDiary.CONTAINER), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicConfig.java index 594a1c7380..1458855db8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicConfig.java @@ -31,6 +31,8 @@ import net.runelite.client.config.ConfigItem; @ConfigGroup("music") public interface MusicConfig extends Config { + String GRANULAR_SLIDERS = "granularSliders"; + @ConfigItem( keyName = "muteOwnAreaSounds", name = "Mute player area sounds", @@ -86,6 +88,17 @@ public interface MusicConfig extends Config return false; } + @ConfigItem( + keyName = GRANULAR_SLIDERS, + name = "Granular volume sliders", + description = "Make the volume sliders allow better control of volume", + position = 5 + ) + default boolean granularSliders() + { + return true; + } + @ConfigItem( keyName = "musicVolume", name = "", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicPlugin.java index ab6757e11a..ca140e2bd4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicPlugin.java @@ -27,28 +27,46 @@ package net.runelite.client.plugins.music; import com.google.common.collect.ImmutableSet; +import com.google.common.primitives.Ints; import com.google.inject.Provides; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Set; +import java.util.function.Consumer; +import java.util.function.IntConsumer; +import java.util.function.IntSupplier; import java.util.stream.Collectors; import javax.inject.Inject; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Actor; import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.NPC; +import net.runelite.api.ParamID; import net.runelite.api.Player; +import net.runelite.api.ScriptEvent; import net.runelite.api.ScriptID; +import net.runelite.api.SettingID; import net.runelite.api.SoundEffectID; import net.runelite.api.SpriteID; +import net.runelite.api.StructComposition; +import net.runelite.api.StructID; import net.runelite.api.VarClientInt; +import net.runelite.api.VarPlayer; +import net.runelite.api.Varbits; import net.runelite.api.events.AreaSoundEffectPlayed; +import net.runelite.api.events.BeforeRender; import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.PostStructComposition; +import net.runelite.api.events.ScriptPreFired; import net.runelite.api.events.SoundEffectPlayed; import net.runelite.api.events.VarClientIntChanged; +import net.runelite.api.events.VolumeChanged; import net.runelite.api.events.WidgetLoaded; import net.runelite.api.widgets.JavaScriptCallback; import net.runelite.api.widgets.Widget; @@ -59,11 +77,15 @@ import net.runelite.api.widgets.WidgetType; import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; import net.runelite.client.game.chatbox.ChatboxPanelManager; import net.runelite.client.game.chatbox.ChatboxTextInput; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.tooltip.Tooltip; +import net.runelite.client.ui.overlay.tooltip.TooltipManager; +@Slf4j @PluginDescriptor( name = "Music", description = "Adds search and filter for the music list, and additional volume control", @@ -71,6 +93,8 @@ import net.runelite.client.plugins.PluginDescriptor; ) public class MusicPlugin extends Plugin { + private static final int SLIDER_HANDLE_SIZE = 16; + private static final Set SOURCELESS_PLAYER_SOUNDS = ImmutableSet.of( SoundEffectID.TELEPORT_VWOOP ); @@ -117,6 +141,14 @@ public class MusicPlugin extends Plugin @Inject private ChatboxPanelManager chatboxPanelManager; + @Inject + private TooltipManager tooltipManager; + + private Channel musicChannel; + private Channel effectChannel; + private Channel areaChannel; + private Channel[] channels; + private ChatboxTextInput searchInput; private Widget musicSearchButton; @@ -126,12 +158,36 @@ public class MusicPlugin extends Plugin private MusicState currentMusicFilter = MusicState.ALL; + private Tooltip sliderTooltip; + private boolean shuttingDown = false; + @Override protected void startUp() { clientThread.invoke(() -> { + this.shuttingDown = false; + + musicChannel = new Channel("Music", + VarPlayer.MUSIC_VOLUME, Varbits.MUTED_MUSIC_VOLUME, + musicConfig::getMusicVolume, musicConfig::setMusicVolume, + client::setMusicVolume, 255, + WidgetInfo.SETTINGS_SIDE_MUSIC_SLIDER); + effectChannel = new Channel("Sound Effects", + VarPlayer.SOUND_EFFECT_VOLUME, Varbits.MUTED_SOUND_EFFECT_VOLUME, + musicConfig::getSoundEffectVolume, musicConfig::setSoundEffectVolume, + client::setSoundEffectVolume, 127, + WidgetInfo.SETTINGS_SIDE_SOUND_EFFECT_SLIDER); + areaChannel = new Channel("Area Sounds", + VarPlayer.AREA_EFFECT_VOLUME, Varbits.MUTED_AREA_EFFECT_VOLUME, + musicConfig::getAreaSoundEffectVolume, musicConfig::setAreaSoundEffectVolume, + client::setAreaSoundEffectVolume, 127, + WidgetInfo.SETTINGS_SIDE_AREA_SOUND_SLIDER); + channels = new Channel[]{musicChannel, effectChannel, areaChannel}; + addMusicButtons(); + updateMusicOptions(); + resetSettingsWindow(); }); } @@ -145,6 +201,11 @@ public class MusicPlugin extends Plugin } tracks = null; + clientThread.invoke(() -> + { + shuttingDown = true; + teardownMusicOptions(); + }); } @Provides @@ -175,6 +236,12 @@ public class MusicPlugin extends Plugin currentMusicFilter = MusicState.ALL; addMusicButtons(); } + + if ((widgetLoaded.getGroupId() == WidgetID.SETTINGS_GROUP_ID || widgetLoaded.getGroupId() == WidgetID.SETTINGS_SIDE_GROUP_ID) + && musicConfig.granularSliders()) + { + updateMusicOptions(); + } } private void addMusicButtons() @@ -225,6 +292,42 @@ public class MusicPlugin extends Plugin } } + @Subscribe + public void onVolumeChanged(VolumeChanged volumeChanged) + { + if (musicConfig.granularSliders()) + { + updateMusicOptions(); + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged configChanged) + { + if ("music".equals(configChanged.getGroup())) + { + clientThread.invoke(() -> + { + if (MusicConfig.GRANULAR_SLIDERS.equals(configChanged.getKey())) + { + if (musicConfig.granularSliders()) + { + updateMusicOptions(); + resetSettingsWindow(); + } + else + { + teardownMusicOptions(); + } + } + else + { + updateMusicOptions(); + } + }); + } + } + private boolean isOnMusicTab() { return client.getVar(VarClientInt.INVENTORY_TAB) == 13; @@ -350,6 +453,437 @@ public class MusicPlugin extends Plugin private final int spriteID; } + @RequiredArgsConstructor + private class Slider + { + @Getter + protected final Channel channel; + + protected Widget track; + protected Widget handle; + + public void update() + { + handle.setOnDragListener((JavaScriptCallback) this::drag); + handle.setOnDragCompleteListener((JavaScriptCallback) this::drag); + handle.setHasListener(true); + + track.setOnMouseRepeatListener((JavaScriptCallback) ev -> + { + int value = channel.getValue(); + int percent = (int) Math.round((value * 100.0 / channel.getMax())); + + sliderTooltip = new Tooltip(channel.getName() + ": " + percent + "%"); + }); + track.setOnClickListener((JavaScriptCallback) this::click); + track.setHasListener(true); + } + + public void shutDown() + { + if (handle != null) + { + handle.setDragParent(null); + handle.setOnDragListener((Object[]) null); + handle.setOnDragCompleteListener((Object[]) null); + } + if (track != null) + { + track.setOnMouseRepeatListener((Object[]) null); + track.setOnClickListener((Object[]) null); + } + } + + protected void drag(ScriptEvent ev) + { + moveHandle(ev.getMouseX()); + } + + protected void click(ScriptEvent ev) + { + moveHandle(ev.getMouseX() - (SLIDER_HANDLE_SIZE / 2)); + } + + protected void moveHandle(int x) + { + int level = (x * channel.max) / getWidth(); + level = Ints.constrainToRange(level, 0, channel.max); + channel.setLevel(level); + } + + protected int getWidth() + { + return track.getWidth() - SLIDER_HANDLE_SIZE; + } + } + + private class SettingsSideSlider extends Slider + { + private final WidgetInfo root; + private Widget icon; + + SettingsSideSlider(Channel channel, WidgetInfo root) + { + super(channel); + this.root = root; + } + + @Override + public void update() + { + Widget root = client.getWidget(this.root); + if (root == null) + { + return; + } + + Object[] onLoad = root.getOnLoadListener(); + if (onLoad == null || onLoad.length != 5) + { + return; + } + + this.icon = client.getWidget((Integer) onLoad[1]); + this.track = client.getWidget((Integer) onLoad[2]); + this.handle = client.getWidget((Integer) onLoad[3]); + if (this.track == null || this.handle == null) + { + return; + } + + for (Widget w : track.getChildren()) + { + if (w != null) + { + w.setAction(0, null); + } + } + + handle.setOnVarTransmitListener((Object[]) null); + handle.setDragParent(track); + handle.setSpriteId(SpriteID.SETTINGS_SLIDER_HANDLE_GREEN); + super.update(); + + int val = channel.getValue(); + handle.setOriginalX((val * getWidth()) / channel.getMax()); + handle.revalidate(); + + // emulate [proc,settings_update_icon] + boolean unmuted = val != 0; + icon.getChild(1).setHidden(unmuted); + icon.setAction(0, unmuted ? "Unmute" : "Mute"); + // Set name + no tooltip; we have our own for ops + icon.setName(channel.getName()); + icon.setOnMouseRepeatListener((Object[]) null); + icon.setOnOpListener((JavaScriptCallback) ev -> channel.toggleMute()); + } + + @Override + public void shutDown() + { + super.shutDown(); + handle.setSpriteId(SpriteID.SETTINGS_SLIDER_HANDLE_BLUE); + + this.icon.setOnOpListener((Object[]) null); + + Widget root = client.getWidget(this.root); + if (root != null) + { + client.createScriptEvent(root.getOnLoadListener()) + .setSource(root) + .run(); + } + + this.handle = this.track = this.icon = null; + } + } + + private class SettingsSlider extends Slider + { + private final int offsetX; + private final int offsetY; + private final int width; + private final Widget realTrack; + + SettingsSlider(Channel channel, Widget handle, Widget track, int width, int offsetY, int offsetX, Widget realTrack) + { + super(channel); + this.handle = handle; + this.track = track; + this.width = width; + this.offsetX = offsetX; + this.offsetY = offsetY; + this.realTrack = realTrack; + } + + @Override + public void update() + { + super.update(); + + int val = channel.getValue(); + handle.setOriginalX(offsetX + (val * getWidth()) / channel.getMax()); + handle.setOriginalY(offsetY); + handle.revalidate(); + } + + @Override + protected int getWidth() + { + return width - SLIDER_HANDLE_SIZE; + } + + @Override + protected void click(ScriptEvent ev) + { + super.click(ev); + realTrack.setOriginalX(offsetX); + realTrack.setOriginalY(offsetY); + realTrack.setOriginalWidth(this.width); + realTrack.setOriginalHeight(SLIDER_HANDLE_SIZE); + realTrack.revalidate(); + } + + @Override + @SuppressWarnings("PMD.UselessOverridingMethod") + public void shutDown() + { + // calling settings_init will do teardown for us + super.shutDown(); + } + } + + @Subscribe + private void onPostStructComposition(PostStructComposition ev) + { + if (shuttingDown) + { + return; + } + + StructComposition sc = ev.getStructComposition(); + switch (sc.getId()) + { + case StructID.SETTINGS_MUSIC_VOLUME: + case StructID.SETTINGS_EFFECT_VOLUME: + case StructID.SETTINGS_AREA_VOLUME: + if (!musicConfig.granularSliders()) + { + return; + } + + sc.setValue(ParamID.SETTING_SLIDER_STEPS, 1); + sc.setValue(ParamID.SETTING_CUSTOM_TRANSMIT, 0); + sc.setValue(ParamID.SETTING_FOREGROUND_CLICKZONE, 0); + sc.setValue(ParamID.SETTING_SLIDER_CUSTOM_ONOP, 1); + sc.setValue(ParamID.SETTING_SLIDER_CUSTOM_SETPOS, 1); + sc.setValue(ParamID.SETTING_SLIDER_IS_DRAGGABLE, 1); + sc.setValue(ParamID.SETTING_SLIDER_DEADZONE, 0); + sc.setValue(ParamID.SETTING_SLIDER_DEADZONE, 0); + break; + } + } + + @Subscribe + private void onScriptPreFired(ScriptPreFired ev) + { + if (shuttingDown) + { + return; + } + + if (ev.getScriptId() == ScriptID.SETTINGS_SLIDER_CHOOSE_ONOP) + { + if (!musicConfig.granularSliders()) + { + return; + } + + int arg = client.getIntStackSize() - 7; + int[] is = client.getIntStack(); + Channel channel; + switch (is[arg]) + { + case SettingID.MUSIC_VOLUME: + channel = musicChannel; + break; + case SettingID.EFFECT_VOLUME: + channel = effectChannel; + break; + case SettingID.AREA_VOLUME: + channel = areaChannel; + break; + default: + return; + } + + Widget track = client.getScriptActiveWidget(); + Widget handle = client.getWidget(is[arg + 1]) + .getChild(is[arg + 2]); + Widget realTrack = client.getWidget(is[arg + 6]); + SettingsSlider s = new SettingsSlider(channel, handle, track, is[arg + 3], is[arg + 4], is[arg + 5], realTrack); + s.update(); + s.getChannel().setWindowSlider(s); + } + } + + private class Channel + { + @Getter + private final String name; + private final VarPlayer var; + private final Varbits mutedVar; + private final IntSupplier getter; + private final Consumer setter; + private final IntConsumer volumeChanger; + + @Getter + private final int max; + + private final Slider sideSlider; + + @Setter + private Slider windowSlider; + + Channel(String name, + VarPlayer var, Varbits mutedVar, + IntSupplier getter, Consumer setter, + IntConsumer volumeChanger, int max, + WidgetInfo sideRoot) + { + this.name = name; + this.var = var; + this.mutedVar = mutedVar; + this.getter = getter; + this.setter = setter; + this.volumeChanger = volumeChanger; + this.max = max; + this.sideSlider = new SettingsSideSlider(this, sideRoot); + } + + private int getValueRaw() + { + int value = getter.getAsInt(); + if (value == 0) + { + // Use the vanilla value + + // the varps are known by the engine and it requires they are stored so + // 0 = max and 4 = muted + int raw = 4 - client.getVar(var); + if (raw == 0) + { + raw = -(4 - client.getVar(mutedVar)); + } + value = ((raw * max) / 4); + + // readd our 1 offset for unknown's place + value += value < 0 ? -1 : 1; + } + + return value; + } + + private int getValue() + { + int value = getValueRaw(); + + // muted with saved restore point + if (value < 0) + { + return 0; + } + + // 0 is used for unknown, so config values are 1 away from zero + return value - 1; + } + + public void toggleMute() + { + int val = -getValueRaw(); + if (val == -1) + { + // muted without a reset value + val = max / 2; + } + setter.accept(val); + } + + public void setLevel(int level) + { + setter.accept(level + 1); + update(); + } + + public void update() + { + volumeChanger.accept(getValue()); + sideSlider.update(); + if (windowSlider != null) + { + windowSlider.update(); + } + } + + public void shutDown() + { + sideSlider.shutDown(); + if (windowSlider != null) + { + windowSlider.shutDown(); + } + + int raw = 4 - client.getVar(var); + int value = ((raw * max) / 4); + volumeChanger.accept(value); + } + } + + private void updateMusicOptions() + { + for (Channel channel : channels) + { + channel.update(); + } + } + + private void teardownMusicOptions() + { + // the side panel uses this too, so it has to run before they get shut down + client.getStructCompositionCache().reset(); + + for (Channel channel : channels) + { + channel.shutDown(); + } + + resetSettingsWindow(); + } + + private void resetSettingsWindow() + { + client.getStructCompositionCache().reset(); + + Widget init = client.getWidget(WidgetInfo.SETTINGS_INIT); + if (init != null) + { + // [clientscript, settings_init] + client.createScriptEvent(init.getOnLoadListener()) + .setSource(init) + .run(); + } + } + + @Subscribe + private void onBeforeRender(BeforeRender ev) + { + if (sliderTooltip != null) + { + tooltipManager.add(sliderTooltip); + sliderTooltip = null; + } + } + @Subscribe public void onAreaSoundEffectPlayed(AreaSoundEffectPlayed areaSoundEffectPlayed) {