From 5957a55b969e4c4e689a2fbd78f9b7466a2f6d32 Mon Sep 17 00:00:00 2001 From: ItsLJcool Date: Sat, 28 Mar 2026 14:18:46 -0700 Subject: [PATCH 1/4] Add Text displaying if the user is running an Experimental Version of CodenameEngine (#933) * cmon github actions, do something * this will make it easier to know if a user is running an experimental version :trollface: --- .github/workflows/linux.yml | 2 +- .github/workflows/macos.yml | 2 +- .github/workflows/windows.yml | 2 +- .../funkin/backend/system/framerate/CodenameBuildField.hx | 7 ++++++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 10bf0003..e655f202 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -35,7 +35,7 @@ jobs: haxe -cp commandline -D analyzer-optimize --run Main setup -s - name: Building the game run: | - haxelib run lime build linux + haxelib run lime build linux -DCOMPILE_EXPERIMENTAL # - name: Tar files # run: tar -zcvf CodenameEngine.tar.gz -C export/release/linux/bin . - name: Uploading artifact (executable) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index b3da494a..e5f37173 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -32,7 +32,7 @@ jobs: haxe -cp commandline -D analyzer-optimize --run Main setup -s - name: Building the game run: | - arch -x86_64 haxelib run lime build mac + arch -x86_64 haxelib run lime build mac -DCOMPILE_EXPERIMENTAL - name: Tar files run: tar -zcvf CodenameEngine.tar.gz -C export/release/macos/bin . - name: Uploading artifact (executable) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3c4268b9..e74b6ca7 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -32,7 +32,7 @@ jobs: haxe -cp commandline -D analyzer-optimize --run Main setup -s --no-vscheck - name: Building the game run: | - haxelib run lime build windows + haxelib run lime build windows -DCOMPILE_EXPERIMENTAL - name: Uploading artifact (executable) uses: actions/upload-artifact@v4 with: diff --git a/source/funkin/backend/system/framerate/CodenameBuildField.hx b/source/funkin/backend/system/framerate/CodenameBuildField.hx index 57238298..656c0fff 100644 --- a/source/funkin/backend/system/framerate/CodenameBuildField.hx +++ b/source/funkin/backend/system/framerate/CodenameBuildField.hx @@ -13,8 +13,13 @@ class CodenameBuildField extends TextField { } public function reload() { + #if COMPILE_EXPERIMENTAL + text = '${Flags.VERSION_MESSAGE} (Experimental Build)'; + #else text = '${Flags.VERSION_MESSAGE}'; - #if debug + #end + + #if (debug || COMPILE_EXPERIMENTAL) text += '\n${Flags.COMMIT_MESSAGE}'; #end } From 64ea9db1c151ee15022ac78261abf62327b77d3c Mon Sep 17 00:00:00 2001 From: Hero Date: Sun, 29 Mar 2026 00:02:42 +0200 Subject: [PATCH 2/4] EULER, smoothStep, inverseLerp, fract in MathUtil (#913) --- source/funkin/backend/utils/MathUtil.hx | 380 +++++++++++++----------- 1 file changed, 200 insertions(+), 180 deletions(-) diff --git a/source/funkin/backend/utils/MathUtil.hx b/source/funkin/backend/utils/MathUtil.hx index 2ca2f5a2..c71414ed 100644 --- a/source/funkin/backend/utils/MathUtil.hx +++ b/source/funkin/backend/utils/MathUtil.hx @@ -3,200 +3,220 @@ package funkin.backend.utils; import haxe.macro.Expr; final class MathUtil { - /** - * Returns the maximum value in the arguments. - * @param args Array of values - * - * @return The maximum value - **/ - public static function maxInt(...args:Int):Int { - var max = args[0]; - for(i in 1...args.length) { - var arg = args[i]; - if(arg > max) - max = arg; - } - return max; - } + public static inline var EULER:Float = 2.718281828459; - /** - * Returns the minimum value in the arguments. - * @param args Array of values - * - * @return The minimum value - **/ - public static function minInt(...args:Int):Int { - var min = args[0]; - for(i in 1...args.length) { - var arg = args[i]; - if(arg < min) - min = arg; - } - return min; - } + /** + * Returns the maximum value in the arguments. + * @param args Array of values + * + * @return The maximum value + **/ + public static function maxInt(...args:Int):Int { + var max = args[0]; + for(i in 1...args.length) { + var arg = args[i]; + if(arg > max) + max = arg; + } + return max; + } - /** - * Returns the maximum value in the arguments. - * - * NOTE: If you are using this in compile time, you should use `MathUtil.maxSmart` instead of this for better performance. - * - * @param args Array of values - * - * @return The maximum value - **/ - public static function max(...args:Float):Float { - var max = args[0]; - for(i in 1...args.length) { - var arg = args[i]; - if(arg > max) - max = arg; - } - return max; - } + /** + * Returns the minimum value in the arguments. + * @param args Array of values + * + * @return The minimum value + **/ + public static function minInt(...args:Int):Int { + var min = args[0]; + for(i in 1...args.length) { + var arg = args[i]; + if(arg < min) + min = arg; + } + return min; + } - /** - * Returns the minimum value in the arguments. - * - * NOTE: If you are using this in compile time, you should use `MathUtil.minSmart` instead of this for better performance. - * - * @param args Array of values - * - * @return The minimum value - **/ - public static function min(...args:Float):Float { - var min = args[0]; - for(i in 1...args.length) { - var arg = args[i]; - if(arg < min) - min = arg; - } - return min; - } + /** + * Returns the maximum value in the arguments. + * + * NOTE: If you are using this in compile time, you should use `MathUtil.maxSmart` instead of this for better performance. + * + * @param args Array of values + * + * @return The maximum value + **/ + public static function max(...args:Float):Float { + var max = args[0]; + for(i in 1...args.length) { + var arg = args[i]; + if(arg > max) + max = arg; + } + return max; + } - /** - * Checks if a is less than b with considering a margin of error. - * - * @param a Float - * @param b Float - * @param margin Float (Default: EPSILON) - * - * @return Bool - **/ - public static function lessThan(a:Float, b:Float, margin:Float = 0.0000001):Bool { - return a < b - margin; - } + /** + * Returns the minimum value in the arguments. + * + * NOTE: If you are using this in compile time, you should use `MathUtil.minSmart` instead of this for better performance. + * + * @param args Array of values + * + * @return The minimum value + **/ + public static function min(...args:Float):Float { + var min = args[0]; + for(i in 1...args.length) { + var arg = args[i]; + if(arg < min) + min = arg; + } + return min; + } - /** - * Checks if a is less than or equally b with considering a margin of error. - * - * @param a Float - * @param b Float - * @param margin Float (Default: EPSILON) - * - * @return Bool - **/ - public static function lessThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool { - return a <= b - margin; - } + /** + * Checks if a is less than b with considering a margin of error. + * * @param a Float + * @param b Float + * @param margin Float (Default: EPSILON) + * * @return Bool + **/ + public static function lessThan(a:Float, b:Float, margin:Float = 0.0000001):Bool { + return a < b - margin; + } - /** - * Checks if a is greater than b with considering a margin of error. - * - * @param a Float - * @param b Float - * @param margin Float (Default: EPSILON) - * - * @return Bool - **/ - public static function greaterThan(a:Float, b:Float, margin:Float = 0.0000001):Bool { - return a > b + margin; - } + /** + * Checks if a is less than or equally b with considering a margin of error. + * * @param a Float + * @param b Float + * @param margin Float (Default: EPSILON) + * * @return Bool + **/ + public static function lessThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool { + return a <= b - margin; + } - /** - * Checks if a is greater than or equally b with considering a margin of error. - * - * @param a Float - * @param b Float - * @param margin Float (Default: EPSILON) - * - * @return Bool - **/ - public static function greaterThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool { - return a >= b + margin; - } + /** + * Checks if a is greater than b with considering a margin of error. + * * @param a Float + * @param b Float + * @param margin Float (Default: EPSILON) + * * @return Bool + **/ + public static function greaterThan(a:Float, b:Float, margin:Float = 0.0000001):Bool { + return a > b + margin; + } - /** - * Checks if a is approximately equal to b. - * - * @param a Float - * @param b Float - * @param margin Float (Default: EPSILON) - * - * @return Bool - **/ - public static function equal(a:Float, b:Float, margin:Float = 0.0000001):Bool { - return Math.abs(a - b) <= margin; - } + /** + * Checks if a is greater than or equally b with considering a margin of error. + * * @param a Float + * @param b Float + * @param margin Float (Default: EPSILON) + * * @return Bool + **/ + public static function greaterThanEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool { + return a >= b + margin; + } - /** - * Checks if a are not approximately equal to b. - * - * @param a Float - * @param b Float - * @param margin Float (Default: EPSILON) - * - * @return Bool - **/ - public static function notEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool { - return Math.abs(a - b) > margin; - } + /** + * Checks if a is approximately equal to b. + * * @param a Float + * @param b Float + * @param margin Float (Default: EPSILON) + * * @return Bool + **/ + public static function equal(a:Float, b:Float, margin:Float = 0.0000001):Bool { + return Math.abs(a - b) <= margin; + } - /** - * Shortcut to `Math.max` but with infinite amount of arguments - * - * Might not preserve the order of arguments, please test this. - * - * Dont use this in hscript, it doesnt work, it only works on compile time - **/ - @:dox(hide) public static macro function maxSmart(..._args:Expr):Expr { - return genericMinMaxSmart(_args.toArray(), "Math.max"); - } + /** + * Checks if a are not approximately equal to b. + * * @param a Float + * @param b Float + * @param margin Float (Default: EPSILON) + * * @return Bool + **/ + public static function notEqual(a:Float, b:Float, margin:Float = 0.0000001):Bool { + return Math.abs(a - b) > margin; + } - /** - * Shortcut to `Math.min` but with infinite amount of arguments - * - * Might not preserve the order of arguments, please test this. - * - * Dont use this in hscript, it doesnt work, it only works on compile time - **/ - @:dox(hide) public static macro function minSmart(..._args:Expr):Expr { - return genericMinMaxSmart(_args.toArray(), "Math.min"); - } + /** + * @param edge0 Float + * @param edge1 Float + * @param x Float + * @return Float + **/ + public static function smoothStep(edge0:Float, edge1:Float, x:Float):Float { + var t = (x - edge0) / (edge1 - edge0); + var clamped = t < 0.0 ? 0.0 : (t > 1.0 ? 1.0 : t); + return clamped * clamped * (3.0 - 2.0 * clamped); + } - #if macro - @:dox(hide) private static function genericMinMaxSmart(_args:Array, funcPath:String):Expr { - var args = _args.copy(); - if (args.length == 0) return macro 0; + /** + * @param a Float + * @param b Float + * @param v Float + * @return Float + **/ + public static function inverseLerp(a:Float, b:Float, v:Float):Float { + return (v - a) / (b - a); + } - var func = funcPath.split("."); + /** + * @param v Float + * @return Float + **/ + public static function fract(v:Float):Float { + return v - Math.floor(v); + } - function nested(lst:Array):Expr { - if (lst.length == 1) { - return macro ${lst[0]}; - } else if (lst.length == 2) { - return macro $p{func}(${lst[0]}, ${lst[1]}); - } else { - var mid = Std.int(lst.length / 2); - return macro $p{func}(${nested(lst.slice(0, mid))}, ${nested(lst.slice(mid, lst.length))}); - } - } + /** + * Shortcut to `Math.max` but with infinite amount of arguments + * + * Might not preserve the order of arguments, please test this. + * + * Dont use this in hscript, it doesnt work, it only works on compile time + **/ + @:dox(hide) public static macro function maxSmart(..._args:Expr):Expr { + return genericMinMaxSmart(_args.toArray(), "Math.max"); + } - var expr = nested(args); + /** + * Shortcut to `Math.min` but with infinite amount of arguments + * + * Might not preserve the order of arguments, please test this. + * + * Dont use this in hscript, it doesnt work, it only works on compile time + **/ + @:dox(hide) public static macro function minSmart(..._args:Expr):Expr { + return genericMinMaxSmart(_args.toArray(), "Math.min"); + } - //var printer = new haxe.macro.Printer(); - //trace(printer.printExpr(expr)); + #if macro + @:dox(hide) private static function genericMinMaxSmart(_args:Array, funcPath:String):Expr { + var args = _args.copy(); + if (args.length == 0) return macro 0; - return macro $expr; - } - #end -} \ No newline at end of file + var func = funcPath.split("."); + + function nested(lst:Array):Expr { + if (lst.length == 1) { + return macro ${lst[0]}; + } else if (lst.length == 2) { + return macro $p{func}(${lst[0]}, ${lst[1]}); + } else { + var mid = Std.int(lst.length / 2); + return macro $p{func}(${nested(lst.slice(0, mid))}, ${nested(lst.slice(mid, lst.length))}); + } + } + + var expr = nested(args); + + //var printer = new haxe.macro.Printer(); + //trace(printer.printExpr(expr)); + + return macro $expr; + } + #end +} From 39968efb19bd56ac5fed195b42d1088bfea199a7 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sun, 29 Mar 2026 06:03:58 +0800 Subject: [PATCH 3/4] (Charter) Add an ability to copy selected items by Alt-Dragging (#918) --- source/funkin/editors/charter/Charter.hx | 49 +++++++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index e776bd78..f0b451b2 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -796,6 +796,7 @@ class Charter extends UIState { public var mousePos:FlxPoint = new FlxPoint(); public var selectionDragging:Bool = false; public var isSelecting:Bool = false; + public var isAltCopyDrag:Bool = false; public function updateSelectionLogic() { function select(s:ICharterSelectable) { @@ -960,10 +961,17 @@ class Charter extends UIState { if (!(verticalChange == 0 && horizontalChange == 0)) { notesGroup.sortNotes(); - undos.addToUndo(CChangeBundle([ - CSelectionDrag(undoDrags), - updateEventsGroups(selection) - ])); + var changes:Array = []; + if (isAltCopyDrag) { + changes.push(CCreateSelection(selection.copy())); + isAltCopyDrag = false; + } + changes.push(CSelectionDrag(undoDrags)); + changes.push(updateEventsGroups(selection)); + undos.addToUndo(CChangeBundle(changes)); + } else if (isAltCopyDrag) { + undos.addToUndo(CCreateSelection(selection.copy())); + isAltCopyDrag = false; } gridActionType = NONE; @@ -1018,7 +1026,32 @@ class Charter extends UIState { } if ((Math.abs(mousePos.x - dragStartPos.x) > (noteSusDrag ? 1 : 5) || Math.abs(mousePos.y - dragStartPos.y) > (noteSusDrag ? 1 : 5))) { - if (noteHovered) gridActionType = noteHovered ? NOTE_DRAG : INVALID_DRAG; + if (noteHovered) { + if (FlxG.keys.pressed.ALT && selection.length > 0) { + var newSelection:Array = []; + for (s in selection) { + if (s is CharterNote) { + var n:CharterNote = cast s; + var newNote = new CharterNote(); + newNote.updatePos(n.step, n.id, n.susLength, n.type, n.strumLine); + notesGroup.add(newNote); + newSelection.push(newNote); + } else if (s is CharterEvent) { + var e:CharterEvent = cast s; + var newEvent = new CharterEvent(e.step, [for (event in e.events) Reflect.copy(event)], e.global); + newEvent.refreshEventIcons(); + (e.global ? rightEventsGroup : leftEventsGroup).add(newEvent); + newSelection.push(newEvent); + } + } + for (s in selection) s.selected = false; + selection = newSelection; + for (s in selection) s.selected = true; + isAltCopyDrag = true; + UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND); + } + gridActionType = NOTE_DRAG; + } if (noteSusDrag) gridActionType = SUSTAIN_DRAG; } } @@ -1736,7 +1769,11 @@ class Charter extends UIState { case CEditSpecNotesType(notes, oldTypes, newTypes): for(i=>note in notes) note.updatePos(note.step, note.id, note.susLength, oldTypes[i]); case CChangeBundle(changes): - for (change in changes) _undo(change); + var i = changes.length - 1; + while (i >= 0) { + _undo(changes[i]); + i--; + } } } From 270ed6b9b5413fd1eb8615036f27bf613ae48c59 Mon Sep 17 00:00:00 2001 From: Betopia <73242725+betpowo@users.noreply.github.com> Date: Sun, 29 Mar 2026 02:36:16 -0400 Subject: [PATCH 4/4] Note.noteAngle rewrite, fixes minus scrollSpeed (#857) * fix 90% of issues with noteAngle and scrollSpeed of notes cool , sustains now dont look like ass when rotated and negative scroll speed doesnt mess tem up anymore although the consequence of this is that noteAngle will go the other way around if on downscroll but i cant rlly get myself to fix that ;-; * downscroll is done (hopefully --------- Co-authored-by: Ralty <78720179+Raltyro@users.noreply.github.com> --- source/funkin/game/Note.hx | 49 +++++++++++++++------------------- source/funkin/game/Strum.hx | 52 +++++++++++++++---------------------- 2 files changed, 42 insertions(+), 59 deletions(-) diff --git a/source/funkin/game/Note.hx b/source/funkin/game/Note.hx index f46ad19b..82879bd3 100644 --- a/source/funkin/game/Note.hx +++ b/source/funkin/game/Note.hx @@ -1,6 +1,7 @@ package funkin.game; import flixel.math.FlxPoint; +import flixel.math.FlxAngle; import flixel.math.FlxRect; import funkin.backend.chart.ChartData; import funkin.backend.scripting.events.note.NoteCreationEvent; @@ -243,10 +244,14 @@ class Note extends FlxSprite override function drawComplex(camera:FlxCamera) { var downscrollCam = (camera is HudCamera ? ({var _:HudCamera=cast camera;_;}).downscroll : false); if (updateFlipY) flipY = (isSustainNote && flipSustain) && (downscrollCam != (__strum != null && __strum.getScrollSpeed(this) < 0)); - if (downscrollCam) { - frameOffset.y += __notePosFrameOffset.y * 2; + if (downscrollCam && __strum != null) { + final xx = x; + x += origin.x - offset.x; + x -= __strum.x; x *= -1; x += __strum.x; + x -= origin.x - offset.x; + x += __strum.width; // ??? maybe this isnt good super.drawComplex(camera); - frameOffset.y -= __notePosFrameOffset.y * 2; + x = xx; } else super.drawComplex(camera); } @@ -258,36 +263,24 @@ class Note extends FlxSprite @:privateAccess var oldDefaultCameras = FlxCamera._defaultCameras; @:privateAccess if (__strumCameras != null) FlxCamera._defaultCameras = __strumCameras; - var negativeScroll = isSustainNote && nextSustain != null && lastScrollSpeed < 0; - if (negativeScroll) offset.y *= -1; - + var negativeScroll = isSustainNote && strumRelativePos && lastScrollSpeed < 0; + if (negativeScroll) y -= height; if (__strum != null && strumRelativePos) { - var pos = __posPoint.set(x, y); - - setPosition(__strum.x, __strum.y); - - __notePosFrameOffset.set(pos.x / scale.x, pos.y / scale.y); - - frameOffset.x -= __notePosFrameOffset.x; - frameOffset.y -= __notePosFrameOffset.y; - - this.frameOffsetAngle = __noteAngle; - + final pos = __posPoint.set(x, y); + // distance = pos.y , we can use it safely like this + final xx = -origin.x + offset.x + (pos.y * Math.cos((__noteAngle + 90) * FlxAngle.TO_RAD)); + final yy = -origin.y + offset.y + (pos.y * Math.sin((__noteAngle + 90) * FlxAngle.TO_RAD)); + setPosition( + xx + __strum.x + (__strum.width * 0.5), + yy + __strum.y + (__strum.height * 0.5) + ); super.draw(); - - this.frameOffsetAngle = 0; - - frameOffset.x += __notePosFrameOffset.x; - frameOffset.y += __notePosFrameOffset.y; - setPosition(pos.x, pos.y); - //pos.put(); } else { - __notePosFrameOffset.set(0, 0); super.draw(); } + if (negativeScroll) y += height; - if (negativeScroll) offset.y *= -1; @:privateAccess FlxCamera._defaultCameras = oldDefaultCameras; } @@ -301,7 +294,7 @@ class Note extends FlxSprite if (lastScrollSpeed != scrollSpeed) { lastScrollSpeed = scrollSpeed; if (nextSustain != null) { - scale.y = (sustainLength * 0.45 * scrollSpeed) / frameHeight; + scale.y = (sustainLength * 0.45 * Math.abs(scrollSpeed)) / frameHeight; updateHitbox(); scale.y += gapFix / frameHeight; } @@ -311,7 +304,7 @@ class Note extends FlxSprite } public function updateSustainClip() if (wasGoodHit && !noSustainClip) { - var t = CoolUtil.bound((Conductor.songPosition - strumTime) / height * 0.45 * lastScrollSpeed, 0, 1); + var t = CoolUtil.bound((Conductor.songPosition - strumTime) / height * 0.45 * Math.abs(lastScrollSpeed), 0, 1); var rect = clipRect == null ? FlxRect.get() : clipRect; clipRect = rect.set(0, frameHeight * t, frameWidth, frameHeight * (1 - t)); } diff --git a/source/funkin/game/Strum.hx b/source/funkin/game/Strum.hx index 0862154b..b075aad8 100644 --- a/source/funkin/game/Strum.hx +++ b/source/funkin/game/Strum.hx @@ -1,6 +1,7 @@ package funkin.game; import flixel.math.FlxPoint; +import flixel.math.FlxAngle; import funkin.backend.system.Conductor; class Strum extends FlxSprite { @@ -162,41 +163,30 @@ class Strum extends FlxSprite { if (shouldX || shouldY) { if (daNote.strumRelativePos) { - if (shouldX) daNote.x = (this.width - daNote.width) * 0.5; + if (shouldX) daNote.x = 0; if (shouldY) { - daNote.y = (daNote.strumTime - Conductor.songPosition) * (0.45 * getScrollSpeed(daNote)); - if (daNote.isSustainNote) daNote.y += height * 0.5; + daNote.y = ((daNote.strumTime - Conductor.songPosition) * 0.45 * getScrollSpeed(daNote)); + if (daNote.isSustainNote) daNote.y += daNote.height * 0.5; } } else { - var offset = FlxPoint.get(0, (Conductor.songPosition - daNote.strumTime) * (0.45 * getScrollSpeed(daNote))); - var realOffset = FlxPoint.get(0, 0); - - if (daNote.isSustainNote) offset.y -= height * 0.5; - - if (Std.int(daNote.__noteAngle % 360) != 0) { - var noteAngle = FlxMath.fastSinCos(daNote.__noteAngle / PIX180); - var noteAngleCos = noteAngle.cos; - var noteAngleSin = noteAngle.sin; - - var aOffset:FlxPoint = FlxPoint.get( - (daNote.origin.x / daNote.scale.x) - daNote.offset.x, - (daNote.origin.y / daNote.scale.y) - daNote.offset.y - ); - realOffset.x = -aOffset.x + (noteAngleCos * (offset.x + aOffset.x)) + (noteAngleSin * (offset.y + aOffset.y)); - realOffset.y = -aOffset.y + (noteAngleSin * (offset.x + aOffset.x)) + (noteAngleCos * (offset.y + aOffset.y)); - - aOffset.put(); - } else { - realOffset.x = offset.x; - realOffset.y = offset.y; + final speed = getScrollSpeed(daNote); + final distance = (daNote.strumTime - Conductor.songPosition) * 0.45 * speed; + final __noteAngle = FlxMath.fastSinCos((daNote.__noteAngle + 90) * FlxAngle.TO_RAD); + final angleX = __noteAngle.cos; + final angleY = __noteAngle.sin; + final _noteOffset = FlxPoint.get(angleX * distance, angleY * distance); + _noteOffset.x += -daNote.origin.x + daNote.offset.x; + _noteOffset.y += -daNote.origin.y + daNote.offset.y; + if (daNote.isSustainNote) { + final m = (daNote.height * 0.5 * (speed < 0 ? -1 : 1)); // daNote.height works better than this.height in this case ??? + _noteOffset.x += angleX * m; + _noteOffset.y += angleY * m; } - realOffset.y *= -1; - - if (shouldX) daNote.x = x + realOffset.x; - if (shouldY) daNote.y = y + realOffset.y; - - offset.put(); - realOffset.put(); + _noteOffset.x += x + (width * 0.5); + _noteOffset.y += y + (height * 0.5); + if (shouldX) daNote.x = _noteOffset.x; + if (shouldY) daNote.y = _noteOffset.y; + _noteOffset.put(); } } }