From 7f8e4360eaa7e5626bbf15dc2fd3467227307833 Mon Sep 17 00:00:00 2001 From: Ralty <78720179+Raltyro@users.noreply.github.com> Date: Mon, 28 Jul 2025 04:49:01 +0700 Subject: [PATCH] Sliders half finished --- assets/images/menus/options/slider.xml | 8 +- source/funkin/menus/ui/Slider.hx | 204 +++++++++++++++++- .../options/categories/GameplayOptions.hx | 2 +- 3 files changed, 198 insertions(+), 16 deletions(-) diff --git a/assets/images/menus/options/slider.xml b/assets/images/menus/options/slider.xml index facddef9..134049b9 100644 --- a/assets/images/menus/options/slider.xml +++ b/assets/images/menus/options/slider.xml @@ -1,11 +1,11 @@ - - + + - - + + diff --git a/source/funkin/menus/ui/Slider.hx b/source/funkin/menus/ui/Slider.hx index 589dffd4..68eba451 100644 --- a/source/funkin/menus/ui/Slider.hx +++ b/source/funkin/menus/ui/Slider.hx @@ -3,13 +3,16 @@ package funkin.menus.ui; import flixel.animation.FlxAnimation; import flixel.graphics.frames.FlxFramesCollection; import flixel.graphics.frames.FlxFrame; +import flixel.math.FlxAngle; import flixel.math.FlxPoint; +import flixel.math.FlxRect; +import flixel.util.FlxDestroyUtil; class Slider extends FlxSprite { public var playSound:Bool = true; public var selected:Bool = false; public var value:Float; - public var segments:Int; + public var segments(default, set):Int; public var barWidth(default, set):Float; public var barHeight(default, null):Float; @@ -17,15 +20,36 @@ class Slider extends FlxSprite { public var barFramerate:Float = 24.0; var __animTime:Float = 0.0; + var __frameTime:Int; var __curSegments:Int; + var __barFrame:FlxFrame; + var __barClipRect:FlxRect; + var __valueWidth:Float; + var __cornerWidth:Float; + var __segmentWidth:Float; + var __barWidth:Float; + var __flipX:Bool; + var __flipY:Bool; - public function new(?x:Float, ?y:Float, value:Float = 0.5, barWidth:Float = 500, segments:Int = 4) { + public function new(?x:Float, ?y:Float, value:Float = 0.5, barWidth:Float = 500, segments:Int = 5) { super(x, y); this.value = value; this.barWidth = barWidth; this.segments = segments; frames = Paths.getFrames('menus/options/slider'); + antialiasing = true; + } + + override function initVars() { + super.initVars(); + __barClipRect = FlxRect.get(); + } + + override function destroy() { + super.destroy(); + __barFrame = FlxDestroyUtil.destroy(__barFrame); + __barClipRect = FlxDestroyUtil.put(__barClipRect); } function playAnimOrDefault(a:String, d:String) { @@ -35,7 +59,7 @@ class Slider extends FlxSprite { override function update(elapsed:Float) { super.update(elapsed); - __animTime += elapsed; + __frameTime = Math.floor((__animTime += elapsed) * barFramerate); if (animation.curAnim == null) { animation.play(selected ? 'selected' : 'unselected'); @@ -58,12 +82,158 @@ class Slider extends FlxSprite { override function isSimpleRender(?camera:FlxCamera):Bool return false; override function drawComplex(camera:FlxCamera) { - if (showSlider) { - var xOff = frame.sourceSize.x * 0.5 - barWidth * value, yOff = (frame.sourceSize.y - barHeight) * 0.5; - frameOffset.add(xOff, yOff); - super.drawComplex(camera); - frameOffset.subtract(xOff, yOff); + __flipX = checkFlipX(); + __flipY = checkFlipY(); + __valueWidth = barWidth * value; + + var width = barWidth / (segments + 1), x = drawCorner(camera, 0, false); + var end = barWidth - __cornerWidth, nextSegment = width - (segments == 0 ? 0 : __segmentWidth * 0.5); + while (x < end) { + if (x == (x = drawBar(camera, x, Math.min(nextSegment - x, end - x)))) break; + if (x < end && x < nextSegment + __segmentWidth * 0.5) { + x = drawSegment(camera, x, Math.min(__segmentWidth, nextSegment + __segmentWidth - x), CoolUtil.bound(x - end, 0, __segmentWidth)); + } + nextSegment += width; } + drawCorner(camera, x, true); + + if (showSlider) { + _frame.prepareMatrix(_matrix, ANGLE_0, __flipX != camera.flipX, __flipY != camera.flipY); + prepareDrawMatrix(camera, -_frame.sourceSize.x * 0.5 + __valueWidth, -(_frame.sourceSize.y - barHeight) * 0.5); + forceDrawFrame(camera, _frame); + } + } + + inline function prepareDrawMatrix(camera:FlxCamera, x:Float, y:Float) { + _matrix.translate(-origin.x, -origin.y); + + if (frameOffsetAngle != null && frameOffsetAngle != angle) { + var angleOff = (frameOffsetAngle - angle) * FlxAngle.TO_RAD; + var cos = Math.cos(angleOff), sin = Math.sin(angleOff); + + _matrix.rotateWithTrig(cos, -sin); + _matrix.translate(-frameOffset.x + x, -frameOffset.y + y); + _matrix.rotateWithTrig(cos, sin); + } + else + _matrix.translate(-frameOffset.x + x, -frameOffset.y + y); + + _matrix.scale(scale.x, scale.y); + + if (bakedRotationAngle <= 0) { + updateTrig(); + if (angle != 0) _matrix.rotateWithTrig(_cosAngle, _sinAngle); + } + + getScreenPosition(_point, camera).subtractPoint(offset); + _point.add(origin.x, origin.y); + _matrix.translate(_point.x, _point.y); + + if (isPixelPerfectRender(camera)) { + _matrix.tx = Math.floor(_matrix.tx); + _matrix.ty = Math.floor(_matrix.ty); + } + + doAdditionalMatrixStuff(_matrix, camera); + } + + function forceDrawFrame(camera:FlxCamera, frame:FlxFrame) { + if (layer != null) + layer.drawPixels(this, camera, frame, frame.parent.bitmap, _matrix, colorTransform, blend, antialiasing, shaderEnabled ? shader : null); + else + camera.drawPixels(frame, frame.parent.bitmap, _matrix, colorTransform, blend, antialiasing, shaderEnabled ? shader : null); + } + + function drawCorner(camera:FlxCamera, x:Float, flip:Bool):Float { + var anim, frame; + + if (__valueWidth > x) { + anim = getBarAnim(1, true); + frame = frames.frames[anim.frames[__frameTime % anim.numFrames]]; + + if (!flip != !__flipX) __barClipRect.set(0, 0, __valueWidth - x, frame.sourceSize.y); + else __barClipRect.set(__cornerWidth - __valueWidth + x, 0, __valueWidth - x, frame.sourceSize.y); + __barFrame = frame.clipTo(__barClipRect, __barFrame); + __barFrame.prepareMatrix(_matrix, ANGLE_0, flip != camera.flipX, __flipY != camera.flipY); + prepareDrawMatrix(camera, x, 0); + forceDrawFrame(camera, __barFrame); + } + + if (__valueWidth - x < __cornerWidth) { + anim = getBarAnim(1, false); + frame = frames.frames[anim.frames[__frameTime % anim.numFrames]]; + + if (flip != __flipX) __barClipRect.set(0, 0, __cornerWidth - __valueWidth + x, frame.sourceSize.y); + else __barClipRect.set(__valueWidth - x, 0, __cornerWidth - __valueWidth + x, frame.sourceSize.y); + __barFrame = frame.clipTo(__barClipRect, __barFrame); + __barFrame.prepareMatrix(_matrix, ANGLE_0, flip != camera.flipX, __flipY != camera.flipY); + prepareDrawMatrix(camera, x, 0); + forceDrawFrame(camera, __barFrame); + } + + return x + __cornerWidth; + } + + function drawBar(camera:FlxCamera, x:Float, width:Float):Float { + if (width <= 0) return x; + var anim, frame, s; + + if (__valueWidth > x) { + anim = getBarAnim(0, true); + frame = frames.frames[anim.frames[__frameTime % anim.numFrames]]; + s = width / frame.sourceSize.x; + + __barClipRect.set(0, 0, (__valueWidth - x) / s, frame.sourceSize.y); + __barFrame = frame.clipTo(__barClipRect, __barFrame); + __barFrame.prepareMatrix(_matrix, ANGLE_0, __flipX != camera.flipX, __flipY != camera.flipY); + _matrix.scale(s, 1); + prepareDrawMatrix(camera, x, 0); + forceDrawFrame(camera, __barFrame); + } + + if (__valueWidth - x < width) { + anim = getBarAnim(0, false); + frame = frames.frames[anim.frames[__frameTime % anim.numFrames]]; + s = width / frame.sourceSize.x; + + __barClipRect.set((__valueWidth - x) / s, 0, (width - __valueWidth + x) / s, frame.sourceSize.y); + __barFrame = frame.clipTo(__barClipRect, __barFrame); + __barFrame.prepareMatrix(_matrix, ANGLE_0, __flipX != camera.flipX, __flipY != camera.flipY); + _matrix.scale(s, 1); + prepareDrawMatrix(camera, x, 0); + forceDrawFrame(camera, __barFrame); + } + + return x + width; + } + + function drawSegment(camera:FlxCamera, x:Float, width:Float, offset:Float):Float { + if (width <= 0) return x; + var anim, frame; + + if (__valueWidth > x) { + anim = getBarAnim(2, true); + frame = frames.frames[anim.frames[__frameTime % anim.numFrames]]; + + __barClipRect.set(offset, 0, __valueWidth - x, frame.sourceSize.y); + __barFrame = frame.clipTo(__barClipRect, __barFrame); + __barFrame.prepareMatrix(_matrix, ANGLE_0, __flipX != camera.flipX, __flipY != camera.flipY); + prepareDrawMatrix(camera, x, 0); + forceDrawFrame(camera, __barFrame); + } + + if (__valueWidth - x < __segmentWidth) { + anim = getBarAnim(2, false); + frame = frames.frames[anim.frames[__frameTime % anim.numFrames]]; + + __barClipRect.set(__valueWidth - x + offset, 0, __segmentWidth - offset - __valueWidth + x, frame.sourceSize.y); + __barFrame = frame.clipTo(__barClipRect, __barFrame); + __barFrame.prepareMatrix(_matrix, ANGLE_0, __flipX != camera.flipX, __flipY != camera.flipY); + prepareDrawMatrix(camera, x, 0); + forceDrawFrame(camera, __barFrame); + } + + return x + width; } function getBarAnim(type:Int, filled:Bool):FlxAnimation { @@ -88,11 +258,21 @@ class Slider extends FlxSprite { animation.addByPrefix('selecting', 'slider selecting0', 24, false); animation.addByPrefix('segment', 'slider segment0', 24, false); - var anim; + var anim, width; + inline function checkSize(type) { + barHeight = Math.max(barHeight, frames.frames[anim.frames[0]].sourceSize.y); + width = frames.frames[anim.frames[0]].sourceSize.x; + switch (type) { + case 0: __barWidth = Math.max(__barWidth, width); + case 1: __cornerWidth = Math.max(__cornerWidth, width); + case 2: __segmentWidth = Math.max(__segmentWidth, width); + } + } + barHeight = 0; for (type in 0...3) { - if ((anim = getBarAnim(type, false)) != null) barHeight = Math.max(barHeight, frames.frames[anim.frames[0]].sourceSize.y); - if ((anim = getBarAnim(type, true)) != null) barHeight = Math.max(barHeight, frames.frames[anim.frames[0]].sourceSize.y); + if ((anim = getBarAnim(type, false)) != null) checkSize(type); + if ((anim = getBarAnim(type, true)) != null) checkSize(type); } sliderResetFrameSize(); @@ -115,4 +295,6 @@ class Slider extends FlxSprite { origin.x = _halfSize.x = 0.5 * (frameWidth = Std.int(barWidth = v)); return v; } + + function set_segments(v:Int):Int return segments = CoolUtil.maxInt(v, 0); } \ No newline at end of file diff --git a/source/funkin/options/categories/GameplayOptions.hx b/source/funkin/options/categories/GameplayOptions.hx index afb8fc51..c7acbd0e 100644 --- a/source/funkin/options/categories/GameplayOptions.hx +++ b/source/funkin/options/categories/GameplayOptions.hx @@ -16,7 +16,7 @@ class GameplayOptions extends TreeMenuScreen { add(new Checkbox(getNameID('camZoomOnBeat'), getDescID('camZoomOnBeat'), 'camZoomOnBeat')); add(new Checkbox(getNameID('autoPause'), getDescID('autoPause'), 'autoPause', __changeAutoPause)); add(offsetSetting = new NumOption(getNameID('songOffset'), getDescID('songOffset'), -999, 999, 1, 'songOffset', __changeOffset)); - add(new SliderOption(getNameID('volumeSFX'), getDescID('volumeSFX'), 0, 1, 1, 6, 'volumeSFX')); + add(new SliderOption(getNameID('volumeSFX'), getDescID('volumeSFX'), 0, 1, 1, 5, 'volumeSFX')); add(new Separator()); add(new TextOption('optionsMenu.advanced', 'optionsTree.gameplay.advanced-desc', ' >', () ->