diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 50b6a5c4..f2e48a35 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -34,7 +34,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 db7aec27..b9d391d0 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -31,7 +31,7 @@ jobs: haxe -cp commandline -D analyzer-optimize --run Main setup -s - name: Building the game run: | - haxelib run lime build mac + 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) @@ -100,7 +100,7 @@ jobs: haxe -cp commandline -D analyzer-optimize --run Main setup -s - name: Building the game run: | - haxelib run lime build mac -debug + haxelib run lime build mac -debug -DCOMPILE_EXPERIMENTAL - name: Tar files run: tar -zcvf CodenameEngine.tar.gz -C export/debug/macos/bin . - name: Uploading artifact (entire build) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 75f642e6..9730fee6 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -31,7 +31,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@v7 with: diff --git a/source/funkin/backend/shaders/FunkinShaderOLD._hx b/source/funkin/backend/shaders/FunkinShaderOLD._hx new file mode 100644 index 00000000..1853813d --- /dev/null +++ b/source/funkin/backend/shaders/FunkinShaderOLD._hx @@ -0,0 +1,471 @@ +package funkin.backend.shaders; + +import haxe.Exception; +import haxe.io.Path; +import flixel.graphics.FlxGraphic; +import flixel.system.FlxAssets.FlxShader; +import flixel.util.FlxSignal.FlxTypedSignal; +import flixel.util.FlxStringUtil; +import hscript.IHScriptCustomBehaviour; +import openfl.display.BitmapData; +import openfl.display.ShaderInput; +import openfl.display.ShaderParameter; +import openfl.display.ShaderParameterType; +import openfl.display.Shader; +import openfl.display3D._internal.GLProgram; +import openfl.display3D._internal.GLShader; +import openfl.display3D.Program3D; +import openfl.utils._internal.Log; + +using StringTools; +@:access(openfl.display3D.Context3D) +@:access(openfl.display3D.Program3D) +@:access(openfl.display.ShaderInput) +@:access(openfl.display.ShaderParameter) +class FunkinShader extends FlxShader implements IHScriptCustomBehaviour { + #if REGION /* Backward Compatibility */ + private static var __instanceFields = Type.getInstanceFields(FunkinShader); + private static var FRAGMENT_SHADER = 0; + private static var VERTEX_SHADER = 1; + + public var glslVer(get, set):String; + inline function get_glslVer():String return glVersion; + inline function set_glslVer(v:String):String return glVersion = v; + + public var glRawFragmentSource(get, set):String; + inline function get_glRawFragmentSource():String return __glFragmentSourceRaw; + inline function set_glRawFragmentSource(v:String):String return __glFragmentSourceRaw = v; + + public var glRawVertexSource(get, set):String; + inline function get_glRawVertexSource():String return __glVertexSourceRaw; + inline function set_glRawVertexSource(v:String):String return __glVertexSourceRaw = v; + + // Unused... cne-openfl uses a different system + var __cancelNextProcessGLData:Bool = false; + public var onProcessGLData:FlxTypedSignal<(String, String)->Void> = new FlxTypedSignal<(String, String)->Void>(); + #end + + public static function getShaderCode(key:String, isFragment = true):Null { + var path = "shaders/" + key; + key = Path.withoutExtension(key); + + final ext = Path.extension(path); + if (ext == "") path = path + (isFragment ? ".frag" : ".vert"); + else isFragment = ext != "vert"; + + path = Paths.getPath(path); + return Assets.exists(path) ? Assets.getText(path) : null; + } + + private static function processGLSLText(source:String, glVersion:String, isFragment:Bool, ?pragmas:Map):String + return Shader.processGLSLText(_processGLSLText(source, glVersion, isFragment, pragmas), glVersion, isFragment); + + private static function _processGLSLText(source:String, glVersion:String, isFragment:Bool, ?pragmas:Map):String { + if (pragmas != null) { + final pragmaKeyword = ~/#pragma\s+(\w+)/g; + source = pragmaKeyword.map(source, (_) -> { + var name = pragmaKeyword.matched(1), pragma:String; + if (pragmas.exists(name)) pragma = pragmas.get(name); + else { + if (name != "header" && name != "body") return '#pragma $name'; + pragma = ""; + } + return _processGLSLText(pragma, glVersion, isFragment, pragmas); + }); + } + + inline function tryGetShaderCode(key:String) { + final s = getShaderCode(key, isFragment); + if (s == null) { + Logs.traceColored([ + Logs.logText('[Shader] ', RED), + Logs.logText('Failed to import shader $key', RED), + ]); + return ""; + } + return s; + } + + final includeKeyword = ~/#include ['"](.+)['"]/g; + final importKeyword = ~/#import\s+<(.*)>/g; + source = importKeyword.map(source, (_) -> + return _processGLSLText(tryGetShaderCode(importKeyword.matched(1)), glVersion, isFragment, pragmas) ?? ""); + + return source = includeKeyword.map(source, (_) -> + return _processGLSLText(tryGetShaderCode(includeKeyword.matched(1)), glVersion, isFragment, pragmas) ?? ""); + } + + private static var __defaultsAvailable:Bool; + private static var __glFragmentSourceDefault:String; + private static var __glVertexSourceDefault:String; + private static var __glFragmentPragmasDefault:Map; + private static var __glVertexPragmasDefault:Map; + private static var __glFragmentExtensionsDefault:Array; + private static var __glVertexExtensionsDefault:Array; + + public var onGLUpdate:FlxTypedSignalVoid> = new FlxTypedSignalVoid>(); + + public var fileName:String = "FunkinShader"; + public var fragFileName:String = "FunkinShader"; + public var vertFileName:String = "FunkinShader"; + + public var shaderPrefix:String = ""; + public var fragmentPrefix:String = ""; + public var vertexPrefix:String = ""; + + private var __immediate:Bool; + + public function new(?fragmentSource:String, ?vertexSource:String, ?version:String, + ?fragmentExtensions:Array, ?vertexExtensions:Array, immediate = false + ) { + if (!__defaultsAvailable) { + __glFragmentSourceDefault = __glFragmentSourceRaw; + __glVertexSourceDefault = __glVertexSourceRaw; + __glFragmentPragmasDefault = __glFragmentPragmas.copy(); + __glVertexPragmasDefault = __glVertexPragmas.copy(); + __glFragmentExtensionsDefault = __glFragmentExtensions ?? []; + __glVertexExtensionsDefault = __glVertexExtensions ?? []; + __defaultsAvailable = true; + } + + __immediate = immediate; + + if (version != null) glVersion = version; + if (vertexExtensions != null) glVertexExtensions = vertexExtensions; + if (fragmentExtensions != null) glFragmentExtensions = fragmentExtensions; + if (vertexSource != null) glVertexSource = vertexSource; + if (fragmentSource != null) glFragmentSource = fragmentSource; + + super(); + + if (!__isGenerated) { + __isGenerated = true; + __init(); + } + } + + public function loadShader(name:String, ?version:String, immediate = false):FunkinShader { + final fragment = getShaderCode(name, true), vertex = getShaderCode(name, false); + + glVersion = version; + glVertexSource = vertex ?? __glVertexSourceDefault; + glFragmentSource = fragment ?? __glFragmentSourceDefault; + + if (immediate) __init(); + return this; + } + + override function __initGL():Void { + if (__immediate) { + __context = FlxG.stage.context3D; + __enable(); + } + super.__initGL(); + } + + override function __updateGL():Void { + onGLUpdate.dispatch(); + super.__updateGL(); + } + + public function hget(name:String):Dynamic { + if (__glSourceDirty || __data == null) __init(); + + if (thisHasField(name) || thisHasField('get_${name}')) return Reflect.getProperty(this, name); + else if (!Reflect.hasField(__data, name)) return null; + + final field:Dynamic = Reflect.field(__data, name); + + var cl:String = Type.getClassName(Type.getClass(field)); + + // little problem we are facing boys... + + // cant do "field is ShaderInput" because ShaderInput has the @:generic metadata + // aka instead of ShaderInput it gets built as ShaderInput_Float + // this should be fine tho because we check the class, and the fields don't vary based on the type + + // thanks for looking in the code cne fans :D!! -lunar + + if (cl.startsWith("openfl.display.ShaderParameter")) + return (field.__length > 1) ? field.value : field.value[0]; + else if (cl.startsWith("openfl.display.ShaderInput")) + return field.input; + return field; + } + + public function hset(name:String, val:Dynamic):Dynamic { + if (__glSourceDirty || __data == null) __init(); + + if (thisHasField(name) || thisHasField('set_${name}')) { + Reflect.setProperty(this, name, val); + return val; + } + else if (!Reflect.hasField(__data, name)) { + // ??? huh + Reflect.setField(__data, name, val); + return val; + } + + var field = Reflect.field(__data, name); + var cl = Type.getClassName(Type.getClass(field)); + var isNotNull = val != null; + // cant do "field is ShaderInput" for some reason + if (cl.startsWith("openfl.display.ShaderParameter")) { + if (field.__length <= 1) { + // that means we wait for a single number, instead of an array + if (field.__isInt && isNotNull && !(val is Int)) { + throw new ShaderTypeException(name, Type.getClass(val), 'Int'); + return null; + } else + if (field.__isBool && isNotNull && !(val is Bool)) { + throw new ShaderTypeException(name, Type.getClass(val), 'Bool'); + return null; + } else + if (field.__isFloat && isNotNull && !(val is Float)) { + throw new ShaderTypeException(name, Type.getClass(val), 'Float'); + return null; + } + return field.value = isNotNull ? [val] : null; + } else { + if (isNotNull && !(val is Array)) { + throw new ShaderTypeException(name, Type.getClass(val), Array); + return null; + } + return field.value = val; + } + } else if (cl.startsWith("openfl.display.ShaderInput")) { + // shader input!! + var bitmap:BitmapData; + if (!isNotNull) bitmap = null; + else if (val is BitmapData) bitmap = val; + else if (val is FlxGraphic) bitmap = val.bitmap; + else { + throw new ShaderTypeException(name, Type.getClass(val), BitmapData); + return null; + } + field.input = bitmap; + } + + return val; + } + + override function __buildSourcePrefix(isFragment:Bool):String { + var result = super.__buildSourcePrefix(isFragment) + '\n$shaderPrefix'; + return isFragment ? result + '\n$fragmentPrefix' : result + '\n$vertexPrefix'; + } + + override function set_glFragmentExtensions(value:Array):Array { + if (value == null) value = __glFragmentExtensionsDefault; + if (value != __glFragmentExtensions) __glSourceDirty = true; + return __glFragmentExtensions = value; + } + + override function set_glVertexExtensions(value:Array):Array { + if (value == null) value = __glVertexExtensionsDefault; + if (value != __glVertexExtensions) __glSourceDirty = true; + return __glVertexExtensions = value; + } + + override function set_glVersion(value:Null):String { + if (value == null || value == "") value = Flags.DEFAULT_GLSL_VERSION; + if ((__glVersionRaw = value) != __glVersion) { + __glSourceDirty = true; + if (__glVertexSourceRaw != null) __glVertexSource = processGLSLText(__glVertexSourceRaw, value, false, __glVertexPragmas); + if (__glFragmentSourceRaw != null) __glFragmentSource = processGLSLText(__glFragmentSourceRaw, value, true, __glFragmentPragmas); + } + + return __glVersion = value; + } + + override function set_glFragmentSource(value:String):String { + if (value == null || value == "") value = __glFragmentSourceDefault; + if ((__glFragmentSourceRaw = value) != null) { + if (__glVersion != (__glVersion = Shader.getGLSLTextVersion(value, __glVersionRaw))) + __glSourceDirty = true; + + value = processGLSLText(value, __glVersion, true, __glFragmentPragmas); + } + + if (value != __glFragmentSource) __glSourceDirty = true; + return __glFragmentSource = value; + } + + override function set_glVertexSource(value:String):String { + if (value == null || value == "") value = __glVertexSourceDefault; + if ((__glVertexSourceRaw = value) != null) { + if (__glVersion != (__glVersion = Shader.getGLSLTextVersion(value, __glVersionRaw))) + __glSourceDirty = true; + + value = processGLSLText(value, __glVersion, false, __glVertexPragmas); + } + + if (value != __glVertexSource) __glSourceDirty = true; + return __glVertexSource = value; + } + + override function set_glFragmentPragmas(value:Map):Map { + if (value == null) value = __glFragmentPragmasDefault; + if (value != __glFragmentPragmas) + __glSourceDirty = true; + + return __glFragmentPragmas = value; + } + + override function set_glVertexPragmas(value:Map):Map { + if (value == null) value = __glVertexPragmasDefault; + if (value != __glVertexPragmas) + __glSourceDirty = true; + + return __glVertexPragmas = value; + } + + function registerParameter(name:String, type:String, isUniform:Bool):Void { + __registerParameter(name, Program3D.getParameterTypeFromGLString(type, 1), 1, -1, isUniform, false, null); + } + + public function toString():String + return FlxStringUtil.getDebugString([for (field in Reflect.fields(data)) LabelValuePair.weak(field, Reflect.field(data, field))]); +} + +class ShaderTemplates { + #if REGION /* Backward Compatibility */ + public static final vertHeader:String = "attribute float openfl_Alpha; +attribute vec4 openfl_ColorMultiplier; +attribute vec4 openfl_ColorOffset; +attribute vec4 openfl_Position; +attribute vec2 openfl_TextureCoord; + +varying float openfl_Alphav; +varying vec4 openfl_ColorMultiplierv; +varying vec4 openfl_ColorOffsetv; +varying vec2 openfl_TextureCoordv; + +uniform mat4 openfl_Matrix; +uniform bool openfl_HasColorTransform; +uniform vec2 openfl_TextureSize; + +attribute float alpha; +attribute vec4 colorMultiplier; +attribute vec4 colorOffset; + +uniform bool hasColorTransform;"; + + public static final vertBody:String = "openfl_TextureCoordv = openfl_TextureCoord; + +if (hasColorTransform) { + openfl_Alphav = openfl_Alpha * colorMultiplier.a; + if (openfl_HasColorTransform) { + openfl_ColorOffsetv = (openfl_ColorOffset / 255.0 * colorMultiplier) + (colorOffset / 255.0); + openfl_ColorMultiplierv = openfl_ColorMultiplier * vec4(colorMultiplier.rgb, 1.0); + } + else { + openfl_ColorOffsetv = colorOffset / 255.0; + openfl_ColorMultiplierv = vec4(colorMultiplier.rgb, 1.0); + } +} +else { + openfl_Alphav = openfl_Alpha * alpha; + if (openfl_HasColorTransform) { + openfl_ColorOffsetv = (openfl_ColorOffset + colorOffset) / 255.0; + openfl_ColorMultiplierv = openfl_ColorMultiplier; + } + else { + openfl_ColorOffsetv = colorOffset / 255.0; + openfl_ColorMultiplierv = vec4(1.0); + } +}"; + + public static final fragHeader:String = "varying float openfl_Alphav; +varying vec4 openfl_ColorMultiplierv; +varying vec4 openfl_ColorOffsetv; +varying vec2 openfl_TextureCoordv; + +uniform bool openfl_HasColorTransform; +uniform vec2 openfl_TextureSize; +uniform sampler2D bitmap; + +uniform bool hasTransform; +uniform bool hasColorTransform; + +vec4 apply_flixel_transform(vec4 color) { + if (!hasTransform) return color; + else if (color.a <= 0.0 || openfl_Alphav == 0.0) return vec4(0.0); + + color.rgb /= color.a; + color = clamp(openfl_ColorOffsetv + (color * openfl_ColorMultiplierv), 0.0, 1.0); + return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav); +} + +#define applyFlixelEffects(color) apply_flixel_transform(color) + +vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) { + return apply_flixel_transform(texture2D(bitmap, coord)); +} + +uniform vec4 _camSize; + +float map(float value, float min1, float max1, float min2, float max2) { + return min2 + (value - min1) * (max2 - min2) / (max1 - min1); +} + +vec2 getCamPos(vec2 pos) { + vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize); + return vec2(map(pos.x, size.x, size.x + size.z, 0.0, 1.0), map(pos.y, size.y, size.y + size.w, 0.0, 1.0)); +} +vec2 camToOg(vec2 pos) { + vec4 size = _camSize / vec4(openfl_TextureSize, openfl_TextureSize); + return vec2(map(pos.x, 0.0, 1.0, size.x, size.x + size.z), map(pos.y, 0.0, 1.0, size.y, size.y + size.w)); +} +vec4 textureCam(sampler2D bitmap, vec2 pos) { + return flixel_texture2D(bitmap, camToOg(pos)); +}"; + + public static final fragBody:String = "gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv); +if (gl_FragColor.a == 0.0) discard;"; + + public static final vertBackCompatVarList:Array = [ + ~/attribute float alpha/, + ~/attribute vec4 colorMultiplier/, + ~/attribute vec4 colorOffset/, + ~/uniform bool hasColorTransform/ + ]; + + public static final vertHeaderBackCompat:String = "attribute float openfl_Alpha; +attribute vec4 openfl_ColorMultiplier; +attribute vec4 openfl_ColorOffset; +attribute vec4 openfl_Position; +attribute vec2 openfl_TextureCoord; + +varying float openfl_Alphav; +varying vec4 openfl_ColorMultiplierv; +varying vec4 openfl_ColorOffsetv; +varying vec2 openfl_TextureCoordv; + +uniform mat4 openfl_Matrix; +uniform bool openfl_HasColorTransform; +uniform vec2 openfl_TextureSize;"; + + public static final vertBodyBackCompat:String = "openfl_Alphav = openfl_Alpha; +openfl_TextureCoordv = openfl_TextureCoord; + +if(openfl_HasColorTransform) { + openfl_ColorMultiplierv = openfl_ColorMultiplier; + openfl_ColorOffsetv = openfl_ColorOffset / 255.0; +} + +gl_Position = openfl_Matrix * openfl_Position;"; + #end +} + +class ShaderTypeException extends Exception { + var has:Class; + var want:Class; + var name:String; + + public function new(name:String, has:Class, want:Dynamic) { + this.has = has; + this.want = want; + this.name = name; + super('ShaderTypeException - Tried to set the shader uniform "${name}" as a ${Type.getClassName(has)}, but the shader uniform is a ${Std.string(want)}.'); + } +} \ No newline at end of file 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 } diff --git a/source/funkin/backend/utils/MathUtil.hx b/source/funkin/backend/utils/MathUtil.hx index 2ca2f5a2..5e6a3b43 100644 --- a/source/funkin/backend/utils/MathUtil.hx +++ b/source/funkin/backend/utils/MathUtil.hx @@ -3,6 +3,8 @@ package funkin.backend.utils; import haxe.macro.Expr; final class MathUtil { + public static inline var EULER:Float = 2.718281828459; + /** * Returns the maximum value in the arguments. * @param args Array of values @@ -151,6 +153,36 @@ final class MathUtil { return Math.abs(a - b) > margin; } + /** + * @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); + } + + /** + * @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); + } + + /** + * @param v Float + * @return Float + **/ + public static function fract(v:Float):Float { + return v - Math.floor(v); + } + /** * Shortcut to `Math.max` but with infinite amount of arguments * @@ -199,4 +231,4 @@ final class MathUtil { return macro $expr; } #end -} \ No newline at end of file +} 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--; + } } } 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(); } } }