Merge pull request #10078 from abextm/music-ux-external

music: Add ingame granular volume adjustment
This commit is contained in:
Adam
2019-10-21 18:54:29 -04:00
committed by GitHub
9 changed files with 434 additions and 48 deletions

View File

@@ -61,4 +61,9 @@ public interface ScriptEvent
* @see net.runelite.api.events.MenuOptionClicked * @see net.runelite.api.events.MenuOptionClicked
*/ */
String getOpbase(); String getOpbase();
/**
* Parent relative x coordinate for mouse related events
*/
int getMouseX();
} }

View File

@@ -83,6 +83,16 @@ public final class ScriptID
@ScriptArguments(integer = 2) @ScriptArguments(integer = 2)
public static final int MESSAGE_LAYER_CLOSE = 299; public static final int MESSAGE_LAYER_CLOSE = 299;
/**
* Sets the background for sound option bars
* <ul>
* <li> int Value of the slider (0-4) </li>
* <li> int (WidgetID) * 5, segments of the slider </li>
* </ul>
*/
@ScriptArguments(integer = 6)
public static final int OPTIONS_ALLSOUNDS = 358;
/** /**
* Readies the chatbox panel for things like the chatbox input * Readies the chatbox panel for things like the chatbox input
* Inverse of MESSAGE_LAYER_CLOSE * Inverse of MESSAGE_LAYER_CLOSE

View File

@@ -553,7 +553,7 @@ public interface Widget
void setOnMouseOverListener(Object... args); void setOnMouseOverListener(Object... args);
/** /**
* Sets a script to be ran every frame when the mouse is in the widget bounds * Sets a script to be ran every client tick when the mouse is in the widget bounds
* *
* @param args A ScriptID, then the args for the script * @param args A ScriptID, then the args for the script
*/ */
@@ -567,7 +567,7 @@ public interface Widget
void setOnMouseLeaveListener(Object... args); void setOnMouseLeaveListener(Object... args);
/** /**
* Sets a script to be ran every frame * Sets a script to be ran every client tick
* *
* @param args A ScriptID, then the args for the script * @param args A ScriptID, then the args for the script
*/ */
@@ -823,4 +823,31 @@ public interface Widget
* Can widgets under this widgets be scrolled in this widgets bounding box * Can widgets under this widgets be scrolled in this widgets bounding box
*/ */
void setNoScrollThrough(boolean noScrollThrough); void setNoScrollThrough(boolean noScrollThrough);
/**
* {@link net.runelite.api.VarPlayer}s that triggers this widgets varTransmitListener
*/
void setVarTransmitTrigger(int ...trigger);
/**
* Sets a script to be ran the first client tick the mouse is held ontop of this widget
*
* @param args A ScriptID, then the args for the script
*/
void setOnClickListener(Object ...args);
/**
* Sets a script to be ran the every client tick the mouse is held ontop of this widget,
* except the first client tick.
*
* @param args A ScriptID, then the args for the script
*/
void setOnHoldListener(Object ...args);
/**
* Sets a script to be ran the first client tick the mouse is not held ontop of this widget
*
* @param args A ScriptID, then the args for the script
*/
void setOnReleaseListener(Object ...args);
} }

View File

@@ -143,6 +143,7 @@ public class WidgetID
public static final int ITEMS_KEPT_ON_DEATH_GROUP_ID = 4; public static final int ITEMS_KEPT_ON_DEATH_GROUP_ID = 4;
public static final int SEED_VAULT_GROUP_ID = 631; public static final int SEED_VAULT_GROUP_ID = 631;
public static final int EXPLORERS_RING_ALCH_GROUP_ID = 483; public static final int EXPLORERS_RING_ALCH_GROUP_ID = 483;
public static final int OPTIONS_GROUP_ID = 261;
static class WorldMap static class WorldMap
{ {
@@ -834,4 +835,11 @@ public class WidgetID
{ {
static final int INVENTORY = 7; static final int INVENTORY = 7;
} }
static class Options
{
static final int MUSIC_SLIDER = 44;
static final int SOUND_EFFECT_SLIDER = 50;
static final int AREA_SOUND_SLIDER = 56;
}
} }

