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..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.
@@ -897,6 +907,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
*
@@ -1144,6 +1166,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.
*
@@ -1311,11 +1347,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/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..9be05d76ef
--- /dev/null
+++ b/runelite-api/src/main/java/net/runelite/api/ParamID.java
@@ -0,0 +1,48 @@
+/*
+ * 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
+{
+ /**
+ * @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/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();
}
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/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..e1dab81026
--- /dev/null
+++ b/runelite-api/src/main/java/net/runelite/api/StructID.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+{
+ 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/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;
+}
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);
}
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..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
@@ -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;
@@ -609,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;
@@ -616,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;
@@ -838,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;
@@ -880,9 +884,17 @@ public class WidgetID
static final int INVENTORY = 7;
}
- static class Options
+ 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 9205b8d1e2..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
@@ -532,7 +532,12 @@ 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),
+ 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/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/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()))
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..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
@@ -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;
@@ -562,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);
@@ -572,10 +574,29 @@ 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);
}
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());
}
}
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/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)
{
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);