From 1076272d8d9ade5877cac63fdc47a756cab50fb8 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sat, 5 Dec 2020 03:44:59 -0700 Subject: [PATCH] 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} + * + */ + @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) {