View File

@@ -498,7 +498,11 @@ public enum WidgetInfo
SEED_VAULT_TITLE_CONTAINER(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.TITLE_CONTAINER), SEED_VAULT_TITLE_CONTAINER(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.TITLE_CONTAINER),
SEED_VAULT_ITEM_CONTAINER(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.ITEM_CONTAINER), SEED_VAULT_ITEM_CONTAINER(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.ITEM_CONTAINER),
SEED_VAULT_ITEM_TEXT(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.ITEM_TEXT); SEED_VAULT_ITEM_TEXT(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.ITEM_TEXT),
OPTIONS_MUSIC_SLIDER(WidgetID.OPTIONS_GROUP_ID, WidgetID.Options.MUSIC_SLIDER),
OPTIONS_SOUND_EFFECT_SLIDER(WidgetID.OPTIONS_GROUP_ID, WidgetID.Options.SOUND_EFFECT_SLIDER),
OPTIONS_AREA_SOUND_SLIDER(WidgetID.OPTIONS_GROUP_ID, WidgetID.Options.AREA_SOUND_SLIDER);
private final int groupId; private final int groupId;
private final int childId; private final int childId;

View File

@@ -27,20 +27,15 @@ package net.runelite.client.plugins.music;
import net.runelite.client.config.Config; import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.Range;
@ConfigGroup("music") @ConfigGroup("music")
public interface MusicConfig extends Config public interface MusicConfig extends Config
{ {
@ConfigItem( @ConfigItem(
keyName = "musicVolume", keyName = "musicVolume",
name = "Music Volume", name = "",
description = "Overrides music volume in game with more granular control", description = "",
position = 1 hidden = true
)
@Range(
min = 0,
max = 255
) )
default int getMusicVolume() default int getMusicVolume()
{ {
@@ -48,32 +43,47 @@ public interface MusicConfig extends Config
} }
@ConfigItem( @ConfigItem(
keyName = "soundEffectVolume", keyName = "musicVolume",
name = "Sound Effect Volume", name = "",
description = "Overrides the sound effect volume in game with more granular control", description = "",
position = 2 hidden = true
) )
@Range( void setMusicVolume(int vol);
min = 0,
max = 127 @ConfigItem(
keyName = "soundEffectVolume",
name = "",
description = "",
hidden = true
) )
default int getSoundEffectVolume() default int getSoundEffectVolume()
{ {
return 0; return 0;
} }
@ConfigItem(
keyName = "soundEffectVolume",
name = "",
description = "",
hidden = true
)
void setSoundEffectVolume(int val);
@ConfigItem(
keyName = "areaSoundEffectVolume",
name = "",
description = "",
hidden = true
)
default int getAreaSoundEffectVolume()
{
return 0;
}
@ConfigItem( @ConfigItem(
keyName = "areaSoundEffectVolume", keyName = "areaSoundEffectVolume",
name = "Area Sound Effect Volume", name = "",
description = "Overrides the area sound effect volume in game with more granular control", description = "",
position = 3 hidden = true
) )
@Range( void setAreaSoundEffectVolume(int vol);
min = 0,
max = 127
)
default int getAreaSoundEffectVolume()
{
return 0;
}
} }

View File

