diff --git a/assets/images/menus/options/meter.png b/assets/images/menus/options/meter.png
new file mode 100644
index 00000000..4607e9b0
Binary files /dev/null and b/assets/images/menus/options/meter.png differ
diff --git a/assets/images/menus/options/meter.xml b/assets/images/menus/options/meter.xml
new file mode 100644
index 00000000..fddf0e4e
--- /dev/null
+++ b/assets/images/menus/options/meter.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/languages/en/Options.xml b/assets/languages/en/Options.xml
index 4e0b46fd..2557c8ec 100644
--- a/assets/languages/en/Options.xml
+++ b/assets/languages/en/Options.xml
@@ -105,7 +105,10 @@
If unchecked, will censor the Week 7 cutscenes.
Camera Zoom on Beat
- If unchecked, will stop the camera from zooming in every 4 beats
+ If unchecked, will stop the camera from zooming in every 4 beats.
+
+ SFX Volume
+ Changes only the volume of the SFX instead of everything.
diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx
index 767f239e..2e89b29f 100644
--- a/source/funkin/options/Options.hx
+++ b/source/funkin/options/Options.hx
@@ -29,6 +29,7 @@ class Options
public static var autoPause:Bool = true;
public static var antialiasing:Bool = true;
public static var volume:Float = 1;
+ public static var volumeSFX:Float = 1;
public static var week6PixelPerfect:Bool = true;
public static var gameplayShaders:Bool = true;
public static var colorHealthBar:Bool = true;
diff --git a/source/funkin/options/categories/GameplayOptions.hx b/source/funkin/options/categories/GameplayOptions.hx
index 4d6dc72c..0de4ceeb 100644
--- a/source/funkin/options/categories/GameplayOptions.hx
+++ b/source/funkin/options/categories/GameplayOptions.hx
@@ -34,6 +34,13 @@ class GameplayOptions extends OptionsScreen {
getName("camZoomOnBeat"),
getDesc("camZoomOnBeat"),
"camZoomOnBeat"));
+ add(new MeterOption(
+ getName("volumeSFX"),
+ getDesc("volumeSFX"),
+ 0, // minimum
+ 1, // maximum
+ 0.1, // change
+ "volumeSFX"));
}
private function __changeOffset(offset)
diff --git a/source/funkin/options/type/MeterOption.hx b/source/funkin/options/type/MeterOption.hx
new file mode 100644
index 00000000..7902e09e
--- /dev/null
+++ b/source/funkin/options/type/MeterOption.hx
@@ -0,0 +1,208 @@
+package funkin.options.type;
+
+// TODO: RECODE ALL OF THISS!! ITS SO SHITTY
+
+import flixel.graphics.frames.FlxFrame;
+import flixel.math.FlxMatrix;
+import flixel.math.FlxRect;
+
+@:access(flixel.FlxSprite._frame)
+class MeterOption extends OptionType {
+ public var selectCallback:Float->Void;
+
+ public var min:Float;
+ public var max:Float;
+
+ public var currentSelection:Float;
+ public var changeVal:Float;
+
+ public var parent:Dynamic;
+
+ public var barWidth(default, set):Int;
+
+ private var rawText(default, set):String;
+
+ public var text(get, set):String;
+ private function get_text() {return __text.text;}
+ private function set_text(v:String) {return __text.text = v;}
+
+ private var __text:Alphabet;
+ var optionName:String;
+
+ var barScale:Float = 0.75;
+ var realBarWidth:Float;
+ var cornerWidth:Float;
+ var slider:FlxSprite;
+ var cornerEmpty:FlxSprite;
+ var barEmpty:FlxSprite;
+ var cornerFill:FlxSprite;
+ var barFill:FlxSprite;
+ var valueWidth:Float;
+ var cornerClipRect:FlxRect;
+
+ public function new(text:String, desc:String, min:Float, max:Float, changeVal:Float, optionName:String, barWidth:Int = 520,
+ ?selectCallback:Float->Void = null, ?parent:Dynamic)
+ {
+ super(desc);
+
+ this.selectCallback = selectCallback;
+ this.min = min;
+ this.max = max;
+ if (parent == null) parent = Options;
+
+ this.parent = parent;
+
+ if (Reflect.field(parent, optionName) != null) this.currentSelection = Reflect.field(parent, optionName);
+ this.changeVal = changeVal;
+ this.optionName = optionName;
+
+ add(__text = new Alphabet(20, 20, "", "bold"));
+ rawText = text;
+
+ var frames = Paths.getFrames('menus/options/meter');
+ add(slider = new FlxSprite(0, 24));
+ slider.frames = frames;
+ slider.animation.addByPrefix('slider', 'meter slider', 24);
+ slider.animation.play('slider');
+ slider.antialiasing = true;
+ slider.scale.set(barScale, barScale);
+ slider.updateHitbox();
+
+ (cornerEmpty = new FlxSprite()).frames = (barEmpty = new FlxSprite()).frames = frames;
+ (cornerFill = new FlxSprite()).frames = (barFill = new FlxSprite()).frames = frames;
+ cornerEmpty.animation.addByPrefix('corner', 'meter empty corner', 24);
+ cornerEmpty.animation.play('corner');
+ barEmpty.animation.addByPrefix('bar', 'meter empty piece', 24);
+ barEmpty.animation.play('bar');
+ cornerFill.animation.addByPrefix('corner', 'meter filled corner', 24);
+ cornerFill.animation.play('corner');
+ barFill.animation.addByPrefix('bar', 'meter filled piece', 24);
+ barFill.animation.play('bar');
+
+ cornerClipRect = new FlxRect();
+ cornerEmpty.rawClipRect = cornerClipRect;
+ cornerFill.rawClipRect = cornerClipRect;
+
+ _matrix = new FlxMatrix();
+ cornerWidth = Math.max(cornerFill.frameWidth, cornerEmpty.frameWidth);
+ this.barWidth = barWidth;
+ }
+
+ function set_barWidth(value:Int):Int {
+ if (value == barWidth) return value;
+ realBarWidth = Math.max(cornerWidth + value / barScale, cornerWidth * 2);
+ updateBar();
+
+ __text.x = x + 20 + realBarWidth * barScale;
+
+ return barWidth = value;
+ }
+
+ function updateBar() {
+ var w = (currentSelection - min) / (max - min) * (realBarWidth - cornerWidth * 2) + cornerWidth;
+ slider.x = x + w * barScale - slider.width * 0.5;
+ valueWidth = w;
+ }
+
+ inline function drawObject(sprite:FlxSprite, camera:FlxCamera, x:Float, y:Float) {
+ getScreenPosition(_point, camera).subtractPoint(offset);
+ _matrix.translate(_point.x + x, _point.y + y);
+
+ /*if (isPixelPerfectRender(camera)) {
+ _matrix.tx = Math.floor(_matrix.tx);
+ _matrix.ty = Math.floor(_matrix.ty);
+ }*/
+
+ if (layer != null) layer.drawPixels(sprite, camera, sprite._frame, sprite.framePixels, _matrix, colorTransform, blend, true);
+ else camera.drawPixels(sprite._frame, sprite.framePixels, _matrix, colorTransform, blend, true);
+ }
+
+ var lastX:Float;
+ var temp:Float;
+ function drawCorner(width:Float, flip:Bool, camera:FlxCamera) {
+ if ((temp = Math.max(Math.min(width, cornerFill.frameWidth), 0)) > 0) {
+ cornerClipRect.set(0, 0, temp, cornerFill.frameHeight);
+ cornerFill.frame = cornerFill.frames.frames[cornerFill.animation.frameIndex];
+ cornerFill._frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, flip != camera.flipX, flipY != camera.flipY);
+ _matrix.scale(barScale, barScale);
+ drawObject(cornerFill, camera, 0, 37);
+ }
+
+ if (temp < cornerEmpty.frameWidth) {
+ cornerClipRect.set(temp, 0, cornerEmpty.frameWidth - temp, cornerEmpty.frameHeight);
+ cornerEmpty.frame = cornerEmpty.frames.frames[cornerEmpty.animation.frameIndex];
+ cornerEmpty._frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, flip != camera.flipX, flipY != camera.flipY);
+ _matrix.scale(barScale, barScale);
+ drawObject(cornerEmpty, camera, temp * barScale, 37);
+ }
+
+ x += cornerWidth * barScale;
+ }
+
+ function drawBar(width:Float, camera:FlxCamera) {
+ temp = realBarWidth - cornerWidth * 2;
+ if (width > 0) {
+ barFill._frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, camera.flipX, flipY != camera.flipY);
+ _matrix.scale((Math.min(width, temp) + 2) / barFill.frameWidth * barScale, barScale);
+ drawObject(barFill, camera, -1, 37);
+ }
+
+ if (width < temp) {
+ barEmpty._frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, camera.flipX, flipY != camera.flipY);
+ _matrix.scale((Math.max(temp - width, 0) + 1) / barEmpty.frameWidth * barScale, barScale);
+ drawObject(barEmpty, camera, width * barScale, 37);
+ }
+
+ /*if (width < (temp = realBarWidth - cornerWidth * 2)) {
+ barEmpty._frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, camera.flipX, flipY != camera.flipY);
+ _matrix.scale((width - realBarWidth + cornerWidth + 1) / barFill.frameWidth, 1);
+ drawObject(barEmpty, camera, realBarWidth - width, 37);
+ }*/
+
+ x += temp * barScale;
+ }
+
+ function set_rawText(v:String) {
+ rawText = v;
+ __text.text = TU.exists(rawText) ? TU.translate(rawText) : rawText;
+ return v;
+ }
+
+ override function draw() {
+ lastX = x;
+
+ for (camera in cameras) {
+ if (!camera.visible || !camera.exists) continue;
+
+ drawCorner(currentSelection == min ? 0 : valueWidth, false, camera);
+ drawBar(valueWidth - cornerWidth, camera);
+ drawCorner(currentSelection == max ? cornerWidth : valueWidth - realBarWidth + cornerWidth, true, camera);
+
+ x = lastX;
+ }
+ super.draw();
+ }
+
+ override function destroy() {
+ super.destroy();
+ cornerEmpty.destroy();
+ barEmpty.destroy();
+ cornerFill.destroy();
+ barFill.destroy();
+ }
+
+ override function reloadStrings() {
+ super.reloadStrings();
+ this.rawText = rawText;
+ }
+
+ override function onChangeSelection(change:Float) {
+ if (currentSelection <= min && change == -1 || currentSelection >= max && change == 1) return;
+ currentSelection = FlxMath.roundDecimal(currentSelection + (change * changeVal), FlxMath.getDecimals(changeVal));
+
+ updateBar();
+
+ Reflect.setField(parent, optionName, currentSelection);
+ if (selectCallback != null) selectCallback(currentSelection);
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/options/type/NumOption.hx b/source/funkin/options/type/NumOption.hx
index 75aa2b5a..e570e197 100644
--- a/source/funkin/options/type/NumOption.hx
+++ b/source/funkin/options/type/NumOption.hx
@@ -42,9 +42,8 @@ class NumOption extends OptionType {
this.changeVal = changeVal;
this.optionName = optionName;
- add(__text = new Alphabet(100, 20, text, "bold"));
- add(__number = new Alphabet(__text.width + 120, 20, ': $currentSelection', "normal"));
-
+ add(__text = new Alphabet(20, 20, "", "bold"));
+ add(__number = new Alphabet(0, 20, ': $currentSelection', "bold"));
rawText = text;
}
@@ -56,10 +55,11 @@ class NumOption extends OptionType {
function set_rawText(v:String) {
rawText = v;
__text.text = TU.exists(rawText) ? TU.translate(rawText) : rawText;
+ __number.x = __text.x + __text.width + 12;
return v;
}
- public override function onChangeSelection(change:Float):Void
+ override function onChangeSelection(change:Float):Void
{
if(currentSelection <= min && change == -1 || currentSelection >= max && change == 1) return;
currentSelection = FlxMath.roundDecimal(currentSelection + (change * changeVal), FlxMath.getDecimals(changeVal));
diff --git a/source/funkin/options/type/TextOption.hx b/source/funkin/options/type/TextOption.hx
index 5026211f..2d2b5187 100644
--- a/source/funkin/options/type/TextOption.hx
+++ b/source/funkin/options/type/TextOption.hx
@@ -23,7 +23,7 @@ class TextOption extends OptionType {
public function new(text:String, desc:String, ?suffix:String = "", ?selectCallback:Void->Void = null) {
super(desc);
this.selectCallback = selectCallback;
- add(__text = new Alphabet(100, 20, text, "bold"));
+ add(__text = new Alphabet(100, 20, "", "bold"));
this.suffix = suffix;
rawText = text;
}