@@ -29,23 +29,30 @@ import com.google.inject.Provides;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.function.BiConsumer;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.GameState; import net.runelite.api.GameState;
import net.runelite.api.ScriptID; import net.runelite.api.ScriptID;
import net.runelite.api.SoundEffectID; import net.runelite.api.SoundEffectID;
import net.runelite.api.SpriteID; import net.runelite.api.SpriteID;
import net.runelite.api.VarClientInt; import net.runelite.api.VarClientInt;
import net.runelite.api.VarPlayer;
import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.events.VarClientIntChanged; import net.runelite.api.events.VarClientIntChanged;
import net.runelite.api.events.VolumeChanged; import net.runelite.api.events.VolumeChanged;
import net.runelite.api.events.WidgetLoaded; import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.widgets.JavaScriptCallback; import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetConfig;
import net.runelite.api.widgets.WidgetID; import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetInfo;
import net.runelite.api.widgets.WidgetPositionMode; import net.runelite.api.widgets.WidgetPositionMode;
@@ -85,17 +92,15 @@ public class MusicPlugin extends Plugin
private MusicState currentMusicFilter = MusicState.ALL; private MusicState currentMusicFilter = MusicState.ALL;
private int lastMusicVolume;
private int lastEffectVolume;
private int lastAreaEffectVolume;
@Override @Override
protected void startUp() protected void startUp()
{ {
lastMusicVolume = lastEffectVolume = lastAreaEffectVolume = -1; clientThread.invoke(() ->
{
clientThread.invoke(this::addMusicButtons); addMusicButtons();
clientThread.invoke(this::applyMusicVolumeConfig); applyMusicVolumeConfig();
updateMusicOptions();
});
} }
@Override @Override
@@ -108,6 +113,8 @@ public class MusicPlugin extends Plugin
} }
tracks = null; tracks = null;
clientThread.invoke(this::teardownMusicOptions);
} }
@Provides @Provides
@@ -138,6 +145,10 @@ public class MusicPlugin extends Plugin
currentMusicFilter = MusicState.ALL; currentMusicFilter = MusicState.ALL;
addMusicButtons(); addMusicButtons();
} }
if (widgetLoaded.getGroupId() == WidgetID.OPTIONS_GROUP_ID)
{
updateMusicOptions();
}
} }
private void addMusicButtons() private void addMusicButtons()
@@ -206,26 +217,24 @@ public class MusicPlugin extends Plugin
private void applyMusicVolumeConfig() private void applyMusicVolumeConfig()
{ {
int musicVolume = musicConfig.getMusicVolume(); int musicVolume = musicConfig.getMusicVolume();
// Set the volume if it is >0, or if it was >0 and is now going back to 0 if (musicVolume > 0)
if (musicVolume > 0 || lastMusicVolume > 0)
{ {
client.setMusicVolume(musicVolume); client.setMusicVolume(musicVolume - 1);
lastMusicVolume = musicVolume;
} }
int soundEffectVolume = musicConfig.getSoundEffectVolume(); int soundEffectVolume = musicConfig.getSoundEffectVolume();
if (soundEffectVolume > 0 || lastEffectVolume > 0) if (soundEffectVolume > 0)
{ {
client.setSoundEffectVolume(soundEffectVolume); client.setSoundEffectVolume(soundEffectVolume - 1);
lastEffectVolume = soundEffectVolume;
} }
int areaSoundEffectVolume = musicConfig.getAreaSoundEffectVolume(); int areaSoundEffectVolume = musicConfig.getAreaSoundEffectVolume();
if (areaSoundEffectVolume > 0 || lastAreaEffectVolume > 0) if (areaSoundEffectVolume > 0)
{ {
client.setAreaSoundEffectVolume(areaSoundEffectVolume); client.setAreaSoundEffectVolume(areaSoundEffectVolume - 1);
lastAreaEffectVolume = areaSoundEffectVolume;
} }
updateMusicOptions();
} }
private boolean isOnMusicTab() private boolean isOnMusicTab()
@@ -351,4 +360,176 @@ public class MusicPlugin extends Plugin
private final String name; private final String name;
private final int spriteID; private final int spriteID;
} }
}
@RequiredArgsConstructor
@Getter
private enum MusicSlider
{
MUSIC(WidgetInfo.OPTIONS_MUSIC_SLIDER, VarPlayer.MUSIC_VOLUME, MusicConfig::getMusicVolume, MusicConfig::setMusicVolume, 255),
AREA(WidgetInfo.OPTIONS_AREA_SOUND_SLIDER, VarPlayer.AREA_EFFECT_VOLUME, MusicConfig::getAreaSoundEffectVolume, MusicConfig::setAreaSoundEffectVolume, 127),
EFFECT(WidgetInfo.OPTIONS_SOUND_EFFECT_SLIDER, VarPlayer.SOUND_EFFECT_VOLUME, MusicConfig::getSoundEffectVolume, MusicConfig::setSoundEffectVolume, 127);
private final WidgetInfo widgetID;
private final VarPlayer var;
private final ToIntFunction<MusicConfig> getter;
private final BiConsumer<MusicConfig, Integer> setter;
private final int max;
@Setter
private Widget handle;
@Setter
private Widget track;
private static int PADDING = 8;
private int getX()
{
return getTrack().getRelativeX() + PADDING;
}
private int getWidth()
{
return getTrack().getWidth() - (PADDING * 2) - handle.getWidth();
}
}
private void teardownMusicOptions()
{
for (MusicSlider slider : MusicSlider.values())
{
Widget icon = client.getWidget(slider.getWidgetID());
if (icon == null)
{
return;
}
if (slider.getHandle() != null)
{
{
Widget handle = slider.getHandle();
Widget[] siblings = handle.getParent().getChildren();
if (siblings.length < handle.getIndex() || siblings[handle.getIndex()] != handle)
{
continue;
}
siblings[slider.getTrack().getIndex()] = null;
siblings[handle.getIndex()] = null;
}
Object[] init = icon.getOnLoadListener();
init[1] = slider.getWidgetID().getId();
// Readd the var transmit triggers and rerun options_allsounds
client.runScript(init);
slider.setHandle(null);
slider.setTrack(null);
}
}
}
private void updateMusicOptions()
{
for (MusicSlider slider : MusicSlider.values())
{
Widget icon = client.getWidget(slider.getWidgetID());
if (icon == null)
{
return;
}
Widget handle = slider.getHandle();
if (handle != null)
{
Widget[] siblings = handle.getParent().getChildren();
if (siblings.length < handle.getIndex() || siblings[handle.getIndex()] != handle)
{
handle = null;
}
}
if (handle == null)
{
Object[] init = icon.getOnLoadListener();
icon.setVarTransmitTrigger((int[]) null);
Widget track = icon.getParent().createChild(-1, WidgetType.TEXT);
slider.setTrack(track);
handle = icon.getParent().createChild(-1, WidgetType.GRAPHIC);
slider.setHandle(handle);
{
// First widget of the track
int wid = (Integer) init[2];
Widget w = client.getWidget(WidgetInfo.TO_GROUP(wid), WidgetInfo.TO_CHILD(wid));
track.setOriginalX(w.getRelativeX());
track.setOriginalY(w.getRelativeY());
}
{
// Last widget of the track
int wid = (Integer) init[6];
Widget w = client.getWidget(WidgetInfo.TO_GROUP(wid), WidgetInfo.TO_CHILD(wid));
track.setOriginalWidth((w.getRelativeX() + w.getWidth()) - track.getOriginalX());
}
track.setOriginalHeight(16);
track.setNoClickThrough(true);
track.revalidate();
handle.setSpriteId(SpriteID.OPTIONS_ZOOM_SLIDER_THUMB);
handle.setOriginalWidth(16);
handle.setOriginalHeight(16);
handle.setClickMask(WidgetConfig.DRAG);
JavaScriptCallback move = ev ->
{
int newVal = ((ev.getMouseX() - MusicSlider.PADDING - (slider.getHandle().getWidth() / 2)) * slider.getMax())
/ slider.getWidth();
if (newVal < 0)
{
newVal = 0;
}
if (newVal > slider.getMax())
{
newVal = slider.getMax();
}
// We store +1 so we can tell the difference between 0 and muted
slider.getSetter().accept(musicConfig, newVal + 1);
applyMusicVolumeConfig();
};
track.setOnClickListener(move);
track.setOnHoldListener(move);
track.setOnReleaseListener(move);
track.setHasListener(true);
client.runScript(ScriptID.OPTIONS_ALLSOUNDS, -1, init[2], init[3], init[4], init[5], init[6]);
}
int value = slider.getGetter().applyAsInt(musicConfig) - 1;
if (value <= -1)
{
// Use the vanilla value
value = ((4 - client.getVar(slider.getVar())) * slider.getMax()) / 4;
}
int newX = ((value * slider.getWidth()) / slider.getMax()) + slider.getX();
slider.getHandle().setOriginalX(newX);
slider.getHandle().setOriginalY(slider.getTrack().getOriginalY());
slider.getHandle().revalidate();
}
}
@Subscribe
public void onScriptCallbackEvent(ScriptCallbackEvent ev)
{
switch (ev.getEventName())
{
case "optionsAllSounds":
// We have to override this script because it gets invoked periodically from the server
client.getIntStack()[client.getIntStackSize() - 1] = -1;
}
}
}

View File

@@ -0,0 +1 @@
950ADB6A28E029005D24F99A65EF4D2AC4486EDC680D8770F4435F0300AA1299

View File

@@ -0,0 +1,140 @@
.id 358
.int_stack_count 6
.string_stack_count 0
.int_var_count 6
.string_var_count 0
; callback "optionsAllSounds"
; Used by the MusicPlugin to hide the vanilla (blue) volume handles
; Enable the MusicPlugin and go to the volume options panel. There should
; only be a green handle on the slider
iload 0
sconst "optionsAllSounds"
runelite_callback
istore 0
iload 0
iconst 4
if_icmpeq LABEL4
jump LABEL20
LABEL4:
iconst 687
iload 1
if_setgraphic
iconst 693
iload 2
if_setgraphic
iconst 694
iload 3
if_setgraphic
iconst 695
iload 4
if_setgraphic
iconst 696
iload 5
if_setgraphic
jump LABEL115
LABEL20:
iload 0
iconst 3
if_icmpeq LABEL24
jump LABEL40
LABEL24:
iconst 692
iload 1
if_setgraphic
iconst 688
iload 2
if_setgraphic
iconst 694
iload 3
if_setgraphic
iconst 695
iload 4
if_setgraphic
iconst 696
iload 5
if_setgraphic
jump LABEL115
LABEL40:
iload 0
iconst 2
if_icmpeq LABEL44
jump LABEL60
LABEL44:
iconst 692
iload 1
if_setgraphic
iconst 693
iload 2
if_setgraphic
iconst 689
iload 3
if_setgraphic
iconst 695
iload 4
if_setgraphic
iconst 696
iload 5
if_setgraphic
jump LABEL115
LABEL60:
iload 0
iconst 1
if_icmpeq LABEL64
jump LABEL80
LABEL64:
iconst 692
iload 1
if_setgraphic
iconst 693
iload 2
if_setgraphic
iconst 694
iload 3
if_setgraphic
iconst 690
iload 4
if_setgraphic
iconst 696
iload 5
if_setgraphic
jump LABEL115
LABEL80:
iload 0
iconst 0
if_icmpeq LABEL84
jump LABEL100
LABEL84:
iconst 692
iload 1
if_setgraphic
iconst 693
iload 2
if_setgraphic
iconst 694
iload 3
if_setgraphic
iconst 695
iload 4
if_setgraphic
iconst 691
iload 5
if_setgraphic
jump LABEL115
LABEL100:
iconst 692
iload 1
if_setgraphic
iconst 693
iload 2
if_setgraphic
iconst 694
iload 3
if_setgraphic
iconst 695
iload 4
if_setgraphic
iconst 696
iload 5
if_setgraphic
LABEL115:
return