From 7ce3eabd17eb918ce326cf401e1e43cd80f62084 Mon Sep 17 00:00:00 2001 From: Sword352 Date: Wed, 8 May 2024 07:35:46 +0200 Subject: [PATCH 001/469] Better character animation offsets handling. --- source/funkin/play/character/BaseCharacter.hx | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index 2796f8123b..cb9d5ae3f9 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -1,6 +1,7 @@ package funkin.play.character; import flixel.math.FlxPoint; +import flixel.FlxCamera; import funkin.modding.events.ScriptEvent; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.play.character.CharacterData.CharacterRenderType; @@ -118,19 +119,17 @@ class BaseCharacter extends Bopper */ public var cameraFocusPoint(default, null):FlxPoint = new FlxPoint(0, 0); + /** + * Defines the animation offset. + */ + public var animOffset:FlxPoint = FlxPoint.get(); + override function set_animOffsets(value:Array):Array { if (animOffsets == null) value = [0, 0]; if ((animOffsets[0] == value[0]) && (animOffsets[1] == value[1])) return value; - // Make sure animOffets are halved when scale is 0.5. - var xDiff = (animOffsets[0] * this.scale.x / (this.isPixel ? 6 : 1)) - value[0]; - var yDiff = (animOffsets[1] * this.scale.y / (this.isPixel ? 6 : 1)) - value[1]; - - // Call the super function so that camera focus point is not affected. - super.set_x(this.x + xDiff); - super.set_y(this.y + yDiff); - + animOffset.set(value[0], value[1]); return animOffsets = value; } @@ -570,11 +569,25 @@ class BaseCharacter extends Bopper } } + // override getScreenPosition (used by FlxSprite's draw method) to account for animation offsets. + override function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint + { + var output:FlxPoint = super.getScreenPosition(result, camera); + output -= animOffset; + return output; + } + public override function onDestroy(event:ScriptEvent):Void { this.characterType = OTHER; } + override function destroy():Void + { + animOffset = flixel.util.FlxDestroyUtil.put(animOffset); + super.destroy(); + } + /** * Play the appropriate singing animation, for the given note direction. * @param dir The direction of the note. From dd86934712276f136193033237cb15095cc0e787 Mon Sep 17 00:00:00 2001 From: Sword352 Date: Wed, 8 May 2024 09:22:49 +0200 Subject: [PATCH 002/469] did the requested changes --- source/funkin/play/character/BaseCharacter.hx | 29 ------------------- source/funkin/play/stage/Bopper.hx | 27 +++++++++++++---- 2 files changed, 21 insertions(+), 35 deletions(-) diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index cb9d5ae3f9..eee7d15a25 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -1,7 +1,6 @@ package funkin.play.character; import flixel.math.FlxPoint; -import flixel.FlxCamera; import funkin.modding.events.ScriptEvent; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.play.character.CharacterData.CharacterRenderType; @@ -119,20 +118,6 @@ class BaseCharacter extends Bopper */ public var cameraFocusPoint(default, null):FlxPoint = new FlxPoint(0, 0); - /** - * Defines the animation offset. - */ - public var animOffset:FlxPoint = FlxPoint.get(); - - override function set_animOffsets(value:Array):Array - { - if (animOffsets == null) value = [0, 0]; - if ((animOffsets[0] == value[0]) && (animOffsets[1] == value[1])) return value; - - animOffset.set(value[0], value[1]); - return animOffsets = value; - } - /** * If the x position changes, other than via changing the animation offset, * then we need to update the camera focus point. @@ -569,25 +554,11 @@ class BaseCharacter extends Bopper } } - // override getScreenPosition (used by FlxSprite's draw method) to account for animation offsets. - override function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint - { - var output:FlxPoint = super.getScreenPosition(result, camera); - output -= animOffset; - return output; - } - public override function onDestroy(event:ScriptEvent):Void { this.characterType = OTHER; } - override function destroy():Void - { - animOffset = flixel.util.FlxDestroyUtil.put(animOffset); - super.destroy(); - } - /** * Play the appropriate singing animation, for the given note direction. * @param dir The direction of the note. diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index 262aff7bca..141a85c8fb 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -1,6 +1,7 @@ package funkin.play.stage; import flixel.FlxSprite; +import flixel.FlxCamera; import flixel.math.FlxPoint; import flixel.util.FlxTimer; import funkin.modding.IScriptedClass.IPlayStateScriptedClass; @@ -67,6 +68,11 @@ class Bopper extends StageProp implements IPlayStateScriptedClass return value; } + /** + * Internally used to define the animation offsets to apply. + */ + var _currentAnimOffset:FlxPoint = FlxPoint.get(); + /** * The offset of the character relative to the position specified by the stage. */ @@ -95,12 +101,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass if (animOffsets == null) animOffsets = [0, 0]; if ((animOffsets[0] == value[0]) && (animOffsets[1] == value[1])) return value; - var xDiff = animOffsets[0] - value[0]; - var yDiff = animOffsets[1] - value[1]; - - this.x += xDiff; - this.y += yDiff; - + _currentAnimOffset.set(value[0], value[1]); return animOffsets = value; } @@ -349,6 +350,20 @@ class Bopper extends StageProp implements IPlayStateScriptedClass return this.animation.curAnim.name; } + // override getScreenPosition (used by FlxSprite's draw method) to account for animation offsets. + override function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint + { + var output:FlxPoint = super.getScreenPosition(result, camera); + output -= _currentAnimOffset; + return output; + } + + override function destroy():Void + { + _currentAnimOffset = flixel.util.FlxDestroyUtil.put(_currentAnimOffset); + super.destroy(); + } + public function onPause(event:PauseScriptEvent) {} public function onResume(event:ScriptEvent) {} From f6334fb30b12b44635a9574587f7689d2f3beb28 Mon Sep 17 00:00:00 2001 From: Sword352 Date: Wed, 8 May 2024 22:18:41 +0200 Subject: [PATCH 003/469] Use `animOffsets` directly. --- source/funkin/play/stage/Bopper.hx | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index 141a85c8fb..5485edb3e4 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -68,11 +68,6 @@ class Bopper extends StageProp implements IPlayStateScriptedClass return value; } - /** - * Internally used to define the animation offsets to apply. - */ - var _currentAnimOffset:FlxPoint = FlxPoint.get(); - /** * The offset of the character relative to the position specified by the stage. */ @@ -101,7 +96,6 @@ class Bopper extends StageProp implements IPlayStateScriptedClass if (animOffsets == null) animOffsets = [0, 0]; if ((animOffsets[0] == value[0]) && (animOffsets[1] == value[1])) return value; - _currentAnimOffset.set(value[0], value[1]); return animOffsets = value; } @@ -354,16 +348,11 @@ class Bopper extends StageProp implements IPlayStateScriptedClass override function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint { var output:FlxPoint = super.getScreenPosition(result, camera); - output -= _currentAnimOffset; + output.x -= animOffsets[0]; + output.y -= animOffsets[1]; return output; } - override function destroy():Void - { - _currentAnimOffset = flixel.util.FlxDestroyUtil.put(_currentAnimOffset); - super.destroy(); - } - public function onPause(event:PauseScriptEvent) {} public function onResume(event:ScriptEvent) {} From 6a52efe8a4293389502b4d2e763b0c88105649f0 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Sat, 11 May 2024 18:10:15 +0900 Subject: [PATCH 004/469] Update ScriptEventType.hx relevent -> relevant --- source/funkin/modding/events/ScriptEventType.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/modding/events/ScriptEventType.hx b/source/funkin/modding/events/ScriptEventType.hx index eeeb8ef299..6ac85649f9 100644 --- a/source/funkin/modding/events/ScriptEventType.hx +++ b/source/funkin/modding/events/ScriptEventType.hx @@ -20,7 +20,7 @@ enum abstract ScriptEventType(String) from String to String var DESTROY = 'DESTROY'; /** - * Called when the relevent object is added to the game state. + * Called when the relevant object is added to the game state. * This assumes all data is loaded and ready to go. * * This event is not cancelable. From c49b66f0e48881d7ad978428bb8237038cf6f2f6 Mon Sep 17 00:00:00 2001 From: cyn Date: Mon, 13 May 2024 11:23:16 -0700 Subject: [PATCH 005/469] flash fix --- source/funkin/ui/MenuItem.hx | 23 +++++++++++------------ source/funkin/ui/story/LevelTitle.hx | 24 ++++++++++++------------ 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/source/funkin/ui/MenuItem.hx b/source/funkin/ui/MenuItem.hx index ba5cc066bc..2a483ea789 100644 --- a/source/funkin/ui/MenuItem.hx +++ b/source/funkin/ui/MenuItem.hx @@ -11,7 +11,6 @@ class MenuItem extends FlxSpriteGroup { public var targetY:Float = 0; public var week:FlxSprite; - public var flashingInt:Int = 0; public function new(x:Float, y:Float, weekNum:Int = 0, weekType:WeekType) { @@ -30,28 +29,28 @@ class MenuItem extends FlxSpriteGroup } var isFlashing:Bool = false; + var flashTick:Float = 0; + final flashFramerate:Float = 20; public function startFlashing():Void { isFlashing = true; } - // if it runs at 60fps, fake framerate will be 6 - // if it runs at 144 fps, fake framerate will be like 14, and will update the graphic every 0.016666 * 3 seconds still??? - // so it runs basically every so many seconds, not dependant on framerate?? - // I'm still learning how math works thanks whoever is reading this lol - var fakeFramerate:Int = Math.round((1 / FlxG.elapsed) / 10); - override function update(elapsed:Float) { super.update(elapsed); y = MathUtil.coolLerp(y, (targetY * 120) + 480, 0.17); - if (isFlashing) flashingInt += 1; - - if (flashingInt % fakeFramerate >= Math.floor(fakeFramerate / 2)) week.color = 0xFF33ffff; - else - week.color = FlxColor.WHITE; + if (isFlashing) + { + flashTick += elapsed; + if (flashTick >= 1 / flashFramerate) + { + flashTick %= 1 / flashFramerate; + week.color = (week.color == FlxColor.WHITE) ? 0xFF33ffff : FlxColor.WHITE; + } + } } } diff --git a/source/funkin/ui/story/LevelTitle.hx b/source/funkin/ui/story/LevelTitle.hx index e6f9890162..2be2da154b 100644 --- a/source/funkin/ui/story/LevelTitle.hx +++ b/source/funkin/ui/story/LevelTitle.hx @@ -13,13 +13,10 @@ class LevelTitle extends FlxSpriteGroup public final level:Level; public var targetY:Float; - public var isFlashing:Bool = false; var title:FlxSprite; var lock:FlxSprite; - var flashingInt:Int = 0; - public function new(x:Int, y:Int, level:Level) { super(x, y); @@ -46,20 +43,23 @@ class LevelTitle extends FlxSpriteGroup } } - // if it runs at 60fps, fake framerate will be 6 - // if it runs at 144 fps, fake framerate will be like 14, and will update the graphic every 0.016666 * 3 seconds still??? - // so it runs basically every so many seconds, not dependant on framerate?? - // I'm still learning how math works thanks whoever is reading this lol - var fakeFramerate:Int = Math.round((1 / FlxG.elapsed) / 10); + public var isFlashing:Bool = false; + var flashTick:Float = 0; + final flashFramerate:Float = 20; public override function update(elapsed:Float):Void { this.y = MathUtil.coolLerp(y, targetY, 0.17); - if (isFlashing) flashingInt += 1; - if (flashingInt % fakeFramerate >= Math.floor(fakeFramerate / 2)) title.color = 0xFF33ffff; - else - title.color = FlxColor.WHITE; + if (isFlashing) + { + flashTick += elapsed; + if (flashTick >= 1 / flashFramerate) + { + flashTick %= 1 / flashFramerate; + title.color = (title.color == FlxColor.WHITE) ? 0xFF33ffff : FlxColor.WHITE; + } + } } public function showLock():Void From 92b84168e1007342997bf0f7ee4619c4ad09e25f Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Mon, 20 May 2024 02:56:57 +0200 Subject: [PATCH 006/469] Add camOther to fix zooms on pause and stickers --- source/funkin/play/PlayState.hx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 43dd485cf9..20b9d3661a 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -491,9 +491,9 @@ class PlayState extends MusicBeatSubState public var camGame:FlxCamera; /** - * The camera which contains, and controls visibility of, a video cutscene. + * The camera which contains, and controls visibility of, a video cutscene, dialogue, pause menu and sticker transition. */ - public var camCutscene:FlxCamera; + public var camOther:FlxCamera; /** * The combo popups. Includes the real-time combo counter and the rating. @@ -960,7 +960,7 @@ class PlayState extends MusicBeatSubState FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - pauseSubState.camera = camHUD; + pauseSubState.camera = camOther; openSubState(pauseSubState); // boyfriendPos.put(); // TODO: Why is this here? } @@ -1501,12 +1501,12 @@ class PlayState extends MusicBeatSubState camGame.bgColor = BACKGROUND_COLOR; // Show a pink background behind the stage. camHUD = new FlxCamera(); camHUD.bgColor.alpha = 0; // Show the game scene behind the camera. - camCutscene = new FlxCamera(); - camCutscene.bgColor.alpha = 0; // Show the game scene behind the camera. + camOther = new FlxCamera(); + camOther.bgColor.alpha = 0; // Show the game scene behind the camera. FlxG.cameras.reset(camGame); FlxG.cameras.add(camHUD, false); - FlxG.cameras.add(camCutscene, false); + FlxG.cameras.add(camOther, false); // Configure camera follow point. if (previousCameraFollowPoint != null) @@ -1900,7 +1900,6 @@ class PlayState extends MusicBeatSubState if (!result) return; isInCutscene = false; - camCutscene.visible = false; // TODO: Maybe tween in the camera after any cutscenes. camHUD.visible = true; @@ -1919,7 +1918,7 @@ class PlayState extends MusicBeatSubState if (!currentConversation.alive) currentConversation.revive(); currentConversation.completeCallback = onConversationComplete; - currentConversation.cameras = [camCutscene]; + currentConversation.cameras = [camOther]; currentConversation.zIndex = 1000; add(currentConversation); refresh(); @@ -2751,7 +2750,7 @@ class PlayState extends MusicBeatSubState persistentUpdate = false; FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - pauseSubState.camera = camCutscene; + pauseSubState.camera = camOther; openSubState(pauseSubState); } } @@ -2767,7 +2766,7 @@ class PlayState extends MusicBeatSubState persistentUpdate = false; FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - pauseSubState.camera = camCutscene; + pauseSubState.camera = camOther; openSubState(pauseSubState); } } From 0a6f1abd33d8cb474dcd8efe81380e5736538fc8 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Mon, 20 May 2024 23:52:45 +0200 Subject: [PATCH 007/469] Fix references to camCutscene --- source/funkin/play/cutscene/VideoCutscene.hx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index 01a492a77a..2177114aad 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -81,12 +81,11 @@ class VideoCutscene // Trigger the cutscene. Don't play the song in the background. PlayState.instance.isInCutscene = true; PlayState.instance.camHUD.visible = false; - PlayState.instance.camCutscene.visible = true; // Display a black screen to hide the game while the video is playing. blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK); blackScreen.scrollFactor.set(0, 0); - blackScreen.cameras = [PlayState.instance.camCutscene]; + blackScreen.cameras = [PlayState.instance.camOther]; PlayState.instance.add(blackScreen); VideoCutscene.cutsceneType = cutsceneType; @@ -120,7 +119,7 @@ class VideoCutscene vid.finishCallback = finishVideo.bind(0.5); - vid.cameras = [PlayState.instance.camCutscene]; + vid.cameras = [PlayState.instance.camOther]; PlayState.instance.add(vid); @@ -147,7 +146,7 @@ class VideoCutscene vid.bitmap.onEndReached.add(finishVideo.bind(0.5)); vid.autoPause = false; - vid.cameras = [PlayState.instance.camCutscene]; + vid.cameras = [PlayState.instance.camOther]; PlayState.instance.add(vid); @@ -305,7 +304,6 @@ class VideoCutscene vid = null; #end - PlayState.instance.camCutscene.visible = true; PlayState.instance.camHUD.visible = true; FlxTween.tween(blackScreen, {alpha: 0}, transitionTime, From 47cbd4b62038cbd07079b7c1d6ee88c73d54d724 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 21 May 2024 19:11:10 -0400 Subject: [PATCH 008/469] Update compiling guide with more troubleshooting --- docs/COMPILING.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index 07df6367f4..b08fc528ec 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -21,4 +21,64 @@ # Troubleshooting -- During the cloning process, you may experience an error along the lines of `error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)` due to poor connectivity. A common fix is to run ` git config --global http.postBuffer 4096M`. +While performing the process of compilation, you may experience one of the following issues: + +## PolymodHandler: extra field coreAssetRedirect + +``` +Installing funkin.vis from https://github.com/FunkinCrew/funkVis branch: 98c9db09f0bbfedfe67a84538a5814aaef80bdea +Error: std@sys_remove_dir +Execution error: command "haxelib --never git funkin.vis https://github.com/FunkinCrew/funkVis 98c9db09f0bbfedfe67a84538a5814aaef80bdea" failed with status: 1 in cwd +``` + +If you receive this error, you are on an outdated version of Polymod. + +To solve, you should try reinstalling Polymod: + +``` +haxelib run hmm reinstall --force polymod +``` + +You can also try deleting your `.haxelib` folder in your Funkin' project, then reinstalling all your Haxelibs to prevent any other errors: + +``` +rm -rf ./.haxelib +haxelib run hmm reinstall --force +``` + +## PolymodHandler: Couldn't find a match for this asset library: (vlc) + +``` +source/funkin/modding/PolymodErrorHandler.hx:84: [ERROR] Your Lime/OpenFL configuration is using custom asset libraries, and you provided frameworkParams in Polymod.init(), but we couldn't find a match for this asset library: (vlc) +source/funkin/modding/PolymodHandler.hx:158: An error occurred! Failed when loading mods! +source/funkin/util/logging/CrashHandler.hx:62: Error while handling crash: Null Object Reference +``` + +This error is specific to Linux targets. If you receive this error, you are on an outdated verison of hxCodec. + +To solve, you should try reinstalling hxCodec: + +``` +haxelib run hmm reinstall --force hxCodec +``` + +You can also try deleting your `.haxelib` folder in your Funkin' project, then reinstalling all your Haxelibs to prevent any other errors: + +``` +rm -rf ./.haxelib +haxelib run hmm reinstall --force +``` + +## Git: stream 0 was not closed cleanly: PROTOCOL_ERROR + +``` +error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1) +``` + +If you receive this error while cloning, you may be experiencing issues with your network connection. + +To solve, you should try modifying your git configuration before cloning again: + +``` +git config --global http.postBuffer 4096M +``` From ade4aeb3f7c49115591d69f739b41857886edbbc Mon Sep 17 00:00:00 2001 From: Karim Akra <144803230+KarimAkra@users.noreply.github.com> Date: Sun, 9 Jun 2024 01:24:02 +0300 Subject: [PATCH 009/469] get Float instead of Int in cpp (Float is 64-bit in so it works easier than Int64) --- source/funkin/util/MemoryUtil.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/util/MemoryUtil.hx b/source/funkin/util/MemoryUtil.hx index f5935ed672..18fd41472c 100644 --- a/source/funkin/util/MemoryUtil.hx +++ b/source/funkin/util/MemoryUtil.hx @@ -48,11 +48,11 @@ class MemoryUtil * Calculate the total memory usage of the program, in bytes. * @return Int */ - public static function getMemoryUsed():Int + public static function getMemoryUsed():#if cpp Float #else Int #end { #if cpp // There is also Gc.MEM_INFO_RESERVED, MEM_INFO_CURRENT, and MEM_INFO_LARGE. - return cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_USAGE); + return cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_USAGE); #else return openfl.system.System.totalMemory; #end From 7904a6a20ff76364f6342578ec7d6849e4b098a0 Mon Sep 17 00:00:00 2001 From: Karim Akra <144803230+KarimAkra@users.noreply.github.com> Date: Sun, 9 Jun 2024 01:25:21 +0300 Subject: [PATCH 010/469] use Math.fround instead of Math.round --- source/funkin/ui/debug/MemoryCounter.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/debug/MemoryCounter.hx b/source/funkin/ui/debug/MemoryCounter.hx index b25b556451..50421f3987 100644 --- a/source/funkin/ui/debug/MemoryCounter.hx +++ b/source/funkin/ui/debug/MemoryCounter.hx @@ -36,7 +36,7 @@ class MemoryCounter extends TextField @:noCompletion #if !flash override #end function __enterFrame(deltaTime:Float):Void { - var mem:Float = Math.round(MemoryUtil.getMemoryUsed() / BYTES_PER_MEG / ROUND_TO) * ROUND_TO; + var mem:Float = Math.fround(MemoryUtil.getMemoryUsed() / BYTES_PER_MEG / ROUND_TO) * ROUND_TO; if (mem > memPeak) memPeak = mem; From 73982fbd606051fd6e0f2bf9e78ca174fdab0d88 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Sun, 9 Jun 2024 00:29:55 +0200 Subject: [PATCH 011/469] Fix Stack Overflow if song doesn't have "normal" difficulty --- source/funkin/ui/freeplay/FreeplayState.hx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 5e07fb3967..0ef268d31f 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -2046,6 +2046,8 @@ class FreeplaySongData function set_currentDifficulty(value:String):String { + if (currentDifficulty == value) return value; + currentDifficulty = value; updateValues(displayedVariations); return value; From 4997bc27fd0473b5b6f3996ac7150b0240cb62c0 Mon Sep 17 00:00:00 2001 From: Karim Akra <144803230+KarimAkra@users.noreply.github.com> Date: Sun, 9 Jun 2024 12:47:17 +0300 Subject: [PATCH 012/469] remove the library strip --- source/funkin/audio/FunkinSound.hx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 4f61e70c27..be5cc6931a 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -491,8 +491,10 @@ class FunkinSound extends FlxSound implements ICloneable var promise:lime.app.Promise> = new lime.app.Promise>(); // split the path and get only after first : - // we are bypassing the openfl/lime asset library fuss + // we are bypassing the openfl/lime asset library fuss on web only + #if web path = Paths.stripLibrary(path); + #end var soundRequest = FlxPartialSound.partialLoadFromFile(path, start, end); From 9618cd2128323889d19246fb519b56494a9d20c7 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:53:32 +0200 Subject: [PATCH 013/469] Fix chart reset when charting and pressing chart key --- source/funkin/play/PlayState.hx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index b3d0a9f8a0..1e22e98afe 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2284,7 +2284,7 @@ class PlayState extends MusicBeatSubState health += Constants.HEALTH_HOLD_BONUS_PER_SECOND * elapsed; songScore += Std.int(Constants.SCORE_HOLD_BONUS_PER_SECOND * elapsed); } - + // Make sure the player keeps singing while the note is held by the bot. if (isBotPlayMode && currentStage != null && currentStage.getBoyfriend() != null && currentStage.getBoyfriend().isSinging()) { @@ -2612,10 +2612,18 @@ class PlayState extends MusicBeatSubState { disableKeys = true; persistentUpdate = false; - FlxG.switchState(() -> new ChartEditorState( - { - targetSongId: currentSong.id, - })); + if (isChartingMode) + { + FlxG.sound.music?.pause(); + this.close(); + } + else + { + FlxG.switchState(() -> new ChartEditorState( + { + targetSongId: currentSong.id, + })); + } } #end From 9fa603363c9ecf50d4576f18f3d2692372e12ca5 Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Mon, 10 Jun 2024 19:42:27 +0300 Subject: [PATCH 014/469] [BUGFIX] Made freeplay use the metadata to get the instrumental suffix Song previews in freeplay will now use the instrumental suffix from the current difficulty's corresponding song variation metadata instead of using the variation id as an instrumental suffix and checking only the "erect" variation. --- source/funkin/ui/freeplay/FreeplayState.hx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 5e07fb3967..5a7fa54d26 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1890,14 +1890,17 @@ class FreeplayState extends MusicBeatSubState } else { - var potentiallyErect:String = (currentDifficulty == "erect") || (currentDifficulty == "nightmare") ? "-erect" : ""; + var previewSong:Null = SongRegistry.instance.fetchEntry(daSongCapsule.songData.songId); + var instSuffix:String = previewSong?.getDifficulty(currentDifficulty, + previewSong?.variations ?? Constants.DEFAULT_VARIATION_LIST)?.characters?.instrumental ?? ''; + instSuffix = (instSuffix != '') ? '-$instSuffix' : ''; FunkinSound.playMusic(daSongCapsule.songData.songId, { startingVolume: 0.0, overrideExisting: true, restartTrack: false, pathsFunction: INST, - suffix: potentiallyErect, + suffix: instSuffix, partialParams: { loadPartial: true, From fb71f9087d546854d4b44311682d84dcdd44fd32 Mon Sep 17 00:00:00 2001 From: NotHyper-474 Date: Mon, 10 Jun 2024 23:12:28 -0300 Subject: [PATCH 015/469] Fix trace --- source/funkin/ui/transition/LoadingState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index bc26ad97a1..0f2ce10766 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -346,7 +346,7 @@ class LoadingState extends MusicBeatSubState return 'Done precaching ${path}'; }, true); - trace("Queued ${path} for precaching"); + trace('Queued ${path} for precaching'); // FunkinSprite.cacheTexture(path); } From b7eaa238e01ba0003ebd53cf71ecb2930c6c7b76 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Wed, 12 Jun 2024 00:34:04 +0200 Subject: [PATCH 016/469] Revert camCutscene rename --- source/funkin/play/PlayState.hx | 16 ++++++++-------- source/funkin/play/cutscene/VideoCutscene.hx | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 20b9d3661a..b88fef1887 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -493,7 +493,7 @@ class PlayState extends MusicBeatSubState /** * The camera which contains, and controls visibility of, a video cutscene, dialogue, pause menu and sticker transition. */ - public var camOther:FlxCamera; + public var camCutscene:FlxCamera; /** * The combo popups. Includes the real-time combo counter and the rating. @@ -960,7 +960,7 @@ class PlayState extends MusicBeatSubState FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - pauseSubState.camera = camOther; + pauseSubState.camera = camCutscene; openSubState(pauseSubState); // boyfriendPos.put(); // TODO: Why is this here? } @@ -1501,12 +1501,12 @@ class PlayState extends MusicBeatSubState camGame.bgColor = BACKGROUND_COLOR; // Show a pink background behind the stage. camHUD = new FlxCamera(); camHUD.bgColor.alpha = 0; // Show the game scene behind the camera. - camOther = new FlxCamera(); - camOther.bgColor.alpha = 0; // Show the game scene behind the camera. + camCutscene = new FlxCamera(); + camCutscene.bgColor.alpha = 0; // Show the game scene behind the camera. FlxG.cameras.reset(camGame); FlxG.cameras.add(camHUD, false); - FlxG.cameras.add(camOther, false); + FlxG.cameras.add(camCutscene, false); // Configure camera follow point. if (previousCameraFollowPoint != null) @@ -1918,7 +1918,7 @@ class PlayState extends MusicBeatSubState if (!currentConversation.alive) currentConversation.revive(); currentConversation.completeCallback = onConversationComplete; - currentConversation.cameras = [camOther]; + currentConversation.cameras = [camCutscene]; currentConversation.zIndex = 1000; add(currentConversation); refresh(); @@ -2750,7 +2750,7 @@ class PlayState extends MusicBeatSubState persistentUpdate = false; FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - pauseSubState.camera = camOther; + pauseSubState.camera = camCutscene; openSubState(pauseSubState); } } @@ -2766,7 +2766,7 @@ class PlayState extends MusicBeatSubState persistentUpdate = false; FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - pauseSubState.camera = camOther; + pauseSubState.camera = camCutscene; openSubState(pauseSubState); } } diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index 2177114aad..7612c3ab6c 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -85,7 +85,7 @@ class VideoCutscene // Display a black screen to hide the game while the video is playing. blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK); blackScreen.scrollFactor.set(0, 0); - blackScreen.cameras = [PlayState.instance.camOther]; + blackScreen.cameras = [PlayState.instance.camCutscene]; PlayState.instance.add(blackScreen); VideoCutscene.cutsceneType = cutsceneType; @@ -119,7 +119,7 @@ class VideoCutscene vid.finishCallback = finishVideo.bind(0.5); - vid.cameras = [PlayState.instance.camOther]; + vid.cameras = [PlayState.instance.camCutscene]; PlayState.instance.add(vid); @@ -146,7 +146,7 @@ class VideoCutscene vid.bitmap.onEndReached.add(finishVideo.bind(0.5)); vid.autoPause = false; - vid.cameras = [PlayState.instance.camOther]; + vid.cameras = [PlayState.instance.camCutscene]; PlayState.instance.add(vid); From c4d6e18885bc02fb723e1f73a87ad2ca67a6ede3 Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:23:45 +0300 Subject: [PATCH 017/469] now using getVariationsByCharId instead --- source/funkin/ui/freeplay/FreeplayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 5a7fa54d26..1caad3ee5c 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1892,7 +1892,7 @@ class FreeplayState extends MusicBeatSubState { var previewSong:Null = SongRegistry.instance.fetchEntry(daSongCapsule.songData.songId); var instSuffix:String = previewSong?.getDifficulty(currentDifficulty, - previewSong?.variations ?? Constants.DEFAULT_VARIATION_LIST)?.characters?.instrumental ?? ''; + previewSong?.getVariationsByCharId(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST)?.characters?.instrumental ?? ''; instSuffix = (instSuffix != '') ? '-$instSuffix' : ''; FunkinSound.playMusic(daSongCapsule.songData.songId, { From 1f1fe62a06827f2487fb99d45b5a551dc07ed454 Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:53:33 +0300 Subject: [PATCH 018/469] [BUGFIX] Fixed Ranks not appearing in freeplay for custom variations Freeplay tries to access a song's rank using the current difficulty name alone, but custom variation ranks are being saved with a variation prefix. This PR makes freeplay look for the variation prefix when necessary. --- source/funkin/ui/freeplay/FreeplayState.hx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 0caaf45915..23c0f6afbc 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -2094,8 +2094,13 @@ class FreeplaySongData { this.albumId = songDifficulty.album; } + + // TODO: This line of code makes me sad, but you can't really fix it without a breaking migration. + // `easy`, `erect`, `normal-pico`, etc. + var suffixedDifficulty = (songDifficulty.variation != Constants.DEFAULT_VARIATION + && songDifficulty.variation != 'erect') ? '$currentDifficulty-${songDifficulty.variation}' : currentDifficulty; - this.scoringRank = Save.instance.getSongRank(songId, currentDifficulty); + this.scoringRank = Save.instance.getSongRank(songId, suffixedDifficulty); this.isNew = song.isSongNew(currentDifficulty); } From ba4677857aefbd10def4e03b3def59756deccdba Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Sat, 15 Jun 2024 20:23:55 +0200 Subject: [PATCH 019/469] Few animation editor bugfixes --- hmm.json | 2 +- .../ui/debug/anim/DebugBoundingState.hx | 23 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/hmm.json b/hmm.json index 68e0c5cb03..d217740db5 100644 --- a/hmm.json +++ b/hmm.json @@ -30,7 +30,7 @@ "name": "flixel-ui", "type": "git", "dir": null, - "ref": "719b4f10d94186ed55f6fef1b6618d32abec8c15", + "ref": "d0afed7293c71ffdb1184751317fc709b44c9056", "url": "https://github.com/HaxeFlixel/flixel-ui" }, { diff --git a/source/funkin/ui/debug/anim/DebugBoundingState.hx b/source/funkin/ui/debug/anim/DebugBoundingState.hx index 04784a5b79..d82bcc612f 100644 --- a/source/funkin/ui/debug/anim/DebugBoundingState.hx +++ b/source/funkin/ui/debug/anim/DebugBoundingState.hx @@ -1,6 +1,7 @@ package funkin.ui.debug.anim; import flixel.addons.display.FlxGridOverlay; +import flixel.addons.display.FlxBackdrop; import flixel.addons.ui.FlxInputText; import flixel.addons.ui.FlxUIDropDownMenu; import flixel.FlxCamera; @@ -55,7 +56,7 @@ class DebugBoundingState extends FlxState TODAY'S TO-DO - Cleaner UI */ - var bg:FlxSprite; + var bg:FlxBackdrop; var fileInfo:FlxText; var txtGrp:FlxGroup; @@ -80,7 +81,7 @@ class DebugBoundingState extends FlxState { // get the screen position, according to the HUD camera, temp default to FlxG.camera juuust in case? var hudMousePos:FlxPoint = FlxG.mouse.getScreenPosition(hudCam ?? FlxG.camera); - return Screen.instance.hasSolidComponentUnderPoint(hudMousePos.x, hudMousePos.y); + return Screen.instance.hasSolidComponentUnderPoint(hudMousePos.x, hudMousePos.y) || FlxG.mouse.overlaps(animDropDownMenu, hudCam); } override function create() @@ -103,10 +104,7 @@ class DebugBoundingState extends FlxState hudCam = new FlxCamera(); hudCam.bgColor.alpha = 0; - bg = FlxGridOverlay.create(10, 10); - // bg = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.GREEN); - - bg.scrollFactor.set(); + bg = new FlxBackdrop(FlxGridOverlay.createGrid(10, 10, FlxG.width, FlxG.height, true, 0xffe7e6e6, 0xffd9d5d5)); add(bg); // we are setting this as the default draw camera only temporarily, to trick haxeui @@ -289,16 +287,20 @@ class DebugBoundingState extends FlxState public var mouseOffset:FlxPoint = FlxPoint.get(0, 0); public var oldPos:FlxPoint = FlxPoint.get(0, 0); + public var movingCharacter:Bool = false; function mouseOffsetMovement() { if (swagChar != null) { - if (FlxG.mouse.justPressed) + if (FlxG.mouse.justPressed && !haxeUIFocused) { + movingCharacter = true; mouseOffset.set(FlxG.mouse.x - -swagChar.animOffsets[0], FlxG.mouse.y - -swagChar.animOffsets[1]); } + if (!movingCharacter) return; + if (FlxG.mouse.pressed) { swagChar.animOffsets = [(FlxG.mouse.x - mouseOffset.x) * -1, (FlxG.mouse.y - mouseOffset.y) * -1]; @@ -307,6 +309,11 @@ class DebugBoundingState extends FlxState txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets; } + + if (FlxG.mouse.justReleased) + { + movingCharacter = false; + } } } @@ -373,7 +380,7 @@ class DebugBoundingState extends FlxState offsetView.visible = true; offsetView.active = true; offsetControls(); - if (!haxeUIFocused) mouseOffsetMovement(); + mouseOffsetMovement(); } if (FlxG.keys.justPressed.H) hudCam.visible = !hudCam.visible; From 250ec840efa17b323f69748271982659ad61cc06 Mon Sep 17 00:00:00 2001 From: MaybeMaru <97055307+MaybeMaru@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:11:00 +0200 Subject: [PATCH 020/469] Add missing constants --- source/funkin/play/notes/Strumline.hx | 2 +- source/funkin/play/notes/SustainTrail.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index 0e4b6645f1..3c114b5e0d 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -37,7 +37,7 @@ class Strumline extends FlxSpriteGroup static function get_RENDER_DISTANCE_MS():Float { - return FlxG.height / 0.45; + return FlxG.height / Constants.PIXELS_PER_MS; } /** diff --git a/source/funkin/play/notes/SustainTrail.hx b/source/funkin/play/notes/SustainTrail.hx index b358d7f033..f6d43b33f6 100644 --- a/source/funkin/play/notes/SustainTrail.hx +++ b/source/funkin/play/notes/SustainTrail.hx @@ -160,7 +160,7 @@ class SustainTrail extends FlxSprite */ public static inline function sustainHeight(susLength:Float, scroll:Float) { - return (susLength * 0.45 * scroll); + return (susLength * Constants.PIXELS_PER_MS * scroll); } function set_sustainLength(s:Float):Float From 24bdf2db644b9f225764b825ba01b6287e618199 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sun, 16 Jun 2024 18:34:10 -0400 Subject: [PATCH 021/469] Fix a bug where songs with no notes would crash the results screen. --- source/funkin/play/ResultState.hx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index 48fb3b04e9..a2c5f7e623 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -464,7 +464,9 @@ class ResultState extends MusicBeatSubState { bgFlash.visible = true; FlxTween.tween(bgFlash, {alpha: 0}, 5 / 24); - var clearPercentFloat = (params.scoreData.tallies.sick + params.scoreData.tallies.good) / params.scoreData.tallies.totalNotes * 100; + // NOTE: Only divide if totalNotes > 0 to prevent divide-by-zero errors. + var clearPercentFloat = params.scoreData.tallies.totalNotes == 0 ? 0.0 : (params.scoreData.tallies.sick + + params.scoreData.tallies.good) / params.scoreData.tallies.totalNotes * 100; clearPercentTarget = Math.floor(clearPercentFloat); // Prevent off-by-one errors. From 42192dfe97712f24a43c9ec45694049a6c5f4c70 Mon Sep 17 00:00:00 2001 From: Karim Akra <144803230+KarimAkra@users.noreply.github.com> Date: Sun, 9 Jun 2024 12:47:17 +0300 Subject: [PATCH 022/469] remove the library strip --- source/funkin/audio/FunkinSound.hx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index c70f195d2f..11b713f4db 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -491,8 +491,10 @@ class FunkinSound extends FlxSound implements ICloneable var promise:lime.app.Promise> = new lime.app.Promise>(); // split the path and get only after first : - // we are bypassing the openfl/lime asset library fuss + // we are bypassing the openfl/lime asset library fuss on web only + #if web path = Paths.stripLibrary(path); + #end var soundRequest = FlxPartialSound.partialLoadFromFile(path, start, end); From e2be19039c2a11bcf2050dd823b61b043c950fc5 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 17 Jun 2024 12:22:49 -0400 Subject: [PATCH 023/469] Get rid of a warning about missing BPM info --- source/funkin/ui/freeplay/FreeplayState.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 0caaf45915..68c63efc45 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1888,6 +1888,7 @@ class FreeplayState extends MusicBeatSubState startingVolume: 0.0, overrideExisting: true, restartTrack: false, + mapTimeChanges: false, // The music metadata is not alongside the audio file so this won't work. pathsFunction: INST, suffix: potentiallyErect, partialParams: From 0ed103b2f0c18061c45b50eebac8e30450ee2773 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 15 Jun 2024 06:33:30 -0400 Subject: [PATCH 024/469] submoduel --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 2e1594ee4c..15a816fb83 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 2e1594ee4c04c7148628bae471bdd061c9deb6b7 +Subproject commit 15a816fb833555c22be92bd408eaf147ed4c64be From cf44774b218c3b323b975c4dd075be8e04c96826 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 15 Jun 2024 06:53:41 -0400 Subject: [PATCH 025/469] submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 15a816fb83..01a285b38e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 15a816fb833555c22be92bd408eaf147ed4c64be +Subproject commit 01a285b38ecf10218153a1d40a2e4ea5b2132d7b From 78bbf5a7aab2ea50df4a817039062922fec562fd Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 15 Jun 2024 06:55:08 -0400 Subject: [PATCH 026/469] submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 01a285b38e..4b95075255 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 01a285b38ecf10218153a1d40a2e4ea5b2132d7b +Subproject commit 4b95075255baeaba3585fabff7052c257856fafe From 60e741434c6ecd1f15c4e62f4ea364b8648c3538 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 18 Jun 2024 17:56:24 -0400 Subject: [PATCH 027/469] Implemented playable character registry, added Freeplay character filtering, added alt instrumental support --- assets | 2 +- source/funkin/InitState.hx | 2 + .../funkin/data/freeplay/player/CHANGELOG.md | 9 ++ .../funkin/data/freeplay/player/PlayerData.hx | 63 ++++++++ .../data/freeplay/player/PlayerRegistry.hx | 151 ++++++++++++++++++ source/funkin/modding/PolymodHandler.hx | 6 +- source/funkin/play/song/Song.hx | 34 ++-- .../handlers/ChartEditorDialogHandler.hx | 7 +- source/funkin/ui/freeplay/FreeplayState.hx | 80 +++++++--- .../freeplay/charselect/PlayableCharacter.hx | 108 +++++++++++++ .../charselect/ScriptedPlayableCharacter.hx | 8 + source/funkin/util/VersionUtil.hx | 1 - 12 files changed, 433 insertions(+), 38 deletions(-) create mode 100644 source/funkin/data/freeplay/player/CHANGELOG.md create mode 100644 source/funkin/data/freeplay/player/PlayerData.hx create mode 100644 source/funkin/data/freeplay/player/PlayerRegistry.hx create mode 100644 source/funkin/ui/freeplay/charselect/PlayableCharacter.hx create mode 100644 source/funkin/ui/freeplay/charselect/ScriptedPlayableCharacter.hx diff --git a/assets b/assets index 2e1594ee4c..fece99b3b1 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 2e1594ee4c04c7148628bae471bdd061c9deb6b7 +Subproject commit fece99b3b121045fb2f6f02dba485201b32f1c87 diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 49b15ddf60..c2a56bdc2f 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -1,5 +1,6 @@ package funkin; +import funkin.data.freeplay.player.PlayerRegistry; import funkin.ui.debug.charting.ChartEditorState; import funkin.ui.transition.LoadingState; import flixel.FlxState; @@ -164,6 +165,7 @@ class InitState extends FlxState SongRegistry.instance.loadEntries(); LevelRegistry.instance.loadEntries(); NoteStyleRegistry.instance.loadEntries(); + PlayerRegistry.instance.loadEntries(); ConversationRegistry.instance.loadEntries(); DialogueBoxRegistry.instance.loadEntries(); SpeakerRegistry.instance.loadEntries(); diff --git a/source/funkin/data/freeplay/player/CHANGELOG.md b/source/funkin/data/freeplay/player/CHANGELOG.md new file mode 100644 index 0000000000..7a31e11ca9 --- /dev/null +++ b/source/funkin/data/freeplay/player/CHANGELOG.md @@ -0,0 +1,9 @@ +# Freeplay Playable Character Data Schema Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] +Initial release. diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx new file mode 100644 index 0000000000..d7b8145847 --- /dev/null +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -0,0 +1,63 @@ +package funkin.data.freeplay.player; + +import funkin.data.animation.AnimationData; + +@:nullSafety +class PlayerData +{ + /** + * The sematic version number of the player data JSON format. + * Supports fancy comparisons like NPM does it's neat. + */ + @:default(funkin.data.freeplay.player.PlayerRegistry.PLAYER_DATA_VERSION) + public var version:String; + + /** + * A readable name for this playable character. + */ + public var name:String = 'Unknown'; + + /** + * The character IDs this character is associated with. + * Only songs that use these characters will show up in Freeplay. + */ + @:default([]) + public var ownedChars:Array = []; + + /** + * Whether to show songs with character IDs that aren't associated with any specific character. + */ + @:optional + @:default(false) + public var showUnownedChars:Bool = false; + + /** + * Whether this character is unlocked by default. + * Use a ScriptedPlayableCharacter to add custom logic. + */ + @:optional + @:default(true) + public var unlocked:Bool = true; + + public function new() + { + this.version = PlayerRegistry.PLAYER_DATA_VERSION; + } + + /** + * Convert this StageData into a JSON string. + */ + public function serialize(pretty:Bool = true):String + { + // Update generatedBy and version before writing. + updateVersionToLatest(); + + var writer = new json2object.JsonWriter(); + return writer.write(this, pretty ? ' ' : null); + } + + public function updateVersionToLatest():Void + { + this.version = PlayerRegistry.PLAYER_DATA_VERSION; + } +} diff --git a/source/funkin/data/freeplay/player/PlayerRegistry.hx b/source/funkin/data/freeplay/player/PlayerRegistry.hx new file mode 100644 index 0000000000..3de9efd411 --- /dev/null +++ b/source/funkin/data/freeplay/player/PlayerRegistry.hx @@ -0,0 +1,151 @@ +package funkin.data.freeplay.player; + +import funkin.data.freeplay.player.PlayerData; +import funkin.ui.freeplay.charselect.PlayableCharacter; +import funkin.ui.freeplay.charselect.ScriptedPlayableCharacter; + +class PlayerRegistry extends BaseRegistry +{ + /** + * The current version string for the stage data format. + * Handle breaking changes by incrementing this value + * and adding migration to the `migratePlayerData()` function. + */ + public static final PLAYER_DATA_VERSION:thx.semver.Version = "1.0.0"; + + public static final PLAYER_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; + + public static var instance(get, never):PlayerRegistry; + static var _instance:Null = null; + + static function get_instance():PlayerRegistry + { + if (_instance == null) _instance = new PlayerRegistry(); + return _instance; + } + + /** + * A mapping between stage character IDs and Freeplay playable character IDs. + */ + var ownedCharacterIds:Map = []; + + public function new() + { + super('PLAYER', 'players', PLAYER_DATA_VERSION_RULE); + } + + public override function loadEntries():Void + { + super.loadEntries(); + + for (playerId in listEntryIds()) + { + var player = fetchEntry(playerId); + if (player == null) continue; + + var currentPlayerCharIds = player.getOwnedCharacterIds(); + for (characterId in currentPlayerCharIds) + { + ownedCharacterIds.set(characterId, playerId); + } + } + + log('Loaded ${countEntries()} playable characters with ${ownedCharacterIds.size()} associations.'); + } + + /** + * Get the playable character associated with a given stage character. + * @param characterId The stage character ID. + * @return The playable character. + */ + public function getCharacterOwnerId(characterId:String):String + { + return ownedCharacterIds[characterId]; + } + + /** + * Return true if the given stage character is associated with a specific playable character. + * If so, the level should only appear if that character is selected in Freeplay. + * @param characterId The stage character ID. + * @return Whether the character is owned by any one character. + */ + public function isCharacterOwned(characterId:String):Bool + { + return ownedCharacterIds.exists(characterId); + } + + /** + * Read, parse, and validate the JSON data and produce the corresponding data object. + */ + public function parseEntryData(id:String):Null + { + // JsonParser does not take type parameters, + // otherwise this function would be in BaseRegistry. + var parser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + + switch (loadEntryFile(id)) + { + case {fileName: fileName, contents: contents}: + parser.fromJson(contents, fileName); + default: + return null; + } + + if (parser.errors.length > 0) + { + printErrors(parser.errors, id); + return null; + } + return parser.value; + } + + /** + * Parse and validate the JSON data and produce the corresponding data object. + * + * NOTE: Must be implemented on the implementation class. + * @param contents The JSON as a string. + * @param fileName An optional file name for error reporting. + */ + public function parseEntryDataRaw(contents:String, ?fileName:String):Null + { + var parser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + parser.fromJson(contents, fileName); + + if (parser.errors.length > 0) + { + printErrors(parser.errors, fileName); + return null; + } + return parser.value; + } + + function createScriptedEntry(clsName:String):PlayableCharacter + { + return ScriptedPlayableCharacter.init(clsName, "unknown"); + } + + function getScriptedClassNames():Array + { + return ScriptedPlayableCharacter.listScriptClasses(); + } + + /** + * A list of all the playable characters from the base game, in order. + */ + public function listBaseGamePlayerIds():Array + { + return ["bf", "pico"]; + } + + /** + * A list of all installed playable characters that are not from the base game. + */ + public function listModdedPlayerIds():Array + { + return listEntryIds().filter(function(id:String):Bool { + return listBaseGamePlayerIds().indexOf(id) == -1; + }); + } +} diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index ae754b780b..c352aa6062 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -8,6 +8,7 @@ import funkin.data.event.SongEventRegistry; import funkin.data.story.level.LevelRegistry; import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.song.SongRegistry; +import funkin.data.freeplay.player.PlayerRegistry; import funkin.data.stage.StageRegistry; import funkin.data.freeplay.album.AlbumRegistry; import funkin.modding.module.ModuleHandler; @@ -369,15 +370,18 @@ class PolymodHandler // These MUST be imported at the top of the file and not referred to by fully qualified name, // to ensure build macros work properly. + SongEventRegistry.loadEventCache(); + SongRegistry.instance.loadEntries(); LevelRegistry.instance.loadEntries(); NoteStyleRegistry.instance.loadEntries(); - SongEventRegistry.loadEventCache(); + PlayerRegistry.instance.loadEntries(); ConversationRegistry.instance.loadEntries(); DialogueBoxRegistry.instance.loadEntries(); SpeakerRegistry.instance.loadEntries(); AlbumRegistry.instance.loadEntries(); StageRegistry.instance.loadEntries(); + CharacterDataParser.loadCharacterCache(); // TODO: Migrate characters to BaseRegistry. ModuleHandler.loadModuleCache(); } diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index dde5ee7b8d..91d35d8fa2 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -14,6 +14,7 @@ import funkin.data.song.SongData.SongTimeFormat; import funkin.data.song.SongRegistry; import funkin.modding.IScriptedClass.IPlayStateScriptedClass; import funkin.modding.events.ScriptEvent; +import funkin.ui.freeplay.charselect.PlayableCharacter; import funkin.util.SortUtil; import openfl.utils.Assets; @@ -401,11 +402,11 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry):Null + public function getFirstValidVariation(?diffId:String, ?currentCharacter:PlayableCharacter, ?possibleVariations:Array):Null { if (possibleVariations == null) { - possibleVariations = variations; + possibleVariations = getVariationsByCharacter(currentCharacter); possibleVariations.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_VARIATION_LIST)); } if (diffId == null) diffId = listDifficulties(null, possibleVariations)[0]; @@ -422,22 +423,29 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry + public function getVariationsByCharacter(?char:PlayableCharacter):Array { - if (charId == null) charId = Constants.DEFAULT_CHARACTER; + if (char == null) return variations; - if (variations.contains(charId)) - { - return [charId]; - } - else + var result = []; + trace('Evaluating variations for ${this.id} ${char.id}: ${this.variations}'); + for (variation in variations) { - // TODO: How to exclude character variations while keeping other custom variations? - return variations; + var metadata = _metadata.get(variation); + + var playerCharId = metadata?.playData?.characters?.player; + if (playerCharId == null) continue; + + if (char.shouldShowCharacter(playerCharId)) + { + result.push(variation); + } } + + return result; } /** @@ -455,6 +463,8 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry = song.listDifficulties(displayedVariations, false); - trace(availableDifficultiesForSong); + trace('Available Difficulties: $availableDifficultiesForSong'); if (availableDifficultiesForSong.length == 0) continue; songs.push(new FreeplaySongData(levelId, songId, song, displayedVariations)); @@ -454,7 +458,7 @@ class FreeplayState extends MusicBeatSubState }); // TODO: Replace this. - if (currentCharacter == 'pico') dj.visible = false; + if (currentCharacterId == 'pico') dj.visible = false; add(dj); @@ -1195,6 +1199,16 @@ class FreeplayState extends MusicBeatSubState rankAnimStart(fromResultsParams); } + if (FlxG.keys.justPressed.P) + { + FlxG.switchState(FreeplayState.build( + { + { + character: currentCharacterId == "pico" ? "bf" : "pico", + } + })); + } + // if (FlxG.keys.justPressed.H) // { // rankDisplayNew(fromResultsParams); @@ -1302,9 +1316,9 @@ class FreeplayState extends MusicBeatSubState { if (busy) return; - var upP:Bool = controls.UI_UP_P && !FlxG.keys.pressed.CONTROL; - var downP:Bool = controls.UI_DOWN_P && !FlxG.keys.pressed.CONTROL; - var accepted:Bool = controls.ACCEPT && !FlxG.keys.pressed.CONTROL; + var upP:Bool = controls.UI_UP_P; + var downP:Bool = controls.UI_DOWN_P; + var accepted:Bool = controls.ACCEPT; if (FlxG.onMobile) { @@ -1378,7 +1392,7 @@ class FreeplayState extends MusicBeatSubState } #end - if (!FlxG.keys.pressed.CONTROL && (controls.UI_UP || controls.UI_DOWN)) + if ((controls.UI_UP || controls.UI_DOWN)) { if (spamming) { @@ -1440,13 +1454,13 @@ class FreeplayState extends MusicBeatSubState } #end - if (controls.UI_LEFT_P && !FlxG.keys.pressed.CONTROL) + if (controls.UI_LEFT_P) { dj.resetAFKTimer(); changeDiff(-1); generateSongList(currentFilter, true); } - if (controls.UI_RIGHT_P && !FlxG.keys.pressed.CONTROL) + if (controls.UI_RIGHT_P) { dj.resetAFKTimer(); changeDiff(1); @@ -1720,7 +1734,7 @@ class FreeplayState extends MusicBeatSubState return; } var targetDifficultyId:String = currentDifficulty; - var targetVariation:String = targetSong.getFirstValidVariation(targetDifficultyId); + var targetVariation:String = targetSong.getFirstValidVariation(targetDifficultyId, currentCharacter); PlayStatePlaylist.campaignId = cap.songData.levelId; var targetDifficulty:SongDifficulty = targetSong.getDifficulty(targetDifficultyId, targetVariation); @@ -1730,8 +1744,18 @@ class FreeplayState extends MusicBeatSubState return; } - // TODO: Change this with alternate instrumentals - var targetInstId:String = targetDifficulty.characters.instrumental; + var baseInstrumentalId:String = targetDifficulty?.characters?.instrumental ?? ''; + var altInstrumentalIds:Array = targetDifficulty?.characters?.altInstrumentals ?? []; + + var targetInstId:String = baseInstrumentalId; + + // TODO: Make this a UI element. + #if (debug || FORCE_DEBUG_VERSION) + if (altInstrumentalIds.length > 0 && FlxG.keys.pressed.CONTROL) + { + targetInstId = altInstrumentalIds[0]; + } + #end // Visual and audio effects. FunkinSound.playOnce(Paths.sound('confirmMenu')); @@ -1883,9 +1907,23 @@ class FreeplayState extends MusicBeatSubState else { var previewSong:Null = SongRegistry.instance.fetchEntry(daSongCapsule.songData.songId); - var instSuffix:String = previewSong?.getDifficulty(currentDifficulty, - previewSong?.getVariationsByCharId(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST)?.characters?.instrumental ?? ''; + var songDifficulty = previewSong?.getDifficulty(currentDifficulty, + previewSong?.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST); + var baseInstrumentalId:String = songDifficulty?.characters?.instrumental ?? ''; + var altInstrumentalIds:Array = songDifficulty?.characters?.altInstrumentals ?? []; + + var instSuffix:String = baseInstrumentalId; + + // TODO: Make this a UI element. + #if (debug || FORCE_DEBUG_VERSION) + if (altInstrumentalIds.length > 0 && FlxG.keys.pressed.CONTROL) + { + instSuffix = altInstrumentalIds[0]; + } + #end + instSuffix = (instSuffix != '') ? '-$instSuffix' : ''; + FunkinSound.playMusic(daSongCapsule.songData.songId, { startingVolume: 0.0, @@ -1913,7 +1951,7 @@ class FreeplayState extends MusicBeatSubState public static function build(?params:FreeplayStateParams, ?stickers:StickerSubState):MusicBeatState { var result:MainMenuState; - if (params?.fromResults.playRankAnim) result = new MainMenuState(true); + if (params?.fromResults?.playRankAnim) result = new MainMenuState(true); else result = new MainMenuState(false); @@ -1951,8 +1989,8 @@ class DifficultySelector extends FlxSprite override function update(elapsed:Float):Void { - if (flipX && controls.UI_RIGHT_P && !FlxG.keys.pressed.CONTROL) moveShitDown(); - if (!flipX && controls.UI_LEFT_P && !FlxG.keys.pressed.CONTROL) moveShitDown(); + if (flipX && controls.UI_RIGHT_P) moveShitDown(); + if (!flipX && controls.UI_LEFT_P) moveShitDown(); super.update(elapsed); } diff --git a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx new file mode 100644 index 0000000000..743345004e --- /dev/null +++ b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx @@ -0,0 +1,108 @@ +package funkin.ui.freeplay.charselect; + +import funkin.data.IRegistryEntry; +import funkin.data.freeplay.player.PlayerData; +import funkin.data.freeplay.player.PlayerRegistry; + +/** + * An object used to retrieve data about a playable character (also known as "weeks"). + * Can be scripted to override each function, for custom behavior. + */ +class PlayableCharacter implements IRegistryEntry +{ + /** + * The ID of the playable character. + */ + public final id:String; + + /** + * Playable character data as parsed from the JSON file. + */ + public final _data:PlayerData; + + /** + * @param id The ID of the JSON file to parse. + */ + public function new(id:String) + { + this.id = id; + _data = _fetchData(id); + + if (_data == null) + { + throw 'Could not parse playable character data for id: $id'; + } + } + + /** + * Retrieve the readable name of the playable character. + */ + public function getName():String + { + // TODO: Maybe add localization support? + return _data.name; + } + + /** + * Retrieve the list of stage character IDs associated with this playable character. + * @return The list of associated character IDs + */ + public function getOwnedCharacterIds():Array + { + return _data.ownedChars; + } + + /** + * Return `true` if, when this character is selected in Freeplay, + * songs unassociated with a specific character should appear. + */ + public function shouldShowUnownedChars():Bool + { + return _data.showUnownedChars; + } + + public function shouldShowCharacter(id:String):Bool + { + if (_data.ownedChars.contains(id)) + { + return true; + } + + if (_data.showUnownedChars) + { + var result = !PlayerRegistry.instance.isCharacterOwned(id); + return result; + } + + return false; + } + + /** + * Returns whether this character is unlocked. + */ + public function isUnlocked():Bool + { + return _data.unlocked; + } + + /** + * Called when the character is destroyed. + * TODO: Document when this gets called + */ + public function destroy():Void {} + + public function toString():String + { + return 'PlayableCharacter($id)'; + } + + /** + * Retrieve and parse the JSON data for a playable character by ID. + * @param id The ID of the character + * @return The parsed player data, or null if not found or invalid + */ + static function _fetchData(id:String):Null + { + return PlayerRegistry.instance.parseEntryDataWithMigration(id, PlayerRegistry.instance.fetchEntryVersion(id)); + } +} diff --git a/source/funkin/ui/freeplay/charselect/ScriptedPlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/ScriptedPlayableCharacter.hx new file mode 100644 index 0000000000..f75a58092f --- /dev/null +++ b/source/funkin/ui/freeplay/charselect/ScriptedPlayableCharacter.hx @@ -0,0 +1,8 @@ +package funkin.ui.freeplay.charselect; + +/** + * A script that can be tied to a PlayableCharacter. + * Create a scripted class that extends PlayableCharacter to use this. + */ +@:hscriptClass +class ScriptedPlayableCharacter extends funkin.ui.freeplay.charselect.PlayableCharacter implements polymod.hscript.HScriptedClass {} diff --git a/source/funkin/util/VersionUtil.hx b/source/funkin/util/VersionUtil.hx index 832ce008ab..9bf46a1881 100644 --- a/source/funkin/util/VersionUtil.hx +++ b/source/funkin/util/VersionUtil.hx @@ -24,7 +24,6 @@ class VersionUtil try { var versionRaw:thx.semver.Version.SemVer = version; - trace('${versionRaw} satisfies (${versionRule})? ${version.satisfies(versionRule)}'); return version.satisfies(versionRule); } catch (e) From 263039f52ce5096abc5929c9af3b95ef9440e229 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 18 Jun 2024 20:07:27 -0400 Subject: [PATCH 028/469] Make Boyfriend DJ animations data driven --- assets | 2 +- .../funkin/data/freeplay/player/PlayerData.hx | 93 +++++ source/funkin/ui/freeplay/DJBoyfriend.hx | 371 ------------------ source/funkin/ui/freeplay/FreeplayDJ.hx | 369 +++++++++++++++++ source/funkin/ui/freeplay/FreeplayState.hx | 24 +- .../freeplay/charselect/PlayableCharacter.hx | 5 + source/funkin/util/tools/MapTools.hx | 4 + 7 files changed, 484 insertions(+), 384 deletions(-) delete mode 100644 source/funkin/ui/freeplay/DJBoyfriend.hx create mode 100644 source/funkin/ui/freeplay/FreeplayDJ.hx diff --git a/assets b/assets index fece99b3b1..6ec72f8aeb 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit fece99b3b121045fb2f6f02dba485201b32f1c87 +Subproject commit 6ec72f8aeb5ab77997dee4e2e98ae03f0ec347b8 diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx index d7b8145847..10fc54b783 100644 --- a/source/funkin/data/freeplay/player/PlayerData.hx +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -31,6 +31,12 @@ class PlayerData @:default(false) public var showUnownedChars:Bool = false; + /** + * Data for displaying this character in the Freeplay menu. + * If null, display no DJ. + */ + public var freeplayDJ:Null = null; + /** * Whether this character is unlocked by default. * Use a ScriptedPlayableCharacter to add custom logic. @@ -61,3 +67,90 @@ class PlayerData this.version = PlayerRegistry.PLAYER_DATA_VERSION; } } + +class PlayerFreeplayDJData +{ + var assetPath:String; + var animations:Array; + + @:jignored + var animationMap:Map; + + @:optional + var cartoon:Null; + + public function new() + { + animationMap = new Map(); + } + + function mapAnimations() + { + if (animationMap == null) animationMap = new Map(); + + animationMap.clear(); + for (anim in animations) + { + animationMap.set(anim.name, anim); + } + } + + public function getAtlasPath():String + { + return Paths.animateAtlas(assetPath); + } + + public function getAnimationPrefix(name:String):Null + { + if (animationMap.size() == 0) mapAnimations(); + + var anim = animationMap.get(name); + if (anim == null) return null; + return anim.prefix; + } + + public function getAnimationOffsets(name:String):Null> + { + if (animationMap.size() == 0) mapAnimations(); + + var anim = animationMap.get(name); + if (anim == null) return null; + return anim.offsets; + } + + // TODO: These should really be frame labels, ehe. + + public function getCartoonSoundClickFrame():Int + { + return cartoon?.soundClickFrame ?? 80; + } + + public function getCartoonSoundCartoonFrame():Int + { + return cartoon?.soundCartoonFrame ?? 85; + } + + public function getCartoonLoopBlinkFrame():Int + { + return cartoon?.loopBlinkFrame ?? 112; + } + + public function getCartoonLoopFrame():Int + { + return cartoon?.loopFrame ?? 166; + } + + public function getCartoonChannelChangeFrame():Int + { + return cartoon?.channelChangeFrame ?? 60; + } +} + +typedef PlayerFreeplayDJCartoonData = +{ + var soundClickFrame:Int; + var soundCartoonFrame:Int; + var loopBlinkFrame:Int; + var loopFrame:Int; + var channelChangeFrame:Int; +} diff --git a/source/funkin/ui/freeplay/DJBoyfriend.hx b/source/funkin/ui/freeplay/DJBoyfriend.hx deleted file mode 100644 index bbf043dd48..0000000000 --- a/source/funkin/ui/freeplay/DJBoyfriend.hx +++ /dev/null @@ -1,371 +0,0 @@ -package funkin.ui.freeplay; - -import flixel.FlxSprite; -import flixel.util.FlxSignal; -import funkin.util.assets.FlxAnimationUtil; -import funkin.graphics.adobeanimate.FlxAtlasSprite; -import funkin.audio.FunkinSound; -import flixel.util.FlxTimer; -import funkin.audio.FunkinSound; -import funkin.audio.FlxStreamSound; - -class DJBoyfriend extends FlxAtlasSprite -{ - // Represents the sprite's current status. - // Without state machines I would have driven myself crazy years ago. - public var currentState:DJBoyfriendState = Intro; - - // A callback activated when the intro animation finishes. - public var onIntroDone:FlxSignal = new FlxSignal(); - - // A callback activated when Boyfriend gets spooked. - public var onSpook:FlxSignal = new FlxSignal(); - - // playAnim stolen from Character.hx, cuz im lazy lol! - // TODO: Switch this class to use SwagSprite instead. - public var animOffsets:Map>; - - var gotSpooked:Bool = false; - - static final SPOOK_PERIOD:Float = 60.0; - static final TV_PERIOD:Float = 120.0; - - // Time since dad last SPOOKED you. - var timeSinceSpook:Float = 0; - - public function new(x:Float, y:Float) - { - super(x, y, Paths.animateAtlas("freeplay/freeplay-boyfriend", "preload")); - - animOffsets = new Map>(); - - anim.callback = function(name, number) { - switch (name) - { - case "Boyfriend DJ watchin tv OG": - if (number == 80) - { - FunkinSound.playOnce(Paths.sound('remote_click')); - } - if (number == 85) - { - runTvLogic(); - } - default: - } - }; - - setupAnimations(); - - FlxG.debugger.track(this); - FlxG.console.registerObject("dj", this); - - anim.onComplete = onFinishAnim; - - FlxG.console.registerFunction("tv", function() { - currentState = TV; - }); - } - - /* - [remote hand under,boyfriend top head,brim piece,arm cringe l,red lazer,dj arm in,bf fist pump arm,hand raised right,forearm left,fist shaking,bf smile eyes closed face,arm cringe r,bf clenched face,face shrug,boyfriend falling,blue tint 1,shirt sleeve,bf clenched fist,head BF relaxed,blue tint 2,hand down left,blue tint 3,blue tint 4,head less smooshed,blue tint 5,boyfriend freeplay,BF head slight turn,blue tint 6,arm shrug l,blue tint 7,shoulder raised w sleeve,blue tint 8,fist pump face,blue tint 9,foot rested light,hand turnaround,arm chill right,Boyfriend DJ,arm shrug r,head back bf,hat top piece,dad bod,face surprise snap,Boyfriend DJ fist pump,office chair,foot rested right,chest down,office chair upright,body chill,bf dj afk,head mouth open dad,BF Head defalt HAIR BLOWING,hand shrug l,face piece,foot wag,turn table,shoulder up left,turntable lights,boyfriend dj body shirt blowing,body chunk turned,hand down right,dj arm out,hand shrug r,body chest out,rave hand,palm,chill face default,head back semi bf,boyfriend bottom head,DJ arm,shoulder right dad,bf surprise,boyfriend dj body,hs1,Boyfriend DJ watchin tv OG,spinning disk,hs2,arm chill left,boyfriend dj intro,hs3,hs4,chill face extra,hs5,remote hand upright,hs6,pant over table,face surprise,bf arm peace,arm turnaround,bf eyes 1,arm slammed table,eye squit,leg BF,head mid piece,arm backing,arm swoopin in,shoe right lowering,forearm right,hand out,blue tint 10,body falling back,remote thumb press,shoulder,hair spike single,bf bent - arm,crt,foot raised right,dad hand,chill face 1,chill face 2,clenched fist,head SMOOSHED,shoulder left dad,df1,body chunk upright,df2,df3,df4,hat front piece,df5,foot rested right 2,hand in,arm spun,shoe raised left,bf 1 finger hand,bf mouth 1,Boyfriend DJ confirm,forearm down ,hand raised left,remote thumb up] - */ - override public function listAnimations():Array - { - var anims:Array = []; - @:privateAccess - for (animKey in anim.symbolDictionary) - { - anims.push(animKey.name); - } - return anims; - } - - var lowPumpLoopPoint:Int = 4; - - public override function update(elapsed:Float):Void - { - super.update(elapsed); - - switch (currentState) - { - case Intro: - // Play the intro animation then leave this state immediately. - if (getCurrentAnimation() != 'boyfriend dj intro') playFlashAnimation('boyfriend dj intro', true); - timeSinceSpook = 0; - case Idle: - // We are in this state the majority of the time. - if (getCurrentAnimation() != 'Boyfriend DJ') - { - playFlashAnimation('Boyfriend DJ', true); - } - - if (getCurrentAnimation() == 'Boyfriend DJ' && this.isLoopFinished()) - { - if (timeSinceSpook >= SPOOK_PERIOD && !gotSpooked) - { - currentState = Spook; - } - else if (timeSinceSpook >= TV_PERIOD) - { - currentState = TV; - } - } - timeSinceSpook += elapsed; - case Confirm: - if (getCurrentAnimation() != 'Boyfriend DJ confirm') playFlashAnimation('Boyfriend DJ confirm', false); - timeSinceSpook = 0; - case PumpIntro: - if (getCurrentAnimation() != 'Boyfriend DJ fist pump') playFlashAnimation('Boyfriend DJ fist pump', false); - if (getCurrentAnimation() == 'Boyfriend DJ fist pump' && anim.curFrame >= 4) - { - anim.play("Boyfriend DJ fist pump", true, false, 0); - } - case FistPump: - - case Spook: - if (getCurrentAnimation() != 'bf dj afk') - { - onSpook.dispatch(); - playFlashAnimation('bf dj afk', false); - gotSpooked = true; - } - timeSinceSpook = 0; - case TV: - if (getCurrentAnimation() != 'Boyfriend DJ watchin tv OG') playFlashAnimation('Boyfriend DJ watchin tv OG', true); - timeSinceSpook = 0; - default: - // I shit myself. - } - - if (FlxG.keys.pressed.CONTROL) - { - if (FlxG.keys.justPressed.LEFT) - { - this.offsetX -= FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); - } - - if (FlxG.keys.justPressed.RIGHT) - { - this.offsetX += FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); - } - - if (FlxG.keys.justPressed.UP) - { - this.offsetY -= FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); - } - - if (FlxG.keys.justPressed.DOWN) - { - this.offsetY += FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); - } - - if (FlxG.keys.justPressed.SPACE) - { - currentState = (currentState == Idle ? TV : Idle); - } - } - } - - function onFinishAnim():Void - { - var name = anim.curSymbol.name; - switch (name) - { - case "boyfriend dj intro": - // trace('Finished intro'); - currentState = Idle; - onIntroDone.dispatch(); - case "Boyfriend DJ": - // trace('Finished idle'); - case "bf dj afk": - // trace('Finished spook'); - currentState = Idle; - case "Boyfriend DJ confirm": - - case "Boyfriend DJ fist pump": - currentState = Idle; - - case "Boyfriend DJ loss reaction 1": - currentState = Idle; - - case "Boyfriend DJ watchin tv OG": - var frame:Int = FlxG.random.bool(33) ? 112 : 166; - - // BF switches channels when the video ends, or at a 10% chance each time his idle loops. - if (FlxG.random.bool(5)) - { - frame = 60; - // boyfriend switches channel code? - // runTvLogic(); - } - trace('Replay idle: ${frame}'); - anim.play("Boyfriend DJ watchin tv OG", true, false, frame); - // trace('Finished confirm'); - } - } - - public function resetAFKTimer():Void - { - timeSinceSpook = 0; - gotSpooked = false; - } - - var offsetX:Float = 0.0; - var offsetY:Float = 0.0; - - function setupAnimations():Void - { - // Intro - addOffset('boyfriend dj intro', 8.0 - 1.3, 3.0 - 0.4); - - // Idle - addOffset('Boyfriend DJ', 0, 0); - - // Confirm - addOffset('Boyfriend DJ confirm', 0, 0); - - // AFK: Spook - addOffset('bf dj afk', 649.5, 58.5); - - // AFK: TV - addOffset('Boyfriend DJ watchin tv OG', 0, 0); - } - - var cartoonSnd:Null = null; - - public var playingCartoon:Bool = false; - - public function runTvLogic() - { - if (cartoonSnd == null) - { - // tv is OFF, but getting turned on - FunkinSound.playOnce(Paths.sound('tv_on'), 1.0, function() { - loadCartoon(); - }); - } - else - { - // plays it smidge after the click - FunkinSound.playOnce(Paths.sound('channel_switch'), 1.0, function() { - cartoonSnd.destroy(); - loadCartoon(); - }); - } - - // loadCartoon(); - } - - function loadCartoon() - { - cartoonSnd = FunkinSound.load(Paths.sound(getRandomFlashToon()), 1.0, false, true, true, function() { - anim.play("Boyfriend DJ watchin tv OG", true, false, 60); - }); - - // Fade out music to 40% volume over 1 second. - // This helps make the TV a bit more audible. - FlxG.sound.music.fadeOut(1.0, 0.1); - - // Play the cartoon at a random time between the start and 5 seconds from the end. - cartoonSnd.time = FlxG.random.float(0, Math.max(cartoonSnd.length - (5 * Constants.MS_PER_SEC), 0.0)); - } - - final cartoonList:Array = openfl.utils.Assets.list().filter(function(path) return path.startsWith("assets/sounds/cartoons/")); - - function getRandomFlashToon():String - { - var randomFile = FlxG.random.getObject(cartoonList); - - // Strip folder prefix - randomFile = randomFile.replace("assets/sounds/", ""); - // Strip file extension - randomFile = randomFile.substring(0, randomFile.length - 4); - - return randomFile; - } - - public function confirm():Void - { - currentState = Confirm; - } - - public function fistPump():Void - { - currentState = PumpIntro; - } - - public function pumpFist():Void - { - currentState = FistPump; - anim.play("Boyfriend DJ fist pump", true, false, 4); - } - - public function pumpFistBad():Void - { - currentState = FistPump; - anim.play("Boyfriend DJ loss reaction 1", true, false, 4); - } - - public inline function addOffset(name:String, x:Float = 0, y:Float = 0) - { - animOffsets[name] = [x, y]; - } - - override public function getCurrentAnimation():String - { - if (this.anim == null || this.anim.curSymbol == null) return ""; - return this.anim.curSymbol.name; - } - - public function playFlashAnimation(id:String, ?Force:Bool = false, ?Reverse:Bool = false, ?Frame:Int = 0):Void - { - anim.play(id, Force, Reverse, Frame); - applyAnimOffset(); - } - - function applyAnimOffset() - { - var AnimName = getCurrentAnimation(); - var daOffset = animOffsets.get(AnimName); - if (animOffsets.exists(AnimName)) - { - var xValue = daOffset[0]; - var yValue = daOffset[1]; - if (AnimName == "Boyfriend DJ watchin tv OG") - { - xValue += offsetX; - yValue += offsetY; - } - - offset.set(xValue, yValue); - } - else - { - offset.set(0, 0); - } - } - - public override function destroy():Void - { - super.destroy(); - - if (cartoonSnd != null) - { - cartoonSnd.destroy(); - cartoonSnd = null; - } - } -} - -enum DJBoyfriendState -{ - Intro; - Idle; - Confirm; - PumpIntro; - FistPump; - Spook; - TV; -} diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx new file mode 100644 index 0000000000..f9effe793c --- /dev/null +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -0,0 +1,369 @@ +package funkin.ui.freeplay; + +import flixel.FlxSprite; +import flixel.util.FlxSignal; +import funkin.util.assets.FlxAnimationUtil; +import funkin.graphics.adobeanimate.FlxAtlasSprite; +import funkin.audio.FunkinSound; +import flixel.util.FlxTimer; +import funkin.data.freeplay.player.PlayerRegistry; +import funkin.data.freeplay.player.PlayerData.PlayerFreeplayDJData; +import funkin.audio.FunkinSound; +import funkin.audio.FlxStreamSound; + +class FreeplayDJ extends FlxAtlasSprite +{ + // Represents the sprite's current status. + // Without state machines I would have driven myself crazy years ago. + public var currentState:DJBoyfriendState = Intro; + + // A callback activated when the intro animation finishes. + public var onIntroDone:FlxSignal = new FlxSignal(); + + // A callback activated when the idle easter egg plays. + public var onIdleEasterEgg:FlxSignal = new FlxSignal(); + + var seenIdleEasterEgg:Bool = false; + + static final IDLE_EGG_PERIOD:Float = 60.0; + static final IDLE_CARTOON_PERIOD:Float = 120.0; + + // Time since last special idle animation you. + var timeIdling:Float = 0; + + final characterId:String = Constants.DEFAULT_CHARACTER; + final playableCharData:PlayerFreeplayDJData; + + public function new(x:Float, y:Float, characterId:String) + { + this.characterId = characterId; + + var playableChar = PlayerRegistry.instance.fetchEntry(characterId); + playableCharData = playableChar.getFreeplayDJData(); + + super(x, y, playableCharData.getAtlasPath()); + + anim.callback = function(name, number) { + if (name == playableCharData.getAnimationPrefix('cartoon')) + { + if (number == playableCharData.getCartoonSoundClickFrame()) + { + FunkinSound.playOnce(Paths.sound('remote_click')); + } + if (number == playableCharData.getCartoonSoundCartoonFrame()) + { + runTvLogic(); + } + } + }; + + FlxG.debugger.track(this); + FlxG.console.registerObject("dj", this); + + anim.onComplete = onFinishAnim; + + FlxG.console.registerFunction("freeplayCartoon", function() { + currentState = Cartoon; + }); + } + + override public function listAnimations():Array + { + var anims:Array = []; + @:privateAccess + for (animKey in anim.symbolDictionary) + { + anims.push(animKey.name); + } + return anims; + } + + var lowPumpLoopPoint:Int = 4; + + public override function update(elapsed:Float):Void + { + super.update(elapsed); + + switch (currentState) + { + case Intro: + // Play the intro animation then leave this state immediately. + var animPrefix = playableCharData.getAnimationPrefix('intro'); + if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true); + timeIdling = 0; + case Idle: + // We are in this state the majority of the time. + var animPrefix = playableCharData.getAnimationPrefix('idle'); + if (getCurrentAnimation() != animPrefix) + { + playFlashAnimation(animPrefix, true); + } + + if (getCurrentAnimation() == animPrefix && this.isLoopFinished()) + { + if (timeIdling >= IDLE_EGG_PERIOD && !seenIdleEasterEgg) + { + currentState = IdleEasterEgg; + } + else if (timeIdling >= IDLE_CARTOON_PERIOD) + { + currentState = Cartoon; + } + } + timeIdling += elapsed; + case Confirm: + var animPrefix = playableCharData.getAnimationPrefix('confirm'); + if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, false); + timeIdling = 0; + case FistPumpIntro: + var animPrefix = playableCharData.getAnimationPrefix('fistPump'); + if (getCurrentAnimation() != animPrefix) playFlashAnimation('Boyfriend DJ fist pump', false); + if (getCurrentAnimation() == animPrefix && anim.curFrame >= 4) + { + anim.play("Boyfriend DJ fist pump", true, false, 0); + } + case FistPump: + + case IdleEasterEgg: + var animPrefix = playableCharData.getAnimationPrefix('idleEasterEgg'); + if (getCurrentAnimation() != animPrefix) + { + onIdleEasterEgg.dispatch(); + playFlashAnimation(animPrefix, false); + seenIdleEasterEgg = true; + } + timeIdling = 0; + case Cartoon: + var animPrefix = playableCharData.getAnimationPrefix('cartoon'); + if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true); + timeIdling = 0; + default: + // I shit myself. + } + + if (FlxG.keys.pressed.CONTROL) + { + if (FlxG.keys.justPressed.LEFT) + { + this.offsetX -= FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); + } + + if (FlxG.keys.justPressed.RIGHT) + { + this.offsetX += FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); + } + + if (FlxG.keys.justPressed.UP) + { + this.offsetY -= FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); + } + + if (FlxG.keys.justPressed.DOWN) + { + this.offsetY += FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); + } + + if (FlxG.keys.justPressed.SPACE) + { + currentState = (currentState == Idle ? Cartoon : Idle); + } + } + } + + function onFinishAnim():Void + { + var name = anim.curSymbol.name; + + if (name == playableCharData.getAnimationPrefix('intro')) + { + currentState = Idle; + onIntroDone.dispatch(); + } + else if (name == playableCharData.getAnimationPrefix('idle')) + { + // trace('Finished idle'); + } + else if (name == playableCharData.getAnimationPrefix('confirm')) + { + // trace('Finished confirm'); + } + else if (name == playableCharData.getAnimationPrefix('fistPump')) + { + // trace('Finished fist pump'); + currentState = Idle; + } + else if (name == playableCharData.getAnimationPrefix('idleEasterEgg')) + { + // trace('Finished spook'); + currentState = Idle; + } + else if (name == playableCharData.getAnimationPrefix('loss')) + { + // trace('Finished loss reaction'); + currentState = Idle; + } + else if (name == playableCharData.getAnimationPrefix('cartoon')) + { + // trace('Finished cartoon'); + + var frame:Int = FlxG.random.bool(33) ? playableCharData.getCartoonLoopBlinkFrame() : playableCharData.getCartoonLoopFrame(); + + // Character switches channels when the video ends, or at a 10% chance each time his idle loops. + if (FlxG.random.bool(5)) + { + frame = playableCharData.getCartoonChannelChangeFrame(); + // boyfriend switches channel code? + // runTvLogic(); + } + trace('Replay idle: ${frame}'); + anim.play(playableCharData.getAnimationPrefix('cartoon'), true, false, frame); + // trace('Finished confirm'); + } + else + { + trace('Finished ${name}'); + } + } + + public function resetAFKTimer():Void + { + timeIdling = 0; + seenIdleEasterEgg = false; + } + + var offsetX:Float = 0.0; + var offsetY:Float = 0.0; + + var cartoonSnd:Null = null; + + public var playingCartoon:Bool = false; + + public function runTvLogic() + { + if (cartoonSnd == null) + { + // tv is OFF, but getting turned on + FunkinSound.playOnce(Paths.sound('tv_on'), 1.0, function() { + loadCartoon(); + }); + } + else + { + // plays it smidge after the click + FunkinSound.playOnce(Paths.sound('channel_switch'), 1.0, function() { + cartoonSnd.destroy(); + loadCartoon(); + }); + } + + // loadCartoon(); + } + + function loadCartoon() + { + cartoonSnd = FunkinSound.load(Paths.sound(getRandomFlashToon()), 1.0, false, true, true, function() { + anim.play("Boyfriend DJ watchin tv OG", true, false, 60); + }); + + // Fade out music to 40% volume over 1 second. + // This helps make the TV a bit more audible. + FlxG.sound.music.fadeOut(1.0, 0.1); + + // Play the cartoon at a random time between the start and 5 seconds from the end. + cartoonSnd.time = FlxG.random.float(0, Math.max(cartoonSnd.length - (5 * Constants.MS_PER_SEC), 0.0)); + } + + final cartoonList:Array = openfl.utils.Assets.list().filter(function(path) return path.startsWith("assets/sounds/cartoons/")); + + function getRandomFlashToon():String + { + var randomFile = FlxG.random.getObject(cartoonList); + + // Strip folder prefix + randomFile = randomFile.replace("assets/sounds/", ""); + // Strip file extension + randomFile = randomFile.substring(0, randomFile.length - 4); + + return randomFile; + } + + public function confirm():Void + { + currentState = Confirm; + } + + public function fistPump():Void + { + currentState = FistPumpIntro; + } + + public function pumpFist():Void + { + currentState = FistPump; + anim.play("Boyfriend DJ fist pump", true, false, 4); + } + + public function pumpFistBad():Void + { + currentState = FistPump; + anim.play("Boyfriend DJ loss reaction 1", true, false, 4); + } + + override public function getCurrentAnimation():String + { + if (this.anim == null || this.anim.curSymbol == null) return ""; + return this.anim.curSymbol.name; + } + + public function playFlashAnimation(id:String, ?Force:Bool = false, ?Reverse:Bool = false, ?Frame:Int = 0):Void + { + anim.play(id, Force, Reverse, Frame); + applyAnimOffset(); + } + + function applyAnimOffset() + { + var AnimName = getCurrentAnimation(); + var daOffset = playableCharData.getAnimationOffsets(AnimName); + if (daOffset != null) + { + var xValue = daOffset[0]; + var yValue = daOffset[1]; + if (AnimName == "Boyfriend DJ watchin tv OG") + { + xValue += offsetX; + yValue += offsetY; + } + + trace('Successfully applied offset: ' + xValue + ', ' + yValue); + offset.set(xValue, yValue); + } + else + { + trace('No offset found, defaulting to: 0, 0'); + offset.set(0, 0); + } + } + + public override function destroy():Void + { + super.destroy(); + + if (cartoonSnd != null) + { + cartoonSnd.destroy(); + cartoonSnd = null; + } + } +} + +enum DJBoyfriendState +{ + Intro; + Idle; + Confirm; + FistPumpIntro; + FistPump; + IdleEasterEgg; + Cartoon; +} diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 30863f2a96..6b342fb409 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -167,7 +167,7 @@ class FreeplayState extends MusicBeatSubState var curCapsule:SongMenuItem; var curPlaying:Bool = false; - var dj:DJBoyfriend; + var dj:FreeplayDJ; var ostName:FlxText; var albumRoll:AlbumRoll; @@ -211,6 +211,7 @@ class FreeplayState extends MusicBeatSubState { currentCharacterId = params?.character ?? Constants.DEFAULT_CHARACTER; currentCharacter = PlayerRegistry.instance.fetchEntry(currentCharacterId); + if (currentCharacter == null) throw 'Could not build Freeplay state for character: $currentCharacterId'; fromResultsParams = params?.fromResults; @@ -450,17 +451,16 @@ class FreeplayState extends MusicBeatSubState add(cardGlow); - dj = new DJBoyfriend(640, 366); - exitMovers.set([dj], - { - x: -dj.width * 1.6, - speed: 0.5 - }); - - // TODO: Replace this. - if (currentCharacterId == 'pico') dj.visible = false; - - add(dj); + if (currentCharacter?.getFreeplayDJData() != null) + { + dj = new FreeplayDJ(640, 366, currentCharacterId); + exitMovers.set([dj], + { + x: -dj.width * 1.6, + speed: 0.5 + }); + add(dj); + } bgDad = new FlxSprite(pinkBack.width * 0.74, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad')); bgDad.shader = new AngleMask(); diff --git a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx index 743345004e..282e35d7a6 100644 --- a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx +++ b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx @@ -77,6 +77,11 @@ class PlayableCharacter implements IRegistryEntry return false; } + public function getFreeplayDJData():PlayerFreeplayDJData + { + return _data.freeplayDJ; + } + /** * Returns whether this character is unlocked. */ diff --git a/source/funkin/util/tools/MapTools.hx b/source/funkin/util/tools/MapTools.hx index b98cb0adf9..807f0aebd9 100644 --- a/source/funkin/util/tools/MapTools.hx +++ b/source/funkin/util/tools/MapTools.hx @@ -14,6 +14,7 @@ class MapTools */ public static function size(map:Map):Int { + if (map == null) return 0; return map.keys().array().length; } @@ -22,6 +23,7 @@ class MapTools */ public static function values(map:Map):Array { + if (map == null) return []; return [for (i in map.iterator()) i]; } @@ -30,6 +32,7 @@ class MapTools */ public static function clone(map:Map):Map { + if (map == null) return null; return map.copy(); } @@ -76,6 +79,7 @@ class MapTools */ public static function keyValues(map:Map):Array { + if (map == null) return []; return map.keys().array(); } } From dd413d42ab5bdd63b18e2663f69d8182b032c76e Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 19 Jun 2024 23:47:11 -0400 Subject: [PATCH 029/469] more retro week 6 stuff --- assets | 2 +- source/funkin/effects/RetroCameraFade.hx | 106 ++++++++++++++++++ source/funkin/graphics/FunkinSprite.hx | 101 +++++++++++++++++ source/funkin/play/Countdown.hx | 19 +++- source/funkin/play/GameOverSubState.hx | 25 ++++- .../play/character/MultiSparrowCharacter.hx | 2 + .../funkin/play/character/PackerCharacter.hx | 2 + .../funkin/play/character/SparrowCharacter.hx | 2 + source/funkin/play/components/PopUpStuff.hx | 30 +++-- source/funkin/play/stage/Stage.hx | 4 + 10 files changed, 277 insertions(+), 16 deletions(-) create mode 100644 source/funkin/effects/RetroCameraFade.hx diff --git a/assets b/assets index 4b95075255..225e248f14 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 4b95075255baeaba3585fabff7052c257856fafe +Subproject commit 225e248f148a92500a6fe90e4f10e4cd2acee782 diff --git a/source/funkin/effects/RetroCameraFade.hx b/source/funkin/effects/RetroCameraFade.hx new file mode 100644 index 0000000000..d4c1da5efc --- /dev/null +++ b/source/funkin/effects/RetroCameraFade.hx @@ -0,0 +1,106 @@ +package funkin.effects; + +import flixel.util.FlxTimer; +import flixel.FlxCamera; +import openfl.filters.ColorMatrixFilter; + +class RetroCameraFade +{ + // im lazy, but we only use this for week 6 + // and also sorta yoinked for djflixel, lol ! + public static function fadeWhite(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void + { + var steps:Int = 0; + var stepsTotal:Int = camSteps; + + new FlxTimer().start(time / stepsTotal, _ -> { + var V:Float = (1 / stepsTotal) * steps; + if (steps == stepsTotal) V = 1; + + var matrix = [ + 1, 0, 0, 0, V * 255, + 0, 1, 0, 0, V * 255, + 0, 0, 1, 0, V * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrix)]; + steps++; + }, stepsTotal + 1); + } + + public static function fadeFromWhite(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void + { + var steps:Int = camSteps; + var stepsTotal:Int = camSteps; + + var matrixDerp = [ + 1, 0, 0, 0, 1.0 * 255, + 0, 1, 0, 0, 1.0 * 255, + 0, 0, 1, 0, 1.0 * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrixDerp)]; + + new FlxTimer().start(time / stepsTotal, _ -> { + var V:Float = (1 / stepsTotal) * steps; + if (steps == stepsTotal) V = 1; + + var matrix = [ + 1, 0, 0, 0, V * 255, + 0, 1, 0, 0, V * 255, + 0, 0, 1, 0, V * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrix)]; + steps--; + }, camSteps); + } + + public static function fadeToBlack(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void + { + var steps:Int = 0; + var stepsTotal:Int = camSteps; + + new FlxTimer().start(time / stepsTotal, _ -> { + var V:Float = (1 / stepsTotal) * steps; + if (steps == stepsTotal) V = 1; + + var matrix = [ + 1, 0, 0, 0, -V * 255, + 0, 1, 0, 0, -V * 255, + 0, 0, 1, 0, -V * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrix)]; + steps++; + }, camSteps); + } + + public static function fadeBlack(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void + { + var steps:Int = camSteps; + var stepsTotal:Int = camSteps; + + var matrixDerp = [ + 1, 0, 0, 0, -1.0 * 255, + 0, 1, 0, 0, -1.0 * 255, + 0, 0, 1, 0, -1.0 * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrixDerp)]; + + new FlxTimer().start(time / stepsTotal, _ -> { + var V:Float = (1 / stepsTotal) * steps; + if (steps == stepsTotal) V = 1; + + var matrix = [ + 1, 0, 0, 0, -V * 255, + 0, 1, 0, 0, -V * 255, + 0, 0, 1, 0, -V * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrix)]; + steps--; + }, camSteps + 1); + } +} diff --git a/source/funkin/graphics/FunkinSprite.hx b/source/funkin/graphics/FunkinSprite.hx index bfd2e80285..521553527c 100644 --- a/source/funkin/graphics/FunkinSprite.hx +++ b/source/funkin/graphics/FunkinSprite.hx @@ -7,6 +7,10 @@ import flixel.tweens.FlxTween; import openfl.display3D.textures.TextureBase; import funkin.graphics.framebuffer.FixedBitmapData; import openfl.display.BitmapData; +import flixel.math.FlxRect; +import flixel.math.FlxPoint; +import flixel.graphics.frames.FlxFrame; +import flixel.FlxCamera; /** * An FlxSprite with additional functionality. @@ -269,6 +273,103 @@ class FunkinSprite extends FlxSprite return result; } + @:access(flixel.FlxCamera) + override function getBoundingBox(camera:FlxCamera):FlxRect + { + getScreenPosition(_point, camera); + + _rect.set(_point.x, _point.y, width, height); + _rect = camera.transformRect(_rect); + + if (isPixelPerfectRender(camera)) + { + _rect.width = _rect.width / this.scale.x; + _rect.height = _rect.height / this.scale.y; + _rect.x = _rect.x / this.scale.x; + _rect.y = _rect.y / this.scale.y; + _rect.floor(); + _rect.x = _rect.x * this.scale.x; + _rect.y = _rect.y * this.scale.y; + _rect.width = _rect.width * this.scale.x; + _rect.height = _rect.height * this.scale.y; + } + + return _rect; + } + + /** + * Returns the screen position of this object. + * + * @param result Optional arg for the returning point + * @param camera The desired "screen" coordinate space. If `null`, `FlxG.camera` is used. + * @return The screen position of this object. + */ + public override function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint + { + if (result == null) result = FlxPoint.get(); + + if (camera == null) camera = FlxG.camera; + + result.set(x, y); + if (pixelPerfectPosition) + { + _rect.width = _rect.width / this.scale.x; + _rect.height = _rect.height / this.scale.y; + _rect.x = _rect.x / this.scale.x; + _rect.y = _rect.y / this.scale.y; + _rect.round(); + _rect.x = _rect.x * this.scale.x; + _rect.y = _rect.y * this.scale.y; + _rect.width = _rect.width * this.scale.x; + _rect.height = _rect.height * this.scale.y; + } + + return result.subtract(camera.scroll.x * scrollFactor.x, camera.scroll.y * scrollFactor.y); + } + + override function drawSimple(camera:FlxCamera):Void + { + getScreenPosition(_point, camera).subtractPoint(offset); + if (isPixelPerfectRender(camera)) + { + _point.x = _point.x / this.scale.x; + _point.y = _point.y / this.scale.y; + _point.round(); + + _point.x = _point.x * this.scale.x; + _point.y = _point.y * this.scale.y; + } + + _point.copyToFlash(_flashPoint); + camera.copyPixels(_frame, framePixels, _flashRect, _flashPoint, colorTransform, blend, antialiasing); + } + + override function drawComplex(camera:FlxCamera):Void + { + _frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, checkFlipX(), checkFlipY()); + _matrix.translate(-origin.x, -origin.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.round(_matrix.tx / this.scale.x) * this.scale.x; + _matrix.ty = Math.round(_matrix.ty / this.scale.y) * this.scale.y; + } + + camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shader); + } + public override function destroy():Void { frames = null; diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 10636afdf9..55c2a8992a 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -9,6 +9,7 @@ import funkin.modding.module.ModuleHandler; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEvent.CountdownScriptEvent; import flixel.util.FlxTimer; +import funkin.util.EaseUtil; import funkin.audio.FunkinSound; class Countdown @@ -117,7 +118,7 @@ class Countdown * * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event. */ - public static function pauseCountdown() + public static function pauseCountdown():Void { if (countdownTimer != null && !countdownTimer.finished) { @@ -130,7 +131,7 @@ class Countdown * * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event. */ - public static function resumeCountdown() + public static function resumeCountdown():Void { if (countdownTimer != null && !countdownTimer.finished) { @@ -143,7 +144,7 @@ class Countdown * * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStart event. */ - public static function stopCountdown() + public static function stopCountdown():Void { if (countdownTimer != null) { @@ -156,7 +157,7 @@ class Countdown /** * Stops the current countdown, then starts the song for you. */ - public static function skipCountdown() + public static function skipCountdown():Void { stopCountdown(); // This will trigger PlayState.startSong() @@ -185,8 +186,11 @@ class Countdown { var spritePath:String = null; + var fadeEase = FlxEase.cubeInOut; + if (isPixelStyle) { + fadeEase = EaseUtil.stepped(8); switch (index) { case TWO: @@ -227,7 +231,7 @@ class Countdown countdownSprite.screenCenter(); // Fade sprite in, then out, then destroy it. - FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.instance.beatLengthMs / 1000, + FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100}, Conductor.instance.beatLengthMs / 1000, { ease: FlxEase.cubeInOut, onComplete: function(twn:FlxTween) { @@ -235,6 +239,11 @@ class Countdown } }); + FlxTween.tween(countdownSprite, {alpha: 0}, Conductor.instance.beatLengthMs / 1000, + { + ease: fadeEase + }); + PlayState.instance.add(countdownSprite); } diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index c84d5b154a..1e1c91dc5f 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -16,6 +16,7 @@ import funkin.ui.MusicBeatSubState; import funkin.ui.story.StoryMenuState; import funkin.util.MathUtil; import openfl.utils.Assets; +import funkin.effects.RetroCameraFade; /** * A substate which renders over the PlayState when the player dies. @@ -331,9 +332,12 @@ class GameOverSubState extends MusicBeatSubState // After the animation finishes... new FlxTimer().start(0.7, function(tmr:FlxTimer) { // ...fade out the graphics. Then after that happens... - FlxG.camera.fade(FlxColor.BLACK, 2, false, function() { + + var resetPlaying = function(pixel:Bool = false) { // ...close the GameOverSubState. - FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true); + if (pixel) RetroCameraFade.fadeBlack(FlxG.camera, 10, 1); + else + FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true); PlayState.instance.needsReset = true; if (PlayState.instance.isMinimalMode || boyfriend == null) {} @@ -350,7 +354,22 @@ class GameOverSubState extends MusicBeatSubState // Close the substate. close(); - }); + }; + + if (musicSuffix == '-pixel') + { + RetroCameraFade.fadeToBlack(FlxG.camera, 10, 2); + new FlxTimer().start(2, _ -> { + FlxG.camera.filters = []; + resetPlaying(true); + }); + } + else + { + FlxG.camera.fade(FlxColor.BLACK, 2, false, function() { + resetPlaying(); + }); + } }); } } diff --git a/source/funkin/play/character/MultiSparrowCharacter.hx b/source/funkin/play/character/MultiSparrowCharacter.hx index 48c5afb58d..41c96fbfaa 100644 --- a/source/funkin/play/character/MultiSparrowCharacter.hx +++ b/source/funkin/play/character/MultiSparrowCharacter.hx @@ -41,6 +41,8 @@ class MultiSparrowCharacter extends BaseCharacter { this.isPixel = true; this.antialiasing = false; + pixelPerfectRender = true; + pixelPerfectPosition = true; } else { diff --git a/source/funkin/play/character/PackerCharacter.hx b/source/funkin/play/character/PackerCharacter.hx index 2bfac800ac..22edbe339d 100644 --- a/source/funkin/play/character/PackerCharacter.hx +++ b/source/funkin/play/character/PackerCharacter.hx @@ -43,6 +43,8 @@ class PackerCharacter extends BaseCharacter { this.isPixel = true; this.antialiasing = false; + pixelPerfectRender = true; + pixelPerfectPosition = true; } else { diff --git a/source/funkin/play/character/SparrowCharacter.hx b/source/funkin/play/character/SparrowCharacter.hx index a36aed84da..81d98b138b 100644 --- a/source/funkin/play/character/SparrowCharacter.hx +++ b/source/funkin/play/character/SparrowCharacter.hx @@ -46,6 +46,8 @@ class SparrowCharacter extends BaseCharacter { this.isPixel = true; this.antialiasing = false; + pixelPerfectRender = true; + pixelPerfectPosition = true; } else { diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index b7e206e977..1bdfd98a83 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -7,8 +7,9 @@ import flixel.util.FlxDirection; import funkin.graphics.FunkinSprite; import funkin.play.PlayState; import funkin.util.TimerUtil; +import funkin.util.EaseUtil; -class PopUpStuff extends FlxTypedGroup +class PopUpStuff extends FlxTypedGroup { public var offsets:Array = [0, 0]; @@ -17,7 +18,7 @@ class PopUpStuff extends FlxTypedGroup super(); } - public function displayRating(daRating:String) + public function displayRating(daRating:String):Void { var perfStart:Float = TimerUtil.start(); @@ -40,10 +41,15 @@ class PopUpStuff extends FlxTypedGroup add(rating); + var fadeEase = null; + if (PlayState.instance.currentStageId.startsWith('school')) { rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.7)); rating.antialiasing = false; + rating.pixelPerfectRender = true; + rating.pixelPerfectPosition = true; + fadeEase = EaseUtil.stepped(2); } else { @@ -61,7 +67,8 @@ class PopUpStuff extends FlxTypedGroup remove(rating, true); rating.destroy(); }, - startDelay: Conductor.instance.beatLengthMs * 0.001 + startDelay: Conductor.instance.beatLengthMs * 0.001, + ease: fadeEase }); trace('displayRating took: ${TimerUtil.seconds(perfStart)}'); @@ -92,10 +99,15 @@ class PopUpStuff extends FlxTypedGroup // add(comboSpr); + var fadeEase = null; + if (PlayState.instance.currentStageId.startsWith('school')) { - comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 0.7)); + comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 1)); comboSpr.antialiasing = false; + comboSpr.pixelPerfectRender = true; + comboSpr.pixelPerfectPosition = true; + fadeEase = EaseUtil.stepped(2); } else { @@ -110,7 +122,8 @@ class PopUpStuff extends FlxTypedGroup remove(comboSpr, true); comboSpr.destroy(); }, - startDelay: Conductor.instance.beatLengthMs * 0.001 + startDelay: Conductor.instance.beatLengthMs * 0.001, + ease: fadeEase }); var seperatedScore:Array = []; @@ -133,8 +146,10 @@ class PopUpStuff extends FlxTypedGroup if (PlayState.instance.currentStageId.startsWith('school')) { - numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 0.7)); + numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 1)); numScore.antialiasing = false; + numScore.pixelPerfectRender = true; + numScore.pixelPerfectPosition = true; } else { @@ -156,7 +171,8 @@ class PopUpStuff extends FlxTypedGroup remove(numScore, true); numScore.destroy(); }, - startDelay: Conductor.instance.beatLengthMs * 0.002 + startDelay: Conductor.instance.beatLengthMs * 0.002, + ease: fadeEase }); daLoop++; diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index 4f8ab4434b..f4e22e380f 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -249,6 +249,10 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements // If pixel, disable antialiasing. propSprite.antialiasing = !dataProp.isPixel; + // If pixel, we render it pixel perfect so there's less "mixels" + propSprite.pixelPerfectRender = dataProp.isPixel; + propSprite.pixelPerfectPosition = dataProp.isPixel; + propSprite.scrollFactor.x = dataProp.scroll[0]; propSprite.scrollFactor.y = dataProp.scroll[1]; From 73efd963b9e1a98c77fdb6a2a8f57517d08f16d1 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Thu, 20 Jun 2024 07:24:36 +0200 Subject: [PATCH 030/469] Fix crash after pressing F5 and coming back from stickers --- source/funkin/ui/freeplay/FreeplayState.hx | 2 +- source/funkin/ui/story/StoryMenuState.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 0caaf45915..949aa4bfef 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -214,7 +214,7 @@ class FreeplayState extends MusicBeatSubState prepForNewRank = true; } - if (stickers != null) + if (stickers?.members != null) { stickerSubState = stickers; } diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 06a83ab4db..7707850cea 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -113,7 +113,7 @@ class StoryMenuState extends MusicBeatState { super(); - if (stickers != null) + if (stickers?.members != null) { stickerSubState = stickers; } From 9b3a748f3768f2d742b922aeedb7e62ee9766e40 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 20 Jun 2024 16:17:53 -0400 Subject: [PATCH 031/469] Working Pico DJ --- assets | 2 +- source/funkin/Paths.hx | 11 +- .../funkin/data/freeplay/player/PlayerData.hx | 42 ++- source/funkin/play/scoring/Scoring.hx | 4 +- source/funkin/ui/freeplay/FreeplayDJ.hx | 14 +- source/funkin/ui/freeplay/FreeplayState.hx | 278 ++++++++++-------- .../freeplay/charselect/PlayableCharacter.hx | 5 + source/funkin/ui/mainmenu/MainMenuState.hx | 5 +- source/funkin/util/SortUtil.hx | 2 +- 9 files changed, 216 insertions(+), 147 deletions(-) diff --git a/assets b/assets index 6ec72f8aeb..8dd51cde0b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 6ec72f8aeb5ab77997dee4e2e98ae03f0ec347b8 +Subproject commit 8dd51cde0b9a3730abe9f97d0f50365c396ca784 diff --git a/source/funkin/Paths.hx b/source/funkin/Paths.hx index b0a97c4faf..285af7ca22 100644 --- a/source/funkin/Paths.hx +++ b/source/funkin/Paths.hx @@ -11,9 +11,16 @@ class Paths { static var currentLevel:Null = null; - public static function setCurrentLevel(name:String):Void + public static function setCurrentLevel(name:Null):Void { - currentLevel = name.toLowerCase(); + if (name == null) + { + currentLevel = null; + } + else + { + currentLevel = name.toLowerCase(); + } } public static function stripLibrary(path:String):String diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx index 10fc54b783..c461c9555d 100644 --- a/source/funkin/data/freeplay/player/PlayerData.hx +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -35,6 +35,7 @@ class PlayerData * Data for displaying this character in the Freeplay menu. * If null, display no DJ. */ + @:optional public var freeplayDJ:Null = null; /** @@ -73,9 +74,25 @@ class PlayerFreeplayDJData var assetPath:String; var animations:Array; + @:optional + @:default("BOYFRIEND") + var text1:String; + + @:optional + @:default("HOT BLOODED IN MORE WAYS THAN ONE") + var text2:String; + + @:optional + @:default("PROTECT YO NUTS") + var text3:String; + + @:jignored var animationMap:Map; + @:jignored + var prefixToOffsetsMap:Map>; + @:optional var cartoon:Null; @@ -87,11 +104,14 @@ class PlayerFreeplayDJData function mapAnimations() { if (animationMap == null) animationMap = new Map(); + if (prefixToOffsetsMap == null) prefixToOffsetsMap = new Map(); animationMap.clear(); + prefixToOffsetsMap.clear(); for (anim in animations) { animationMap.set(anim.name, anim); + prefixToOffsetsMap.set(anim.prefix, anim.offsets); } } @@ -100,6 +120,15 @@ class PlayerFreeplayDJData return Paths.animateAtlas(assetPath); } + public function getFreeplayDJText(index:Int):String { + switch (index) { + case 1: return text1; + case 2: return text2; + case 3: return text3; + default: return ''; + } + } + public function getAnimationPrefix(name:String):Null { if (animationMap.size() == 0) mapAnimations(); @@ -109,13 +138,16 @@ class PlayerFreeplayDJData return anim.prefix; } - public function getAnimationOffsets(name:String):Null> + public function getAnimationOffsetsByPrefix(?prefix:String):Array { - if (animationMap.size() == 0) mapAnimations(); + if (prefixToOffsetsMap.size() == 0) mapAnimations(); + if (prefix == null) return [0, 0]; + return prefixToOffsetsMap.get(prefix); + } - var anim = animationMap.get(name); - if (anim == null) return null; - return anim.offsets; + public function getAnimationOffsets(name:String):Array + { + return getAnimationOffsetsByPrefix(getAnimationPrefix(name)); } // TODO: These should really be frame labels, ehe. diff --git a/source/funkin/play/scoring/Scoring.hx b/source/funkin/play/scoring/Scoring.hx index dc2c406475..02e5750bcb 100644 --- a/source/funkin/play/scoring/Scoring.hx +++ b/source/funkin/play/scoring/Scoring.hx @@ -590,7 +590,7 @@ enum abstract ScoringRank(String) } } - public function getFreeplayRankIconAsset():Null + public function getFreeplayRankIconAsset():String { switch (abstract) { @@ -607,7 +607,7 @@ enum abstract ScoringRank(String) case SHIT: return 'LOSS'; default: - return null; + return 'LOSS'; } } diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index f9effe793c..72eddd0ca2 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -135,8 +135,12 @@ class FreeplayDJ extends FlxAtlasSprite timeIdling = 0; case Cartoon: var animPrefix = playableCharData.getAnimationPrefix('cartoon'); - if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true); - timeIdling = 0; + if (animPrefix == null) { + currentState = IdleEasterEgg; + } else { + if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true); + timeIdling = 0; + } default: // I shit myself. } @@ -324,7 +328,7 @@ class FreeplayDJ extends FlxAtlasSprite function applyAnimOffset() { var AnimName = getCurrentAnimation(); - var daOffset = playableCharData.getAnimationOffsets(AnimName); + var daOffset = playableCharData.getAnimationOffsetsByPrefix(AnimName); if (daOffset != null) { var xValue = daOffset[0]; @@ -335,12 +339,12 @@ class FreeplayDJ extends FlxAtlasSprite yValue += offsetY; } - trace('Successfully applied offset: ' + xValue + ', ' + yValue); + trace('Successfully applied offset ($AnimName): ' + xValue + ', ' + yValue); offset.set(xValue, yValue); } else { - trace('No offset found, defaulting to: 0, 0'); + trace('No offset found ($AnimName), defaulting to: 0, 0'); offset.set(0, 0); } } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 6b342fb409..06a090769d 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1,54 +1,55 @@ package funkin.ui.freeplay; -import funkin.graphics.adobeanimate.FlxAtlasSprite; import flixel.addons.transition.FlxTransitionableState; import flixel.addons.ui.FlxInputText; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.group.FlxGroup; -import funkin.graphics.shaders.GaussianBlurShader; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; import flixel.input.touch.FlxTouch; import flixel.math.FlxAngle; import flixel.math.FlxPoint; -import openfl.display.BlendMode; import flixel.system.debug.watch.Tracker.TrackerProfile; import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; +import flixel.tweens.misc.ShakeTween; import flixel.util.FlxColor; import flixel.util.FlxSpriteUtil; import flixel.util.FlxTimer; import funkin.audio.FunkinSound; -import funkin.data.story.level.LevelRegistry; -import funkin.data.song.SongRegistry; import funkin.data.freeplay.player.PlayerRegistry; +import funkin.data.song.SongRegistry; +import funkin.data.story.level.LevelRegistry; +import funkin.effects.IntervalShake; +import funkin.graphics.adobeanimate.FlxAtlasSprite; import funkin.graphics.FunkinCamera; import funkin.graphics.FunkinSprite; import funkin.graphics.shaders.AngleMask; +import funkin.graphics.shaders.GaussianBlurShader; import funkin.graphics.shaders.HSVShader; import funkin.graphics.shaders.PureColor; import funkin.graphics.shaders.StrokeShader; import funkin.input.Controls; import funkin.play.PlayStatePlaylist; +import funkin.play.scoring.Scoring; +import funkin.play.scoring.Scoring.ScoringRank; import funkin.play.song.Song; -import funkin.ui.story.Level; import funkin.save.Save; import funkin.save.Save.SaveScoreData; import funkin.ui.AtlasText; -import funkin.play.scoring.Scoring; -import funkin.play.scoring.Scoring.ScoringRank; +import funkin.ui.freeplay.charselect.PlayableCharacter; +import funkin.ui.freeplay.SongMenuItem.FreeplayRank; import funkin.ui.mainmenu.MainMenuState; import funkin.ui.MusicBeatSubState; +import funkin.ui.story.Level; import funkin.ui.transition.LoadingState; import funkin.ui.transition.StickerSubState; import funkin.util.MathUtil; +import funkin.util.SortUtil; import lime.utils.Assets; -import flixel.tweens.misc.ShakeTween; -import funkin.effects.IntervalShake; -import funkin.ui.freeplay.SongMenuItem.FreeplayRank; -import funkin.ui.freeplay.charselect.PlayableCharacter; +import openfl.display.BlendMode; /** * Parameters used to initialize the FreeplayState. @@ -94,6 +95,7 @@ typedef FromResultsParams = /** * The state for the freeplay menu, allowing the player to select any song to play. */ +@:nullSafety class FreeplayState extends MusicBeatSubState { // @@ -164,10 +166,9 @@ class FreeplayState extends MusicBeatSubState var grpSongs:FlxTypedGroup; var grpCapsules:FlxTypedGroup; - var curCapsule:SongMenuItem; var curPlaying:Bool = false; - var dj:FreeplayDJ; + var dj:Null = null; var ostName:FlxText; var albumRoll:AlbumRoll; @@ -175,7 +176,7 @@ class FreeplayState extends MusicBeatSubState var letterSort:LetterSort; var exitMovers:ExitMoverData = new Map(); - var stickerSubState:StickerSubState; + var stickerSubState:Null = null; public static var rememberedDifficulty:Null = Constants.DEFAULT_DIFFICULTY; public static var rememberedSongId:Null = 'tutorial'; @@ -210,8 +211,12 @@ class FreeplayState extends MusicBeatSubState public function new(?params:FreeplayStateParams, ?stickers:StickerSubState) { currentCharacterId = params?.character ?? Constants.DEFAULT_CHARACTER; - currentCharacter = PlayerRegistry.instance.fetchEntry(currentCharacterId); - if (currentCharacter == null) throw 'Could not build Freeplay state for character: $currentCharacterId'; + var fetchPlayableCharacter = function():PlayableCharacter { + var result = PlayerRegistry.instance.fetchEntry(params?.character ?? Constants.DEFAULT_CHARACTER); + if (result == null) throw 'No valid playable character with id ${params?.character}'; + return result; + }; + currentCharacter = fetchPlayableCharacter(); fromResultsParams = params?.fromResults; @@ -220,12 +225,54 @@ class FreeplayState extends MusicBeatSubState prepForNewRank = true; } + super(FlxColor.TRANSPARENT); + if (stickers != null) { stickerSubState = stickers; } - super(FlxColor.TRANSPARENT); + // We build a bunch of sprites BEFORE create() so we can guarantee they aren't null later on. + albumRoll = new AlbumRoll(); + fp = new FreeplayScore(460, 60, 7, 100); + cardGlow = new FlxSprite(-30, -30).loadGraphic(Paths.image('freeplay/cardGlow')); + confirmGlow = new FlxSprite(-30, 240).loadGraphic(Paths.image('freeplay/confirmGlow')); + confirmTextGlow = new FlxSprite(-8, 115).loadGraphic(Paths.image('freeplay/glowingText')); + rankCamera = new FunkinCamera('rankCamera', 0, 0, FlxG.width, FlxG.height); + funnyCam = new FunkinCamera('freeplayFunny', 0, 0, FlxG.width, FlxG.height); + funnyScroll = new BGScrollingText(0, 220, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, false, 60); + funnyScroll2 = new BGScrollingText(0, 335, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, false, 60); + grpCapsules = new FlxTypedGroup(); + grpDifficulties = new FlxTypedSpriteGroup(-300, 80); + letterSort = new LetterSort(400, 75); + grpSongs = new FlxTypedGroup(); + moreWays = new BGScrollingText(0, 160, currentCharacter.getFreeplayDJText(2), FlxG.width, true, 43); + moreWays2 = new BGScrollingText(0, 397, currentCharacter.getFreeplayDJText(2), FlxG.width, true, 43); + pinkBack = FunkinSprite.create('freeplay/pinkBack'); + rankBg = new FunkinSprite(0, 0); + rankVignette = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/rankVignette')); + sparks = new FlxSprite(0, 0); + sparksADD = new FlxSprite(0, 0); + txtCompletion = new AtlasText(1185, 87, '69', AtlasFont.FREEPLAY_CLEAR); + txtNuts = new BGScrollingText(0, 285, currentCharacter.getFreeplayDJText(3), FlxG.width / 2, true, 43); + + ostName = new FlxText(8, 8, FlxG.width - 8 - 8, 'OFFICIAL OST', 48); + + orangeBackShit = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFFEDA00); + + bgDad = new FlxSprite(pinkBack.width * 0.74, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad')); + alsoOrangeLOL = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFFFD400); + confirmGlow2 = new FlxSprite(confirmGlow.x, confirmGlow.y).loadGraphic(Paths.image('freeplay/confirmGlow2')); + funnyScroll3 = new BGScrollingText(0, orangeBackShit.y + 10, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, 60); + backingTextYeah = new FlxAtlasSprite(640, 370, Paths.animateAtlas("freeplay/backing-text-yeah"), + { + FrameRate: 24.0, + Reversed: false, + // ?OnComplete:Void -> Void, + ShowPivot: false, + Antialiasing: true, + ScrollFactor: new FlxPoint(1, 1), + }); } override function create():Void @@ -236,12 +283,6 @@ class FreeplayState extends MusicBeatSubState FlxTransitionableState.skipNextTransIn = true; - // dedicated camera for the state so we don't need to fuk around with camera scrolls from the mainmenu / elsewhere - funnyCam = new FunkinCamera('freeplayFunny', 0, 0, FlxG.width, FlxG.height); - funnyCam.bgColor = FlxColor.TRANSPARENT; - FlxG.cameras.add(funnyCam, false); - this.cameras = [funnyCam]; - if (stickerSubState != null) { this.persistentUpdate = true; @@ -277,7 +318,7 @@ class FreeplayState extends MusicBeatSubState // programmatically adds the songs via LevelRegistry and SongRegistry for (levelId in LevelRegistry.instance.listSortedLevelIds()) { - var level:Level = LevelRegistry.instance.fetchEntry(levelId); + var level:Null = LevelRegistry.instance.fetchEntry(levelId); if (level == null) { @@ -287,7 +328,7 @@ class FreeplayState extends MusicBeatSubState for (songId in level.getSongs()) { - var song:Song = SongRegistry.instance.fetchEntry(songId); + var song:Null = SongRegistry.instance.fetchEntry(songId); if (song == null) { @@ -319,17 +360,14 @@ class FreeplayState extends MusicBeatSubState trace(FlxG.camera.initialZoom); trace(FlxCamera.defaultZoom); - pinkBack = FunkinSprite.create('freeplay/pinkBack'); pinkBack.color = 0xFFFFD4E9; // sets it to pink! pinkBack.x -= pinkBack.width; FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut}); add(pinkBack); - orangeBackShit = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFFEDA00); add(orangeBackShit); - alsoOrangeLOL = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFFFD400); add(alsoOrangeLOL); exitMovers.set([pinkBack, orangeBackShit, alsoOrangeLOL], @@ -344,15 +382,11 @@ class FreeplayState extends MusicBeatSubState orangeBackShit.visible = false; alsoOrangeLOL.visible = false; - confirmTextGlow = new FlxSprite(-8, 115).loadGraphic(Paths.image('freeplay/glowingText')); confirmTextGlow.blend = BlendMode.ADD; confirmTextGlow.visible = false; - confirmGlow = new FlxSprite(-30, 240).loadGraphic(Paths.image('freeplay/confirmGlow')); confirmGlow.blend = BlendMode.ADD; - confirmGlow2 = new FlxSprite(confirmGlow.x, confirmGlow.y).loadGraphic(Paths.image('freeplay/confirmGlow2')); - confirmGlow.visible = false; confirmGlow2.visible = false; @@ -367,7 +401,6 @@ class FreeplayState extends MusicBeatSubState FlxG.debugger.addTrackerProfile(new TrackerProfile(BGScrollingText, ['x', 'y', 'speed', 'size'])); - moreWays = new BGScrollingText(0, 160, 'HOT BLOODED IN MORE WAYS THAN ONE', FlxG.width, true, 43); moreWays.funnyColor = 0xFFFFF383; moreWays.speed = 6.8; grpTxtScrolls.add(moreWays); @@ -378,7 +411,6 @@ class FreeplayState extends MusicBeatSubState speed: 0.4, }); - funnyScroll = new BGScrollingText(0, 220, 'BOYFRIEND', FlxG.width / 2, false, 60); funnyScroll.funnyColor = 0xFFFF9963; funnyScroll.speed = -3.8; grpTxtScrolls.add(funnyScroll); @@ -391,7 +423,6 @@ class FreeplayState extends MusicBeatSubState wait: 0 }); - txtNuts = new BGScrollingText(0, 285, 'PROTECT YO NUTS', FlxG.width / 2, true, 43); txtNuts.speed = 3.5; grpTxtScrolls.add(txtNuts); exitMovers.set([txtNuts], @@ -400,7 +431,6 @@ class FreeplayState extends MusicBeatSubState speed: 0.4, }); - funnyScroll2 = new BGScrollingText(0, 335, 'BOYFRIEND', FlxG.width / 2, false, 60); funnyScroll2.funnyColor = 0xFFFF9963; funnyScroll2.speed = -3.8; grpTxtScrolls.add(funnyScroll2); @@ -411,7 +441,6 @@ class FreeplayState extends MusicBeatSubState speed: 0.5, }); - moreWays2 = new BGScrollingText(0, 397, 'HOT BLOODED IN MORE WAYS THAN ONE', FlxG.width, true, 43); moreWays2.funnyColor = 0xFFFFF383; moreWays2.speed = 6.8; grpTxtScrolls.add(moreWays2); @@ -422,7 +451,6 @@ class FreeplayState extends MusicBeatSubState speed: 0.4 }); - funnyScroll3 = new BGScrollingText(0, orangeBackShit.y + 10, 'BOYFRIEND', FlxG.width / 2, 60); funnyScroll3.funnyColor = 0xFFFEA400; funnyScroll3.speed = -3.8; grpTxtScrolls.add(funnyScroll3); @@ -433,19 +461,8 @@ class FreeplayState extends MusicBeatSubState speed: 0.3 }); - backingTextYeah = new FlxAtlasSprite(640, 370, Paths.animateAtlas("freeplay/backing-text-yeah"), - { - FrameRate: 24.0, - Reversed: false, - // ?OnComplete:Void -> Void, - ShowPivot: false, - Antialiasing: true, - ScrollFactor: new FlxPoint(1, 1), - }); - add(backingTextYeah); - cardGlow = new FlxSprite(-30, -30).loadGraphic(Paths.image('freeplay/cardGlow')); cardGlow.blend = BlendMode.ADD; cardGlow.visible = false; @@ -462,7 +479,6 @@ class FreeplayState extends MusicBeatSubState add(dj); } - bgDad = new FlxSprite(pinkBack.width * 0.74, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad')); bgDad.shader = new AngleMask(); bgDad.visible = false; @@ -488,17 +504,13 @@ class FreeplayState extends MusicBeatSubState blackOverlayBullshitLOLXD.shader = bgDad.shader; - rankBg = new FunkinSprite(0, 0); rankBg.makeSolidColor(FlxG.width, FlxG.height, 0xD3000000); add(rankBg); - grpSongs = new FlxTypedGroup(); add(grpSongs); - grpCapsules = new FlxTypedGroup(); add(grpCapsules); - grpDifficulties = new FlxTypedSpriteGroup(-300, 80); add(grpDifficulties); exitMovers.set([grpDifficulties], @@ -525,7 +537,6 @@ class FreeplayState extends MusicBeatSubState if (diffSprite.difficultyId == currentDifficulty) diffSprite.visible = true; } - albumRoll = new AlbumRoll(); albumRoll.albumId = null; add(albumRoll); @@ -540,7 +551,6 @@ class FreeplayState extends MusicBeatSubState fnfFreeplay.font = 'VCR OSD Mono'; fnfFreeplay.visible = false; - ostName = new FlxText(8, 8, FlxG.width - 8 - 8, 'OFFICIAL OST', 48); ostName.font = 'VCR OSD Mono'; ostName.alignment = RIGHT; ostName.visible = false; @@ -572,7 +582,6 @@ class FreeplayState extends MusicBeatSubState tmr.time = FlxG.random.float(20, 60); }, 0); - fp = new FreeplayScore(460, 60, 7, 100); fp.visible = false; add(fp); @@ -580,11 +589,9 @@ class FreeplayState extends MusicBeatSubState clearBoxSprite.visible = false; add(clearBoxSprite); - txtCompletion = new AtlasText(1185, 87, '69', AtlasFont.FREEPLAY_CLEAR); txtCompletion.visible = false; add(txtCompletion); - letterSort = new LetterSort(400, 75); add(letterSort); letterSort.visible = false; @@ -632,7 +639,8 @@ class FreeplayState extends MusicBeatSubState // be careful not to "add()" things in here unless it's to a group that's already added to the state // otherwise it won't be properly attatched to funnyCamera (relavent code should be at the bottom of create()) - dj.onIntroDone.add(function() { + var onDJIntroDone = function() { + // when boyfriend hits dat shiii albumRoll.playIntro(); @@ -679,20 +687,24 @@ class FreeplayState extends MusicBeatSubState cardGlow.visible = true; FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.45, {ease: FlxEase.sineOut}); - if (prepForNewRank) + if (prepForNewRank && fromResultsParams != null) { rankAnimStart(fromResultsParams); } - }); + }; + + if (dj != null) { + dj.onIntroDone.add(onDJIntroDone); + } else { + onDJIntroDone(); + } generateSongList(null, false); // dedicated camera for the state so we don't need to fuk around with camera scrolls from the mainmenu / elsewhere - funnyCam = new FunkinCamera('freeplayFunny', 0, 0, FlxG.width, FlxG.height); funnyCam.bgColor = FlxColor.TRANSPARENT; FlxG.cameras.add(funnyCam, false); - rankVignette = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/rankVignette')); rankVignette.scale.set(2, 2); rankVignette.updateHitbox(); rankVignette.blend = BlendMode.ADD; @@ -704,7 +716,6 @@ class FreeplayState extends MusicBeatSubState bs.cameras = [funnyCam]; }); - rankCamera = new FunkinCamera('rankCamera', 0, 0, FlxG.width, FlxG.height); rankCamera.bgColor = FlxColor.TRANSPARENT; FlxG.cameras.add(rankCamera, false); rankBg.cameras = [rankCamera]; @@ -716,8 +727,8 @@ class FreeplayState extends MusicBeatSubState } } - var currentFilter:SongFilter = null; - var currentFilteredSongs:Array = []; + var currentFilter:Null = null; + var currentFilteredSongs:Array> = []; /** * Given the current filter, rebuild the current song list. @@ -728,7 +739,7 @@ class FreeplayState extends MusicBeatSubState */ public function generateSongList(filterStuff:Null, force:Bool = false, onlyIfChanged:Bool = true):Void { - var tempSongs:Array = songs; + var tempSongs:Array> = songs; // Remember just the difficulty because it's important for song sorting. if (rememberedDifficulty != null) @@ -790,11 +801,12 @@ class FreeplayState extends MusicBeatSubState for (i in 0...tempSongs.length) { - if (tempSongs[i] == null) continue; + var tempSong = tempSongs[i]; + if (tempSong == null) continue; var funnyMenu:SongMenuItem = grpCapsules.recycle(SongMenuItem); - funnyMenu.init(FlxG.width, 0, tempSongs[i]); + funnyMenu.init(FlxG.width, 0, tempSong); funnyMenu.onConfirm = function() { capsuleOnConfirmDefault(funnyMenu); }; @@ -803,8 +815,8 @@ class FreeplayState extends MusicBeatSubState funnyMenu.ID = i; funnyMenu.capsule.alpha = 0.5; funnyMenu.songText.visible = false; - funnyMenu.favIcon.visible = tempSongs[i].isFav; - funnyMenu.favIconBlurred.visible = tempSongs[i].isFav; + funnyMenu.favIcon.visible = tempSong.isFav; + funnyMenu.favIconBlurred.visible = tempSong.isFav; funnyMenu.hsvShader = hsvShader; funnyMenu.newText.animation.curAnim.curFrame = 45 - ((i * 4) % 45); @@ -828,13 +840,10 @@ class FreeplayState extends MusicBeatSubState * @param songFilter The filter to apply * @return Array */ - public function sortSongs(songsToFilter:Array, songFilter:SongFilter):Array + public function sortSongs(songsToFilter:Array>, songFilter:SongFilter):Array> { - var filterAlphabetically = function(a:FreeplaySongData, b:FreeplaySongData):Int { - if (a?.songName.toLowerCase() < b?.songName.toLowerCase()) return -1; - else if (a?.songName.toLowerCase() > b?.songName.toLowerCase()) return 1; - else - return 0; + var filterAlphabetically = function(a:Null, b:Null):Int { + return SortUtil.alphabetically(a?.songName ?? '', b?.songName ?? ''); }; switch (songFilter.filterType) @@ -858,7 +867,7 @@ class FreeplayState extends MusicBeatSubState songsToFilter = songsToFilter.filter(str -> { if (str == null) return true; // Random - return str.songName.toLowerCase().startsWith(songFilter.filterData); + return str.songName.toLowerCase().startsWith(songFilter.filterData ?? ''); }); case ALL: // no filter! @@ -880,32 +889,28 @@ class FreeplayState extends MusicBeatSubState var sparks:FlxSprite; var sparksADD:FlxSprite; - function rankAnimStart(fromResults:Null):Void + function rankAnimStart(fromResults:FromResultsParams):Void { busy = true; grpCapsules.members[curSelected].sparkle.alpha = 0; // grpCapsules.members[curSelected].forcePosition(); - if (fromResults != null) - { - rememberedSongId = fromResults.songId; - rememberedDifficulty = fromResults.difficultyId; - changeSelection(); - changeDiff(); - } + rememberedSongId = fromResults.songId; + rememberedDifficulty = fromResults.difficultyId; + changeSelection(); + changeDiff(); - dj.fistPump(); + if (dj != null) dj.fistPump(); // rankCamera.fade(FlxColor.BLACK, 0.5, true); rankCamera.fade(0xFF000000, 0.5, true, null, true); if (FlxG.sound.music != null) FlxG.sound.music.volume = 0; rankBg.alpha = 1; - if (fromResults?.oldRank != null) + if (fromResults.oldRank != null) { grpCapsules.members[curSelected].fakeRanking.rank = fromResults.oldRank; grpCapsules.members[curSelected].fakeBlurredRanking.rank = fromResults.oldRank; - sparks = new FlxSprite(0, 0); sparks.frames = Paths.getSparrowAtlas('freeplay/sparks'); sparks.animation.addByPrefix('sparks', 'sparks', 24, false); sparks.visible = false; @@ -915,7 +920,6 @@ class FreeplayState extends MusicBeatSubState add(sparks); sparks.cameras = [rankCamera]; - sparksADD = new FlxSprite(0, 0); sparksADD.visible = false; sparksADD.frames = Paths.getSparrowAtlas('freeplay/sparksadd'); sparksADD.animation.addByPrefix('sparks add', 'sparks add', 24, false); @@ -980,14 +984,14 @@ class FreeplayState extends MusicBeatSubState grpCapsules.members[curSelected].ranking.scale.set(20, 20); grpCapsules.members[curSelected].blurredRanking.scale.set(20, 20); - if (fromResults?.newRank != null) + if (fromResults != null && fromResults.newRank != null) { grpCapsules.members[curSelected].ranking.animation.play(fromResults.newRank.getFreeplayRankIconAsset(), true); } FlxTween.tween(grpCapsules.members[curSelected].ranking, {"scale.x": 1, "scale.y": 1}, 0.1); - if (fromResults?.newRank != null) + if (fromResults != null && fromResults.newRank != null) { grpCapsules.members[curSelected].blurredRanking.animation.play(fromResults.newRank.getFreeplayRankIconAsset(), true); } @@ -1078,11 +1082,11 @@ class FreeplayState extends MusicBeatSubState if (fromResultsParams?.newRank == SHIT) { - dj.pumpFistBad(); + if (dj != null) dj.pumpFistBad(); } else { - dj.pumpFist(); + if (dj != null) dj.pumpFist(); } rankCamera.zoom = 0.8; @@ -1196,7 +1200,13 @@ class FreeplayState extends MusicBeatSubState #if debug if (FlxG.keys.justPressed.T) { - rankAnimStart(fromResultsParams); + rankAnimStart(fromResultsParams ?? + { + playRankAnim: true, + newRank: PERFECT_GOLD, + songId: "tutorial", + difficultyId: "hard" + }); } if (FlxG.keys.justPressed.P) @@ -1427,7 +1437,7 @@ class FreeplayState extends MusicBeatSubState } spamTimer += elapsed; - dj.resetAFKTimer(); + if (dj != null) dj.resetAFKTimer(); } else { @@ -1438,31 +1448,31 @@ class FreeplayState extends MusicBeatSubState #if !html5 if (FlxG.mouse.wheel != 0) { - dj.resetAFKTimer(); + if (dj != null) dj.resetAFKTimer(); changeSelection(-Math.round(FlxG.mouse.wheel)); } #else if (FlxG.mouse.wheel < 0) { - dj.resetAFKTimer(); + if (dj != null) dj.resetAFKTimer(); changeSelection(-Math.round(FlxG.mouse.wheel / 8)); } else if (FlxG.mouse.wheel > 0) { - dj.resetAFKTimer(); + if (dj != null) dj.resetAFKTimer(); changeSelection(-Math.round(FlxG.mouse.wheel / 8)); } #end if (controls.UI_LEFT_P) { - dj.resetAFKTimer(); + if (dj != null) dj.resetAFKTimer(); changeDiff(-1); generateSongList(currentFilter, true); } if (controls.UI_RIGHT_P) { - dj.resetAFKTimer(); + if (dj != null) dj.resetAFKTimer(); changeDiff(1); generateSongList(currentFilter, true); } @@ -1472,7 +1482,7 @@ class FreeplayState extends MusicBeatSubState busy = true; FlxTween.globalManager.clear(); FlxTimer.globalManager.clear(); - dj.onIntroDone.removeAll(); + if (dj != null) dj.onIntroDone.removeAll(); FunkinSound.playOnce(Paths.sound('cancelMenu')); @@ -1498,7 +1508,8 @@ class FreeplayState extends MusicBeatSubState for (grpSpr in exitMovers.keys()) { - var moveData:MoveData = exitMovers.get(grpSpr); + var moveData:Null = exitMovers.get(grpSpr); + if (moveData == null) continue; for (spr in grpSpr) { @@ -1506,14 +1517,14 @@ class FreeplayState extends MusicBeatSubState var funnyMoveShit:MoveData = moveData; - if (moveData.x == null) funnyMoveShit.x = spr.x; - if (moveData.y == null) funnyMoveShit.y = spr.y; - if (moveData.speed == null) funnyMoveShit.speed = 0.2; - if (moveData.wait == null) funnyMoveShit.wait = 0; + var moveDataX = funnyMoveShit.x ?? spr.x; + var moveDataY = funnyMoveShit.y ?? spr.y; + var moveDataSpeed = funnyMoveShit.speed ?? 0.2; + var moveDataWait = funnyMoveShit.wait ?? 0; - FlxTween.tween(spr, {x: funnyMoveShit.x, y: funnyMoveShit.y}, funnyMoveShit.speed, {ease: FlxEase.expoIn}); + FlxTween.tween(spr, {x: moveDataX, y: moveDataY}, moveDataSpeed, {ease: FlxEase.expoIn}); - longestTimer = Math.max(longestTimer, funnyMoveShit.speed + funnyMoveShit.wait); + longestTimer = Math.max(longestTimer, moveDataSpeed + moveDataWait); } } @@ -1586,19 +1597,18 @@ class FreeplayState extends MusicBeatSubState var daSong:Null = grpCapsules.members[curSelected].songData; if (daSong != null) { - // TODO: Make this actually be the variation you're focused on. We don't need to fetch the song metadata just to calculate it. - var targetSong:Song = SongRegistry.instance.fetchEntry(grpCapsules.members[curSelected].songData.songId); + var targetSong:Null = SongRegistry.instance.fetchEntry(daSong.songId); if (targetSong == null) { - FlxG.log.warn('WARN: could not find song with id (${grpCapsules.members[curSelected].songData.songId})'); + FlxG.log.warn('WARN: could not find song with id (${daSong.songId})'); return; } - var targetVariation:String = targetSong.getFirstValidVariation(currentDifficulty); + var targetVariation:String = targetSong.getFirstValidVariation(currentDifficulty) ?? ''; // TODO: This line of code makes me sad, but you can't really fix it without a breaking migration. var suffixedDifficulty = (targetVariation != Constants.DEFAULT_VARIATION && targetVariation != 'erect') ? '$currentDifficulty-${targetVariation}' : currentDifficulty; - var songScore:SaveScoreData = Save.instance.getSongScore(grpCapsules.members[curSelected].songData.songId, suffixedDifficulty); + var songScore:Null = Save.instance.getSongScore(daSong.songId, suffixedDifficulty); intendedScore = songScore?.score ?? 0; intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes); rememberedDifficulty = currentDifficulty; @@ -1660,7 +1670,7 @@ class FreeplayState extends MusicBeatSubState } // Set the album graphic and play the animation if relevant. - var newAlbumId:String = daSong?.albumId; + var newAlbumId:Null = daSong?.albumId; if (albumRoll.albumId != newAlbumId) { albumRoll.albumId = newAlbumId; @@ -1698,7 +1708,7 @@ class FreeplayState extends MusicBeatSubState }); trace('Available songs: ${availableSongCapsules.map(function(cap) { - return cap.songData.songName; + return cap?.songData?.songName; })}'); if (availableSongCapsules.length == 0) @@ -1727,17 +1737,20 @@ class FreeplayState extends MusicBeatSubState PlayStatePlaylist.isStoryMode = false; - var targetSong:Song = SongRegistry.instance.fetchEntry(cap.songData.songId); - if (targetSong == null) + var targetSongId:String = cap?.songData?.songId ?? 'unknown'; + var targetSongNullable:Null = SongRegistry.instance.fetchEntry(targetSongId); + if (targetSongNullable == null) { - FlxG.log.warn('WARN: could not find song with id (${cap.songData.songId})'); + FlxG.log.warn('WARN: could not find song with id (${targetSongId})'); return; } + var targetSong:Song = targetSongNullable; var targetDifficultyId:String = currentDifficulty; - var targetVariation:String = targetSong.getFirstValidVariation(targetDifficultyId, currentCharacter); - PlayStatePlaylist.campaignId = cap.songData.levelId; + var targetVariation:Null = targetSong.getFirstValidVariation(targetDifficultyId, currentCharacter); + var targetLevelId:Null = cap?.songData?.levelId; + PlayStatePlaylist.campaignId = targetLevelId ?? null; - var targetDifficulty:SongDifficulty = targetSong.getDifficulty(targetDifficultyId, targetVariation); + var targetDifficulty:Null = targetSong.getDifficulty(targetDifficultyId, targetVariation); if (targetDifficulty == null) { FlxG.log.warn('WARN: could not find difficulty with id (${targetDifficultyId})'); @@ -1759,7 +1772,7 @@ class FreeplayState extends MusicBeatSubState // Visual and audio effects. FunkinSound.playOnce(Paths.sound('confirmMenu')); - dj.confirm(); + if (dj != null) dj.confirm(); grpCapsules.members[curSelected].forcePosition(); grpCapsules.members[curSelected].songText.flickerText(); @@ -1801,7 +1814,7 @@ class FreeplayState extends MusicBeatSubState new FlxTimer().start(1, function(tmr:FlxTimer) { FunkinSound.emptyPartialQueue(); - Paths.setCurrentLevel(cap.songData.levelId); + Paths.setCurrentLevel(cap?.songData?.levelId); LoadingState.loadPlayState( { targetSong: targetSong, @@ -1856,7 +1869,7 @@ class FreeplayState extends MusicBeatSubState var daSongCapsule:SongMenuItem = grpCapsules.members[curSelected]; if (daSongCapsule.songData != null) { - var songScore:SaveScoreData = Save.instance.getSongScore(daSongCapsule.songData.songId, currentDifficulty); + var songScore:Null = Save.instance.getSongScore(daSongCapsule.songData.songId, currentDifficulty); intendedScore = songScore?.score ?? 0; intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes); diffIdsCurrent = daSongCapsule.songData.songDifficulties; @@ -1906,7 +1919,10 @@ class FreeplayState extends MusicBeatSubState } else { - var previewSong:Null = SongRegistry.instance.fetchEntry(daSongCapsule.songData.songId); + var previewSongId:Null = daSongCapsule?.songData?.songId; + if (previewSongId == null) return; + + var previewSong:Null = SongRegistry.instance.fetchEntry(previewSongId); var songDifficulty = previewSong?.getDifficulty(currentDifficulty, previewSong?.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST); var baseInstrumentalId:String = songDifficulty?.characters?.instrumental ?? ''; @@ -1924,7 +1940,9 @@ class FreeplayState extends MusicBeatSubState instSuffix = (instSuffix != '') ? '-$instSuffix' : ''; - FunkinSound.playMusic(daSongCapsule.songData.songId, + trace('Attempting to play partial preview: ${previewSongId}:${instSuffix}'); + + FunkinSound.playMusic(previewSongId, { startingVolume: 0.0, overrideExisting: true, @@ -1951,7 +1969,7 @@ class FreeplayState extends MusicBeatSubState public static function build(?params:FreeplayStateParams, ?stickers:StickerSubState):MusicBeatState { var result:MainMenuState; - if (params?.fromResults?.playRankAnim) result = new MainMenuState(true); + if (params?.fromResults?.playRankAnim ?? false) result = new MainMenuState(true); else result = new MainMenuState(false); diff --git a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx index 282e35d7a6..6d7b96c581 100644 --- a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx +++ b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx @@ -82,6 +82,11 @@ class PlayableCharacter implements IRegistryEntry return _data.freeplayDJ; } + public function getFreeplayDJText(index:Int):String + { + return _data.freeplayDJ.getFreeplayDJText(index); + } + /** * Returns whether this character is unlocked. */ diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index d09536eea2..56ffc9a270 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -117,7 +117,10 @@ class MainMenuState extends MusicBeatState FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - openSubState(new FreeplayState()); + openSubState(new FreeplayState( + { + character: FlxG.keys.pressed.SHIFT ? 'pico' : 'bf', + })); }); #if CAN_OPEN_LINKS diff --git a/source/funkin/util/SortUtil.hx b/source/funkin/util/SortUtil.hx index c5ac175be7..f6d3721f0a 100644 --- a/source/funkin/util/SortUtil.hx +++ b/source/funkin/util/SortUtil.hx @@ -97,7 +97,7 @@ class SortUtil * @param b The second string to compare. * @return 1 if `a` comes before `b`, -1 if `b` comes before `a`, 0 if they are equal */ - public static function alphabetically(a:String, b:String):Int + public static function alphabetically(?a:String, ?b:String):Int { a = a.toUpperCase(); b = b.toUpperCase(); From dd7a894b518e6cf6467ad73ee15e277c244217a3 Mon Sep 17 00:00:00 2001 From: Pixel <146671762+JVNpixels@users.noreply.github.com> Date: Fri, 21 Jun 2024 08:42:51 -0700 Subject: [PATCH 032/469] Update StoryMenuState.hx --- source/funkin/ui/story/StoryMenuState.hx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 06a83ab4db..8623d8f397 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -336,6 +336,24 @@ class StoryMenuState extends MusicBeatState changeDifficulty(0); } + #if !html5 + if (FlxG.mouse.wheel != 0) + { + changeLevel(-Math.round(FlxG.mouse.wheel)); + } + #else + if (FlxG.mouse.wheel < 0) + { + changeLevel(-Math.round(FlxG.mouse.wheel / 8)); + } + else if (FlxG.mouse.wheel > 0) + { + changeLevel(-Math.round(FlxG.mouse.wheel / 8)); + } + #end + + // HTML and NON HTML builds mouse fix. + // TODO: Querying UI_RIGHT_P (justPressed) after UI_RIGHT always returns false. Fix it! if (controls.UI_RIGHT_P) { From 0589624d117d52f3ec6224af26b4041205db612c Mon Sep 17 00:00:00 2001 From: Pixel <146671762+JVNpixels@users.noreply.github.com> Date: Fri, 21 Jun 2024 22:46:07 -0700 Subject: [PATCH 033/469] Update source/funkin/ui/story/StoryMenuState.hx Co-authored-by: gamerbross <55158797+gamerbross@users.noreply.github.com> --- source/funkin/ui/story/StoryMenuState.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 8623d8f397..5f21a9a889 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -352,7 +352,6 @@ class StoryMenuState extends MusicBeatState } #end - // HTML and NON HTML builds mouse fix. // TODO: Querying UI_RIGHT_P (justPressed) after UI_RIGHT always returns false. Fix it! if (controls.UI_RIGHT_P) From be38ae6c0003ac63aea38f490b66e07b10f0bd6e Mon Sep 17 00:00:00 2001 From: Pixel <146671762+JVNpixels@users.noreply.github.com> Date: Sat, 22 Jun 2024 19:23:40 -0700 Subject: [PATCH 034/469] Extra newline removal. Removes the extra line in code. --- source/funkin/ui/story/StoryMenuState.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 5f21a9a889..90786b3f62 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -352,7 +352,6 @@ class StoryMenuState extends MusicBeatState } #end - // TODO: Querying UI_RIGHT_P (justPressed) after UI_RIGHT always returns false. Fix it! if (controls.UI_RIGHT_P) { From ef2885ed0e0b68ddf2e534eef1b5f6269c6700d2 Mon Sep 17 00:00:00 2001 From: AbnormalPoof Date: Mon, 24 Jun 2024 03:55:59 -0500 Subject: [PATCH 035/469] video cutscene autopause magic --- source/funkin/play/PlayState.hx | 16 ++++++++++++++-- source/funkin/play/cutscene/VideoCutscene.hx | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index f55cef3888..de84928826 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1301,12 +1301,18 @@ class PlayState extends MusicBeatSubState super.closeSubState(); } - #if discord_rpc /** * Function called when the game window gains focus. */ public override function onFocus():Void { + if (VideoCutscene.isPlaying() && FlxG.autoPause && isGamePaused) VideoCutscene.pauseVideo(); + #if html5 + else + VideoCutscene.resumeVideo(); + #end + + #if discord_rpc if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause) { if (Conductor.instance.songPosition > 0.0) DiscordClient.changePresence(detailsText, currentSong.song @@ -1318,6 +1324,7 @@ class PlayState extends MusicBeatSubState else DiscordClient.changePresence(detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); } + #end super.onFocus(); } @@ -1327,12 +1334,17 @@ class PlayState extends MusicBeatSubState */ public override function onFocusLost():Void { + #if html5 + if (FlxG.autoPause) VideoCutscene.pauseVideo(); + #end + + #if discord_rpc if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause) DiscordClient.changePresence(detailsPausedText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); + #end super.onFocusLost(); } - #end /** * Removes any references to the current stage, then clears the stage cache, diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index 01a492a77a..abbcd4f540 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -145,7 +145,7 @@ class VideoCutscene { vid.zIndex = 0; vid.bitmap.onEndReached.add(finishVideo.bind(0.5)); - vid.autoPause = false; + vid.autoPause = FlxG.autoPause; vid.cameras = [PlayState.instance.camCutscene]; From aa24dc4aa03b1fc91ea6af000f9d2d87dc755e7a Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 13:34:33 -0400 Subject: [PATCH 036/469] remove bopper trace --- source/funkin/play/stage/Bopper.hx | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index 262aff7bca..11fb9e4999 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -200,12 +200,10 @@ class Bopper extends StageProp implements IPlayStateScriptedClass { if (hasDanced) { - trace('DanceRight (alternate)'); playAnimation('danceRight$idleSuffix', forceRestart); } else { - trace('DanceLeft (alternate)'); playAnimation('danceLeft$idleSuffix', forceRestart); } hasDanced = !hasDanced; From aa3e3eb9b65dc26f8a2332cdb3f03050fa3e459b Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 14:13:30 -0400 Subject: [PATCH 037/469] fix: bf v pose playing during a gameover --- source/funkin/play/GameOverSubState.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index c84d5b154a..e8568aac62 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -144,6 +144,7 @@ class GameOverSubState extends MusicBeatSubState else { boyfriend = PlayState.instance.currentStage.getBoyfriend(true); + boyfriend.canPlayOtherAnims = true; boyfriend.isDead = true; add(boyfriend); boyfriend.resetCharacter(); From 81cada675c99359c461da88193564ba22b32bbba Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 12 Jun 2024 20:31:27 -0400 Subject: [PATCH 038/469] fix for release, no bf printing directly to stdout! --- source/funkin/util/logging/AnsiTrace.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/util/logging/AnsiTrace.hx b/source/funkin/util/logging/AnsiTrace.hx index 9fdc19e1be..2c18d494d2 100644 --- a/source/funkin/util/logging/AnsiTrace.hx +++ b/source/funkin/util/logging/AnsiTrace.hx @@ -51,7 +51,7 @@ class AnsiTrace public static function traceBF() { - #if sys + #if (sys && debug) if (colorSupported) { for (line in ansiBF) From d44275c2c7050ed207cb031328f0d9966b812c85 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sun, 16 Jun 2024 18:34:10 -0400 Subject: [PATCH 039/469] Fix a bug where songs with no notes would crash the results screen. --- source/funkin/play/ResultState.hx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index 48fb3b04e9..a2c5f7e623 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -464,7 +464,9 @@ class ResultState extends MusicBeatSubState { bgFlash.visible = true; FlxTween.tween(bgFlash, {alpha: 0}, 5 / 24); - var clearPercentFloat = (params.scoreData.tallies.sick + params.scoreData.tallies.good) / params.scoreData.tallies.totalNotes * 100; + // NOTE: Only divide if totalNotes > 0 to prevent divide-by-zero errors. + var clearPercentFloat = params.scoreData.tallies.totalNotes == 0 ? 0.0 : (params.scoreData.tallies.sick + + params.scoreData.tallies.good) / params.scoreData.tallies.totalNotes * 100; clearPercentTarget = Math.floor(clearPercentFloat); // Prevent off-by-one errors. From 983d241e47845e97f2e059358c1a31b0ff5d18e3 Mon Sep 17 00:00:00 2001 From: Karim Akra <144803230+KarimAkra@users.noreply.github.com> Date: Sun, 9 Jun 2024 12:47:17 +0300 Subject: [PATCH 040/469] remove the library strip --- source/funkin/audio/FunkinSound.hx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index c70f195d2f..11b713f4db 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -491,8 +491,10 @@ class FunkinSound extends FlxSound implements ICloneable var promise:lime.app.Promise> = new lime.app.Promise>(); // split the path and get only after first : - // we are bypassing the openfl/lime asset library fuss + // we are bypassing the openfl/lime asset library fuss on web only + #if web path = Paths.stripLibrary(path); + #end var soundRequest = FlxPartialSound.partialLoadFromFile(path, start, end); From 4e65c49e0e1a0f7c0bf8edb3c2c5a7d55a5828ef Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 17 Jun 2024 12:22:49 -0400 Subject: [PATCH 041/469] Get rid of a warning about missing BPM info --- source/funkin/ui/freeplay/FreeplayState.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 0caaf45915..68c63efc45 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1888,6 +1888,7 @@ class FreeplayState extends MusicBeatSubState startingVolume: 0.0, overrideExisting: true, restartTrack: false, + mapTimeChanges: false, // The music metadata is not alongside the audio file so this won't work. pathsFunction: INST, suffix: potentiallyErect, partialParams: From 0f972cb11c1d4bdf3e376f09a1229436dcb3fe47 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 15 Jun 2024 06:33:30 -0400 Subject: [PATCH 042/469] submoduel --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 2e1594ee4c..15a816fb83 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 2e1594ee4c04c7148628bae471bdd061c9deb6b7 +Subproject commit 15a816fb833555c22be92bd408eaf147ed4c64be From 92917e17c689a6f43b0d7230e019d5cd5c0a63bc Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 15 Jun 2024 06:53:41 -0400 Subject: [PATCH 043/469] submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 15a816fb83..01a285b38e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 15a816fb833555c22be92bd408eaf147ed4c64be +Subproject commit 01a285b38ecf10218153a1d40a2e4ea5b2132d7b From 93aa02868eb9af3ca1701aeb1467a997e7cf90a0 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 15 Jun 2024 06:55:08 -0400 Subject: [PATCH 044/469] submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 01a285b38e..4b95075255 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 01a285b38ecf10218153a1d40a2e4ea5b2132d7b +Subproject commit 4b95075255baeaba3585fabff7052c257856fafe From de6ba0c741854adc8ce6cb394cebc13f77c0876b Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 14:32:40 -0400 Subject: [PATCH 045/469] flxpartialsound hmm update --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index 68e0c5cb03..9daba99025 100644 --- a/hmm.json +++ b/hmm.json @@ -44,7 +44,7 @@ "name": "FlxPartialSound", "type": "git", "dir": null, - "ref": "f986332ba5ab02abd386ce662578baf04904604a", + "ref": "a1eab7b9bf507b87200a3341719054fe427f3b15", "url": "https://github.com/FunkinCrew/FlxPartialSound.git" }, { From 47b7aa702a0b0cccf803632d81829db6040e962c Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 14:46:09 -0400 Subject: [PATCH 046/469] submod --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index be5e0aaa81..452c0089b7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "assets"] path = assets - url = https://github.com/FunkinCrew/funkin.assets + url = https://github.com/FunkinCrew/Funkin-Assets-secret [submodule "art"] path = art - url = https://github.com/FunkinCrew/funkin.art + url = https://github.com/FunkinCrew/Funkin-Art-secret From a5442b407ea09c4fccf0d6366841f05355164a3b Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 21:53:56 -0400 Subject: [PATCH 047/469] haxeui --- hmm.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hmm.json b/hmm.json index 9daba99025..278541e1fd 100644 --- a/hmm.json +++ b/hmm.json @@ -75,14 +75,14 @@ "name": "haxeui-core", "type": "git", "dir": null, - "ref": "0212d8fdfcafeb5f0d5a41e1ddba8ff21d0e183b", + "ref": "5dc4c933bdc029f6139a47962e3b8c754060f210", "url": "https://github.com/haxeui/haxeui-core" }, { "name": "haxeui-flixel", "type": "git", "dir": null, - "ref": "63a906a6148958dbfde8c7b48d90b0693767fd95", + "ref": "57c1604d6b5174839d7e0e012a4dd5dcbfc129da", "url": "https://github.com/haxeui/haxeui-flixel" }, { From bdbc1364973543cb2186f67420a7420021df1165 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 23:43:34 -0400 Subject: [PATCH 048/469] fix the camera not panning when opening then closing the debug menu during transition --- source/funkin/ui/mainmenu/MainMenuState.hx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index d09536eea2..2eba406d9b 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -153,6 +153,9 @@ class MainMenuState extends MusicBeatState resetCamStuff(); + // reset camera when debug menu is closed + subStateClosed.add(_ -> resetCamStuff(false)); + subStateOpened.add(sub -> { if (Type.getClass(sub) == FreeplayState) { @@ -182,10 +185,11 @@ class MainMenuState extends MusicBeatState }); } - function resetCamStuff():Void + function resetCamStuff(?snap:Bool = true):Void { FlxG.camera.follow(camFollow, null, 0.06); - FlxG.camera.snapToTarget(); + + if (snap) FlxG.camera.snapToTarget(); } function createMenuItem(name:String, atlas:String, callback:Void->Void, fireInstantly:Bool = false):Void @@ -344,8 +348,6 @@ class MainMenuState extends MusicBeatState persistentUpdate = false; FlxG.state.openSubState(new DebugMenuSubState()); - // reset camera when debug menu is closed - subStateClosed.addOnce(_ -> resetCamStuff()); } #end From da834dab8d243b0da3b99b48f07b1a436333586f Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 16:26:42 -0400 Subject: [PATCH 049/469] fix for heart icons being on the wrong frames --- source/funkin/ui/freeplay/SongMenuItem.hx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx index 7708b3bcf9..65a5d2e2d0 100644 --- a/source/funkin/ui/freeplay/SongMenuItem.hx +++ b/source/funkin/ui/freeplay/SongMenuItem.hx @@ -213,6 +213,7 @@ class SongMenuItem extends FlxSpriteGroup favIconBlurred.frames = Paths.getSparrowAtlas('freeplay/favHeart'); favIconBlurred.animation.addByPrefix('fav', 'favorite heart', 24, false); favIconBlurred.animation.play('fav'); + favIconBlurred.setGraphicSize(50, 50); favIconBlurred.blend = BlendMode.ADD; favIconBlurred.shader = new GaussianBlurShader(1.2); @@ -516,6 +517,9 @@ class SongMenuItem extends FlxSpriteGroup updateDifficultyRating(songData?.difficultyRating ?? 0); updateScoringRank(songData?.scoringRank); newText.visible = songData?.isNew; + favIcon.animation.curAnim.curFrame = favIcon.animation.curAnim.numFrames - 1; + favIconBlurred.animation.curAnim.curFrame = favIconBlurred.animation.curAnim.numFrames - 1; + // Update opacity, offsets, etc. updateSelected(); From 458e51c327dcd7162ae1a39742e36c38cebaf61b Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 21:53:56 -0400 Subject: [PATCH 050/469] haxeui --- hmm.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hmm.json b/hmm.json index 9daba99025..278541e1fd 100644 --- a/hmm.json +++ b/hmm.json @@ -75,14 +75,14 @@ "name": "haxeui-core", "type": "git", "dir": null, - "ref": "0212d8fdfcafeb5f0d5a41e1ddba8ff21d0e183b", + "ref": "5dc4c933bdc029f6139a47962e3b8c754060f210", "url": "https://github.com/haxeui/haxeui-core" }, { "name": "haxeui-flixel", "type": "git", "dir": null, - "ref": "63a906a6148958dbfde8c7b48d90b0693767fd95", + "ref": "57c1604d6b5174839d7e0e012a4dd5dcbfc129da", "url": "https://github.com/haxeui/haxeui-flixel" }, { From 9b4f9db73e2f65be52e4129579fd0f8f73837a83 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 22:01:44 -0400 Subject: [PATCH 051/469] update thx related haxelibs --- hmm.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hmm.json b/hmm.json index 278541e1fd..db00b878cc 100644 --- a/hmm.json +++ b/hmm.json @@ -174,15 +174,15 @@ "name": "thx.core", "type": "git", "dir": null, - "ref": "22605ff44f01971d599641790d6bae4869f7d9f4", - "url": "https://github.com/FunkinCrew/thx.core" + "ref": "6240b6e136f7490d9298edbe8c1891374bd7cdf2", + "url": "https://github.com/fponticelli/thx.core" }, { "name": "thx.semver", "type": "git", "dir": null, - "ref": "cf8d213589a2c7ce4a59b0fdba9e8ff36bc029fa", - "url": "https://github.com/FunkinCrew/thx.semver" + "ref": "bdb191fe7cf745c02a980749906dbf22719e200b", + "url": "https://github.com/fponticelli/thx.semver" } ] } From 39c3af58f24fb01b4c48db0864f46993e8d6dd38 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 14:13:30 -0400 Subject: [PATCH 052/469] fix: bf v pose playing during a gameover --- source/funkin/play/GameOverSubState.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index c84d5b154a..e8568aac62 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -144,6 +144,7 @@ class GameOverSubState extends MusicBeatSubState else { boyfriend = PlayState.instance.currentStage.getBoyfriend(true); + boyfriend.canPlayOtherAnims = true; boyfriend.isDead = true; add(boyfriend); boyfriend.resetCharacter(); From 57514302616b8033604e452c506bde9c419f2ff2 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 23:43:34 -0400 Subject: [PATCH 053/469] fix the camera not panning when opening then closing the debug menu during transition --- source/funkin/ui/mainmenu/MainMenuState.hx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index d09536eea2..2eba406d9b 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -153,6 +153,9 @@ class MainMenuState extends MusicBeatState resetCamStuff(); + // reset camera when debug menu is closed + subStateClosed.add(_ -> resetCamStuff(false)); + subStateOpened.add(sub -> { if (Type.getClass(sub) == FreeplayState) { @@ -182,10 +185,11 @@ class MainMenuState extends MusicBeatState }); } - function resetCamStuff():Void + function resetCamStuff(?snap:Bool = true):Void { FlxG.camera.follow(camFollow, null, 0.06); - FlxG.camera.snapToTarget(); + + if (snap) FlxG.camera.snapToTarget(); } function createMenuItem(name:String, atlas:String, callback:Void->Void, fireInstantly:Bool = false):Void @@ -344,8 +348,6 @@ class MainMenuState extends MusicBeatState persistentUpdate = false; FlxG.state.openSubState(new DebugMenuSubState()); - // reset camera when debug menu is closed - subStateClosed.addOnce(_ -> resetCamStuff()); } #end From ab2f949d7e4fd2011c4d228fc147e5ac70b2f2d9 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 16:26:42 -0400 Subject: [PATCH 054/469] fix for heart icons being on the wrong frames --- source/funkin/ui/freeplay/SongMenuItem.hx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx index 7708b3bcf9..65a5d2e2d0 100644 --- a/source/funkin/ui/freeplay/SongMenuItem.hx +++ b/source/funkin/ui/freeplay/SongMenuItem.hx @@ -213,6 +213,7 @@ class SongMenuItem extends FlxSpriteGroup favIconBlurred.frames = Paths.getSparrowAtlas('freeplay/favHeart'); favIconBlurred.animation.addByPrefix('fav', 'favorite heart', 24, false); favIconBlurred.animation.play('fav'); + favIconBlurred.setGraphicSize(50, 50); favIconBlurred.blend = BlendMode.ADD; favIconBlurred.shader = new GaussianBlurShader(1.2); @@ -516,6 +517,9 @@ class SongMenuItem extends FlxSpriteGroup updateDifficultyRating(songData?.difficultyRating ?? 0); updateScoringRank(songData?.scoringRank); newText.visible = songData?.isNew; + favIcon.animation.curAnim.curFrame = favIcon.animation.curAnim.numFrames - 1; + favIconBlurred.animation.curAnim.curFrame = favIconBlurred.animation.curAnim.numFrames - 1; + // Update opacity, offsets, etc. updateSelected(); From 1757532097efb747e5e64366095da6ef5e12ca30 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 19 Jun 2024 23:47:11 -0400 Subject: [PATCH 055/469] more retro week 6 stuff --- assets | 2 +- source/funkin/effects/RetroCameraFade.hx | 106 ++++++++++++++++++ source/funkin/graphics/FunkinSprite.hx | 101 +++++++++++++++++ source/funkin/play/Countdown.hx | 19 +++- source/funkin/play/GameOverSubState.hx | 25 ++++- .../play/character/MultiSparrowCharacter.hx | 2 + .../funkin/play/character/PackerCharacter.hx | 2 + .../funkin/play/character/SparrowCharacter.hx | 2 + source/funkin/play/components/PopUpStuff.hx | 30 +++-- source/funkin/play/stage/Stage.hx | 4 + 10 files changed, 277 insertions(+), 16 deletions(-) create mode 100644 source/funkin/effects/RetroCameraFade.hx diff --git a/assets b/assets index 4b95075255..225e248f14 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 4b95075255baeaba3585fabff7052c257856fafe +Subproject commit 225e248f148a92500a6fe90e4f10e4cd2acee782 diff --git a/source/funkin/effects/RetroCameraFade.hx b/source/funkin/effects/RetroCameraFade.hx new file mode 100644 index 0000000000..d4c1da5efc --- /dev/null +++ b/source/funkin/effects/RetroCameraFade.hx @@ -0,0 +1,106 @@ +package funkin.effects; + +import flixel.util.FlxTimer; +import flixel.FlxCamera; +import openfl.filters.ColorMatrixFilter; + +class RetroCameraFade +{ + // im lazy, but we only use this for week 6 + // and also sorta yoinked for djflixel, lol ! + public static function fadeWhite(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void + { + var steps:Int = 0; + var stepsTotal:Int = camSteps; + + new FlxTimer().start(time / stepsTotal, _ -> { + var V:Float = (1 / stepsTotal) * steps; + if (steps == stepsTotal) V = 1; + + var matrix = [ + 1, 0, 0, 0, V * 255, + 0, 1, 0, 0, V * 255, + 0, 0, 1, 0, V * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrix)]; + steps++; + }, stepsTotal + 1); + } + + public static function fadeFromWhite(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void + { + var steps:Int = camSteps; + var stepsTotal:Int = camSteps; + + var matrixDerp = [ + 1, 0, 0, 0, 1.0 * 255, + 0, 1, 0, 0, 1.0 * 255, + 0, 0, 1, 0, 1.0 * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrixDerp)]; + + new FlxTimer().start(time / stepsTotal, _ -> { + var V:Float = (1 / stepsTotal) * steps; + if (steps == stepsTotal) V = 1; + + var matrix = [ + 1, 0, 0, 0, V * 255, + 0, 1, 0, 0, V * 255, + 0, 0, 1, 0, V * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrix)]; + steps--; + }, camSteps); + } + + public static function fadeToBlack(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void + { + var steps:Int = 0; + var stepsTotal:Int = camSteps; + + new FlxTimer().start(time / stepsTotal, _ -> { + var V:Float = (1 / stepsTotal) * steps; + if (steps == stepsTotal) V = 1; + + var matrix = [ + 1, 0, 0, 0, -V * 255, + 0, 1, 0, 0, -V * 255, + 0, 0, 1, 0, -V * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrix)]; + steps++; + }, camSteps); + } + + public static function fadeBlack(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void + { + var steps:Int = camSteps; + var stepsTotal:Int = camSteps; + + var matrixDerp = [ + 1, 0, 0, 0, -1.0 * 255, + 0, 1, 0, 0, -1.0 * 255, + 0, 0, 1, 0, -1.0 * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrixDerp)]; + + new FlxTimer().start(time / stepsTotal, _ -> { + var V:Float = (1 / stepsTotal) * steps; + if (steps == stepsTotal) V = 1; + + var matrix = [ + 1, 0, 0, 0, -V * 255, + 0, 1, 0, 0, -V * 255, + 0, 0, 1, 0, -V * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrix)]; + steps--; + }, camSteps + 1); + } +} diff --git a/source/funkin/graphics/FunkinSprite.hx b/source/funkin/graphics/FunkinSprite.hx index bfd2e80285..521553527c 100644 --- a/source/funkin/graphics/FunkinSprite.hx +++ b/source/funkin/graphics/FunkinSprite.hx @@ -7,6 +7,10 @@ import flixel.tweens.FlxTween; import openfl.display3D.textures.TextureBase; import funkin.graphics.framebuffer.FixedBitmapData; import openfl.display.BitmapData; +import flixel.math.FlxRect; +import flixel.math.FlxPoint; +import flixel.graphics.frames.FlxFrame; +import flixel.FlxCamera; /** * An FlxSprite with additional functionality. @@ -269,6 +273,103 @@ class FunkinSprite extends FlxSprite return result; } + @:access(flixel.FlxCamera) + override function getBoundingBox(camera:FlxCamera):FlxRect + { + getScreenPosition(_point, camera); + + _rect.set(_point.x, _point.y, width, height); + _rect = camera.transformRect(_rect); + + if (isPixelPerfectRender(camera)) + { + _rect.width = _rect.width / this.scale.x; + _rect.height = _rect.height / this.scale.y; + _rect.x = _rect.x / this.scale.x; + _rect.y = _rect.y / this.scale.y; + _rect.floor(); + _rect.x = _rect.x * this.scale.x; + _rect.y = _rect.y * this.scale.y; + _rect.width = _rect.width * this.scale.x; + _rect.height = _rect.height * this.scale.y; + } + + return _rect; + } + + /** + * Returns the screen position of this object. + * + * @param result Optional arg for the returning point + * @param camera The desired "screen" coordinate space. If `null`, `FlxG.camera` is used. + * @return The screen position of this object. + */ + public override function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint + { + if (result == null) result = FlxPoint.get(); + + if (camera == null) camera = FlxG.camera; + + result.set(x, y); + if (pixelPerfectPosition) + { + _rect.width = _rect.width / this.scale.x; + _rect.height = _rect.height / this.scale.y; + _rect.x = _rect.x / this.scale.x; + _rect.y = _rect.y / this.scale.y; + _rect.round(); + _rect.x = _rect.x * this.scale.x; + _rect.y = _rect.y * this.scale.y; + _rect.width = _rect.width * this.scale.x; + _rect.height = _rect.height * this.scale.y; + } + + return result.subtract(camera.scroll.x * scrollFactor.x, camera.scroll.y * scrollFactor.y); + } + + override function drawSimple(camera:FlxCamera):Void + { + getScreenPosition(_point, camera).subtractPoint(offset); + if (isPixelPerfectRender(camera)) + { + _point.x = _point.x / this.scale.x; + _point.y = _point.y / this.scale.y; + _point.round(); + + _point.x = _point.x * this.scale.x; + _point.y = _point.y * this.scale.y; + } + + _point.copyToFlash(_flashPoint); + camera.copyPixels(_frame, framePixels, _flashRect, _flashPoint, colorTransform, blend, antialiasing); + } + + override function drawComplex(camera:FlxCamera):Void + { + _frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, checkFlipX(), checkFlipY()); + _matrix.translate(-origin.x, -origin.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.round(_matrix.tx / this.scale.x) * this.scale.x; + _matrix.ty = Math.round(_matrix.ty / this.scale.y) * this.scale.y; + } + + camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shader); + } + public override function destroy():Void { frames = null; diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 10636afdf9..55c2a8992a 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -9,6 +9,7 @@ import funkin.modding.module.ModuleHandler; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEvent.CountdownScriptEvent; import flixel.util.FlxTimer; +import funkin.util.EaseUtil; import funkin.audio.FunkinSound; class Countdown @@ -117,7 +118,7 @@ class Countdown * * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event. */ - public static function pauseCountdown() + public static function pauseCountdown():Void { if (countdownTimer != null && !countdownTimer.finished) { @@ -130,7 +131,7 @@ class Countdown * * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event. */ - public static function resumeCountdown() + public static function resumeCountdown():Void { if (countdownTimer != null && !countdownTimer.finished) { @@ -143,7 +144,7 @@ class Countdown * * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStart event. */ - public static function stopCountdown() + public static function stopCountdown():Void { if (countdownTimer != null) { @@ -156,7 +157,7 @@ class Countdown /** * Stops the current countdown, then starts the song for you. */ - public static function skipCountdown() + public static function skipCountdown():Void { stopCountdown(); // This will trigger PlayState.startSong() @@ -185,8 +186,11 @@ class Countdown { var spritePath:String = null; + var fadeEase = FlxEase.cubeInOut; + if (isPixelStyle) { + fadeEase = EaseUtil.stepped(8); switch (index) { case TWO: @@ -227,7 +231,7 @@ class Countdown countdownSprite.screenCenter(); // Fade sprite in, then out, then destroy it. - FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.instance.beatLengthMs / 1000, + FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100}, Conductor.instance.beatLengthMs / 1000, { ease: FlxEase.cubeInOut, onComplete: function(twn:FlxTween) { @@ -235,6 +239,11 @@ class Countdown } }); + FlxTween.tween(countdownSprite, {alpha: 0}, Conductor.instance.beatLengthMs / 1000, + { + ease: fadeEase + }); + PlayState.instance.add(countdownSprite); } diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index e8568aac62..6eb954c0fa 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -16,6 +16,7 @@ import funkin.ui.MusicBeatSubState; import funkin.ui.story.StoryMenuState; import funkin.util.MathUtil; import openfl.utils.Assets; +import funkin.effects.RetroCameraFade; /** * A substate which renders over the PlayState when the player dies. @@ -332,9 +333,12 @@ class GameOverSubState extends MusicBeatSubState // After the animation finishes... new FlxTimer().start(0.7, function(tmr:FlxTimer) { // ...fade out the graphics. Then after that happens... - FlxG.camera.fade(FlxColor.BLACK, 2, false, function() { + + var resetPlaying = function(pixel:Bool = false) { // ...close the GameOverSubState. - FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true); + if (pixel) RetroCameraFade.fadeBlack(FlxG.camera, 10, 1); + else + FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true); PlayState.instance.needsReset = true; if (PlayState.instance.isMinimalMode || boyfriend == null) {} @@ -351,7 +355,22 @@ class GameOverSubState extends MusicBeatSubState // Close the substate. close(); - }); + }; + + if (musicSuffix == '-pixel') + { + RetroCameraFade.fadeToBlack(FlxG.camera, 10, 2); + new FlxTimer().start(2, _ -> { + FlxG.camera.filters = []; + resetPlaying(true); + }); + } + else + { + FlxG.camera.fade(FlxColor.BLACK, 2, false, function() { + resetPlaying(); + }); + } }); } } diff --git a/source/funkin/play/character/MultiSparrowCharacter.hx b/source/funkin/play/character/MultiSparrowCharacter.hx index 48c5afb58d..41c96fbfaa 100644 --- a/source/funkin/play/character/MultiSparrowCharacter.hx +++ b/source/funkin/play/character/MultiSparrowCharacter.hx @@ -41,6 +41,8 @@ class MultiSparrowCharacter extends BaseCharacter { this.isPixel = true; this.antialiasing = false; + pixelPerfectRender = true; + pixelPerfectPosition = true; } else { diff --git a/source/funkin/play/character/PackerCharacter.hx b/source/funkin/play/character/PackerCharacter.hx index 2bfac800ac..22edbe339d 100644 --- a/source/funkin/play/character/PackerCharacter.hx +++ b/source/funkin/play/character/PackerCharacter.hx @@ -43,6 +43,8 @@ class PackerCharacter extends BaseCharacter { this.isPixel = true; this.antialiasing = false; + pixelPerfectRender = true; + pixelPerfectPosition = true; } else { diff --git a/source/funkin/play/character/SparrowCharacter.hx b/source/funkin/play/character/SparrowCharacter.hx index a36aed84da..81d98b138b 100644 --- a/source/funkin/play/character/SparrowCharacter.hx +++ b/source/funkin/play/character/SparrowCharacter.hx @@ -46,6 +46,8 @@ class SparrowCharacter extends BaseCharacter { this.isPixel = true; this.antialiasing = false; + pixelPerfectRender = true; + pixelPerfectPosition = true; } else { diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index b7e206e977..1bdfd98a83 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -7,8 +7,9 @@ import flixel.util.FlxDirection; import funkin.graphics.FunkinSprite; import funkin.play.PlayState; import funkin.util.TimerUtil; +import funkin.util.EaseUtil; -class PopUpStuff extends FlxTypedGroup +class PopUpStuff extends FlxTypedGroup { public var offsets:Array = [0, 0]; @@ -17,7 +18,7 @@ class PopUpStuff extends FlxTypedGroup super(); } - public function displayRating(daRating:String) + public function displayRating(daRating:String):Void { var perfStart:Float = TimerUtil.start(); @@ -40,10 +41,15 @@ class PopUpStuff extends FlxTypedGroup add(rating); + var fadeEase = null; + if (PlayState.instance.currentStageId.startsWith('school')) { rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.7)); rating.antialiasing = false; + rating.pixelPerfectRender = true; + rating.pixelPerfectPosition = true; + fadeEase = EaseUtil.stepped(2); } else { @@ -61,7 +67,8 @@ class PopUpStuff extends FlxTypedGroup remove(rating, true); rating.destroy(); }, - startDelay: Conductor.instance.beatLengthMs * 0.001 + startDelay: Conductor.instance.beatLengthMs * 0.001, + ease: fadeEase }); trace('displayRating took: ${TimerUtil.seconds(perfStart)}'); @@ -92,10 +99,15 @@ class PopUpStuff extends FlxTypedGroup // add(comboSpr); + var fadeEase = null; + if (PlayState.instance.currentStageId.startsWith('school')) { - comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 0.7)); + comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 1)); comboSpr.antialiasing = false; + comboSpr.pixelPerfectRender = true; + comboSpr.pixelPerfectPosition = true; + fadeEase = EaseUtil.stepped(2); } else { @@ -110,7 +122,8 @@ class PopUpStuff extends FlxTypedGroup remove(comboSpr, true); comboSpr.destroy(); }, - startDelay: Conductor.instance.beatLengthMs * 0.001 + startDelay: Conductor.instance.beatLengthMs * 0.001, + ease: fadeEase }); var seperatedScore:Array = []; @@ -133,8 +146,10 @@ class PopUpStuff extends FlxTypedGroup if (PlayState.instance.currentStageId.startsWith('school')) { - numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 0.7)); + numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 1)); numScore.antialiasing = false; + numScore.pixelPerfectRender = true; + numScore.pixelPerfectPosition = true; } else { @@ -156,7 +171,8 @@ class PopUpStuff extends FlxTypedGroup remove(numScore, true); numScore.destroy(); }, - startDelay: Conductor.instance.beatLengthMs * 0.002 + startDelay: Conductor.instance.beatLengthMs * 0.002, + ease: fadeEase }); daLoop++; diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index 4f8ab4434b..f4e22e380f 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -249,6 +249,10 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements // If pixel, disable antialiasing. propSprite.antialiasing = !dataProp.isPixel; + // If pixel, we render it pixel perfect so there's less "mixels" + propSprite.pixelPerfectRender = dataProp.isPixel; + propSprite.pixelPerfectPosition = dataProp.isPixel; + propSprite.scrollFactor.x = dataProp.scroll[0]; propSprite.scrollFactor.y = dataProp.scroll[1]; From 8a144352a030d24f78eea554bb5ee89daaeff815 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 13:34:33 -0400 Subject: [PATCH 056/469] remove bopper trace --- source/funkin/play/stage/Bopper.hx | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index 262aff7bca..11fb9e4999 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -200,12 +200,10 @@ class Bopper extends StageProp implements IPlayStateScriptedClass { if (hasDanced) { - trace('DanceRight (alternate)'); playAnimation('danceRight$idleSuffix', forceRestart); } else { - trace('DanceLeft (alternate)'); playAnimation('danceLeft$idleSuffix', forceRestart); } hasDanced = !hasDanced; From e7dd724be6e0f95324ffbdbb1d19e4a5075619da Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 25 Jun 2024 13:56:42 -0400 Subject: [PATCH 057/469] assets --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 225e248f14..4b95075255 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 225e248f148a92500a6fe90e4f10e4cd2acee782 +Subproject commit 4b95075255baeaba3585fabff7052c257856fafe From ef7612d1c25921f92f076058bc460ccae3bccac5 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 22:05:16 -0400 Subject: [PATCH 058/469] discord RPC haxelib --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index db00b878cc..bedfd74471 100644 --- a/hmm.json +++ b/hmm.json @@ -5,7 +5,7 @@ "type": "git", "dir": null, "ref": "2d83fa863ef0c1eace5f1cf67c3ac315d1a3a8a5", - "url": "https://github.com/Aidan63/linc_discord-rpc" + "url": "https://github.com/FunkinCrew/linc_discord-rpc" }, { "name": "flixel", From e7f39ea08183eb0f2a2a532e2736ebfbfebd4706 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 22:07:01 -0400 Subject: [PATCH 059/469] hxp hmm update --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index bedfd74471..8eaf242122 100644 --- a/hmm.json +++ b/hmm.json @@ -112,7 +112,7 @@ { "name": "hxp", "type": "haxelib", - "version": "1.2.2" + "version": "1.3.0" }, { "name": "json2object", From 08d35eb00d0f1a7a2c1af823fedab384e9c176f1 Mon Sep 17 00:00:00 2001 From: Hundrec Date: Tue, 25 Jun 2024 18:07:09 -0400 Subject: [PATCH 060/469] Fix tankmanpixel icon in Chart Editor --- source/funkin/play/character/CharacterData.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/character/CharacterData.hx b/source/funkin/play/character/CharacterData.hx index 7d3d6cfb92..8b1649e26b 100644 --- a/source/funkin/play/character/CharacterData.hx +++ b/source/funkin/play/character/CharacterData.hx @@ -305,8 +305,8 @@ class CharacterDataParser icon = "darnell"; case "senpai-angry": icon = "senpai"; - case "tankman" | "tankman-atlas": - icon = "tankmen"; + case "tankman-atlas": + icon = "tankman"; } var path = Paths.image("freeplay/icons/" + icon + "pixel"); From dad8b0815dc790181abd375b25946b05c41a1bda Mon Sep 17 00:00:00 2001 From: cyn Date: Mon, 13 May 2024 11:23:16 -0700 Subject: [PATCH 061/469] flash fix --- source/funkin/ui/MenuItem.hx | 23 +++++++++++------------ source/funkin/ui/story/LevelTitle.hx | 24 ++++++++++++------------ 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/source/funkin/ui/MenuItem.hx b/source/funkin/ui/MenuItem.hx index ba5cc066bc..2a483ea789 100644 --- a/source/funkin/ui/MenuItem.hx +++ b/source/funkin/ui/MenuItem.hx @@ -11,7 +11,6 @@ class MenuItem extends FlxSpriteGroup { public var targetY:Float = 0; public var week:FlxSprite; - public var flashingInt:Int = 0; public function new(x:Float, y:Float, weekNum:Int = 0, weekType:WeekType) { @@ -30,28 +29,28 @@ class MenuItem extends FlxSpriteGroup } var isFlashing:Bool = false; + var flashTick:Float = 0; + final flashFramerate:Float = 20; public function startFlashing():Void { isFlashing = true; } - // if it runs at 60fps, fake framerate will be 6 - // if it runs at 144 fps, fake framerate will be like 14, and will update the graphic every 0.016666 * 3 seconds still??? - // so it runs basically every so many seconds, not dependant on framerate?? - // I'm still learning how math works thanks whoever is reading this lol - var fakeFramerate:Int = Math.round((1 / FlxG.elapsed) / 10); - override function update(elapsed:Float) { super.update(elapsed); y = MathUtil.coolLerp(y, (targetY * 120) + 480, 0.17); - if (isFlashing) flashingInt += 1; - - if (flashingInt % fakeFramerate >= Math.floor(fakeFramerate / 2)) week.color = 0xFF33ffff; - else - week.color = FlxColor.WHITE; + if (isFlashing) + { + flashTick += elapsed; + if (flashTick >= 1 / flashFramerate) + { + flashTick %= 1 / flashFramerate; + week.color = (week.color == FlxColor.WHITE) ? 0xFF33ffff : FlxColor.WHITE; + } + } } } diff --git a/source/funkin/ui/story/LevelTitle.hx b/source/funkin/ui/story/LevelTitle.hx index e6f9890162..2be2da154b 100644 --- a/source/funkin/ui/story/LevelTitle.hx +++ b/source/funkin/ui/story/LevelTitle.hx @@ -13,13 +13,10 @@ class LevelTitle extends FlxSpriteGroup public final level:Level; public var targetY:Float; - public var isFlashing:Bool = false; var title:FlxSprite; var lock:FlxSprite; - var flashingInt:Int = 0; - public function new(x:Int, y:Int, level:Level) { super(x, y); @@ -46,20 +43,23 @@ class LevelTitle extends FlxSpriteGroup } } - // if it runs at 60fps, fake framerate will be 6 - // if it runs at 144 fps, fake framerate will be like 14, and will update the graphic every 0.016666 * 3 seconds still??? - // so it runs basically every so many seconds, not dependant on framerate?? - // I'm still learning how math works thanks whoever is reading this lol - var fakeFramerate:Int = Math.round((1 / FlxG.elapsed) / 10); + public var isFlashing:Bool = false; + var flashTick:Float = 0; + final flashFramerate:Float = 20; public override function update(elapsed:Float):Void { this.y = MathUtil.coolLerp(y, targetY, 0.17); - if (isFlashing) flashingInt += 1; - if (flashingInt % fakeFramerate >= Math.floor(fakeFramerate / 2)) title.color = 0xFF33ffff; - else - title.color = FlxColor.WHITE; + if (isFlashing) + { + flashTick += elapsed; + if (flashTick >= 1 / flashFramerate) + { + flashTick %= 1 / flashFramerate; + title.color = (title.color == FlxColor.WHITE) ? 0xFF33ffff : FlxColor.WHITE; + } + } } public function showLock():Void From a7bdf2383267cbdfc294b9cf0f8e92eb4711777a Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:53:32 +0200 Subject: [PATCH 062/469] Fix chart reset when charting and pressing chart key --- source/funkin/play/PlayState.hx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index f55cef3888..d7173b9a70 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2619,10 +2619,18 @@ class PlayState extends MusicBeatSubState { disableKeys = true; persistentUpdate = false; - FlxG.switchState(() -> new ChartEditorState( - { - targetSongId: currentSong.id, - })); + if (isChartingMode) + { + FlxG.sound.music?.pause(); + this.close(); + } + else + { + FlxG.switchState(() -> new ChartEditorState( + { + targetSongId: currentSong.id, + })); + } } #end From 6f26fbba5f55a1f7e1fbfdf918dfd4b8e806c3b2 Mon Sep 17 00:00:00 2001 From: NotHyper-474 Date: Mon, 10 Jun 2024 23:12:28 -0300 Subject: [PATCH 063/469] Fix trace --- source/funkin/ui/transition/LoadingState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index bc26ad97a1..0f2ce10766 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -346,7 +346,7 @@ class LoadingState extends MusicBeatSubState return 'Done precaching ${path}'; }, true); - trace("Queued ${path} for precaching"); + trace('Queued ${path} for precaching'); // FunkinSprite.cacheTexture(path); } From 16d64991edfcfba64b4715d59cd570ed1b40f366 Mon Sep 17 00:00:00 2001 From: MaybeMaru <97055307+MaybeMaru@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:11:00 +0200 Subject: [PATCH 064/469] Add missing constants --- source/funkin/play/notes/Strumline.hx | 2 +- source/funkin/play/notes/SustainTrail.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index 0e4b6645f1..3c114b5e0d 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -37,7 +37,7 @@ class Strumline extends FlxSpriteGroup static function get_RENDER_DISTANCE_MS():Float { - return FlxG.height / 0.45; + return FlxG.height / Constants.PIXELS_PER_MS; } /** diff --git a/source/funkin/play/notes/SustainTrail.hx b/source/funkin/play/notes/SustainTrail.hx index b358d7f033..f6d43b33f6 100644 --- a/source/funkin/play/notes/SustainTrail.hx +++ b/source/funkin/play/notes/SustainTrail.hx @@ -160,7 +160,7 @@ class SustainTrail extends FlxSprite */ public static inline function sustainHeight(susLength:Float, scroll:Float) { - return (susLength * 0.45 * scroll); + return (susLength * Constants.PIXELS_PER_MS * scroll); } function set_sustainLength(s:Float):Float From 91bc63004abdd1a669ac6779d83fde5c88b641bd Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Thu, 27 Jun 2024 21:00:25 +0200 Subject: [PATCH 065/469] FunkinSound.playOnce return sound --- source/funkin/audio/FunkinSound.hx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index c70f195d2f..8d2ad9a323 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -533,11 +533,12 @@ class FunkinSound extends FlxSound implements ICloneable * Play a sound effect once, then destroy it. * @param key * @param volume - * @return static function construct():FunkinSound + * @return A `FunkinSound` object, or `null` if the sound could not be loaded. */ - public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):Void + public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):FunkinSound { var result = FunkinSound.load(key, volume, false, true, true, onComplete, onLoad); + return result; } /** From 02dd7e7264ab8c2d9f2136a1e07ff7e0e41e6904 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Thu, 27 Jun 2024 21:05:01 +0200 Subject: [PATCH 066/469] Implement danceEvery for Characters --- source/funkin/play/character/BaseCharacter.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index 4ef86c6a9f..f9e1f00f20 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -180,6 +180,7 @@ class BaseCharacter extends Bopper { this.characterName = _data.name; this.name = _data.name; + this.danceEvery = _data.danceEvery; this.singTimeSteps = _data.singTime; this.globalOffsets = _data.offsets; this.flipX = _data.flipX; From 84dd1cd3b98d58d230196036db61556ad40eae43 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 28 Jun 2024 22:29:59 -0400 Subject: [PATCH 067/469] Fix several character animation issues --- assets | 2 +- source/funkin/modding/events/ScriptEvent.hx | 3 +- source/funkin/play/character/BaseCharacter.hx | 109 ++++++++++++++---- source/funkin/util/Constants.hx | 15 +++ 4 files changed, 102 insertions(+), 27 deletions(-) diff --git a/assets b/assets index 225e248f14..9050732ec1 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 225e248f148a92500a6fe90e4f10e4cd2acee782 +Subproject commit 9050732ec1cc69cbca9a9a73ee817459f84bdc53 diff --git a/source/funkin/modding/events/ScriptEvent.hx b/source/funkin/modding/events/ScriptEvent.hx index dd55de23b0..70055b2624 100644 --- a/source/funkin/modding/events/ScriptEvent.hx +++ b/source/funkin/modding/events/ScriptEvent.hx @@ -151,7 +151,8 @@ class HitNoteScriptEvent extends NoteScriptEvent public var hitDiff:Float = 0; /** - * If the hit causes a notesplash + * Whether this note hit causes a note splash to display. + * Defaults to true only on "sick" notes. */ public var doesNotesplash:Bool = false; diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index 4ef86c6a9f..980cf2106d 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -308,13 +308,26 @@ class BaseCharacter extends Bopper // so we can query which ones are available. this.comboNoteCounts = findCountAnimations('combo'); // example: combo50 this.dropNoteCounts = findCountAnimations('drop'); // example: drop50 - // trace('${this.animation.getNameList()}'); - // trace('Combo note counts: ' + this.comboNoteCounts); - // trace('Drop note counts: ' + this.dropNoteCounts); + if (comboNoteCounts.length > 0) trace('Combo note counts: ' + this.comboNoteCounts); + if (dropNoteCounts.length > 0) trace('Drop note counts: ' + this.dropNoteCounts); super.onCreate(event); } + override function onAnimationFinished(animationName:String):Void + { + super.onAnimationFinished(animationName); + + trace('${characterId} has finished animation: ${animationName}'); + if ((animationName.endsWith(Constants.ANIMATION_END_SUFFIX) && !animationName.startsWith('idle') && !animationName.startsWith('dance')) + || animationName.startsWith('combo') + || animationName.startsWith('drop')) + { + // Force the character to play the idle after the animation ends. + this.dance(true); + } + } + function resetCameraFocusPoint():Void { // Calculate the camera focus point @@ -368,9 +381,11 @@ class BaseCharacter extends Bopper // and Darnell (this keeps the flame on his lighter flickering). // Works for idle, singLEFT/RIGHT/UP/DOWN, alt singing animations, and anything else really. - if (!getCurrentAnimation().endsWith('-hold') && hasAnimation(getCurrentAnimation() + '-hold') && isAnimationFinished()) + if (!getCurrentAnimation().endsWith(Constants.ANIMATION_HOLD_SUFFIX) + && hasAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX) + && isAnimationFinished()) { - playAnimation(getCurrentAnimation() + '-hold'); + playAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX); } // Handle character note hold time. @@ -395,7 +410,25 @@ class BaseCharacter extends Bopper { trace('holdTimer reached ${holdTimer}sec (> ${singTimeSec}), stopping sing animation'); holdTimer = 0; - dance(true); + + var currentAnimation:String = getCurrentAnimation(); + // Strip "-hold" from the end. + if (currentAnimation.endsWith(Constants.ANIMATION_HOLD_SUFFIX)) currentAnimation = currentAnimation.substring(0, + currentAnimation.length - Constants.ANIMATION_HOLD_SUFFIX.length); + + var endAnimation:String = currentAnimation + Constants.ANIMATION_END_SUFFIX; + if (hasAnimation(endAnimation)) + { + // Play the '-end' animation, if one exists. + trace('${characterId}: playing ${endAnimation}'); + playAnimation(endAnimation); + } + else + { + // Play the idle animation. + trace('${characterId}: attempting dance'); + dance(true); + } } } else @@ -408,7 +441,8 @@ class BaseCharacter extends Bopper public function isSinging():Bool { - return getCurrentAnimation().startsWith('sing'); + var currentAnimation:String = getCurrentAnimation(); + return currentAnimation.startsWith('sing') && !currentAnimation.endsWith(Constants.ANIMATION_END_SUFFIX); } override function dance(force:Bool = false):Void @@ -418,15 +452,15 @@ class BaseCharacter extends Bopper if (!force) { + // Prevent dancing while a singing animation is playing. if (isSinging()) return; + // Prevent dancing while a non-idle special animation is playing. var currentAnimation:String = getCurrentAnimation(); - if ((currentAnimation == 'hey' || currentAnimation == 'cheer') && !isAnimationFinished()) return; + if (!currentAnimation.startsWith('dance') && !currentAnimation.startsWith('idle') && !isAnimationFinished()) return; } - // Prevent dancing while another animation is playing. - if (!force && isSinging()) return; - + trace('${characterId}: Actually dancing'); // Otherwise, fallback to the super dance() method, which handles playing the idle animation. super.dance(); } @@ -499,6 +533,16 @@ class BaseCharacter extends Bopper this.playSingAnimation(event.note.noteData.getDirection(), false); holdTimer = 0; } + else if (characterType == GF && event.note.noteData.getMustHitNote()) + { + switch (event.judgement) + { + case 'sick' | 'good': + playComboAnimation(event.comboCount); + default: + playComboDropAnimation(event.comboCount); + } + } } /** @@ -521,25 +565,40 @@ class BaseCharacter extends Bopper } else if (event.note.noteData.getMustHitNote() && characterType == GF) { - var dropAnim = ''; + playComboDropAnimation(Highscore.tallies.combo); + } + } - // Choose the combo drop anim to play. - // If there are several (for example, drop10 and drop50) the highest one will be used. - // If the combo count is too low, no animation will be played. - for (count in dropNoteCounts) - { - if (event.comboCount >= count) - { - dropAnim = 'drop${count}'; - } - } + function playComboAnimation(comboCount:Int):Void + { + var comboAnim = 'combo${comboCount}'; + if (hasAnimation(comboAnim)) + { + trace('Playing GF combo animation: ${comboAnim}'); + this.playAnimation(comboAnim, true, true); + } + } + + function playComboDropAnimation(comboCount:Int):Void + { + var dropAnim:Null = null; - if (dropAnim != '') + // Choose the combo drop anim to play. + // If there are several (for example, drop10 and drop50) the highest one will be used. + // If the combo count is too low, no animation will be played. + for (count in dropNoteCounts) + { + if (comboCount >= count) { - trace('Playing GF combo drop animation: ${dropAnim}'); - this.playAnimation(dropAnim, true, true); + dropAnim = 'drop${count}'; } } + + if (dropAnim != null) + { + trace('Playing GF combo drop animation: ${dropAnim}'); + this.playAnimation(dropAnim, true, true); + } } /** diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index 1e0978839a..2d4fef1f44 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -283,6 +283,21 @@ class Constants */ public static final DEFAULT_TIME_SIGNATURE_DEN:Int = 4; + /** + * ANIMATIONS + */ + // ============================== + + /** + * A suffix used for animations played when an animation would loop. + */ + public static final ANIMATION_HOLD_SUFFIX:String = '-hold'; + + /** + * A suffix used for animations played when an animation would end before transitioning to another. + */ + public static final ANIMATION_END_SUFFIX:String = '-end'; + /** * TIMING */ From 9d9d7a853cee3e162fb4ddb041e2ed67fddb3701 Mon Sep 17 00:00:00 2001 From: AbnormalPoof Date: Mon, 24 Jun 2024 03:55:59 -0500 Subject: [PATCH 068/469] video cutscene autopause magic --- source/funkin/play/PlayState.hx | 16 ++++++++++++++-- source/funkin/play/cutscene/VideoCutscene.hx | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index f55cef3888..de84928826 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1301,12 +1301,18 @@ class PlayState extends MusicBeatSubState super.closeSubState(); } - #if discord_rpc /** * Function called when the game window gains focus. */ public override function onFocus():Void { + if (VideoCutscene.isPlaying() && FlxG.autoPause && isGamePaused) VideoCutscene.pauseVideo(); + #if html5 + else + VideoCutscene.resumeVideo(); + #end + + #if discord_rpc if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause) { if (Conductor.instance.songPosition > 0.0) DiscordClient.changePresence(detailsText, currentSong.song @@ -1318,6 +1324,7 @@ class PlayState extends MusicBeatSubState else DiscordClient.changePresence(detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); } + #end super.onFocus(); } @@ -1327,12 +1334,17 @@ class PlayState extends MusicBeatSubState */ public override function onFocusLost():Void { + #if html5 + if (FlxG.autoPause) VideoCutscene.pauseVideo(); + #end + + #if discord_rpc if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause) DiscordClient.changePresence(detailsPausedText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); + #end super.onFocusLost(); } - #end /** * Removes any references to the current stage, then clears the stage cache, diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index 01a492a77a..abbcd4f540 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -145,7 +145,7 @@ class VideoCutscene { vid.zIndex = 0; vid.bitmap.onEndReached.add(finishVideo.bind(0.5)); - vid.autoPause = false; + vid.autoPause = FlxG.autoPause; vid.cameras = [PlayState.instance.camCutscene]; From 6bf28a54165a57cd15beb945b7ac1ac6c7691696 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Thu, 27 Jun 2024 21:05:01 +0200 Subject: [PATCH 069/469] Implement danceEvery for Characters --- source/funkin/play/character/BaseCharacter.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index 4ef86c6a9f..f9e1f00f20 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -180,6 +180,7 @@ class BaseCharacter extends Bopper { this.characterName = _data.name; this.name = _data.name; + this.danceEvery = _data.danceEvery; this.singTimeSteps = _data.singTime; this.globalOffsets = _data.offsets; this.flipX = _data.flipX; From b022581e2321eb0b214f0745ae39e35bf485192a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 28 Jun 2024 23:25:59 -0400 Subject: [PATCH 070/469] Make danceEvery a float precise up to 0.25 beats (1 step) --- source/funkin/data/stage/StageData.hx | 8 ++-- source/funkin/data/story/level/LevelData.hx | 8 ++-- source/funkin/play/character/BaseCharacter.hx | 2 +- source/funkin/play/character/CharacterData.hx | 38 ++++++++++--------- source/funkin/play/stage/Bopper.hx | 18 +++++---- 5 files changed, 41 insertions(+), 33 deletions(-) diff --git a/source/funkin/data/stage/StageData.hx b/source/funkin/data/stage/StageData.hx index bebd86d02b..eda8e31481 100644 --- a/source/funkin/data/stage/StageData.hx +++ b/source/funkin/data/stage/StageData.hx @@ -140,12 +140,12 @@ typedef StageDataProp = * If not zero, this prop will play an animation every X beats of the song. * This requires animations to be defined. If `danceLeft` and `danceRight` are defined, * they will alternated between, otherwise the `idle` animation will be used. - * - * @default 0 + * Supports up to 0.25 precision. + * @default 0.0 */ - @:default(0) + @:default(0.0) @:optional - var danceEvery:Int; + var danceEvery:Float; /** * How much the prop scrolls relative to the camera. Used to create a parallax effect. diff --git a/source/funkin/data/story/level/LevelData.hx b/source/funkin/data/story/level/LevelData.hx index ceb2cc054c..d01689a826 100644 --- a/source/funkin/data/story/level/LevelData.hx +++ b/source/funkin/data/story/level/LevelData.hx @@ -91,11 +91,13 @@ typedef LevelPropData = /** * The frequency to bop at, in beats. - * @default 1 = every beat, 2 = every other beat, etc. + * 1 = every beat, 2 = every other beat, etc. + * Supports up to 0.25 precision. + * @default 0.0 */ - @:default(1) + @:default(0.0) @:optional - var danceEvery:Int; + var danceEvery:Float; /** * The offset on the position to render the prop at. diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index f9e1f00f20..863fccafa4 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -164,7 +164,7 @@ class BaseCharacter extends Bopper public function new(id:String, renderType:CharacterRenderType) { - super(); + super(CharacterDataParser.DEFAULT_DANCEEVERY); this.characterId = id; _data = CharacterDataParser.fetchCharacterData(this.characterId); diff --git a/source/funkin/play/character/CharacterData.hx b/source/funkin/play/character/CharacterData.hx index 8b1649e26b..d447eb97f2 100644 --- a/source/funkin/play/character/CharacterData.hx +++ b/source/funkin/play/character/CharacterData.hx @@ -383,21 +383,21 @@ class CharacterDataParser * Values that are too high will cause the character to hold their singing pose for too long after they're done. * @default `8 steps` */ - static final DEFAULT_SINGTIME:Float = 8.0; - - static final DEFAULT_DANCEEVERY:Int = 1; - static final DEFAULT_FLIPX:Bool = false; - static final DEFAULT_FLIPY:Bool = false; - static final DEFAULT_FRAMERATE:Int = 24; - static final DEFAULT_ISPIXEL:Bool = false; - static final DEFAULT_LOOP:Bool = false; - static final DEFAULT_NAME:String = 'Untitled Character'; - static final DEFAULT_OFFSETS:Array = [0, 0]; - static final DEFAULT_HEALTHICON_OFFSETS:Array = [0, 25]; - static final DEFAULT_RENDERTYPE:CharacterRenderType = CharacterRenderType.Sparrow; - static final DEFAULT_SCALE:Float = 1; - static final DEFAULT_SCROLL:Array = [0, 0]; - static final DEFAULT_STARTINGANIM:String = 'idle'; + public static final DEFAULT_SINGTIME:Float = 8.0; + + public static final DEFAULT_DANCEEVERY:Float = 1.0; + public static final DEFAULT_FLIPX:Bool = false; + public static final DEFAULT_FLIPY:Bool = false; + public static final DEFAULT_FRAMERATE:Int = 24; + public static final DEFAULT_ISPIXEL:Bool = false; + public static final DEFAULT_LOOP:Bool = false; + public static final DEFAULT_NAME:String = 'Untitled Character'; + public static final DEFAULT_OFFSETS:Array = [0, 0]; + public static final DEFAULT_HEALTHICON_OFFSETS:Array = [0, 25]; + public static final DEFAULT_RENDERTYPE:CharacterRenderType = CharacterRenderType.Sparrow; + public static final DEFAULT_SCALE:Float = 1; + public static final DEFAULT_SCROLL:Array = [0, 0]; + public static final DEFAULT_STARTINGANIM:String = 'idle'; /** * Set unspecified parameters to their defaults. @@ -665,10 +665,12 @@ typedef CharacterData = /** * The frequency at which the character will play its idle animation, in beats. * Increasing this number will make the character dance less often. - * - * @default 1 + * Supports up to `0.25` precision. + * @default `1.0` on characters */ - var danceEvery:Null; + @:optional + @:default(1.0) + var danceEvery:Null; /** * The minimum duration that a character will play a note animation for, in beats. diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index 11fb9e4999..0061e85fb0 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -19,8 +19,10 @@ class Bopper extends StageProp implements IPlayStateScriptedClass /** * The bopper plays the dance animation once every `danceEvery` beats. * Set to 0 to disable idle animation. + * Supports up to 0.25 precision. + * @default 0.0 on props, 1.0 on characters */ - public var danceEvery:Int = 1; + public var danceEvery:Float = 0.0; /** * Whether the bopper should dance left and right. @@ -110,7 +112,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass */ var hasDanced:Bool = false; - public function new(danceEvery:Int = 1) + public function new(danceEvery:Float = 0.0) { super(); this.danceEvery = danceEvery; @@ -171,16 +173,20 @@ class Bopper extends StageProp implements IPlayStateScriptedClass } /** - * Called once every beat of the song. + * Called once every step of the song. */ - public function onBeatHit(event:SongTimeScriptEvent):Void + public function onStepHit(event:SongTimeScriptEvent) { - if (danceEvery > 0 && event.beat % danceEvery == 0) + if (danceEvery > 0) trace('step hit(${danceEvery}): ${event.step % (danceEvery * Constants.STEPS_PER_BEAT)} == 0?'); + if (danceEvery > 0 && (event.step % (danceEvery * Constants.STEPS_PER_BEAT)) == 0) { + trace('dance onStepHit!'); dance(shouldBop); } } + public function onBeatHit(event:SongTimeScriptEvent):Void {} + /** * Called every `danceEvery` beats of the song. */ @@ -367,8 +373,6 @@ class Bopper extends StageProp implements IPlayStateScriptedClass public function onNoteGhostMiss(event:GhostMissNoteScriptEvent) {} - public function onStepHit(event:SongTimeScriptEvent) {} - public function onCountdownStart(event:CountdownScriptEvent) {} public function onCountdownStep(event:CountdownScriptEvent) {} From 03eab140f15ca68610892f0720b5c139217522b4 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Thu, 27 Jun 2024 21:00:25 +0200 Subject: [PATCH 071/469] FunkinSound.playOnce return sound --- source/funkin/audio/FunkinSound.hx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 11b713f4db..c6a9650ddf 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -535,11 +535,12 @@ class FunkinSound extends FlxSound implements ICloneable * Play a sound effect once, then destroy it. * @param key * @param volume - * @return static function construct():FunkinSound + * @return A `FunkinSound` object, or `null` if the sound could not be loaded. */ - public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):Void + public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):FunkinSound { var result = FunkinSound.load(key, volume, false, true, true, onComplete, onLoad); + return result; } /** From 62501a1dedd444426ed7cd946b70df880940e371 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 29 Jun 2024 00:03:28 -0400 Subject: [PATCH 072/469] Fix null safety error. --- source/funkin/audio/FunkinSound.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index c6a9650ddf..9cb131c912 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -537,7 +537,7 @@ class FunkinSound extends FlxSound implements ICloneable * @param volume * @return A `FunkinSound` object, or `null` if the sound could not be loaded. */ - public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):FunkinSound + public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):Null { var result = FunkinSound.load(key, volume, false, true, true, onComplete, onLoad); return result; From 32deb8d190b4adf1797ad6a1fbe2eb7d9a24e3fb Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Thu, 20 Jun 2024 07:24:36 +0200 Subject: [PATCH 073/469] Fix crash after pressing F5 and coming back from stickers --- source/funkin/ui/freeplay/FreeplayState.hx | 2 +- source/funkin/ui/story/StoryMenuState.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 68c63efc45..19bf601e31 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -214,7 +214,7 @@ class FreeplayState extends MusicBeatSubState prepForNewRank = true; } - if (stickers != null) + if (stickers?.members != null) { stickerSubState = stickers; } diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 06a83ab4db..7707850cea 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -113,7 +113,7 @@ class StoryMenuState extends MusicBeatState { super(); - if (stickers != null) + if (stickers?.members != null) { stickerSubState = stickers; } From 7e6ef61169ed79cefec39cf5539eb96e172dda09 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Sun, 9 Jun 2024 00:29:55 +0200 Subject: [PATCH 074/469] Fix Stack Overflow if song doesn't have "normal" difficulty --- source/funkin/ui/freeplay/FreeplayState.hx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 68c63efc45..a330c5eed3 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -2039,6 +2039,8 @@ class FreeplaySongData function set_currentDifficulty(value:String):String { + if (currentDifficulty == value) return value; + currentDifficulty = value; updateValues(displayedVariations); return value; From 55d02d49c2b39a7139b3f69cae5186c7ea9224d2 Mon Sep 17 00:00:00 2001 From: Pixel <146671762+JVNpixels@users.noreply.github.com> Date: Fri, 21 Jun 2024 08:42:51 -0700 Subject: [PATCH 075/469] Update StoryMenuState.hx --- source/funkin/ui/story/StoryMenuState.hx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 06a83ab4db..8623d8f397 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -336,6 +336,24 @@ class StoryMenuState extends MusicBeatState changeDifficulty(0); } + #if !html5 + if (FlxG.mouse.wheel != 0) + { + changeLevel(-Math.round(FlxG.mouse.wheel)); + } + #else + if (FlxG.mouse.wheel < 0) + { + changeLevel(-Math.round(FlxG.mouse.wheel / 8)); + } + else if (FlxG.mouse.wheel > 0) + { + changeLevel(-Math.round(FlxG.mouse.wheel / 8)); + } + #end + + // HTML and NON HTML builds mouse fix. + // TODO: Querying UI_RIGHT_P (justPressed) after UI_RIGHT always returns false. Fix it! if (controls.UI_RIGHT_P) { From 90f3a8f2853fdba6124cb0ff874e79186db9512a Mon Sep 17 00:00:00 2001 From: Pixel <146671762+JVNpixels@users.noreply.github.com> Date: Fri, 21 Jun 2024 22:46:07 -0700 Subject: [PATCH 076/469] Update source/funkin/ui/story/StoryMenuState.hx Co-authored-by: gamerbross <55158797+gamerbross@users.noreply.github.com> --- source/funkin/ui/story/StoryMenuState.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 8623d8f397..5f21a9a889 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -352,7 +352,6 @@ class StoryMenuState extends MusicBeatState } #end - // HTML and NON HTML builds mouse fix. // TODO: Querying UI_RIGHT_P (justPressed) after UI_RIGHT always returns false. Fix it! if (controls.UI_RIGHT_P) From a9c6b9ff8a4533db315c399d4fb216fad3bbc2d1 Mon Sep 17 00:00:00 2001 From: Pixel <146671762+JVNpixels@users.noreply.github.com> Date: Sat, 22 Jun 2024 19:23:40 -0700 Subject: [PATCH 077/469] Extra newline removal. Removes the extra line in code. --- source/funkin/ui/story/StoryMenuState.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 5f21a9a889..90786b3f62 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -352,7 +352,6 @@ class StoryMenuState extends MusicBeatState } #end - // TODO: Querying UI_RIGHT_P (justPressed) after UI_RIGHT always returns false. Fix it! if (controls.UI_RIGHT_P) { From 09c8a2600ee2d36a6ab23cfd4e68b7227bc15a90 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Sat, 29 Jun 2024 22:54:25 +0200 Subject: [PATCH 078/469] Fix null safety compiling --- source/funkin/audio/FunkinSound.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 8d2ad9a323..a0175ad4db 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -535,9 +535,9 @@ class FunkinSound extends FlxSound implements ICloneable * @param volume * @return A `FunkinSound` object, or `null` if the sound could not be loaded. */ - public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):FunkinSound + public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):Null { - var result = FunkinSound.load(key, volume, false, true, true, onComplete, onLoad); + var result:Null = FunkinSound.load(key, volume, false, true, true, onComplete, onLoad); return result; } From 488fc6888f7d8588866ddcc1310c434365eae2d0 Mon Sep 17 00:00:00 2001 From: FlooferLand! <76737186+FlooferLand@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:42:50 +0300 Subject: [PATCH 079/469] Added new settings items --- source/funkin/ui/options/MenuItemEnums.hx | 10 ++ source/funkin/ui/options/PreferencesMenu.hx | 137 +++++++++++------- .../options/items/CheckboxPreferenceItem.hx | 49 +++++++ .../ui/options/items/EnumPreferenceItem.hx | 84 +++++++++++ .../ui/options/items/NumberPreferenceItem.hx | 136 +++++++++++++++++ 5 files changed, 366 insertions(+), 50 deletions(-) create mode 100644 source/funkin/ui/options/MenuItemEnums.hx create mode 100644 source/funkin/ui/options/items/CheckboxPreferenceItem.hx create mode 100644 source/funkin/ui/options/items/EnumPreferenceItem.hx create mode 100644 source/funkin/ui/options/items/NumberPreferenceItem.hx diff --git a/source/funkin/ui/options/MenuItemEnums.hx b/source/funkin/ui/options/MenuItemEnums.hx new file mode 100644 index 0000000000..4513a92af8 --- /dev/null +++ b/source/funkin/ui/options/MenuItemEnums.hx @@ -0,0 +1,10 @@ +package funkin.ui.options; + +// Add enums for use with `EnumPreferenceItem` here! +/* Example: + class MyOptionEnum + { + public static inline var YuhUh = "true"; // "true" is the value's ID + public static inline var NuhUh = "false"; + } + */ diff --git a/source/funkin/ui/options/PreferencesMenu.hx b/source/funkin/ui/options/PreferencesMenu.hx index 783aef0ba7..5fbefceed6 100644 --- a/source/funkin/ui/options/PreferencesMenu.hx +++ b/source/funkin/ui/options/PreferencesMenu.hx @@ -8,6 +8,11 @@ import funkin.ui.AtlasText.AtlasFont; import funkin.ui.options.OptionsState.Page; import funkin.graphics.FunkinCamera; import funkin.ui.TextMenuList.TextMenuItem; +import funkin.audio.FunkinSound; +import funkin.ui.options.MenuItemEnums; +import funkin.ui.options.items.CheckboxPreferenceItem; +import funkin.ui.options.items.NumberPreferenceItem; +import funkin.ui.options.items.EnumPreferenceItem; class PreferencesMenu extends Page { @@ -69,75 +74,107 @@ class PreferencesMenu extends Page }, Preferences.autoPause); } - function createPrefItemCheckbox(prefName:String, prefDesc:String, onChange:Bool->Void, defaultValue:Bool):Void - { - var checkbox:CheckboxPreferenceItem = new CheckboxPreferenceItem(0, 120 * (items.length - 1 + 1), defaultValue); - - items.createItem(120, (120 * items.length) + 30, prefName, AtlasFont.BOLD, function() { - var value = !checkbox.currentValue; - onChange(value); - checkbox.currentValue = value; - }); - - preferenceItems.add(checkbox); - } - - override function update(elapsed:Float) + override function update(elapsed:Float):Void { super.update(elapsed); // Indent the selected item. - // TODO: Only do this on menu change? items.forEach(function(daItem:TextMenuItem) { - if (items.selectedItem == daItem) daItem.x = 150; + var thyOffset:Int = 0; + + // Initializing thy text width (if thou text present) + var thyTextWidth:Int = 0; + if (Std.isOfType(daItem, EnumPreferenceItem)) thyTextWidth = cast(daItem, EnumPreferenceItem).lefthandText.getWidth(); + else if (Std.isOfType(daItem, NumberPreferenceItem)) thyTextWidth = cast(daItem, NumberPreferenceItem).lefthandText.getWidth(); + + if (thyTextWidth != 0) + { + // Magic number because of the weird offset thats being added by default + thyOffset += thyTextWidth - 75; + } + + if (items.selectedItem == daItem) + { + thyOffset += 150; + } else - daItem.x = 120; + { + thyOffset += 120; + } + + daItem.x = thyOffset; }); } -} -class CheckboxPreferenceItem extends FlxSprite -{ - public var currentValue(default, set):Bool; + // - Preference item creation methods - + // Should be moved into a separate PreferenceItems class but you can't access PreferencesMenu.items and PreferencesMenu.preferenceItems from outside. - public function new(x:Float, y:Float, defaultValue:Bool = false) + /** + * Creates a pref item that works with booleans + * @param onChange Gets called every time the player changes the value; use this to apply the value + * @param defaultValue The value that is loaded in when the pref item is created (usually your Preferences.settingVariable) + */ + function createPrefItemCheckbox(prefName:String, prefDesc:String, onChange:Bool->Void, defaultValue:Bool):Void { - super(x, y); - - frames = Paths.getSparrowAtlas('checkboxThingie'); - animation.addByPrefix('static', 'Check Box unselected', 24, false); - animation.addByPrefix('checked', 'Check Box selecting animation', 24, false); + var checkbox:CheckboxPreferenceItem = new CheckboxPreferenceItem(0, 120 * (items.length - 1 + 1), defaultValue); - setGraphicSize(Std.int(width * 0.7)); - updateHitbox(); + items.createItem(0, (120 * items.length) + 30, prefName, AtlasFont.BOLD, function() { + var value = !checkbox.currentValue; + onChange(value); + checkbox.currentValue = value; + }); - this.currentValue = defaultValue; + preferenceItems.add(checkbox); } - override function update(elapsed:Float) + /** + * Creates a pref item that works with general numbers + * @param onChange Gets called every time the player changes the value; use this to apply the value + * @param valueFormatter Will get called every time the game needs to display the float value; use this to change how the displayed value looks + * @param defaultValue The value that is loaded in when the pref item is created (usually your Preferences.settingVariable) + * @param min Minimum value (example: 0) + * @param max Maximum value (example: 10) + * @param step The value to increment/decrement by (default = 0.1) + * @param precision Rounds decimals up to a `precision` amount of digits (ex: 4 -> 0.1234, 2 -> 0.12) + */ + function createPrefItemNumber(prefName:String, prefDesc:String, onChange:Float->Void, ?valueFormatter:Float->String, defaultValue:Int, min:Int, max:Int, + step:Float = 0.1, precision:Int):Void { - super.update(elapsed); + var item = new NumberPreferenceItem(0, (120 * items.length) + 30, prefName, defaultValue, min, max, step, precision, onChange, valueFormatter); + items.addItem(prefName, item); + preferenceItems.add(item.lefthandText); + } - switch (animation.curAnim.name) - { - case 'static': - offset.set(); - case 'checked': - offset.set(17, 70); - } + /** + * Creates a pref item that works with number percentages + * @param onChange Gets called every time the player changes the value; use this to apply the value + * @param defaultValue The value that is loaded in when the pref item is created (usually your Preferences.settingVariable) + * @param min Minimum value (default = 0) + * @param max Maximum value (default = 100) + */ + function createPrefItemPercentage(prefName:String, prefDesc:String, onChange:Int->Void, defaultValue:Int, min:Int = 0, max:Int = 100):Void + { + var newCallback = function(value:Float) { + onChange(Std.int(value)); + }; + var formatter = function(value:Float) { + return '${value}%'; + }; + var item = new NumberPreferenceItem(0, (120 * items.length) + 30, prefName, defaultValue, min, max, 10, 0, newCallback, formatter); + items.addItem(prefName, item); + preferenceItems.add(item.lefthandText); } - function set_currentValue(value:Bool):Bool + /** + * Creates a pref item that works with enums + * @param values Maps enum values to display strings _(ex: `NoteHitSoundType.PingPong => "Ping pong"`)_ + * @param onChange Gets called every time the player changes the value; use this to apply the value + * @param defaultValue The value that is loaded in when the pref item is created (usually your Preferences.settingVariable) + */ + function createPrefItemEnum(prefName:String, prefDesc:String, values:Map, onChange:String->Void, defaultValue:String):Void { - if (value) - { - animation.play('checked', true); - } - else - { - animation.play('static'); - } - - return currentValue = value; + var item = new EnumPreferenceItem(0, (120 * items.length) + 30, prefName, values, defaultValue, onChange); + items.addItem(prefName, item); + preferenceItems.add(item.lefthandText); } } diff --git a/source/funkin/ui/options/items/CheckboxPreferenceItem.hx b/source/funkin/ui/options/items/CheckboxPreferenceItem.hx new file mode 100644 index 0000000000..88c4fb6b00 --- /dev/null +++ b/source/funkin/ui/options/items/CheckboxPreferenceItem.hx @@ -0,0 +1,49 @@ +package funkin.ui.options.items; + +import flixel.FlxSprite.FlxSprite; + +class CheckboxPreferenceItem extends FlxSprite +{ + public var currentValue(default, set):Bool; + + public function new(x:Float, y:Float, defaultValue:Bool = false) + { + super(x, y); + + frames = Paths.getSparrowAtlas('checkboxThingie'); + animation.addByPrefix('static', 'Check Box unselected', 24, false); + animation.addByPrefix('checked', 'Check Box selecting animation', 24, false); + + setGraphicSize(Std.int(width * 0.7)); + updateHitbox(); + + this.currentValue = defaultValue; + } + + override function update(elapsed:Float) + { + super.update(elapsed); + + switch (animation.curAnim.name) + { + case 'static': + offset.set(); + case 'checked': + offset.set(17, 70); + } + } + + function set_currentValue(value:Bool):Bool + { + if (value) + { + animation.play('checked', true); + } + else + { + animation.play('static'); + } + + return currentValue = value; + } +} diff --git a/source/funkin/ui/options/items/EnumPreferenceItem.hx b/source/funkin/ui/options/items/EnumPreferenceItem.hx new file mode 100644 index 0000000000..02a2733537 --- /dev/null +++ b/source/funkin/ui/options/items/EnumPreferenceItem.hx @@ -0,0 +1,84 @@ +package funkin.ui.options.items; + +import funkin.ui.TextMenuList; +import funkin.ui.AtlasText; +import funkin.input.Controls; +import funkin.ui.options.MenuItemEnums; +import haxe.EnumTools; + +/** + * Preference item that allows the player to pick a value from an enum (list of values) + */ +class EnumPreferenceItem extends TextMenuItem +{ + function controls():Controls + { + return PlayerSettings.player1.controls; + } + + public var lefthandText:AtlasText; + + public var currentValue:String; + public var onChangeCallback:NullVoid>; + public var map:Map; + public var keys:Array = []; + + var index = 0; + + public function new(x:Float, y:Float, name:String, map:Map, defaultValue:String, ?callback:String->Void) + { + super(x, y, name, function() { + callback(this.currentValue); + }); + + updateHitbox(); + + this.map = map; + this.currentValue = defaultValue; + this.onChangeCallback = callback; + + var i:Int = 0; + for (key in map.keys()) + { + this.keys.push(key); + if (this.currentValue == key) index = i; + i += 1; + } + + lefthandText = new AtlasText(15, y, formatted(defaultValue), AtlasFont.DEFAULT); + } + + override function update(elapsed:Float):Void + { + super.update(elapsed); + + // var fancyTextFancyColor:Color; + if (selected) + { + var shouldDecrease:Bool = controls().UI_LEFT_P; + var shouldIncrease:Bool = controls().UI_RIGHT_P; + + if (shouldDecrease) index -= 1; + if (shouldIncrease) index += 1; + + if (index > keys.length - 1) index = 0; + if (index < 0) index = keys.length - 1; + + currentValue = keys[index]; + if (onChangeCallback != null && (shouldIncrease || shouldDecrease)) + { + onChangeCallback(currentValue); + } + } + + lefthandText.text = formatted(currentValue); + } + + function formatted(value:String):String + { + // FIXME: Can't add arrows around the text because the font doesn't support < > + // var leftArrow:String = selected ? '<' : ''; + // var rightArrow:String = selected ? '>' : ''; + return '${map.get(value) ?? value}'; + } +} diff --git a/source/funkin/ui/options/items/NumberPreferenceItem.hx b/source/funkin/ui/options/items/NumberPreferenceItem.hx new file mode 100644 index 0000000000..f3cd3cd46f --- /dev/null +++ b/source/funkin/ui/options/items/NumberPreferenceItem.hx @@ -0,0 +1,136 @@ +package funkin.ui.options.items; + +import funkin.ui.TextMenuList; +import funkin.ui.AtlasText; +import funkin.input.Controls; + +/** + * Preference item that allows the player to pick a value between min and max + */ +class NumberPreferenceItem extends TextMenuItem +{ + function controls():Controls + { + return PlayerSettings.player1.controls; + } + + // Widgets + public var lefthandText:AtlasText; + + // Constants + static final HOLD_DELAY:Float = 0.3; // seconds + static final CHANGE_RATE:Float = 0.08; // seconds + + // Constructor-initialized variables + public var currentValue:Float; + public var min:Float; + public var max:Float; + public var step:Float; + public var precision:Int; + public var onChangeCallback:NullVoid>; + public var valueFormatter:NullString>; + + // Variables + var holdDelayTimer:Float = HOLD_DELAY; // seconds + var changeRateTimer:Float = 0.0; // seconds + + /** + * @param min Minimum value (example: 0) + * @param max Maximum value (example: 100) + * @param step The value to increment/decrement by (example: 10) + * @param callback Will get called every time the user changes the setting; use this to apply/save the setting. + * @param valueFormatter Will get called every time the game needs to display the float value; use this to change how the displayed string looks + */ + public function new(x:Float, y:Float, name:String, defaultValue:Float, min:Float, max:Float, step:Float, precision:Int, ?callback:Float->Void, + ?valueFormatter:Float->String):Void + { + super(x, y, name, function() { + callback(this.currentValue); + }); + lefthandText = new AtlasText(15, y, formatted(defaultValue), AtlasFont.DEFAULT); + + updateHitbox(); + + this.currentValue = defaultValue; + this.min = min; + this.max = max; + this.step = step; + this.precision = precision; + this.onChangeCallback = callback; + this.valueFormatter = valueFormatter; + } + + override function update(elapsed:Float):Void + { + super.update(elapsed); + + // var fancyTextFancyColor:Color; + if (selected) + { + holdDelayTimer -= elapsed; + if (holdDelayTimer <= 0.0) + { + changeRateTimer -= elapsed; + } + + var jpLeft:Bool = controls().UI_LEFT_P; + var jpRight:Bool = controls().UI_RIGHT_P; + + if (jpLeft || jpRight) + { + holdDelayTimer = HOLD_DELAY; + changeRateTimer = 0.0; + } + + var shouldDecrease:Bool = jpLeft; + var shouldIncrease:Bool = jpRight; + + if (controls().UI_LEFT && holdDelayTimer <= 0.0 && changeRateTimer <= 0.0) + { + shouldDecrease = true; + changeRateTimer = CHANGE_RATE; + } + else if (controls().UI_RIGHT && holdDelayTimer <= 0.0 && changeRateTimer <= 0.0) + { + shouldIncrease = true; + changeRateTimer = CHANGE_RATE; + } + + // Actually increasing/decreasing the value + if (shouldDecrease) + { + var isBelowMin:Bool = currentValue - step < min; + currentValue = (currentValue - step).clamp(min, max); + if (onChangeCallback != null && !isBelowMin) onChangeCallback(currentValue); + } + else if (shouldIncrease) + { + var isAboveMax:Bool = currentValue + step > max; + currentValue = (currentValue + step).clamp(min, max); + if (onChangeCallback != null && !isAboveMax) onChangeCallback(currentValue); + } + } + + lefthandText.text = formatted(currentValue); + } + + /** Turns the float into a string */ + function formatted(value:Float):String + { + var float:Float = toFixed(value); + if (valueFormatter != null) + { + return valueFormatter(float); + } + else + { + return '${float}'; + } + } + + function toFixed(value:Float):Float + { + var multiplier:Float = Math.pow(10, precision); + return Math.floor(value * multiplier) / multiplier; + } +} From be5e0aafeb68b366db11227f72befb99d4e7b2f4 Mon Sep 17 00:00:00 2001 From: FlooferLand! <76737186+FlooferLand@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:44:23 +0300 Subject: [PATCH 080/469] Added getWidth --- source/funkin/ui/AtlasText.hx | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/source/funkin/ui/AtlasText.hx b/source/funkin/ui/AtlasText.hx index 186d87c2a1..ef74abc1e2 100644 --- a/source/funkin/ui/AtlasText.hx +++ b/source/funkin/ui/AtlasText.hx @@ -152,6 +152,32 @@ class AtlasText extends FlxTypedSpriteGroup } } + public function getWidth():Int + { + var width = 0; + for (char in this.text.split("")) + { + switch (char) + { + case " ": + { + width += 40; + } + case "\n": + {} + case char: + { + var sprite = new AtlasChar(atlas, char); + sprite.revive(); + sprite.char = char; + sprite.alpha = 1; + width += Std.int(sprite.width); + } + } + } + return width; + } + override function toString() { return "InputItem, " + FlxStringUtil.getDebugString([ From 2916bd6ff7ec9f1e136d84323d8880ca009a3f4e Mon Sep 17 00:00:00 2001 From: Hundrec Date: Fri, 14 Jun 2024 21:52:52 -0700 Subject: [PATCH 081/469] Reorder download Git step in compiling guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved “download Git” from the middle of the guide to the setup step Should prevent errors with Git before installing Git --- docs/COMPILING.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index e7c19875a7..b8ddee4a70 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -2,14 +2,14 @@ 0. Setup - Download Haxe from [Haxe.org](https://haxe.org) + - Download Git from [git-scm.com](https://www.git-scm.com) 1. Cloning the Repository: Make sure when you clone, you clone the submodules to get the assets repo: - `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` - If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way. 2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`) -3. Download Git from [git-scm.com](https://www.git-scm.com) -4. Install all haxelibs of the current branch by running `hmm install` -5. Setup lime: `haxelib run lime setup` -6. Platform setup +3. Install all haxelibs of the current branch by running `hmm install` +4. Setup lime: `haxelib run lime setup` +5. Platform setup - For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe) - When prompted, select "Individual Components" and make sure to download the following: - MSVC v143 VS 2022 C++ x64/x86 build tools @@ -17,8 +17,8 @@ - Mac: [`lime setup mac` Documentation](https://lime.openfl.org/docs/advanced-setup/macos/) - Linux: [`lime setup linux` Documentation](https://lime.openfl.org/docs/advanced-setup/linux/) - HTML5: Compiles without any extra setup -7. If you are targeting for native, you may need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug` -8. `lime test PLATFORM` ! Add `-debug` to enable several debug features such as time travel (`PgUp`/`PgDn` in Play State). +6. If you are targeting for native, you may need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug` +7. `lime test PLATFORM` ! Add `-debug` to enable several debug features such as time travel (`PgUp`/`PgDn` in Play State). # Troubleshooting From 9fb4a8719a6cd07aa2af2638adc494786ca5425e Mon Sep 17 00:00:00 2001 From: Hundrec Date: Sat, 15 Jun 2024 13:37:21 -0400 Subject: [PATCH 082/469] Add ZIP button warning and restructured sentences Each step should be easier to follow with this structure --- docs/COMPILING.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index b8ddee4a70..b2a106c867 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -3,12 +3,13 @@ 0. Setup - Download Haxe from [Haxe.org](https://haxe.org) - Download Git from [git-scm.com](https://www.git-scm.com) -1. Cloning the Repository: Make sure when you clone, you clone the submodules to get the assets repo: - - `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` - - If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way. -2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`) -3. Install all haxelibs of the current branch by running `hmm install` -4. Setup lime: `haxelib run lime setup` + - Do NOT download the repository using the Download ZIP button on GitHub or you may run into errors! + - Instead, open a command prompt and do the following steps... +1. Run `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` to clone the repository with the necessary assets submodule + - _If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way._ +2. Run `haxelib --global install hmm` and then `haxelib --global run hmm setup` to install hmm.json +3. Run `hmm install` to install all haxelibs of the current branch +4. Run `haxelib run lime setup` to set up lime 5. Platform setup - For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe) - When prompted, select "Individual Components" and make sure to download the following: From 841a61408c13ff34760a448c588e35bc3b203bf0 Mon Sep 17 00:00:00 2001 From: Eric Date: Sun, 16 Jun 2024 18:48:01 -0400 Subject: [PATCH 083/469] Make downloading the assets submodule a separate step. --- docs/COMPILING.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index b2a106c867..cc90bd348e 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -5,8 +5,9 @@ - Download Git from [git-scm.com](https://www.git-scm.com) - Do NOT download the repository using the Download ZIP button on GitHub or you may run into errors! - Instead, open a command prompt and do the following steps... -1. Run `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` to clone the repository with the necessary assets submodule - - _If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way._ +1. Run `git clone https://github.com/FunkinCrew/funkin.git` to clone the base repository. +2. Run `git submodule update --init --recursive` to download the game's assets. + - NOTE: By performing this operation, you are downloading Content which is proprietary and protected by national and international copyright and trademark laws. See [the LICENSE.md file for the Funkin.assets](https://github.com/FunkinCrew/funkin.assets/blob/main/LICENSE.md) repo for more information. 2. Run `haxelib --global install hmm` and then `haxelib --global run hmm setup` to install hmm.json 3. Run `hmm install` to install all haxelibs of the current branch 4. Run `haxelib run lime setup` to set up lime From 8a4f19d603b5900692c8c646c05e8b409688e963 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 25 Jun 2024 18:22:30 -0400 Subject: [PATCH 084/469] Add change counts labels to Actions labeler --- .github/changed-lines-count-labeler.yml | 12 ++++++++++++ .github/workflows/labeler.yml | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .github/changed-lines-count-labeler.yml diff --git a/.github/changed-lines-count-labeler.yml b/.github/changed-lines-count-labeler.yml new file mode 100644 index 0000000000..6f890f5342 --- /dev/null +++ b/.github/changed-lines-count-labeler.yml @@ -0,0 +1,12 @@ +# Add 'small' to any changes below 10 lines +small: + max: 9 + +# Add 'medium' to any changes between 10 and 100 lines +medium: + min: 10 + max: 99 + +# Add 'large' to any changes for more than 100 lines +large: + min: 100 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 0bcc420d34..a861af5781 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -9,6 +9,19 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v5 + - name: Set basic labels + uses: actions/labeler@v5 with: sync-labels: true + changed-lines-count-labeler: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + name: An action for automatically labelling pull requests based on the changed lines count + steps: + - name: Set change count labels + uses: vkirilichev/changed-lines-count-labeler@v0.2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + configuration-path: .github/changed-lines-count-labeler.yml From fbc78adb65c7db8b045f1ea5d6599ae7348912a0 Mon Sep 17 00:00:00 2001 From: FlooferLand! <76737186+FlooferLand@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:42:50 +0300 Subject: [PATCH 085/469] Added new settings items --- source/funkin/ui/options/MenuItemEnums.hx | 10 ++ source/funkin/ui/options/PreferencesMenu.hx | 137 +++++++++++------- .../options/items/CheckboxPreferenceItem.hx | 49 +++++++ .../ui/options/items/EnumPreferenceItem.hx | 84 +++++++++++ .../ui/options/items/NumberPreferenceItem.hx | 136 +++++++++++++++++ 5 files changed, 366 insertions(+), 50 deletions(-) create mode 100644 source/funkin/ui/options/MenuItemEnums.hx create mode 100644 source/funkin/ui/options/items/CheckboxPreferenceItem.hx create mode 100644 source/funkin/ui/options/items/EnumPreferenceItem.hx create mode 100644 source/funkin/ui/options/items/NumberPreferenceItem.hx diff --git a/source/funkin/ui/options/MenuItemEnums.hx b/source/funkin/ui/options/MenuItemEnums.hx new file mode 100644 index 0000000000..4513a92af8 --- /dev/null +++ b/source/funkin/ui/options/MenuItemEnums.hx @@ -0,0 +1,10 @@ +package funkin.ui.options; + +// Add enums for use with `EnumPreferenceItem` here! +/* Example: + class MyOptionEnum + { + public static inline var YuhUh = "true"; // "true" is the value's ID + public static inline var NuhUh = "false"; + } + */ diff --git a/source/funkin/ui/options/PreferencesMenu.hx b/source/funkin/ui/options/PreferencesMenu.hx index 783aef0ba7..5fbefceed6 100644 --- a/source/funkin/ui/options/PreferencesMenu.hx +++ b/source/funkin/ui/options/PreferencesMenu.hx @@ -8,6 +8,11 @@ import funkin.ui.AtlasText.AtlasFont; import funkin.ui.options.OptionsState.Page; import funkin.graphics.FunkinCamera; import funkin.ui.TextMenuList.TextMenuItem; +import funkin.audio.FunkinSound; +import funkin.ui.options.MenuItemEnums; +import funkin.ui.options.items.CheckboxPreferenceItem; +import funkin.ui.options.items.NumberPreferenceItem; +import funkin.ui.options.items.EnumPreferenceItem; class PreferencesMenu extends Page { @@ -69,75 +74,107 @@ class PreferencesMenu extends Page }, Preferences.autoPause); } - function createPrefItemCheckbox(prefName:String, prefDesc:String, onChange:Bool->Void, defaultValue:Bool):Void - { - var checkbox:CheckboxPreferenceItem = new CheckboxPreferenceItem(0, 120 * (items.length - 1 + 1), defaultValue); - - items.createItem(120, (120 * items.length) + 30, prefName, AtlasFont.BOLD, function() { - var value = !checkbox.currentValue; - onChange(value); - checkbox.currentValue = value; - }); - - preferenceItems.add(checkbox); - } - - override function update(elapsed:Float) + override function update(elapsed:Float):Void { super.update(elapsed); // Indent the selected item. - // TODO: Only do this on menu change? items.forEach(function(daItem:TextMenuItem) { - if (items.selectedItem == daItem) daItem.x = 150; + var thyOffset:Int = 0; + + // Initializing thy text width (if thou text present) + var thyTextWidth:Int = 0; + if (Std.isOfType(daItem, EnumPreferenceItem)) thyTextWidth = cast(daItem, EnumPreferenceItem).lefthandText.getWidth(); + else if (Std.isOfType(daItem, NumberPreferenceItem)) thyTextWidth = cast(daItem, NumberPreferenceItem).lefthandText.getWidth(); + + if (thyTextWidth != 0) + { + // Magic number because of the weird offset thats being added by default + thyOffset += thyTextWidth - 75; + } + + if (items.selectedItem == daItem) + { + thyOffset += 150; + } else - daItem.x = 120; + { + thyOffset += 120; + } + + daItem.x = thyOffset; }); } -} -class CheckboxPreferenceItem extends FlxSprite -{ - public var currentValue(default, set):Bool; + // - Preference item creation methods - + // Should be moved into a separate PreferenceItems class but you can't access PreferencesMenu.items and PreferencesMenu.preferenceItems from outside. - public function new(x:Float, y:Float, defaultValue:Bool = false) + /** + * Creates a pref item that works with booleans + * @param onChange Gets called every time the player changes the value; use this to apply the value + * @param defaultValue The value that is loaded in when the pref item is created (usually your Preferences.settingVariable) + */ + function createPrefItemCheckbox(prefName:String, prefDesc:String, onChange:Bool->Void, defaultValue:Bool):Void { - super(x, y); - - frames = Paths.getSparrowAtlas('checkboxThingie'); - animation.addByPrefix('static', 'Check Box unselected', 24, false); - animation.addByPrefix('checked', 'Check Box selecting animation', 24, false); + var checkbox:CheckboxPreferenceItem = new CheckboxPreferenceItem(0, 120 * (items.length - 1 + 1), defaultValue); - setGraphicSize(Std.int(width * 0.7)); - updateHitbox(); + items.createItem(0, (120 * items.length) + 30, prefName, AtlasFont.BOLD, function() { + var value = !checkbox.currentValue; + onChange(value); + checkbox.currentValue = value; + }); - this.currentValue = defaultValue; + preferenceItems.add(checkbox); } - override function update(elapsed:Float) + /** + * Creates a pref item that works with general numbers + * @param onChange Gets called every time the player changes the value; use this to apply the value + * @param valueFormatter Will get called every time the game needs to display the float value; use this to change how the displayed value looks + * @param defaultValue The value that is loaded in when the pref item is created (usually your Preferences.settingVariable) + * @param min Minimum value (example: 0) + * @param max Maximum value (example: 10) + * @param step The value to increment/decrement by (default = 0.1) + * @param precision Rounds decimals up to a `precision` amount of digits (ex: 4 -> 0.1234, 2 -> 0.12) + */ + function createPrefItemNumber(prefName:String, prefDesc:String, onChange:Float->Void, ?valueFormatter:Float->String, defaultValue:Int, min:Int, max:Int, + step:Float = 0.1, precision:Int):Void { - super.update(elapsed); + var item = new NumberPreferenceItem(0, (120 * items.length) + 30, prefName, defaultValue, min, max, step, precision, onChange, valueFormatter); + items.addItem(prefName, item); + preferenceItems.add(item.lefthandText); + } - switch (animation.curAnim.name) - { - case 'static': - offset.set(); - case 'checked': - offset.set(17, 70); - } + /** + * Creates a pref item that works with number percentages + * @param onChange Gets called every time the player changes the value; use this to apply the value + * @param defaultValue The value that is loaded in when the pref item is created (usually your Preferences.settingVariable) + * @param min Minimum value (default = 0) + * @param max Maximum value (default = 100) + */ + function createPrefItemPercentage(prefName:String, prefDesc:String, onChange:Int->Void, defaultValue:Int, min:Int = 0, max:Int = 100):Void + { + var newCallback = function(value:Float) { + onChange(Std.int(value)); + }; + var formatter = function(value:Float) { + return '${value}%'; + }; + var item = new NumberPreferenceItem(0, (120 * items.length) + 30, prefName, defaultValue, min, max, 10, 0, newCallback, formatter); + items.addItem(prefName, item); + preferenceItems.add(item.lefthandText); } - function set_currentValue(value:Bool):Bool + /** + * Creates a pref item that works with enums + * @param values Maps enum values to display strings _(ex: `NoteHitSoundType.PingPong => "Ping pong"`)_ + * @param onChange Gets called every time the player changes the value; use this to apply the value + * @param defaultValue The value that is loaded in when the pref item is created (usually your Preferences.settingVariable) + */ + function createPrefItemEnum(prefName:String, prefDesc:String, values:Map, onChange:String->Void, defaultValue:String):Void { - if (value) - { - animation.play('checked', true); - } - else - { - animation.play('static'); - } - - return currentValue = value; + var item = new EnumPreferenceItem(0, (120 * items.length) + 30, prefName, values, defaultValue, onChange); + items.addItem(prefName, item); + preferenceItems.add(item.lefthandText); } } diff --git a/source/funkin/ui/options/items/CheckboxPreferenceItem.hx b/source/funkin/ui/options/items/CheckboxPreferenceItem.hx new file mode 100644 index 0000000000..88c4fb6b00 --- /dev/null +++ b/source/funkin/ui/options/items/CheckboxPreferenceItem.hx @@ -0,0 +1,49 @@ +package funkin.ui.options.items; + +import flixel.FlxSprite.FlxSprite; + +class CheckboxPreferenceItem extends FlxSprite +{ + public var currentValue(default, set):Bool; + + public function new(x:Float, y:Float, defaultValue:Bool = false) + { + super(x, y); + + frames = Paths.getSparrowAtlas('checkboxThingie'); + animation.addByPrefix('static', 'Check Box unselected', 24, false); + animation.addByPrefix('checked', 'Check Box selecting animation', 24, false); + + setGraphicSize(Std.int(width * 0.7)); + updateHitbox(); + + this.currentValue = defaultValue; + } + + override function update(elapsed:Float) + { + super.update(elapsed); + + switch (animation.curAnim.name) + { + case 'static': + offset.set(); + case 'checked': + offset.set(17, 70); + } + } + + function set_currentValue(value:Bool):Bool + { + if (value) + { + animation.play('checked', true); + } + else + { + animation.play('static'); + } + + return currentValue = value; + } +} diff --git a/source/funkin/ui/options/items/EnumPreferenceItem.hx b/source/funkin/ui/options/items/EnumPreferenceItem.hx new file mode 100644 index 0000000000..02a2733537 --- /dev/null +++ b/source/funkin/ui/options/items/EnumPreferenceItem.hx @@ -0,0 +1,84 @@ +package funkin.ui.options.items; + +import funkin.ui.TextMenuList; +import funkin.ui.AtlasText; +import funkin.input.Controls; +import funkin.ui.options.MenuItemEnums; +import haxe.EnumTools; + +/** + * Preference item that allows the player to pick a value from an enum (list of values) + */ +class EnumPreferenceItem extends TextMenuItem +{ + function controls():Controls + { + return PlayerSettings.player1.controls; + } + + public var lefthandText:AtlasText; + + public var currentValue:String; + public var onChangeCallback:NullVoid>; + public var map:Map; + public var keys:Array = []; + + var index = 0; + + public function new(x:Float, y:Float, name:String, map:Map, defaultValue:String, ?callback:String->Void) + { + super(x, y, name, function() { + callback(this.currentValue); + }); + + updateHitbox(); + + this.map = map; + this.currentValue = defaultValue; + this.onChangeCallback = callback; + + var i:Int = 0; + for (key in map.keys()) + { + this.keys.push(key); + if (this.currentValue == key) index = i; + i += 1; + } + + lefthandText = new AtlasText(15, y, formatted(defaultValue), AtlasFont.DEFAULT); + } + + override function update(elapsed:Float):Void + { + super.update(elapsed); + + // var fancyTextFancyColor:Color; + if (selected) + { + var shouldDecrease:Bool = controls().UI_LEFT_P; + var shouldIncrease:Bool = controls().UI_RIGHT_P; + + if (shouldDecrease) index -= 1; + if (shouldIncrease) index += 1; + + if (index > keys.length - 1) index = 0; + if (index < 0) index = keys.length - 1; + + currentValue = keys[index]; + if (onChangeCallback != null && (shouldIncrease || shouldDecrease)) + { + onChangeCallback(currentValue); + } + } + + lefthandText.text = formatted(currentValue); + } + + function formatted(value:String):String + { + // FIXME: Can't add arrows around the text because the font doesn't support < > + // var leftArrow:String = selected ? '<' : ''; + // var rightArrow:String = selected ? '>' : ''; + return '${map.get(value) ?? value}'; + } +} diff --git a/source/funkin/ui/options/items/NumberPreferenceItem.hx b/source/funkin/ui/options/items/NumberPreferenceItem.hx new file mode 100644 index 0000000000..f3cd3cd46f --- /dev/null +++ b/source/funkin/ui/options/items/NumberPreferenceItem.hx @@ -0,0 +1,136 @@ +package funkin.ui.options.items; + +import funkin.ui.TextMenuList; +import funkin.ui.AtlasText; +import funkin.input.Controls; + +/** + * Preference item that allows the player to pick a value between min and max + */ +class NumberPreferenceItem extends TextMenuItem +{ + function controls():Controls + { + return PlayerSettings.player1.controls; + } + + // Widgets + public var lefthandText:AtlasText; + + // Constants + static final HOLD_DELAY:Float = 0.3; // seconds + static final CHANGE_RATE:Float = 0.08; // seconds + + // Constructor-initialized variables + public var currentValue:Float; + public var min:Float; + public var max:Float; + public var step:Float; + public var precision:Int; + public var onChangeCallback:NullVoid>; + public var valueFormatter:NullString>; + + // Variables + var holdDelayTimer:Float = HOLD_DELAY; // seconds + var changeRateTimer:Float = 0.0; // seconds + + /** + * @param min Minimum value (example: 0) + * @param max Maximum value (example: 100) + * @param step The value to increment/decrement by (example: 10) + * @param callback Will get called every time the user changes the setting; use this to apply/save the setting. + * @param valueFormatter Will get called every time the game needs to display the float value; use this to change how the displayed string looks + */ + public function new(x:Float, y:Float, name:String, defaultValue:Float, min:Float, max:Float, step:Float, precision:Int, ?callback:Float->Void, + ?valueFormatter:Float->String):Void + { + super(x, y, name, function() { + callback(this.currentValue); + }); + lefthandText = new AtlasText(15, y, formatted(defaultValue), AtlasFont.DEFAULT); + + updateHitbox(); + + this.currentValue = defaultValue; + this.min = min; + this.max = max; + this.step = step; + this.precision = precision; + this.onChangeCallback = callback; + this.valueFormatter = valueFormatter; + } + + override function update(elapsed:Float):Void + { + super.update(elapsed); + + // var fancyTextFancyColor:Color; + if (selected) + { + holdDelayTimer -= elapsed; + if (holdDelayTimer <= 0.0) + { + changeRateTimer -= elapsed; + } + + var jpLeft:Bool = controls().UI_LEFT_P; + var jpRight:Bool = controls().UI_RIGHT_P; + + if (jpLeft || jpRight) + { + holdDelayTimer = HOLD_DELAY; + changeRateTimer = 0.0; + } + + var shouldDecrease:Bool = jpLeft; + var shouldIncrease:Bool = jpRight; + + if (controls().UI_LEFT && holdDelayTimer <= 0.0 && changeRateTimer <= 0.0) + { + shouldDecrease = true; + changeRateTimer = CHANGE_RATE; + } + else if (controls().UI_RIGHT && holdDelayTimer <= 0.0 && changeRateTimer <= 0.0) + { + shouldIncrease = true; + changeRateTimer = CHANGE_RATE; + } + + // Actually increasing/decreasing the value + if (shouldDecrease) + { + var isBelowMin:Bool = currentValue - step < min; + currentValue = (currentValue - step).clamp(min, max); + if (onChangeCallback != null && !isBelowMin) onChangeCallback(currentValue); + } + else if (shouldIncrease) + { + var isAboveMax:Bool = currentValue + step > max; + currentValue = (currentValue + step).clamp(min, max); + if (onChangeCallback != null && !isAboveMax) onChangeCallback(currentValue); + } + } + + lefthandText.text = formatted(currentValue); + } + + /** Turns the float into a string */ + function formatted(value:Float):String + { + var float:Float = toFixed(value); + if (valueFormatter != null) + { + return valueFormatter(float); + } + else + { + return '${float}'; + } + } + + function toFixed(value:Float):Float + { + var multiplier:Float = Math.pow(10, precision); + return Math.floor(value * multiplier) / multiplier; + } +} From cd08116aed64f4761087e287a00786b77f40d02b Mon Sep 17 00:00:00 2001 From: FlooferLand! <76737186+FlooferLand@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:44:23 +0300 Subject: [PATCH 086/469] Added getWidth --- source/funkin/ui/AtlasText.hx | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/source/funkin/ui/AtlasText.hx b/source/funkin/ui/AtlasText.hx index 186d87c2a1..ef74abc1e2 100644 --- a/source/funkin/ui/AtlasText.hx +++ b/source/funkin/ui/AtlasText.hx @@ -152,6 +152,32 @@ class AtlasText extends FlxTypedSpriteGroup } } + public function getWidth():Int + { + var width = 0; + for (char in this.text.split("")) + { + switch (char) + { + case " ": + { + width += 40; + } + case "\n": + {} + case char: + { + var sprite = new AtlasChar(atlas, char); + sprite.revive(); + sprite.char = char; + sprite.alpha = 1; + width += Std.int(sprite.width); + } + } + } + return width; + } + override function toString() { return "InputItem, " + FlxStringUtil.getDebugString([ From 432c7090750d7abefad68b411ac83bb99543682b Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 27 Jun 2024 19:50:38 -0400 Subject: [PATCH 087/469] Animated pixel icons for freeplay mend --- assets | 2 +- source/funkin/ui/freeplay/FreeplayState.hx | 2 +- source/funkin/ui/freeplay/SongMenuItem.hx | 42 ++++++++++++++++++++-- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/assets b/assets index 225e248f14..c274cf6d5a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 225e248f148a92500a6fe90e4f10e4cd2acee782 +Subproject commit c274cf6d5a9d7e9e275f1f22d87d628eb379da5b diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 68c63efc45..eae123401c 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1738,7 +1738,7 @@ class FreeplayState extends MusicBeatSubState dj.confirm(); grpCapsules.members[curSelected].forcePosition(); - grpCapsules.members[curSelected].songText.flickerText(); + grpCapsules.members[curSelected].confirm(); // FlxTween.color(bgDad, 0.33, 0xFFFFFFFF, 0xFF555555, {ease: FlxEase.quadOut}); FlxTween.color(pinkBack, 0.33, 0xFFFFD0D5, 0xFF171831, {ease: FlxEase.quadOut}); diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx index 65a5d2e2d0..68525a5f7a 100644 --- a/source/funkin/ui/freeplay/SongMenuItem.hx +++ b/source/funkin/ui/freeplay/SongMenuItem.hx @@ -543,8 +543,6 @@ class SongMenuItem extends FlxSpriteGroup charPath += 'monsterpixel'; case 'mom-car': charPath += 'mommypixel'; - case 'dad': - charPath += 'daddypixel'; case 'darnell-blazin': charPath += 'darnellpixel'; case 'senpai-angry': @@ -559,7 +557,17 @@ class SongMenuItem extends FlxSpriteGroup return; } - pixelIcon.loadGraphic(Paths.image(charPath)); + var isAnimated = openfl.utils.Assets.exists(Paths.file('images/$charPath.xml')); + + if (isAnimated) + { + pixelIcon.frames = Paths.getSparrowAtlas(charPath); + } + else + { + pixelIcon.loadGraphic(Paths.image(charPath)); + } + pixelIcon.scale.x = pixelIcon.scale.y = 2; switch (char) @@ -571,6 +579,22 @@ class SongMenuItem extends FlxSpriteGroup } // pixelIcon.origin.x = capsule.origin.x; // pixelIcon.offset.x -= pixelIcon.origin.x; + + if (isAnimated) + { + pixelIcon.active = true; + + pixelIcon.animation.addByPrefix('idle', 'idle0', 10, true); + pixelIcon.animation.addByPrefix('confirm', 'confirm0', 10, false); + pixelIcon.animation.addByPrefix('confirm-hold', 'confirm-hold0', 10, true); + + pixelIcon.animation.finishCallback = function(name:String):Void { + trace('Finish pixel animation: ${name}'); + if (name == 'confirm') pixelIcon.animation.play('confirm-hold'); + }; + + pixelIcon.animation.play('idle'); + } } var frameInTicker:Float = 0; @@ -711,6 +735,18 @@ class SongMenuItem extends FlxSpriteGroup super.update(elapsed); } + /** + * Play any animations associated with selecting this song. + */ + public function confirm():Void + { + if (songText != null) songText.flickerText(); + if (pixelIcon != null) + { + pixelIcon.animation.play('confirm'); + } + } + public function intendedY(index:Int):Float { return (index * ((height * realScaled) + 10)) + 120; From bcaeae27f12be0a170d10b69e607eec29eba740c Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 1 Jul 2024 22:07:50 -0400 Subject: [PATCH 088/469] xml fix derp --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index c274cf6d5a..0dbd2e96ca 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit c274cf6d5a9d7e9e275f1f22d87d628eb379da5b +Subproject commit 0dbd2e96ca25ab5966cef05db6c76fe7fb145abf From d686890f763bd7a4db1841464dd7e3559fac1d0f Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Sat, 15 Jun 2024 05:59:58 +0100 Subject: [PATCH 089/469] most of the old code --- source/funkin/ui/charSelect/CharIcon.hx | 17 + .../funkin/ui/charSelect/CharIconCharacter.hx | 49 ++ source/funkin/ui/charSelect/CharIconLocked.hx | 3 + source/funkin/ui/charSelect/CharSelectGF.hx | 137 ++++ .../funkin/ui/charSelect/CharSelectPlayer.hx | 58 ++ .../ui/charSelect/CharSelectSubState.hx | 618 ++++++++++++++++++ source/funkin/ui/charSelect/Nametag.hx | 101 +++ source/funkin/ui/debug/DebugMenuSubState.hx | 6 + 8 files changed, 989 insertions(+) create mode 100644 source/funkin/ui/charSelect/CharIcon.hx create mode 100644 source/funkin/ui/charSelect/CharIconCharacter.hx create mode 100644 source/funkin/ui/charSelect/CharIconLocked.hx create mode 100644 source/funkin/ui/charSelect/CharSelectGF.hx create mode 100644 source/funkin/ui/charSelect/CharSelectPlayer.hx create mode 100644 source/funkin/ui/charSelect/CharSelectSubState.hx create mode 100644 source/funkin/ui/charSelect/Nametag.hx diff --git a/source/funkin/ui/charSelect/CharIcon.hx b/source/funkin/ui/charSelect/CharIcon.hx new file mode 100644 index 0000000000..6d6274286d --- /dev/null +++ b/source/funkin/ui/charSelect/CharIcon.hx @@ -0,0 +1,17 @@ +package funkin.ui.charSelect; + +import flixel.FlxSprite; + +class CharIcon extends FlxSprite +{ + public var locked:Bool = false; + + public function new(x:Float, y:Float, locked:Bool = false) + { + super(x, y); + + this.locked = locked; + + makeGraphic(128, 128); + } +} diff --git a/source/funkin/ui/charSelect/CharIconCharacter.hx b/source/funkin/ui/charSelect/CharIconCharacter.hx new file mode 100644 index 0000000000..7f7b5c212a --- /dev/null +++ b/source/funkin/ui/charSelect/CharIconCharacter.hx @@ -0,0 +1,49 @@ +package funkin.ui.charSelect; + +import openfl.display.BitmapData; +import openfl.filters.DropShadowFilter; +import openfl.filters.ConvolutionFilter; +import funkin.graphics.shaders.StrokeShader; + +class CharIconCharacter extends CharIcon +{ + public var dropShadowFilter:DropShadowFilter; + + var matrixFilter:Array = [ + 1, 1, 1, + 1, 1, 1, + 1, 1, 1 + ]; + + var divisor:Int = 1; + var bias:Int = 0; + var convolutionFilter:ConvolutionFilter; + + public var noDropShadow:BitmapData; + public var withDropShadow:BitmapData; + + var strokeShader:StrokeShader; + + public function new(path:String) + { + super(0, 0, false); + + loadGraphic(Paths.image('freeplay/icons/' + path + 'pixel')); + setGraphicSize(128, 128); + updateHitbox(); + antialiasing = false; + + strokeShader = new StrokeShader(); + // shader = strokeShader; + + // noDropShadow = pixels.clone(); + + // dropShadowFilter = new DropShadowFilter(5, 45, 0, 1, 0, 0); + // convolutionFilter = new ConvolutionFilter(3, 3, matrixFilter, divisor, bias); + // pixels.applyFilter(pixels, pixels.rect, new openfl.geom.Point(0, 0), dropShadowFilter); + // pixels.applyFilter(pixels, pixels.rect, new openfl.geom.Point(0, 0), convolutionFilter); + // withDropShadow = pixels.clone(); + + // pixels = noDropShadow.clone(); + } +} diff --git a/source/funkin/ui/charSelect/CharIconLocked.hx b/source/funkin/ui/charSelect/CharIconLocked.hx new file mode 100644 index 0000000000..dbe84a6ce1 --- /dev/null +++ b/source/funkin/ui/charSelect/CharIconLocked.hx @@ -0,0 +1,3 @@ +package funkin.ui.charSelect; + +class CharIconLocked extends CharIcon {} diff --git a/source/funkin/ui/charSelect/CharSelectGF.hx b/source/funkin/ui/charSelect/CharSelectGF.hx new file mode 100644 index 0000000000..6d8e3e657b --- /dev/null +++ b/source/funkin/ui/charSelect/CharSelectGF.hx @@ -0,0 +1,137 @@ +package funkin.ui.charSelect; + +import funkin.graphics.adobeanimate.FlxAtlasSprite; +import flixel.tweens.FlxTween; +import flixel.tweens.FlxEase; +import flixel.math.FlxMath; +import funkin.util.FramesJSFLParser; +import funkin.util.FramesJSFLParser.FramesJSFLInfo; +import funkin.util.FramesJSFLParser.FramesJSFLFrame; +import flixel.math.FlxMath; + +class CharSelectGF extends FlxAtlasSprite +{ + var fadeTimer:Float = 0; + var fadingStatus:FadeStatus = OFF; + var fadeAnimIndex:Int = 0; + + var animInInfo:FramesJSFLInfo; + var animOutInfo:FramesJSFLInfo; + + var intendedYPos:Float = 0; + var intendedAlpha:Float = 0; + + public function new() + { + super(0, 0, Paths.animateAtlas("charSelect/gfChill")); + anim.play(""); + switchGF("bf"); + } + + override public function update(elapsed:Float) + { + super.update(elapsed); + + switch (fadingStatus) + { + case OFF: + // do nothing if it's off! + // or maybe force position to be 0,0? + // maybe reset timers? + resetFadeAnimParams(); + case FADE_OUT: + doFade(animOutInfo); + case FADE_IN: + doFade(animInInfo); + default: + } + + if (FlxG.keys.justPressed.J) + { + alpha = 1; + x = y = 0; + fadingStatus = FADE_OUT; + } + if (FlxG.keys.justPressed.K) + { + alpha = 0; + fadingStatus = FADE_IN; + } + } + + /** + * @param animInfo Should not be confused with animInInfo! + * This is merely a local var for the function! + */ + function doFade(animInfo:FramesJSFLInfo) + { + fadeTimer += FlxG.elapsed; + if (fadeTimer >= 1 / 24) + { + fadeTimer = 0; + // only inc the index for the first frame, used for reference of where to "start" + if (fadeAnimIndex == 0) + { + fadeAnimIndex++; + return; + } + + var curFrame:FramesJSFLFrame = animInfo.frames[fadeAnimIndex]; + var prevFrame:FramesJSFLFrame = animInfo.frames[fadeAnimIndex - 1]; + + var xDiff:Float = curFrame.x - prevFrame.x; + var yDiff:Float = curFrame.y - prevFrame.y; + var alphaDiff:Float = curFrame.alpha - prevFrame.alpha; + alphaDiff /= 100; // flash exports alpha as a whole number + + alpha += alphaDiff; + alpha = FlxMath.bound(alpha, 0, 1); + x += xDiff; + y += yDiff; + + fadeAnimIndex++; + } + + if (fadeAnimIndex >= animInfo.frames.length) fadingStatus = OFF; + } + + function resetFadeAnimParams() + { + fadeTimer = 0; + fadeAnimIndex = 0; + } + + public function switchGF(str:String) + { + str = switch (str) + { + case "pico": + "nene"; + case "bf": + "gf"; + default: + "gf"; + } + + switch str + { + default: + loadAtlas(Paths.animateAtlas("charSelect/" + str + "Chill")); + } + + animInInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + str + "AnimInfo/" + str + "In.txt")); + animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + str + "AnimInfo/" + str + "Out.txt")); + + anim.play(""); + playAnimation("idle", true, false, true); + + updateHitbox(); + } +} + +enum FadeStatus +{ + OFF; + FADE_OUT; + FADE_IN; +} diff --git a/source/funkin/ui/charSelect/CharSelectPlayer.hx b/source/funkin/ui/charSelect/CharSelectPlayer.hx new file mode 100644 index 0000000000..9322369baf --- /dev/null +++ b/source/funkin/ui/charSelect/CharSelectPlayer.hx @@ -0,0 +1,58 @@ +package funkin.ui.charSelect; + +import flixel.FlxSprite; +import funkin.graphics.adobeanimate.FlxAtlasSprite; + +class CharSelectPlayer extends FlxAtlasSprite +{ + public function new(x:Float, y:Float) + { + super(x, y, Paths.animateAtlas("charSelect/bfChill")); + + onAnimationFinish.add(function(animLabel:String) { + switch (animLabel) + { + case "slidein": + if (hasAnimation("slidein idle point")) playAnimation("slidein idle point", true, false, false); + else + playAnimation("idle", true, false, true); + case "slidein idle point": + playAnimation("idle", true, false, true); + case "select": + anim.pause(); + case "deselect": + playAnimation("deselect loop start", true, false, true); + } + }); + } + + public function updatePosition(str:String) + { + switch (str) + { + case "bf": + x = 0; + y = 0; + case "pico": + x = 0; + y = 0; + case "random": + } + } + + public function switchChar(str:String) + { + switch str + { + default: + loadAtlas(Paths.animateAtlas("charSelect/" + str + "Chill")); + } + + anim.play(""); + playAnimation("slidein", true, false, false); + + updateHitbox(); + + updatePosition(str); + } +} diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx new file mode 100644 index 0000000000..a360328a25 --- /dev/null +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -0,0 +1,618 @@ +package funkin.ui.charSelect; + +import flixel.text.FlxText; +import flixel.FlxSprite; +import flixel.system.debug.watch.Tracker.TrackerProfile; +import flixel.math.FlxPoint; +import flixel.tweens.FlxTween; +import openfl.display.BlendMode; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.group.FlxSpriteGroup; +import funkin.play.stage.Stage; +import funkin.play.stage.StageData.StageDataParser; +import funkin.modding.events.ScriptEvent; +import funkin.modding.events.ScriptEventDispatcher; +import funkin.graphics.adobeanimate.FlxAtlasSprite; +import flixel.FlxObject; +import openfl.display.BlendMode; +import flixel.group.FlxGroup; +import funkin.util.MathUtil; +import flixel.util.FlxTimer; +import flixel.tweens.FlxEase; +import flixel.sound.FlxSound; +import funkin.audio.FunkinSound; + +class CharSelectSubState extends MusicBeatSubState +{ + var cursor:FlxSprite; + var cursorBlue:FlxSprite; + var cursorDarkBlue:FlxSprite; + + var grpCursors:FlxTypedGroup; + + var cursorConfirmed:FlxSprite; + var cursorDenied:FlxSprite; + + var cursorX:Int = 0; + var cursorY:Int = 0; + + var cursorFactor:Float = 110; + var cursorOffsetX:Float = -16; + var cursorOffsetY:Float = -48; + + var cursorLocIntended:FlxPoint = new FlxPoint(0, 0); + var lerpAmnt:Float = 0.95; + + var tmrFrames:Int = 60; + + var currentStage:Stage; + + var playerChill:CharSelectPlayer; + var playerChillOut:CharSelectPlayer; + var gfChill:CharSelectGF; + var gfChillOut:CharSelectGF; + + var curChar(default, set):String = "pico"; + var nametag:Nametag; + var camFollow:FlxObject; + + var availableChars:Map = new Map(); + var pressedSelect:Bool = false; + + var selectTimer:FlxTimer = new FlxTimer(); + var selectSound:FunkinSound; + + public function new() + { + super(); + + availableChars.set(4, "bf"); + availableChars.set(3, "pico"); + } + + override public function create() + { + super.create(); + + selectSound = new FunkinSound(); + selectSound.loadEmbedded(Paths.sound('CS_select')); + selectSound.pitch = 1; + selectSound.volume = 0.7; + FlxG.sound.defaultSoundGroup.add(selectSound); + + Conductor.forceBPM(90); + + var bg:FlxSprite = new FlxSprite(-153, -140); + bg.loadGraphic(Paths.image('charSelect/charSelectBG')); + bg.scrollFactor.set(0.1, 0.1); + add(bg); + + var crowd:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/crowd")); + crowd.anim.play(""); + crowd.scrollFactor.set(0.3, 0.3); + add(crowd); + + var stageSpr:FlxSprite = new FlxSprite(-40, 391); + stageSpr.frames = Paths.getSparrowAtlas("charSelect/charSelectStage"); + stageSpr.animation.addByPrefix("idle", "stage", 24, true); + stageSpr.animation.play("idle"); + add(stageSpr); + + var curtains:FlxSprite = new FlxSprite(-47, -49); + curtains.loadGraphic(Paths.image('charSelect/curtains')); + curtains.scrollFactor.set(1.4, 1.4); + add(curtains); + + var barthing:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/barThing")); + barthing.anim.play(""); + barthing.blend = BlendMode.MULTIPLY; + barthing.scrollFactor.set(0, 0); + add(barthing); + + var charLight:FlxSprite = new FlxSprite(800, 250); + charLight.loadGraphic(Paths.image('charSelect/charLight')); + add(charLight); + + var charLightGF:FlxSprite = new FlxSprite(180, 240); + charLightGF.loadGraphic(Paths.image('charSelect/charLight')); + add(charLightGF); + + gfChill = new CharSelectGF(); + gfChill.switchGF("bf"); + add(gfChill); + + playerChill = new CharSelectPlayer(0, 0); + playerChill.switchChar("bf"); + add(playerChill); + + playerChillOut = new CharSelectPlayer(0, 0); + playerChillOut.switchChar("bf"); + add(playerChillOut); + + var speakers:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/charSelectSpeakers")); + speakers.anim.play(""); + speakers.scrollFactor.set(1.8, 1.8); + add(speakers); + + var fgBlur:FlxSprite = new FlxSprite(-125, 170); + fgBlur.loadGraphic(Paths.image('charSelect/foregroundBlur')); + fgBlur.blend = openfl.display.BlendMode.MULTIPLY; + add(fgBlur); + + var dipshitBlur:FlxSprite = new FlxSprite(419, -65); + dipshitBlur.frames = Paths.getSparrowAtlas("charSelect/dipshitBlur"); + dipshitBlur.animation.addByPrefix('idle', "CHOOSE vertical", 24, true); + dipshitBlur.blend = BlendMode.ADD; + dipshitBlur.animation.play("idle"); + add(dipshitBlur); + + var dipshitBacking:FlxSprite = new FlxSprite(423, -17); + dipshitBacking.frames = Paths.getSparrowAtlas("charSelect/dipshitBacking"); + dipshitBacking.animation.addByPrefix('idle', "CHOOSE horizontal", 24, true); + dipshitBacking.blend = BlendMode.ADD; + dipshitBacking.animation.play("idle"); + add(dipshitBacking); + + var chooseDipshit:FlxSprite = new FlxSprite(426, -13); + chooseDipshit.loadGraphic(Paths.image('charSelect/chooseDipshit')); + add(chooseDipshit); + + chooseDipshit.scrollFactor.set(); + dipshitBacking.scrollFactor.set(); + dipshitBlur.scrollFactor.set(); + + nametag = new Nametag(); + add(nametag); + + nametag.scrollFactor.set(); + + FlxG.debugger.addTrackerProfile(new TrackerProfile(FlxSprite, ["x", "y", "alpha", "scale", "blend"])); + FlxG.debugger.addTrackerProfile(new TrackerProfile(FlxAtlasSprite, ["x", "y"])); + FlxG.debugger.addTrackerProfile(new TrackerProfile(FlxSound, ["pitch", "volume"])); + + // FlxG.debugger.track(crowd); + // FlxG.debugger.track(stageSpr, "stageSpr"); + // FlxG.debugger.track(bfChill, "bf chill"); + // FlxG.debugger.track(playerChill, "player"); + // FlxG.debugger.track(nametag, "nametag"); + FlxG.debugger.track(selectSound, "selectSound"); + // FlxG.debugger.track(chooseDipshit, "choose dipshit"); + // FlxG.debugger.track(barthing, "barthing"); + // FlxG.debugger.track(fgBlur, "fgBlur"); + // FlxG.debugger.track(dipshitBlur, "dipshitBlur"); + // FlxG.debugger.track(dipshitBacking, "dipshitBacking"); + // FlxG.debugger.track(charLightGF, "charLight"); + // FlxG.debugger.track(gfChill, "gfChill"); + + grpCursors = new FlxTypedGroup(); + add(grpCursors); + + cursor = new FlxSprite(0, 0); + cursor.loadGraphic(Paths.image('charSelect/charSelector')); + cursor.color = 0xFFFFFF00; + + // FFCC00 + + cursorBlue = new FlxSprite(0, 0); + cursorBlue.loadGraphic(Paths.image('charSelect/charSelector')); + cursorBlue.color = 0xFF3EBBFF; + + cursorDarkBlue = new FlxSprite(0, 0); + cursorDarkBlue.loadGraphic(Paths.image('charSelect/charSelector')); + cursorDarkBlue.color = 0xFF3C74F7; + + cursorBlue.blend = BlendMode.SCREEN; + cursorDarkBlue.blend = BlendMode.SCREEN; + + cursorConfirmed = new FlxSprite(0, 0); + cursorConfirmed.scrollFactor.set(); + cursorConfirmed.frames = Paths.getSparrowAtlas("charSelect/charSelectorConfirm"); + cursorConfirmed.animation.addByPrefix("idle", "cursor ACCEPTED", 24, true); + cursorConfirmed.visible = false; + add(cursorConfirmed); + + cursorDenied = new FlxSprite(0, 0); + cursorDenied.scrollFactor.set(); + cursorDenied.frames = Paths.getSparrowAtlas("charSelect/charSelectorDenied"); + cursorDenied.animation.addByPrefix("idle", "cursor DENIED", 24, false); + cursorDenied.visible = false; + add(cursorDenied); + + grpCursors.add(cursorDarkBlue); + grpCursors.add(cursorBlue); + grpCursors.add(cursor); + + initLocks(); + + cursor.scrollFactor.set(); + cursorBlue.scrollFactor.set(); + cursorDarkBlue.scrollFactor.set(); + + FlxTween.color(cursor, 0.2, 0xFFFFFF00, 0xFFFFCC00, {type: FlxTween.PINGPONG}); + + // FlxG.debugger.track(cursor); + + FlxG.debugger.addTrackerProfile(new TrackerProfile(CharSelectSubState, ["curChar", "grpXSpread", "grpYSpread"])); + FlxG.debugger.track(this); + + FlxG.sound.playMusic(Paths.music('charSelect/charSelectMusic')); + + camFollow = new FlxObject(0, 0, 1, 1); + add(camFollow); + camFollow.screenCenter(); + + FlxG.camera.follow(camFollow, LOCKON, 0.01); + + var temp:FlxSprite = new FlxSprite(); + temp.loadGraphic(Paths.image('charSelect/placement')); + add(temp); + temp.alpha = 0.0; + Conductor.stepHit.add(spamOnStep); + // FlxG.debugger.track(temp, "tempBG"); + } + + var grpIcons:FlxSpriteGroup; + + var grpXSpread(default, set):Float = 107; + var grpYSpread(default, set):Float = 127; + + function initLocks() + { + grpIcons = new FlxSpriteGroup(); + add(grpIcons); + + FlxG.debugger.addTrackerProfile(new TrackerProfile(FlxSpriteGroup, ["x", "y"])); + // FlxG.debugger.track(grpIcons, "iconGrp"); + + for (i in 0...9) + { + if (availableChars.exists(i)) + { + var path:String = availableChars.get(i); + var temp:CharIconCharacter = new CharIconCharacter(path); + temp.ID = 0; + + var idleAnimPrefix:String = switch (path) + { + case "pico": + "Pico Icon"; + case "bf": + "boyfriend icon instance 1"; + case _: + "Pico Icon"; + }; + + // temp.animation.addByPrefix("idle", idleAnimPrefix, 0, false); + // temp.animation.play("idle"); + grpIcons.add(temp); + } + else + { + var temp:FlxSprite = new FlxSprite(); + temp.ID = 1; + temp.frames = Paths.getSparrowAtlas("charSelect/locks"); + + var lockIndex:Int = i + 1; + + if (i == 3) lockIndex = 3; + + if (i >= 4) lockIndex = i - 2; + + temp.animation.addByIndices("idle", "LOCK FULL " + lockIndex + " instance 1", [0], "", 24); + temp.animation.addByIndices("selected", "LOCK FULL " + lockIndex + " instance 1", [3, 4, 5], "", 24, false); + temp.animation.addByIndices("clicked", "LOCK FULL " + lockIndex + " instance 1", [9, 10, 11, 12, 13, 14, 15], "", 24, false); + + temp.animation.play("idle"); + + grpIcons.add(temp); + } + } + + updateIconPositions(); + + grpIcons.scrollFactor.set(); + } + + function updateIconPositions() + { + grpIcons.x = 450; + grpIcons.y = 120; + for (index => member in grpIcons.members) + { + var posX:Float = (index % 3); + var posY:Float = Math.floor(index / 3); + + member.x = posX * grpXSpread; + member.y = posY * grpYSpread; + + member.x += grpIcons.x; + member.y += grpIcons.y; + } + } + + var holdTmrUp:Float = 0; + var holdTmrDown:Float = 0; + var holdTmrLeft:Float = 0; + var holdTmrRight:Float = 0; + var spamUp:Bool = false; + var spamDown:Bool = false; + var spamLeft:Bool = false; + var spamRight:Bool = false; + + override public function update(elapsed:Float) + { + super.update(elapsed); + + Conductor.update(); + + if (controls.UI_UP_R || controls.UI_DOWN_R || controls.UI_LEFT_R || controls.UI_RIGHT_R) selectSound.pitch = 1; + + if (controls.UI_UP) holdTmrUp += elapsed; + if (controls.UI_UP_R) + { + holdTmrUp = 0; + spamUp = false; + } + + if (controls.UI_DOWN) holdTmrDown += elapsed; + if (controls.UI_DOWN_R) + { + holdTmrDown = 0; + spamDown = false; + } + + if (controls.UI_LEFT) holdTmrLeft += elapsed; + if (controls.UI_LEFT_R) + { + holdTmrLeft = 0; + spamLeft = false; + } + + if (controls.UI_RIGHT) holdTmrRight += elapsed; + if (controls.UI_RIGHT_R) + { + holdTmrRight = 0; + spamRight = false; + } + + var initSpam = 0.5; + + if (holdTmrUp >= initSpam) spamUp = true; + if (holdTmrDown >= initSpam) spamDown = true; + if (holdTmrLeft >= initSpam) spamLeft = true; + if (holdTmrRight >= initSpam) spamRight = true; + + if (controls.UI_UP_P) + { + cursorY -= 1; + holdTmrUp = 0; + + selectSound.play(true); + } + if (controls.UI_DOWN_P) + { + cursorY += 1; + holdTmrDown = 0; + selectSound.play(true); + } + if (controls.UI_LEFT_P) + { + cursorX -= 1; + holdTmrLeft = 0; + selectSound.play(true); + } + if (controls.UI_RIGHT_P) + { + cursorX += 1; + holdTmrRight = 0; + selectSound.play(true); + } + + if (cursorX < -1) + { + cursorX = 1; + } + if (cursorX > 1) + { + cursorX = -1; + } + if (cursorY < -1) + { + cursorY = 1; + } + if (cursorY > 1) + { + cursorY = -1; + } + + if (availableChars.exists(getCurrentSelected())) + { + curChar = availableChars.get(getCurrentSelected()); + + if (controls.ACCEPT) + { + cursorConfirmed.visible = true; + cursorConfirmed.x = cursor.x - 2; + cursorConfirmed.y = cursor.y - 4; + cursorConfirmed.animation.play("idle", true); + + grpCursors.visible = false; + + FlxG.sound.play(Paths.sound('CS_confirm')); + + FlxTween.tween(FlxG.sound.music, {pitch: 0.1}, 1.5, {ease: FlxEase.quadInOut}); + playerChill.playAnimation("select"); + pressedSelect = true; + selectTimer.start(1.5, (_) -> { + pressedSelect = false; + close(); + }); + } + + if (pressedSelect && controls.BACK) + { + cursorConfirmed.visible = false; + grpCursors.visible = true; + + FlxTween.globalManager.cancelTweensOf(FlxG.sound.music); + FlxTween.tween(FlxG.sound.music, {pitch: 1.0}, 1, {ease: FlxEase.quartInOut}); + playerChill.playAnimation("deselect"); + pressedSelect = false; + selectTimer.cancel(); + } + } + else + { + curChar = "locked"; + + if (controls.ACCEPT) + { + cursorDenied.visible = true; + cursorDenied.x = cursor.x - 2; + cursorDenied.y = cursor.y - 4; + cursorDenied.animation.play("idle", true); + cursorDenied.animation.finishCallback = (_) -> { + cursorDenied.visible = false; + }; + } + } + + updateLockAnims(); + + camFollow.screenCenter(); + camFollow.x += cursorX * 10; + camFollow.y += cursorY * 10; + + cursorLocIntended.x = (cursorFactor * cursorX) + (FlxG.width / 2) - cursor.width / 2; + cursorLocIntended.y = (cursorFactor * cursorY) + (FlxG.height / 2) - cursor.height / 2; + + cursorLocIntended.x += cursorOffsetX; + cursorLocIntended.y += cursorOffsetY; + + cursor.x = MathUtil.coolLerp(cursor.x, cursorLocIntended.x, lerpAmnt); + cursor.y = MathUtil.coolLerp(cursor.y, cursorLocIntended.y, lerpAmnt); + + cursorBlue.x = MathUtil.coolLerp(cursorBlue.x, cursor.x, lerpAmnt * 0.4); + cursorBlue.y = MathUtil.coolLerp(cursorBlue.y, cursor.y, lerpAmnt * 0.4); + + cursorDarkBlue.x = MathUtil.coolLerp(cursorDarkBlue.x, cursorLocIntended.x, lerpAmnt * 0.2); + cursorDarkBlue.y = MathUtil.coolLerp(cursorDarkBlue.y, cursorLocIntended.y, lerpAmnt * 0.2); + } + + function spamOnStep() + { + if (spamUp || spamDown || spamLeft || spamRight) + { + selectSound.changePitchBySemitone(1); + if (selectSound.pitch > 5) selectSound.pitch = 5; + selectSound.play(true); + + if (spamUp) + { + cursorY -= 1; + holdTmrUp = 0; + } + if (spamDown) + { + cursorY += 1; + holdTmrDown = 0; + } + if (spamLeft) + { + cursorX -= 1; + holdTmrLeft = 0; + } + if (spamRight) + { + cursorX += 1; + holdTmrRight = 0; + } + } + } + + private function updateLockAnims() + { + for (index => member in grpIcons.group.members) + { + switch (member.ID) + { + case 1: + if (index == getCurrentSelected()) + { + switch (member.animation.curAnim.name) + { + case "idle": + member.animation.play("selected"); + case "selected" | "clicked": + if (controls.ACCEPT) member.animation.play("clicked", true); + } + } + else + { + member.animation.play("idle"); + } + case 0: + var memb:CharIconCharacter = cast member; + + if (index == getCurrentSelected()) + { + // memb.pixels = memb.withDropShadow.clone(); + memb.setGraphicSize(128 * 1.3); + } + else + { + // memb.pixels = memb.noDropShadow.clone(); + memb.setGraphicSize(128); + } + } + } + } + + function getCurrentSelected() + { + var tempX:Int = cursorX + 1; + var tempY:Int = cursorY + 1; + var gridPosition:Int = tempX + tempY * 3; + return gridPosition; + } + + function set_curChar(value:String):String + { + if (curChar == value) return value; + + curChar = value; + + nametag.switchChar(value); + playerChill.visible = false; + playerChillOut.visible = true; + playerChillOut.anim.goToFrameLabel("slideout"); + playerChillOut.anim.callback = (_, frame:Int) -> { + if (frame == playerChillOut.anim.getFrameLabel("slideout").index + 1) + { + playerChill.visible = true; + playerChill.switchChar(value); + gfChill.switchGF(value); + } + if (frame == playerChillOut.anim.getFrameLabel("slideout").index + 2) + { + playerChillOut.switchChar(value); + playerChillOut.visible = false; + } + }; + return value; + } + + function set_grpXSpread(value:Float):Float + { + grpXSpread = value; + updateIconPositions(); + return value; + } + + function set_grpYSpread(value:Float):Float + { + grpYSpread = value; + updateIconPositions(); + return value; + } +} diff --git a/source/funkin/ui/charSelect/Nametag.hx b/source/funkin/ui/charSelect/Nametag.hx new file mode 100644 index 0000000000..b6cedb0c72 --- /dev/null +++ b/source/funkin/ui/charSelect/Nametag.hx @@ -0,0 +1,101 @@ +package funkin.ui.charSelect; + +import flixel.FlxSprite; +import funkin.graphics.shaders.MosaicEffect; +import flixel.util.FlxTimer; + +class Nametag extends FlxSprite +{ + var midpointX(default, set):Float = 1008; + var midpointY(default, set):Float = 100; + var mosaicShader:MosaicEffect; + + public function new(?x:Float = 0, ?y:Float = 0) + { + super(x, y); + + mosaicShader = new MosaicEffect(); + shader = mosaicShader; + + switchChar("bf"); + + FlxG.debugger.addTrackerProfile(new TrackerProfile(Nametag, ["midpointX", "midpointY"])); + FlxG.debugger.track(this, "Nametag"); + } + + public function updatePosition():Void + { + var offsetX:Float = getMidpoint().x - midpointX; + var offsetY:Float = getMidpoint().y - midpointY; + + x -= offsetX; + y -= offsetY; + } + + public function switchChar(str:String):Void + { + shaderEffect(); + + new FlxTimer().start(4 / 30, _ -> { + var path:String = str; + switch str + { + case "bf": + path = "boyfriend"; + } + + loadGraphic(Paths.image('charSelect/' + path + "Nametag")); + updateHitbox(); + scale.x = scale.y = 0.77; + + updatePosition(); + shaderEffect(true); + }); + } + + function shaderEffect(fadeOut:Bool = false):Void + { + if (fadeOut) + { + setBlockTimer(0, 1, 1); + setBlockTimer(1, width / 27, height / 26); + setBlockTimer(2, width / 10, height / 10); + + setBlockTimer(3, 1, 1); + } + else + { + setBlockTimer(0, (width / 10), (height / 10)); + setBlockTimer(1, width / 73, height / 6); + setBlockTimer(2, width / 10, height / 10); + } + } + + function setBlockTimer(frame:Int, ?forceX:Float, ?forceY:Float) + { + var daX:Float = 10 * FlxG.random.int(1, 4); + var daY:Float = 10 * FlxG.random.int(1, 4); + + if (forceX != null) daX = forceX; + + if (forceY != null) daY = forceY; + + new FlxTimer().start(frame / 30, _ -> { + mosaicShader.setBlockSize(daX, daY); + }); + } + + function set_midpointX(val:Float):Float + { + this.midpointX = val; + updatePosition(); + return val; + } + + function set_midpointY(val:Float):Float + { + this.midpointY = val; + updatePosition(); + return val; + } +} diff --git a/source/funkin/ui/debug/DebugMenuSubState.hx b/source/funkin/ui/debug/DebugMenuSubState.hx index f8b1be9d2d..22dc5dba66 100644 --- a/source/funkin/ui/debug/DebugMenuSubState.hx +++ b/source/funkin/ui/debug/DebugMenuSubState.hx @@ -56,6 +56,7 @@ class DebugMenuSubState extends MusicBeatSubState // Call onMenuChange when the first item is created to move the camera . onMenuChange(createItem("CHART EDITOR", openChartEditor)); // createItem("Input Offset Testing", openInputOffsetTesting); + createItem("CHARACTER SELECT", openCharSelect, true); createItem("ANIMATION EDITOR", openAnimationEditor); // createItem("STAGE EDITOR", openStageEditor); // createItem("TEST STICKERS", testStickers); @@ -102,6 +103,11 @@ class DebugMenuSubState extends MusicBeatSubState trace('Input Offset Testing'); } + function openCharSelect() + { + FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState()); + } + function openAnimationEditor() { FlxG.switchState(() -> new funkin.ui.debug.anim.DebugBoundingState()); From be4ca0f47d2b6736984d4d7933d8e80f264b676a Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Sat, 15 Jun 2024 06:00:13 +0100 Subject: [PATCH 090/469] assets..... submod... --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 0dbd2e96ca..83f658fcd8 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0dbd2e96ca25ab5966cef05db6c76fe7fb145abf +Subproject commit 83f658fcd87de54ff9c1f7a497b916239d53a491 From 28068b1a86f46e7ff2dacaa18c426b6644bb6767 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Sat, 15 Jun 2024 06:08:55 +0100 Subject: [PATCH 091/469] updating more stuff --- .../funkin/graphics/shaders/MosaicEffect.hx | 23 +++++++++ .../ui/charSelect/CharSelectSubState.hx | 1 - source/funkin/util/FramesJSFLParser.hx | 48 +++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 source/funkin/graphics/shaders/MosaicEffect.hx create mode 100644 source/funkin/util/FramesJSFLParser.hx diff --git a/source/funkin/graphics/shaders/MosaicEffect.hx b/source/funkin/graphics/shaders/MosaicEffect.hx new file mode 100644 index 0000000000..fc3737afff --- /dev/null +++ b/source/funkin/graphics/shaders/MosaicEffect.hx @@ -0,0 +1,23 @@ +package funkin.graphics.shaders; + +import flixel.addons.display.FlxRuntimeShader; +import openfl.utils.Assets; +import funkin.Paths; +import flixel.math.FlxPoint; + +class MosaicEffect extends FlxRuntimeShader +{ + public var blockSize:FlxPoint = FlxPoint.get(1.0, 1.0); + + public function new() + { + super(Assets.getText(Paths.frag('mosaic'))); + setBlockSize(1.0, 1.0); + } + + public function setBlockSize(w:Float, h:Float) + { + blockSize.set(w, h); + setFloatArray("uBlocksize", [w, h]); + } +} diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index a360328a25..40fa874f36 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -9,7 +9,6 @@ import openfl.display.BlendMode; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxSpriteGroup; import funkin.play.stage.Stage; -import funkin.play.stage.StageData.StageDataParser; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEventDispatcher; import funkin.graphics.adobeanimate.FlxAtlasSprite; diff --git a/source/funkin/util/FramesJSFLParser.hx b/source/funkin/util/FramesJSFLParser.hx new file mode 100644 index 0000000000..33bcf9d9d9 --- /dev/null +++ b/source/funkin/util/FramesJSFLParser.hx @@ -0,0 +1,48 @@ +package funkin.util; + +import openfl.Assets; + +/** + * See `funScripts/jsfl/frames.jsfl` for more information in the art repo/folder! + * Homemade dipshit proprietary format to get simple animation info out of flash! + * Pure convienience! + */ +class FramesJSFLParser +{ + public static function parse(path:String):FramesJSFLInfo + { + var text:String = Assets.getText(path); + + // TODO: error handle if text is null + + var output:FramesJSFLInfo = {frames: []}; + + var frames:Array = text.split("\n"); + + for (frame in frames) + { + var frameInfo:Array = frame.split(" "); + + var x:Float = Std.parseFloat(frameInfo[0]); + var y:Float = Std.parseFloat(frameInfo[1]); + var alpha:Float = Std.parseFloat(frameInfo[2]); + + var shit:FramesJSFLFrame = {x: x, y: y, alpha: alpha}; + output.frames.push(shit); + } + + return output; + } +} + +typedef FramesJSFLInfo = +{ + var frames:Array; +} + +typedef FramesJSFLFrame = +{ + var x:Float; + var y:Float; + var alpha:Float; +} From b1b7a032bec5cbd43149b8da971a17a9539b2a7f Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Sat, 15 Jun 2024 07:03:22 +0100 Subject: [PATCH 092/469] music updates --- source/funkin/ui/charSelect/CharSelectSubState.hx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 40fa874f36..19f00dc928 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -79,7 +79,12 @@ class CharSelectSubState extends MusicBeatSubState selectSound.volume = 0.7; FlxG.sound.defaultSoundGroup.add(selectSound); - Conductor.forceBPM(90); + FunkinSound.playMusic('charSelect', + { + startingVolume: 0.0, + overrideExisting: true, + restartTrack: true + }); var bg:FlxSprite = new FlxSprite(-153, -140); bg.loadGraphic(Paths.image('charSelect/charSelectBG')); @@ -342,7 +347,7 @@ class CharSelectSubState extends MusicBeatSubState { super.update(elapsed); - Conductor.update(); + // Conductor.update(); if (controls.UI_UP_R || controls.UI_DOWN_R || controls.UI_LEFT_R || controls.UI_RIGHT_R) selectSound.pitch = 1; @@ -502,7 +507,7 @@ class CharSelectSubState extends MusicBeatSubState { if (spamUp || spamDown || spamLeft || spamRight) { - selectSound.changePitchBySemitone(1); + // selectSound.changePitchBySemitone(1); if (selectSound.pitch > 5) selectSound.pitch = 5; selectSound.play(true); From 8dfd36d227d9c9cbf736bff713762ffeb36f0d0f Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 15 Jun 2024 03:27:59 -0400 Subject: [PATCH 093/469] menu spam fixie --- checkstyle.json | 3 ++- source/funkin/ui/charSelect/CharSelectSubState.hx | 14 ++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/checkstyle.json b/checkstyle.json index 41f0a79986..6c463f75a2 100644 --- a/checkstyle.json +++ b/checkstyle.json @@ -327,7 +327,8 @@ "INLINE", "DYNAMIC", "FINAL" - ] + ], + "severity": "IGNORE" }, "type": "ModifierOrder" }, diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 19f00dc928..022db29094 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -69,7 +69,7 @@ class CharSelectSubState extends MusicBeatSubState availableChars.set(3, "pico"); } - override public function create() + override public function create():Void { super.create(); @@ -79,6 +79,8 @@ class CharSelectSubState extends MusicBeatSubState selectSound.volume = 0.7; FlxG.sound.defaultSoundGroup.add(selectSound); + Conductor.instance.forceBPM(90); + FunkinSound.playMusic('charSelect', { startingVolume: 0.0, @@ -343,11 +345,11 @@ class CharSelectSubState extends MusicBeatSubState var spamLeft:Bool = false; var spamRight:Bool = false; - override public function update(elapsed:Float) + override public function update(elapsed:Float):Void { super.update(elapsed); - // Conductor.update(); + Conductor.instance.update(); if (controls.UI_UP_R || controls.UI_DOWN_R || controls.UI_LEFT_R || controls.UI_RIGHT_R) selectSound.pitch = 1; @@ -503,7 +505,7 @@ class CharSelectSubState extends MusicBeatSubState cursorDarkBlue.y = MathUtil.coolLerp(cursorDarkBlue.y, cursorLocIntended.y, lerpAmnt * 0.2); } - function spamOnStep() + function spamOnStep():Void { if (spamUp || spamDown || spamLeft || spamRight) { @@ -534,7 +536,7 @@ class CharSelectSubState extends MusicBeatSubState } } - private function updateLockAnims() + private function updateLockAnims():Void { for (index => member in grpIcons.group.members) { @@ -572,7 +574,7 @@ class CharSelectSubState extends MusicBeatSubState } } - function getCurrentSelected() + function getCurrentSelected():Int { var tempX:Int = cursorX + 1; var tempY:Int = cursorY + 1; From 3fa9b2a635b54a4c94ebbfc2ff4a5a1c20584101 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 20 Jun 2024 02:40:50 +0100 Subject: [PATCH 094/469] new music --- .../funkin/ui/charSelect/CharSelectSubState.hx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 022db29094..0e8bdd1bba 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -79,14 +79,23 @@ class CharSelectSubState extends MusicBeatSubState selectSound.volume = 0.7; FlxG.sound.defaultSoundGroup.add(selectSound); - Conductor.instance.forceBPM(90); - - FunkinSound.playMusic('charSelect', + // playing it here to preload it. not doing this makes a super awkward pause at the end of the intro + // TODO: probably make an intro thing for funkinSound itself that preloads the next audio? + FunkinSound.playMusic('stayFunky', { - startingVolume: 0.0, + startingVolume: 0, overrideExisting: true, restartTrack: true }); + var introMusic:String = Paths.music('stayFunky/stayFunky-intro'); + FunkinSound.load(introMusic, 1.0, false, true, true, () -> { + FunkinSound.playMusic('stayFunky', + { + startingVolume: 1, + overrideExisting: true, + restartTrack: true + }); + }); var bg:FlxSprite = new FlxSprite(-153, -140); bg.loadGraphic(Paths.image('charSelect/charSelectBG')); From 105e3dccba49897e594e8d6c9a23cc2768fd596b Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 2 Jul 2024 21:26:16 -0400 Subject: [PATCH 095/469] spritesheet fixins for char select --- .gitignore | 1 + source/funkin/ui/PixelatedIcon.hx | 79 ++++++++++++++++++ .../ui/charSelect/CharSelectSubState.hx | 29 +++---- source/funkin/ui/freeplay/SongMenuItem.hx | 81 +------------------ 4 files changed, 95 insertions(+), 95 deletions(-) create mode 100644 source/funkin/ui/PixelatedIcon.hx diff --git a/.gitignore b/.gitignore index 84585eee0f..ae402bdee4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ shitAudio/ node_modules/ package.json package-lock.json +.aider* diff --git a/source/funkin/ui/PixelatedIcon.hx b/source/funkin/ui/PixelatedIcon.hx new file mode 100644 index 0000000000..8d9b97d9ca --- /dev/null +++ b/source/funkin/ui/PixelatedIcon.hx @@ -0,0 +1,79 @@ +package funkin.ui; + +import flixel.FlxSprite; + +/** + * The icon that gets used for Freeplay capsules and char select + * NOT to be confused with the CharIcon class, which is for the in-game icons + */ +class PixelatedIcon extends FlxSprite +{ + public function new(x:Float, y:Float) + { + super(x, y); + this.makeGraphic(32, 32, 0x00000000); + this.antialiasing = false; + this.active = false; + } + + public function setCharacter(char:String):Void + { + var charPath:String = "freeplay/icons/"; + + switch (char) + { + case 'monster-christmas': + charPath += 'monsterpixel'; + case 'mom-car': + charPath += 'mommypixel'; + case 'darnell-blazin': + charPath += 'darnellpixel'; + case 'senpai-angry': + charPath += 'senpaipixel'; + default: + charPath += '${char}pixel'; + } + + if (!openfl.utils.Assets.exists(Paths.image(charPath))) + { + trace('[WARN] Character ${char} has no freeplay icon.'); + return; + } + + var isAnimated = openfl.utils.Assets.exists(Paths.file('images/$charPath.xml')); + + if (isAnimated) + { + this.frames = Paths.getSparrowAtlas(charPath); + } + else + { + this.loadGraphic(Paths.image(charPath)); + } + + this.scale.x = this.scale.y = 2; + + switch (char) + { + case 'parents-christmas': + this.origin.x = 140; + default: + this.origin.x = 100; + } + + if (isAnimated) + { + this.active = true; + this.animation.addByPrefix('idle', 'idle0', 10, true); + this.animation.addByPrefix('confirm', 'confirm0', 10, false); + this.animation.addByPrefix('confirm-hold', 'confirm-hold0', 10, true); + + this.animation.finishCallback = function(name:String):Void { + trace('Finish pixel animation: ${name}'); + if (name == 'confirm') this.animation.play('confirm-hold'); + }; + + this.animation.play('idle'); + } + } +} diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 0e8bdd1bba..d733663907 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -1,12 +1,13 @@ package funkin.ui.charSelect; import flixel.text.FlxText; -import flixel.FlxSprite; +import funkin.ui.PixelatedIcon; import flixel.system.debug.watch.Tracker.TrackerProfile; import flixel.math.FlxPoint; import flixel.tweens.FlxTween; import openfl.display.BlendMode; import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.FlxSprite; import flixel.group.FlxSpriteGroup; import funkin.play.stage.Stage; import funkin.modding.events.ScriptEvent; @@ -284,21 +285,11 @@ class CharSelectSubState extends MusicBeatSubState if (availableChars.exists(i)) { var path:String = availableChars.get(i); - var temp:CharIconCharacter = new CharIconCharacter(path); + var temp:PixelatedIcon = new PixelatedIcon(0, 0); + temp.setCharacter(path); + temp.setGraphicSize(128, 128); + temp.updateHitbox(); temp.ID = 0; - - var idleAnimPrefix:String = switch (path) - { - case "pico": - "Pico Icon"; - case "bf": - "boyfriend icon instance 1"; - case _: - "Pico Icon"; - }; - - // temp.animation.addByPrefix("idle", idleAnimPrefix, 0, false); - // temp.animation.play("idle"); grpIcons.add(temp); } else @@ -567,17 +558,19 @@ class CharSelectSubState extends MusicBeatSubState member.animation.play("idle"); } case 0: - var memb:CharIconCharacter = cast member; + var memb:PixelatedIcon = cast member; if (index == getCurrentSelected()) { // memb.pixels = memb.withDropShadow.clone(); - memb.setGraphicSize(128 * 1.3); + memb.scale.set(2.6, 2.6); + + if (controls.ACCEPT) memb.animation.play("confirm"); } else { // memb.pixels = memb.noDropShadow.clone(); - memb.setGraphicSize(128); + memb.scale.set(2, 2); } } } diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx index 68525a5f7a..2eec83223e 100644 --- a/source/funkin/ui/freeplay/SongMenuItem.hx +++ b/source/funkin/ui/freeplay/SongMenuItem.hx @@ -24,12 +24,13 @@ import funkin.play.scoring.Scoring.ScoringRank; import funkin.save.Save; import funkin.save.Save.SaveScoreData; import flixel.util.FlxColor; +import funkin.ui.PixelatedIcon; class SongMenuItem extends FlxSpriteGroup { public var capsule:FlxSprite; - var pixelIcon:FlxSprite; + var pixelIcon:PixelatedIcon; /** * Modify this by calling `init()` @@ -201,11 +202,7 @@ class SongMenuItem extends FlxSpriteGroup // TODO: Use value from metadata instead of random. updateDifficultyRating(FlxG.random.int(0, 20)); - pixelIcon = new FlxSprite(160, 35); - - pixelIcon.makeGraphic(32, 32, 0x00000000); - pixelIcon.antialiasing = false; - pixelIcon.active = false; + pixelIcon = new PixelatedIcon(160, 35); add(pixelIcon); grpHide.add(pixelIcon); @@ -512,7 +509,7 @@ class SongMenuItem extends FlxSpriteGroup // Update capsule text. songText.text = songData?.songName ?? 'Random'; // Update capsule character. - if (songData?.songCharacter != null) setCharacter(songData.songCharacter); + if (songData?.songCharacter != null) pixelIcon.setCharacter(songData.songCharacter); updateBPM(Std.int(songData?.songStartingBpm) ?? 0); updateDifficultyRating(songData?.difficultyRating ?? 0); updateScoringRank(songData?.scoringRank); @@ -526,76 +523,6 @@ class SongMenuItem extends FlxSpriteGroup checkWeek(songData?.songId); } - /** - * Set the character displayed next to this song in the freeplay menu. - * @param char The character ID used by this song. - * If the character has no freeplay icon, a warning will be thrown and nothing will display. - */ - public function setCharacter(char:String):Void - { - var charPath:String = "freeplay/icons/"; - - // TODO: Put this in the character metadata where it belongs. - // TODO: Also, can use CharacterDataParser.getCharPixelIconAsset() - switch (char) - { - case 'monster-christmas': - charPath += 'monsterpixel'; - case 'mom-car': - charPath += 'mommypixel'; - case 'darnell-blazin': - charPath += 'darnellpixel'; - case 'senpai-angry': - charPath += 'senpaipixel'; - default: - charPath += '${char}pixel'; - } - - if (!openfl.utils.Assets.exists(Paths.image(charPath))) - { - trace('[WARN] Character ${char} has no freeplay icon.'); - return; - } - - var isAnimated = openfl.utils.Assets.exists(Paths.file('images/$charPath.xml')); - - if (isAnimated) - { - pixelIcon.frames = Paths.getSparrowAtlas(charPath); - } - else - { - pixelIcon.loadGraphic(Paths.image(charPath)); - } - - pixelIcon.scale.x = pixelIcon.scale.y = 2; - - switch (char) - { - case 'parents-christmas': - pixelIcon.origin.x = 140; - default: - pixelIcon.origin.x = 100; - } - // pixelIcon.origin.x = capsule.origin.x; - // pixelIcon.offset.x -= pixelIcon.origin.x; - - if (isAnimated) - { - pixelIcon.active = true; - - pixelIcon.animation.addByPrefix('idle', 'idle0', 10, true); - pixelIcon.animation.addByPrefix('confirm', 'confirm0', 10, false); - pixelIcon.animation.addByPrefix('confirm-hold', 'confirm-hold0', 10, true); - - pixelIcon.animation.finishCallback = function(name:String):Void { - trace('Finish pixel animation: ${name}'); - if (name == 'confirm') pixelIcon.animation.play('confirm-hold'); - }; - - pixelIcon.animation.play('idle'); - } - } var frameInTicker:Float = 0; var frameInTypeBeat:Int = 0; From 837efcea369467bf69c98e9d7cc5d0e57ad43c64 Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Mon, 10 Jun 2024 19:42:27 +0300 Subject: [PATCH 096/469] [BUGFIX] Made freeplay use the metadata to get the instrumental suffix Song previews in freeplay will now use the instrumental suffix from the current difficulty's corresponding song variation metadata instead of using the variation id as an instrumental suffix and checking only the "erect" variation. --- source/funkin/ui/freeplay/FreeplayState.hx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 3b003cf89b..92410622fd 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1882,7 +1882,10 @@ class FreeplayState extends MusicBeatSubState } else { - var potentiallyErect:String = (currentDifficulty == "erect") || (currentDifficulty == "nightmare") ? "-erect" : ""; + var previewSong:Null = SongRegistry.instance.fetchEntry(daSongCapsule.songData.songId); + var instSuffix:String = previewSong?.getDifficulty(currentDifficulty, + previewSong?.variations ?? Constants.DEFAULT_VARIATION_LIST)?.characters?.instrumental ?? ''; + instSuffix = (instSuffix != '') ? '-$instSuffix' : ''; FunkinSound.playMusic(daSongCapsule.songData.songId, { startingVolume: 0.0, @@ -1890,7 +1893,7 @@ class FreeplayState extends MusicBeatSubState restartTrack: false, mapTimeChanges: false, // The music metadata is not alongside the audio file so this won't work. pathsFunction: INST, - suffix: potentiallyErect, + suffix: instSuffix, partialParams: { loadPartial: true, From b1021530d8bb97e259c9443a62d306c96f54147d Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Wed, 12 Jun 2024 11:23:45 +0300 Subject: [PATCH 097/469] now using getVariationsByCharId instead --- source/funkin/ui/freeplay/FreeplayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 92410622fd..69b499e803 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1884,7 +1884,7 @@ class FreeplayState extends MusicBeatSubState { var previewSong:Null = SongRegistry.instance.fetchEntry(daSongCapsule.songData.songId); var instSuffix:String = previewSong?.getDifficulty(currentDifficulty, - previewSong?.variations ?? Constants.DEFAULT_VARIATION_LIST)?.characters?.instrumental ?? ''; + previewSong?.getVariationsByCharId(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST)?.characters?.instrumental ?? ''; instSuffix = (instSuffix != '') ? '-$instSuffix' : ''; FunkinSound.playMusic(daSongCapsule.songData.songId, { From 72a00b9ae20ff2f2ee20fc58bd7c6fb7f0687696 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 18 Jun 2024 17:56:24 -0400 Subject: [PATCH 098/469] Implemented playable character registry, added Freeplay character filtering, added alt instrumental support --- assets | 2 +- source/funkin/InitState.hx | 2 + .../funkin/data/freeplay/player/CHANGELOG.md | 9 ++ .../funkin/data/freeplay/player/PlayerData.hx | 63 ++++++++ .../data/freeplay/player/PlayerRegistry.hx | 151 ++++++++++++++++++ source/funkin/modding/PolymodHandler.hx | 6 +- source/funkin/play/song/Song.hx | 34 ++-- .../handlers/ChartEditorDialogHandler.hx | 7 +- source/funkin/ui/freeplay/FreeplayState.hx | 80 +++++++--- .../freeplay/charselect/PlayableCharacter.hx | 108 +++++++++++++ .../charselect/ScriptedPlayableCharacter.hx | 8 + source/funkin/util/VersionUtil.hx | 1 - 12 files changed, 433 insertions(+), 38 deletions(-) create mode 100644 source/funkin/data/freeplay/player/CHANGELOG.md create mode 100644 source/funkin/data/freeplay/player/PlayerData.hx create mode 100644 source/funkin/data/freeplay/player/PlayerRegistry.hx create mode 100644 source/funkin/ui/freeplay/charselect/PlayableCharacter.hx create mode 100644 source/funkin/ui/freeplay/charselect/ScriptedPlayableCharacter.hx diff --git a/assets b/assets index 0dbd2e96ca..bc1650ba78 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0dbd2e96ca25ab5966cef05db6c76fe7fb145abf +Subproject commit bc1650ba789d675683a8c0cc27b1e2a42cb686cf diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 49b15ddf60..c2a56bdc2f 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -1,5 +1,6 @@ package funkin; +import funkin.data.freeplay.player.PlayerRegistry; import funkin.ui.debug.charting.ChartEditorState; import funkin.ui.transition.LoadingState; import flixel.FlxState; @@ -164,6 +165,7 @@ class InitState extends FlxState SongRegistry.instance.loadEntries(); LevelRegistry.instance.loadEntries(); NoteStyleRegistry.instance.loadEntries(); + PlayerRegistry.instance.loadEntries(); ConversationRegistry.instance.loadEntries(); DialogueBoxRegistry.instance.loadEntries(); SpeakerRegistry.instance.loadEntries(); diff --git a/source/funkin/data/freeplay/player/CHANGELOG.md b/source/funkin/data/freeplay/player/CHANGELOG.md new file mode 100644 index 0000000000..7a31e11ca9 --- /dev/null +++ b/source/funkin/data/freeplay/player/CHANGELOG.md @@ -0,0 +1,9 @@ +# Freeplay Playable Character Data Schema Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] +Initial release. diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx new file mode 100644 index 0000000000..d7b8145847 --- /dev/null +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -0,0 +1,63 @@ +package funkin.data.freeplay.player; + +import funkin.data.animation.AnimationData; + +@:nullSafety +class PlayerData +{ + /** + * The sematic version number of the player data JSON format. + * Supports fancy comparisons like NPM does it's neat. + */ + @:default(funkin.data.freeplay.player.PlayerRegistry.PLAYER_DATA_VERSION) + public var version:String; + + /** + * A readable name for this playable character. + */ + public var name:String = 'Unknown'; + + /** + * The character IDs this character is associated with. + * Only songs that use these characters will show up in Freeplay. + */ + @:default([]) + public var ownedChars:Array = []; + + /** + * Whether to show songs with character IDs that aren't associated with any specific character. + */ + @:optional + @:default(false) + public var showUnownedChars:Bool = false; + + /** + * Whether this character is unlocked by default. + * Use a ScriptedPlayableCharacter to add custom logic. + */ + @:optional + @:default(true) + public var unlocked:Bool = true; + + public function new() + { + this.version = PlayerRegistry.PLAYER_DATA_VERSION; + } + + /** + * Convert this StageData into a JSON string. + */ + public function serialize(pretty:Bool = true):String + { + // Update generatedBy and version before writing. + updateVersionToLatest(); + + var writer = new json2object.JsonWriter(); + return writer.write(this, pretty ? ' ' : null); + } + + public function updateVersionToLatest():Void + { + this.version = PlayerRegistry.PLAYER_DATA_VERSION; + } +} diff --git a/source/funkin/data/freeplay/player/PlayerRegistry.hx b/source/funkin/data/freeplay/player/PlayerRegistry.hx new file mode 100644 index 0000000000..3de9efd411 --- /dev/null +++ b/source/funkin/data/freeplay/player/PlayerRegistry.hx @@ -0,0 +1,151 @@ +package funkin.data.freeplay.player; + +import funkin.data.freeplay.player.PlayerData; +import funkin.ui.freeplay.charselect.PlayableCharacter; +import funkin.ui.freeplay.charselect.ScriptedPlayableCharacter; + +class PlayerRegistry extends BaseRegistry +{ + /** + * The current version string for the stage data format. + * Handle breaking changes by incrementing this value + * and adding migration to the `migratePlayerData()` function. + */ + public static final PLAYER_DATA_VERSION:thx.semver.Version = "1.0.0"; + + public static final PLAYER_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; + + public static var instance(get, never):PlayerRegistry; + static var _instance:Null = null; + + static function get_instance():PlayerRegistry + { + if (_instance == null) _instance = new PlayerRegistry(); + return _instance; + } + + /** + * A mapping between stage character IDs and Freeplay playable character IDs. + */ + var ownedCharacterIds:Map = []; + + public function new() + { + super('PLAYER', 'players', PLAYER_DATA_VERSION_RULE); + } + + public override function loadEntries():Void + { + super.loadEntries(); + + for (playerId in listEntryIds()) + { + var player = fetchEntry(playerId); + if (player == null) continue; + + var currentPlayerCharIds = player.getOwnedCharacterIds(); + for (characterId in currentPlayerCharIds) + { + ownedCharacterIds.set(characterId, playerId); + } + } + + log('Loaded ${countEntries()} playable characters with ${ownedCharacterIds.size()} associations.'); + } + + /** + * Get the playable character associated with a given stage character. + * @param characterId The stage character ID. + * @return The playable character. + */ + public function getCharacterOwnerId(characterId:String):String + { + return ownedCharacterIds[characterId]; + } + + /** + * Return true if the given stage character is associated with a specific playable character. + * If so, the level should only appear if that character is selected in Freeplay. + * @param characterId The stage character ID. + * @return Whether the character is owned by any one character. + */ + public function isCharacterOwned(characterId:String):Bool + { + return ownedCharacterIds.exists(characterId); + } + + /** + * Read, parse, and validate the JSON data and produce the corresponding data object. + */ + public function parseEntryData(id:String):Null + { + // JsonParser does not take type parameters, + // otherwise this function would be in BaseRegistry. + var parser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + + switch (loadEntryFile(id)) + { + case {fileName: fileName, contents: contents}: + parser.fromJson(contents, fileName); + default: + return null; + } + + if (parser.errors.length > 0) + { + printErrors(parser.errors, id); + return null; + } + return parser.value; + } + + /** + * Parse and validate the JSON data and produce the corresponding data object. + * + * NOTE: Must be implemented on the implementation class. + * @param contents The JSON as a string. + * @param fileName An optional file name for error reporting. + */ + public function parseEntryDataRaw(contents:String, ?fileName:String):Null + { + var parser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + parser.fromJson(contents, fileName); + + if (parser.errors.length > 0) + { + printErrors(parser.errors, fileName); + return null; + } + return parser.value; + } + + function createScriptedEntry(clsName:String):PlayableCharacter + { + return ScriptedPlayableCharacter.init(clsName, "unknown"); + } + + function getScriptedClassNames():Array + { + return ScriptedPlayableCharacter.listScriptClasses(); + } + + /** + * A list of all the playable characters from the base game, in order. + */ + public function listBaseGamePlayerIds():Array + { + return ["bf", "pico"]; + } + + /** + * A list of all installed playable characters that are not from the base game. + */ + public function listModdedPlayerIds():Array + { + return listEntryIds().filter(function(id:String):Bool { + return listBaseGamePlayerIds().indexOf(id) == -1; + }); + } +} diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index ae754b780b..c352aa6062 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -8,6 +8,7 @@ import funkin.data.event.SongEventRegistry; import funkin.data.story.level.LevelRegistry; import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.song.SongRegistry; +import funkin.data.freeplay.player.PlayerRegistry; import funkin.data.stage.StageRegistry; import funkin.data.freeplay.album.AlbumRegistry; import funkin.modding.module.ModuleHandler; @@ -369,15 +370,18 @@ class PolymodHandler // These MUST be imported at the top of the file and not referred to by fully qualified name, // to ensure build macros work properly. + SongEventRegistry.loadEventCache(); + SongRegistry.instance.loadEntries(); LevelRegistry.instance.loadEntries(); NoteStyleRegistry.instance.loadEntries(); - SongEventRegistry.loadEventCache(); + PlayerRegistry.instance.loadEntries(); ConversationRegistry.instance.loadEntries(); DialogueBoxRegistry.instance.loadEntries(); SpeakerRegistry.instance.loadEntries(); AlbumRegistry.instance.loadEntries(); StageRegistry.instance.loadEntries(); + CharacterDataParser.loadCharacterCache(); // TODO: Migrate characters to BaseRegistry. ModuleHandler.loadModuleCache(); } diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index dde5ee7b8d..91d35d8fa2 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -14,6 +14,7 @@ import funkin.data.song.SongData.SongTimeFormat; import funkin.data.song.SongRegistry; import funkin.modding.IScriptedClass.IPlayStateScriptedClass; import funkin.modding.events.ScriptEvent; +import funkin.ui.freeplay.charselect.PlayableCharacter; import funkin.util.SortUtil; import openfl.utils.Assets; @@ -401,11 +402,11 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry):Null + public function getFirstValidVariation(?diffId:String, ?currentCharacter:PlayableCharacter, ?possibleVariations:Array):Null { if (possibleVariations == null) { - possibleVariations = variations; + possibleVariations = getVariationsByCharacter(currentCharacter); possibleVariations.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_VARIATION_LIST)); } if (diffId == null) diffId = listDifficulties(null, possibleVariations)[0]; @@ -422,22 +423,29 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry + public function getVariationsByCharacter(?char:PlayableCharacter):Array { - if (charId == null) charId = Constants.DEFAULT_CHARACTER; + if (char == null) return variations; - if (variations.contains(charId)) - { - return [charId]; - } - else + var result = []; + trace('Evaluating variations for ${this.id} ${char.id}: ${this.variations}'); + for (variation in variations) { - // TODO: How to exclude character variations while keeping other custom variations? - return variations; + var metadata = _metadata.get(variation); + + var playerCharId = metadata?.playData?.characters?.player; + if (playerCharId == null) continue; + + if (char.shouldShowCharacter(playerCharId)) + { + result.push(variation); + } } + + return result; } /** @@ -455,6 +463,8 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry = song.listDifficulties(displayedVariations, false); - trace(availableDifficultiesForSong); + trace('Available Difficulties: $availableDifficultiesForSong'); if (availableDifficultiesForSong.length == 0) continue; songs.push(new FreeplaySongData(levelId, songId, song, displayedVariations)); @@ -454,7 +458,7 @@ class FreeplayState extends MusicBeatSubState }); // TODO: Replace this. - if (currentCharacter == 'pico') dj.visible = false; + if (currentCharacterId == 'pico') dj.visible = false; add(dj); @@ -1195,6 +1199,16 @@ class FreeplayState extends MusicBeatSubState rankAnimStart(fromResultsParams); } + if (FlxG.keys.justPressed.P) + { + FlxG.switchState(FreeplayState.build( + { + { + character: currentCharacterId == "pico" ? "bf" : "pico", + } + })); + } + // if (FlxG.keys.justPressed.H) // { // rankDisplayNew(fromResultsParams); @@ -1302,9 +1316,9 @@ class FreeplayState extends MusicBeatSubState { if (busy) return; - var upP:Bool = controls.UI_UP_P && !FlxG.keys.pressed.CONTROL; - var downP:Bool = controls.UI_DOWN_P && !FlxG.keys.pressed.CONTROL; - var accepted:Bool = controls.ACCEPT && !FlxG.keys.pressed.CONTROL; + var upP:Bool = controls.UI_UP_P; + var downP:Bool = controls.UI_DOWN_P; + var accepted:Bool = controls.ACCEPT; if (FlxG.onMobile) { @@ -1378,7 +1392,7 @@ class FreeplayState extends MusicBeatSubState } #end - if (!FlxG.keys.pressed.CONTROL && (controls.UI_UP || controls.UI_DOWN)) + if ((controls.UI_UP || controls.UI_DOWN)) { if (spamming) { @@ -1440,13 +1454,13 @@ class FreeplayState extends MusicBeatSubState } #end - if (controls.UI_LEFT_P && !FlxG.keys.pressed.CONTROL) + if (controls.UI_LEFT_P) { dj.resetAFKTimer(); changeDiff(-1); generateSongList(currentFilter, true); } - if (controls.UI_RIGHT_P && !FlxG.keys.pressed.CONTROL) + if (controls.UI_RIGHT_P) { dj.resetAFKTimer(); changeDiff(1); @@ -1720,7 +1734,7 @@ class FreeplayState extends MusicBeatSubState return; } var targetDifficultyId:String = currentDifficulty; - var targetVariation:String = targetSong.getFirstValidVariation(targetDifficultyId); + var targetVariation:String = targetSong.getFirstValidVariation(targetDifficultyId, currentCharacter); PlayStatePlaylist.campaignId = cap.songData.levelId; var targetDifficulty:SongDifficulty = targetSong.getDifficulty(targetDifficultyId, targetVariation); @@ -1730,8 +1744,18 @@ class FreeplayState extends MusicBeatSubState return; } - // TODO: Change this with alternate instrumentals - var targetInstId:String = targetDifficulty.characters.instrumental; + var baseInstrumentalId:String = targetDifficulty?.characters?.instrumental ?? ''; + var altInstrumentalIds:Array = targetDifficulty?.characters?.altInstrumentals ?? []; + + var targetInstId:String = baseInstrumentalId; + + // TODO: Make this a UI element. + #if (debug || FORCE_DEBUG_VERSION) + if (altInstrumentalIds.length > 0 && FlxG.keys.pressed.CONTROL) + { + targetInstId = altInstrumentalIds[0]; + } + #end // Visual and audio effects. FunkinSound.playOnce(Paths.sound('confirmMenu')); @@ -1883,9 +1907,23 @@ class FreeplayState extends MusicBeatSubState else { var previewSong:Null = SongRegistry.instance.fetchEntry(daSongCapsule.songData.songId); - var instSuffix:String = previewSong?.getDifficulty(currentDifficulty, - previewSong?.getVariationsByCharId(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST)?.characters?.instrumental ?? ''; + var songDifficulty = previewSong?.getDifficulty(currentDifficulty, + previewSong?.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST); + var baseInstrumentalId:String = songDifficulty?.characters?.instrumental ?? ''; + var altInstrumentalIds:Array = songDifficulty?.characters?.altInstrumentals ?? []; + + var instSuffix:String = baseInstrumentalId; + + // TODO: Make this a UI element. + #if (debug || FORCE_DEBUG_VERSION) + if (altInstrumentalIds.length > 0 && FlxG.keys.pressed.CONTROL) + { + instSuffix = altInstrumentalIds[0]; + } + #end + instSuffix = (instSuffix != '') ? '-$instSuffix' : ''; + FunkinSound.playMusic(daSongCapsule.songData.songId, { startingVolume: 0.0, @@ -1914,7 +1952,7 @@ class FreeplayState extends MusicBeatSubState public static function build(?params:FreeplayStateParams, ?stickers:StickerSubState):MusicBeatState { var result:MainMenuState; - if (params?.fromResults.playRankAnim) result = new MainMenuState(true); + if (params?.fromResults?.playRankAnim) result = new MainMenuState(true); else result = new MainMenuState(false); @@ -1952,8 +1990,8 @@ class DifficultySelector extends FlxSprite override function update(elapsed:Float):Void { - if (flipX && controls.UI_RIGHT_P && !FlxG.keys.pressed.CONTROL) moveShitDown(); - if (!flipX && controls.UI_LEFT_P && !FlxG.keys.pressed.CONTROL) moveShitDown(); + if (flipX && controls.UI_RIGHT_P) moveShitDown(); + if (!flipX && controls.UI_LEFT_P) moveShitDown(); super.update(elapsed); } diff --git a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx new file mode 100644 index 0000000000..743345004e --- /dev/null +++ b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx @@ -0,0 +1,108 @@ +package funkin.ui.freeplay.charselect; + +import funkin.data.IRegistryEntry; +import funkin.data.freeplay.player.PlayerData; +import funkin.data.freeplay.player.PlayerRegistry; + +/** + * An object used to retrieve data about a playable character (also known as "weeks"). + * Can be scripted to override each function, for custom behavior. + */ +class PlayableCharacter implements IRegistryEntry +{ + /** + * The ID of the playable character. + */ + public final id:String; + + /** + * Playable character data as parsed from the JSON file. + */ + public final _data:PlayerData; + + /** + * @param id The ID of the JSON file to parse. + */ + public function new(id:String) + { + this.id = id; + _data = _fetchData(id); + + if (_data == null) + { + throw 'Could not parse playable character data for id: $id'; + } + } + + /** + * Retrieve the readable name of the playable character. + */ + public function getName():String + { + // TODO: Maybe add localization support? + return _data.name; + } + + /** + * Retrieve the list of stage character IDs associated with this playable character. + * @return The list of associated character IDs + */ + public function getOwnedCharacterIds():Array + { + return _data.ownedChars; + } + + /** + * Return `true` if, when this character is selected in Freeplay, + * songs unassociated with a specific character should appear. + */ + public function shouldShowUnownedChars():Bool + { + return _data.showUnownedChars; + } + + public function shouldShowCharacter(id:String):Bool + { + if (_data.ownedChars.contains(id)) + { + return true; + } + + if (_data.showUnownedChars) + { + var result = !PlayerRegistry.instance.isCharacterOwned(id); + return result; + } + + return false; + } + + /** + * Returns whether this character is unlocked. + */ + public function isUnlocked():Bool + { + return _data.unlocked; + } + + /** + * Called when the character is destroyed. + * TODO: Document when this gets called + */ + public function destroy():Void {} + + public function toString():String + { + return 'PlayableCharacter($id)'; + } + + /** + * Retrieve and parse the JSON data for a playable character by ID. + * @param id The ID of the character + * @return The parsed player data, or null if not found or invalid + */ + static function _fetchData(id:String):Null + { + return PlayerRegistry.instance.parseEntryDataWithMigration(id, PlayerRegistry.instance.fetchEntryVersion(id)); + } +} diff --git a/source/funkin/ui/freeplay/charselect/ScriptedPlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/ScriptedPlayableCharacter.hx new file mode 100644 index 0000000000..f75a58092f --- /dev/null +++ b/source/funkin/ui/freeplay/charselect/ScriptedPlayableCharacter.hx @@ -0,0 +1,8 @@ +package funkin.ui.freeplay.charselect; + +/** + * A script that can be tied to a PlayableCharacter. + * Create a scripted class that extends PlayableCharacter to use this. + */ +@:hscriptClass +class ScriptedPlayableCharacter extends funkin.ui.freeplay.charselect.PlayableCharacter implements polymod.hscript.HScriptedClass {} diff --git a/source/funkin/util/VersionUtil.hx b/source/funkin/util/VersionUtil.hx index 832ce008ab..9bf46a1881 100644 --- a/source/funkin/util/VersionUtil.hx +++ b/source/funkin/util/VersionUtil.hx @@ -24,7 +24,6 @@ class VersionUtil try { var versionRaw:thx.semver.Version.SemVer = version; - trace('${versionRaw} satisfies (${versionRule})? ${version.satisfies(versionRule)}'); return version.satisfies(versionRule); } catch (e) From 5c2bad888d520117d81264d4690dd046cb1d208b Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 18 Jun 2024 20:07:27 -0400 Subject: [PATCH 099/469] Make Boyfriend DJ animations data driven --- .../funkin/data/freeplay/player/PlayerData.hx | 93 +++++ source/funkin/ui/freeplay/DJBoyfriend.hx | 371 ------------------ source/funkin/ui/freeplay/FreeplayDJ.hx | 369 +++++++++++++++++ source/funkin/ui/freeplay/FreeplayState.hx | 24 +- .../freeplay/charselect/PlayableCharacter.hx | 5 + source/funkin/util/tools/MapTools.hx | 4 + 6 files changed, 483 insertions(+), 383 deletions(-) delete mode 100644 source/funkin/ui/freeplay/DJBoyfriend.hx create mode 100644 source/funkin/ui/freeplay/FreeplayDJ.hx diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx index d7b8145847..10fc54b783 100644 --- a/source/funkin/data/freeplay/player/PlayerData.hx +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -31,6 +31,12 @@ class PlayerData @:default(false) public var showUnownedChars:Bool = false; + /** + * Data for displaying this character in the Freeplay menu. + * If null, display no DJ. + */ + public var freeplayDJ:Null = null; + /** * Whether this character is unlocked by default. * Use a ScriptedPlayableCharacter to add custom logic. @@ -61,3 +67,90 @@ class PlayerData this.version = PlayerRegistry.PLAYER_DATA_VERSION; } } + +class PlayerFreeplayDJData +{ + var assetPath:String; + var animations:Array; + + @:jignored + var animationMap:Map; + + @:optional + var cartoon:Null; + + public function new() + { + animationMap = new Map(); + } + + function mapAnimations() + { + if (animationMap == null) animationMap = new Map(); + + animationMap.clear(); + for (anim in animations) + { + animationMap.set(anim.name, anim); + } + } + + public function getAtlasPath():String + { + return Paths.animateAtlas(assetPath); + } + + public function getAnimationPrefix(name:String):Null + { + if (animationMap.size() == 0) mapAnimations(); + + var anim = animationMap.get(name); + if (anim == null) return null; + return anim.prefix; + } + + public function getAnimationOffsets(name:String):Null> + { + if (animationMap.size() == 0) mapAnimations(); + + var anim = animationMap.get(name); + if (anim == null) return null; + return anim.offsets; + } + + // TODO: These should really be frame labels, ehe. + + public function getCartoonSoundClickFrame():Int + { + return cartoon?.soundClickFrame ?? 80; + } + + public function getCartoonSoundCartoonFrame():Int + { + return cartoon?.soundCartoonFrame ?? 85; + } + + public function getCartoonLoopBlinkFrame():Int + { + return cartoon?.loopBlinkFrame ?? 112; + } + + public function getCartoonLoopFrame():Int + { + return cartoon?.loopFrame ?? 166; + } + + public function getCartoonChannelChangeFrame():Int + { + return cartoon?.channelChangeFrame ?? 60; + } +} + +typedef PlayerFreeplayDJCartoonData = +{ + var soundClickFrame:Int; + var soundCartoonFrame:Int; + var loopBlinkFrame:Int; + var loopFrame:Int; + var channelChangeFrame:Int; +} diff --git a/source/funkin/ui/freeplay/DJBoyfriend.hx b/source/funkin/ui/freeplay/DJBoyfriend.hx deleted file mode 100644 index bbf043dd48..0000000000 --- a/source/funkin/ui/freeplay/DJBoyfriend.hx +++ /dev/null @@ -1,371 +0,0 @@ -package funkin.ui.freeplay; - -import flixel.FlxSprite; -import flixel.util.FlxSignal; -import funkin.util.assets.FlxAnimationUtil; -import funkin.graphics.adobeanimate.FlxAtlasSprite; -import funkin.audio.FunkinSound; -import flixel.util.FlxTimer; -import funkin.audio.FunkinSound; -import funkin.audio.FlxStreamSound; - -class DJBoyfriend extends FlxAtlasSprite -{ - // Represents the sprite's current status. - // Without state machines I would have driven myself crazy years ago. - public var currentState:DJBoyfriendState = Intro; - - // A callback activated when the intro animation finishes. - public var onIntroDone:FlxSignal = new FlxSignal(); - - // A callback activated when Boyfriend gets spooked. - public var onSpook:FlxSignal = new FlxSignal(); - - // playAnim stolen from Character.hx, cuz im lazy lol! - // TODO: Switch this class to use SwagSprite instead. - public var animOffsets:Map>; - - var gotSpooked:Bool = false; - - static final SPOOK_PERIOD:Float = 60.0; - static final TV_PERIOD:Float = 120.0; - - // Time since dad last SPOOKED you. - var timeSinceSpook:Float = 0; - - public function new(x:Float, y:Float) - { - super(x, y, Paths.animateAtlas("freeplay/freeplay-boyfriend", "preload")); - - animOffsets = new Map>(); - - anim.callback = function(name, number) { - switch (name) - { - case "Boyfriend DJ watchin tv OG": - if (number == 80) - { - FunkinSound.playOnce(Paths.sound('remote_click')); - } - if (number == 85) - { - runTvLogic(); - } - default: - } - }; - - setupAnimations(); - - FlxG.debugger.track(this); - FlxG.console.registerObject("dj", this); - - anim.onComplete = onFinishAnim; - - FlxG.console.registerFunction("tv", function() { - currentState = TV; - }); - } - - /* - [remote hand under,boyfriend top head,brim piece,arm cringe l,red lazer,dj arm in,bf fist pump arm,hand raised right,forearm left,fist shaking,bf smile eyes closed face,arm cringe r,bf clenched face,face shrug,boyfriend falling,blue tint 1,shirt sleeve,bf clenched fist,head BF relaxed,blue tint 2,hand down left,blue tint 3,blue tint 4,head less smooshed,blue tint 5,boyfriend freeplay,BF head slight turn,blue tint 6,arm shrug l,blue tint 7,shoulder raised w sleeve,blue tint 8,fist pump face,blue tint 9,foot rested light,hand turnaround,arm chill right,Boyfriend DJ,arm shrug r,head back bf,hat top piece,dad bod,face surprise snap,Boyfriend DJ fist pump,office chair,foot rested right,chest down,office chair upright,body chill,bf dj afk,head mouth open dad,BF Head defalt HAIR BLOWING,hand shrug l,face piece,foot wag,turn table,shoulder up left,turntable lights,boyfriend dj body shirt blowing,body chunk turned,hand down right,dj arm out,hand shrug r,body chest out,rave hand,palm,chill face default,head back semi bf,boyfriend bottom head,DJ arm,shoulder right dad,bf surprise,boyfriend dj body,hs1,Boyfriend DJ watchin tv OG,spinning disk,hs2,arm chill left,boyfriend dj intro,hs3,hs4,chill face extra,hs5,remote hand upright,hs6,pant over table,face surprise,bf arm peace,arm turnaround,bf eyes 1,arm slammed table,eye squit,leg BF,head mid piece,arm backing,arm swoopin in,shoe right lowering,forearm right,hand out,blue tint 10,body falling back,remote thumb press,shoulder,hair spike single,bf bent - arm,crt,foot raised right,dad hand,chill face 1,chill face 2,clenched fist,head SMOOSHED,shoulder left dad,df1,body chunk upright,df2,df3,df4,hat front piece,df5,foot rested right 2,hand in,arm spun,shoe raised left,bf 1 finger hand,bf mouth 1,Boyfriend DJ confirm,forearm down ,hand raised left,remote thumb up] - */ - override public function listAnimations():Array - { - var anims:Array = []; - @:privateAccess - for (animKey in anim.symbolDictionary) - { - anims.push(animKey.name); - } - return anims; - } - - var lowPumpLoopPoint:Int = 4; - - public override function update(elapsed:Float):Void - { - super.update(elapsed); - - switch (currentState) - { - case Intro: - // Play the intro animation then leave this state immediately. - if (getCurrentAnimation() != 'boyfriend dj intro') playFlashAnimation('boyfriend dj intro', true); - timeSinceSpook = 0; - case Idle: - // We are in this state the majority of the time. - if (getCurrentAnimation() != 'Boyfriend DJ') - { - playFlashAnimation('Boyfriend DJ', true); - } - - if (getCurrentAnimation() == 'Boyfriend DJ' && this.isLoopFinished()) - { - if (timeSinceSpook >= SPOOK_PERIOD && !gotSpooked) - { - currentState = Spook; - } - else if (timeSinceSpook >= TV_PERIOD) - { - currentState = TV; - } - } - timeSinceSpook += elapsed; - case Confirm: - if (getCurrentAnimation() != 'Boyfriend DJ confirm') playFlashAnimation('Boyfriend DJ confirm', false); - timeSinceSpook = 0; - case PumpIntro: - if (getCurrentAnimation() != 'Boyfriend DJ fist pump') playFlashAnimation('Boyfriend DJ fist pump', false); - if (getCurrentAnimation() == 'Boyfriend DJ fist pump' && anim.curFrame >= 4) - { - anim.play("Boyfriend DJ fist pump", true, false, 0); - } - case FistPump: - - case Spook: - if (getCurrentAnimation() != 'bf dj afk') - { - onSpook.dispatch(); - playFlashAnimation('bf dj afk', false); - gotSpooked = true; - } - timeSinceSpook = 0; - case TV: - if (getCurrentAnimation() != 'Boyfriend DJ watchin tv OG') playFlashAnimation('Boyfriend DJ watchin tv OG', true); - timeSinceSpook = 0; - default: - // I shit myself. - } - - if (FlxG.keys.pressed.CONTROL) - { - if (FlxG.keys.justPressed.LEFT) - { - this.offsetX -= FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); - } - - if (FlxG.keys.justPressed.RIGHT) - { - this.offsetX += FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); - } - - if (FlxG.keys.justPressed.UP) - { - this.offsetY -= FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); - } - - if (FlxG.keys.justPressed.DOWN) - { - this.offsetY += FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); - } - - if (FlxG.keys.justPressed.SPACE) - { - currentState = (currentState == Idle ? TV : Idle); - } - } - } - - function onFinishAnim():Void - { - var name = anim.curSymbol.name; - switch (name) - { - case "boyfriend dj intro": - // trace('Finished intro'); - currentState = Idle; - onIntroDone.dispatch(); - case "Boyfriend DJ": - // trace('Finished idle'); - case "bf dj afk": - // trace('Finished spook'); - currentState = Idle; - case "Boyfriend DJ confirm": - - case "Boyfriend DJ fist pump": - currentState = Idle; - - case "Boyfriend DJ loss reaction 1": - currentState = Idle; - - case "Boyfriend DJ watchin tv OG": - var frame:Int = FlxG.random.bool(33) ? 112 : 166; - - // BF switches channels when the video ends, or at a 10% chance each time his idle loops. - if (FlxG.random.bool(5)) - { - frame = 60; - // boyfriend switches channel code? - // runTvLogic(); - } - trace('Replay idle: ${frame}'); - anim.play("Boyfriend DJ watchin tv OG", true, false, frame); - // trace('Finished confirm'); - } - } - - public function resetAFKTimer():Void - { - timeSinceSpook = 0; - gotSpooked = false; - } - - var offsetX:Float = 0.0; - var offsetY:Float = 0.0; - - function setupAnimations():Void - { - // Intro - addOffset('boyfriend dj intro', 8.0 - 1.3, 3.0 - 0.4); - - // Idle - addOffset('Boyfriend DJ', 0, 0); - - // Confirm - addOffset('Boyfriend DJ confirm', 0, 0); - - // AFK: Spook - addOffset('bf dj afk', 649.5, 58.5); - - // AFK: TV - addOffset('Boyfriend DJ watchin tv OG', 0, 0); - } - - var cartoonSnd:Null = null; - - public var playingCartoon:Bool = false; - - public function runTvLogic() - { - if (cartoonSnd == null) - { - // tv is OFF, but getting turned on - FunkinSound.playOnce(Paths.sound('tv_on'), 1.0, function() { - loadCartoon(); - }); - } - else - { - // plays it smidge after the click - FunkinSound.playOnce(Paths.sound('channel_switch'), 1.0, function() { - cartoonSnd.destroy(); - loadCartoon(); - }); - } - - // loadCartoon(); - } - - function loadCartoon() - { - cartoonSnd = FunkinSound.load(Paths.sound(getRandomFlashToon()), 1.0, false, true, true, function() { - anim.play("Boyfriend DJ watchin tv OG", true, false, 60); - }); - - // Fade out music to 40% volume over 1 second. - // This helps make the TV a bit more audible. - FlxG.sound.music.fadeOut(1.0, 0.1); - - // Play the cartoon at a random time between the start and 5 seconds from the end. - cartoonSnd.time = FlxG.random.float(0, Math.max(cartoonSnd.length - (5 * Constants.MS_PER_SEC), 0.0)); - } - - final cartoonList:Array = openfl.utils.Assets.list().filter(function(path) return path.startsWith("assets/sounds/cartoons/")); - - function getRandomFlashToon():String - { - var randomFile = FlxG.random.getObject(cartoonList); - - // Strip folder prefix - randomFile = randomFile.replace("assets/sounds/", ""); - // Strip file extension - randomFile = randomFile.substring(0, randomFile.length - 4); - - return randomFile; - } - - public function confirm():Void - { - currentState = Confirm; - } - - public function fistPump():Void - { - currentState = PumpIntro; - } - - public function pumpFist():Void - { - currentState = FistPump; - anim.play("Boyfriend DJ fist pump", true, false, 4); - } - - public function pumpFistBad():Void - { - currentState = FistPump; - anim.play("Boyfriend DJ loss reaction 1", true, false, 4); - } - - public inline function addOffset(name:String, x:Float = 0, y:Float = 0) - { - animOffsets[name] = [x, y]; - } - - override public function getCurrentAnimation():String - { - if (this.anim == null || this.anim.curSymbol == null) return ""; - return this.anim.curSymbol.name; - } - - public function playFlashAnimation(id:String, ?Force:Bool = false, ?Reverse:Bool = false, ?Frame:Int = 0):Void - { - anim.play(id, Force, Reverse, Frame); - applyAnimOffset(); - } - - function applyAnimOffset() - { - var AnimName = getCurrentAnimation(); - var daOffset = animOffsets.get(AnimName); - if (animOffsets.exists(AnimName)) - { - var xValue = daOffset[0]; - var yValue = daOffset[1]; - if (AnimName == "Boyfriend DJ watchin tv OG") - { - xValue += offsetX; - yValue += offsetY; - } - - offset.set(xValue, yValue); - } - else - { - offset.set(0, 0); - } - } - - public override function destroy():Void - { - super.destroy(); - - if (cartoonSnd != null) - { - cartoonSnd.destroy(); - cartoonSnd = null; - } - } -} - -enum DJBoyfriendState -{ - Intro; - Idle; - Confirm; - PumpIntro; - FistPump; - Spook; - TV; -} diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx new file mode 100644 index 0000000000..f9effe793c --- /dev/null +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -0,0 +1,369 @@ +package funkin.ui.freeplay; + +import flixel.FlxSprite; +import flixel.util.FlxSignal; +import funkin.util.assets.FlxAnimationUtil; +import funkin.graphics.adobeanimate.FlxAtlasSprite; +import funkin.audio.FunkinSound; +import flixel.util.FlxTimer; +import funkin.data.freeplay.player.PlayerRegistry; +import funkin.data.freeplay.player.PlayerData.PlayerFreeplayDJData; +import funkin.audio.FunkinSound; +import funkin.audio.FlxStreamSound; + +class FreeplayDJ extends FlxAtlasSprite +{ + // Represents the sprite's current status. + // Without state machines I would have driven myself crazy years ago. + public var currentState:DJBoyfriendState = Intro; + + // A callback activated when the intro animation finishes. + public var onIntroDone:FlxSignal = new FlxSignal(); + + // A callback activated when the idle easter egg plays. + public var onIdleEasterEgg:FlxSignal = new FlxSignal(); + + var seenIdleEasterEgg:Bool = false; + + static final IDLE_EGG_PERIOD:Float = 60.0; + static final IDLE_CARTOON_PERIOD:Float = 120.0; + + // Time since last special idle animation you. + var timeIdling:Float = 0; + + final characterId:String = Constants.DEFAULT_CHARACTER; + final playableCharData:PlayerFreeplayDJData; + + public function new(x:Float, y:Float, characterId:String) + { + this.characterId = characterId; + + var playableChar = PlayerRegistry.instance.fetchEntry(characterId); + playableCharData = playableChar.getFreeplayDJData(); + + super(x, y, playableCharData.getAtlasPath()); + + anim.callback = function(name, number) { + if (name == playableCharData.getAnimationPrefix('cartoon')) + { + if (number == playableCharData.getCartoonSoundClickFrame()) + { + FunkinSound.playOnce(Paths.sound('remote_click')); + } + if (number == playableCharData.getCartoonSoundCartoonFrame()) + { + runTvLogic(); + } + } + }; + + FlxG.debugger.track(this); + FlxG.console.registerObject("dj", this); + + anim.onComplete = onFinishAnim; + + FlxG.console.registerFunction("freeplayCartoon", function() { + currentState = Cartoon; + }); + } + + override public function listAnimations():Array + { + var anims:Array = []; + @:privateAccess + for (animKey in anim.symbolDictionary) + { + anims.push(animKey.name); + } + return anims; + } + + var lowPumpLoopPoint:Int = 4; + + public override function update(elapsed:Float):Void + { + super.update(elapsed); + + switch (currentState) + { + case Intro: + // Play the intro animation then leave this state immediately. + var animPrefix = playableCharData.getAnimationPrefix('intro'); + if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true); + timeIdling = 0; + case Idle: + // We are in this state the majority of the time. + var animPrefix = playableCharData.getAnimationPrefix('idle'); + if (getCurrentAnimation() != animPrefix) + { + playFlashAnimation(animPrefix, true); + } + + if (getCurrentAnimation() == animPrefix && this.isLoopFinished()) + { + if (timeIdling >= IDLE_EGG_PERIOD && !seenIdleEasterEgg) + { + currentState = IdleEasterEgg; + } + else if (timeIdling >= IDLE_CARTOON_PERIOD) + { + currentState = Cartoon; + } + } + timeIdling += elapsed; + case Confirm: + var animPrefix = playableCharData.getAnimationPrefix('confirm'); + if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, false); + timeIdling = 0; + case FistPumpIntro: + var animPrefix = playableCharData.getAnimationPrefix('fistPump'); + if (getCurrentAnimation() != animPrefix) playFlashAnimation('Boyfriend DJ fist pump', false); + if (getCurrentAnimation() == animPrefix && anim.curFrame >= 4) + { + anim.play("Boyfriend DJ fist pump", true, false, 0); + } + case FistPump: + + case IdleEasterEgg: + var animPrefix = playableCharData.getAnimationPrefix('idleEasterEgg'); + if (getCurrentAnimation() != animPrefix) + { + onIdleEasterEgg.dispatch(); + playFlashAnimation(animPrefix, false); + seenIdleEasterEgg = true; + } + timeIdling = 0; + case Cartoon: + var animPrefix = playableCharData.getAnimationPrefix('cartoon'); + if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true); + timeIdling = 0; + default: + // I shit myself. + } + + if (FlxG.keys.pressed.CONTROL) + { + if (FlxG.keys.justPressed.LEFT) + { + this.offsetX -= FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); + } + + if (FlxG.keys.justPressed.RIGHT) + { + this.offsetX += FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); + } + + if (FlxG.keys.justPressed.UP) + { + this.offsetY -= FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); + } + + if (FlxG.keys.justPressed.DOWN) + { + this.offsetY += FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); + } + + if (FlxG.keys.justPressed.SPACE) + { + currentState = (currentState == Idle ? Cartoon : Idle); + } + } + } + + function onFinishAnim():Void + { + var name = anim.curSymbol.name; + + if (name == playableCharData.getAnimationPrefix('intro')) + { + currentState = Idle; + onIntroDone.dispatch(); + } + else if (name == playableCharData.getAnimationPrefix('idle')) + { + // trace('Finished idle'); + } + else if (name == playableCharData.getAnimationPrefix('confirm')) + { + // trace('Finished confirm'); + } + else if (name == playableCharData.getAnimationPrefix('fistPump')) + { + // trace('Finished fist pump'); + currentState = Idle; + } + else if (name == playableCharData.getAnimationPrefix('idleEasterEgg')) + { + // trace('Finished spook'); + currentState = Idle; + } + else if (name == playableCharData.getAnimationPrefix('loss')) + { + // trace('Finished loss reaction'); + currentState = Idle; + } + else if (name == playableCharData.getAnimationPrefix('cartoon')) + { + // trace('Finished cartoon'); + + var frame:Int = FlxG.random.bool(33) ? playableCharData.getCartoonLoopBlinkFrame() : playableCharData.getCartoonLoopFrame(); + + // Character switches channels when the video ends, or at a 10% chance each time his idle loops. + if (FlxG.random.bool(5)) + { + frame = playableCharData.getCartoonChannelChangeFrame(); + // boyfriend switches channel code? + // runTvLogic(); + } + trace('Replay idle: ${frame}'); + anim.play(playableCharData.getAnimationPrefix('cartoon'), true, false, frame); + // trace('Finished confirm'); + } + else + { + trace('Finished ${name}'); + } + } + + public function resetAFKTimer():Void + { + timeIdling = 0; + seenIdleEasterEgg = false; + } + + var offsetX:Float = 0.0; + var offsetY:Float = 0.0; + + var cartoonSnd:Null = null; + + public var playingCartoon:Bool = false; + + public function runTvLogic() + { + if (cartoonSnd == null) + { + // tv is OFF, but getting turned on + FunkinSound.playOnce(Paths.sound('tv_on'), 1.0, function() { + loadCartoon(); + }); + } + else + { + // plays it smidge after the click + FunkinSound.playOnce(Paths.sound('channel_switch'), 1.0, function() { + cartoonSnd.destroy(); + loadCartoon(); + }); + } + + // loadCartoon(); + } + + function loadCartoon() + { + cartoonSnd = FunkinSound.load(Paths.sound(getRandomFlashToon()), 1.0, false, true, true, function() { + anim.play("Boyfriend DJ watchin tv OG", true, false, 60); + }); + + // Fade out music to 40% volume over 1 second. + // This helps make the TV a bit more audible. + FlxG.sound.music.fadeOut(1.0, 0.1); + + // Play the cartoon at a random time between the start and 5 seconds from the end. + cartoonSnd.time = FlxG.random.float(0, Math.max(cartoonSnd.length - (5 * Constants.MS_PER_SEC), 0.0)); + } + + final cartoonList:Array = openfl.utils.Assets.list().filter(function(path) return path.startsWith("assets/sounds/cartoons/")); + + function getRandomFlashToon():String + { + var randomFile = FlxG.random.getObject(cartoonList); + + // Strip folder prefix + randomFile = randomFile.replace("assets/sounds/", ""); + // Strip file extension + randomFile = randomFile.substring(0, randomFile.length - 4); + + return randomFile; + } + + public function confirm():Void + { + currentState = Confirm; + } + + public function fistPump():Void + { + currentState = FistPumpIntro; + } + + public function pumpFist():Void + { + currentState = FistPump; + anim.play("Boyfriend DJ fist pump", true, false, 4); + } + + public function pumpFistBad():Void + { + currentState = FistPump; + anim.play("Boyfriend DJ loss reaction 1", true, false, 4); + } + + override public function getCurrentAnimation():String + { + if (this.anim == null || this.anim.curSymbol == null) return ""; + return this.anim.curSymbol.name; + } + + public function playFlashAnimation(id:String, ?Force:Bool = false, ?Reverse:Bool = false, ?Frame:Int = 0):Void + { + anim.play(id, Force, Reverse, Frame); + applyAnimOffset(); + } + + function applyAnimOffset() + { + var AnimName = getCurrentAnimation(); + var daOffset = playableCharData.getAnimationOffsets(AnimName); + if (daOffset != null) + { + var xValue = daOffset[0]; + var yValue = daOffset[1]; + if (AnimName == "Boyfriend DJ watchin tv OG") + { + xValue += offsetX; + yValue += offsetY; + } + + trace('Successfully applied offset: ' + xValue + ', ' + yValue); + offset.set(xValue, yValue); + } + else + { + trace('No offset found, defaulting to: 0, 0'); + offset.set(0, 0); + } + } + + public override function destroy():Void + { + super.destroy(); + + if (cartoonSnd != null) + { + cartoonSnd.destroy(); + cartoonSnd = null; + } + } +} + +enum DJBoyfriendState +{ + Intro; + Idle; + Confirm; + FistPumpIntro; + FistPump; + IdleEasterEgg; + Cartoon; +} diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 9f23440df3..98e48b3383 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -167,7 +167,7 @@ class FreeplayState extends MusicBeatSubState var curCapsule:SongMenuItem; var curPlaying:Bool = false; - var dj:DJBoyfriend; + var dj:FreeplayDJ; var ostName:FlxText; var albumRoll:AlbumRoll; @@ -211,6 +211,7 @@ class FreeplayState extends MusicBeatSubState { currentCharacterId = params?.character ?? Constants.DEFAULT_CHARACTER; currentCharacter = PlayerRegistry.instance.fetchEntry(currentCharacterId); + if (currentCharacter == null) throw 'Could not build Freeplay state for character: $currentCharacterId'; fromResultsParams = params?.fromResults; @@ -450,17 +451,16 @@ class FreeplayState extends MusicBeatSubState add(cardGlow); - dj = new DJBoyfriend(640, 366); - exitMovers.set([dj], - { - x: -dj.width * 1.6, - speed: 0.5 - }); - - // TODO: Replace this. - if (currentCharacterId == 'pico') dj.visible = false; - - add(dj); + if (currentCharacter?.getFreeplayDJData() != null) + { + dj = new FreeplayDJ(640, 366, currentCharacterId); + exitMovers.set([dj], + { + x: -dj.width * 1.6, + speed: 0.5 + }); + add(dj); + } bgDad = new FlxSprite(pinkBack.width * 0.74, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad')); bgDad.shader = new AngleMask(); diff --git a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx index 743345004e..282e35d7a6 100644 --- a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx +++ b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx @@ -77,6 +77,11 @@ class PlayableCharacter implements IRegistryEntry return false; } + public function getFreeplayDJData():PlayerFreeplayDJData + { + return _data.freeplayDJ; + } + /** * Returns whether this character is unlocked. */ diff --git a/source/funkin/util/tools/MapTools.hx b/source/funkin/util/tools/MapTools.hx index b98cb0adf9..807f0aebd9 100644 --- a/source/funkin/util/tools/MapTools.hx +++ b/source/funkin/util/tools/MapTools.hx @@ -14,6 +14,7 @@ class MapTools */ public static function size(map:Map):Int { + if (map == null) return 0; return map.keys().array().length; } @@ -22,6 +23,7 @@ class MapTools */ public static function values(map:Map):Array { + if (map == null) return []; return [for (i in map.iterator()) i]; } @@ -30,6 +32,7 @@ class MapTools */ public static function clone(map:Map):Map { + if (map == null) return null; return map.copy(); } @@ -76,6 +79,7 @@ class MapTools */ public static function keyValues(map:Map):Array { + if (map == null) return []; return map.keys().array(); } } From ad57e64994dd8a9f5494d4c6984c9686448ef105 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 20 Jun 2024 16:17:53 -0400 Subject: [PATCH 100/469] Working Pico DJ --- source/funkin/Paths.hx | 11 +- .../funkin/data/freeplay/player/PlayerData.hx | 42 ++- source/funkin/play/scoring/Scoring.hx | 4 +- source/funkin/ui/freeplay/FreeplayDJ.hx | 14 +- source/funkin/ui/freeplay/FreeplayState.hx | 280 ++++++++++-------- .../freeplay/charselect/PlayableCharacter.hx | 5 + source/funkin/ui/mainmenu/MainMenuState.hx | 5 +- source/funkin/util/SortUtil.hx | 2 +- 8 files changed, 217 insertions(+), 146 deletions(-) diff --git a/source/funkin/Paths.hx b/source/funkin/Paths.hx index b0a97c4faf..285af7ca22 100644 --- a/source/funkin/Paths.hx +++ b/source/funkin/Paths.hx @@ -11,9 +11,16 @@ class Paths { static var currentLevel:Null = null; - public static function setCurrentLevel(name:String):Void + public static function setCurrentLevel(name:Null):Void { - currentLevel = name.toLowerCase(); + if (name == null) + { + currentLevel = null; + } + else + { + currentLevel = name.toLowerCase(); + } } public static function stripLibrary(path:String):String diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx index 10fc54b783..c461c9555d 100644 --- a/source/funkin/data/freeplay/player/PlayerData.hx +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -35,6 +35,7 @@ class PlayerData * Data for displaying this character in the Freeplay menu. * If null, display no DJ. */ + @:optional public var freeplayDJ:Null = null; /** @@ -73,9 +74,25 @@ class PlayerFreeplayDJData var assetPath:String; var animations:Array; + @:optional + @:default("BOYFRIEND") + var text1:String; + + @:optional + @:default("HOT BLOODED IN MORE WAYS THAN ONE") + var text2:String; + + @:optional + @:default("PROTECT YO NUTS") + var text3:String; + + @:jignored var animationMap:Map; + @:jignored + var prefixToOffsetsMap:Map>; + @:optional var cartoon:Null; @@ -87,11 +104,14 @@ class PlayerFreeplayDJData function mapAnimations() { if (animationMap == null) animationMap = new Map(); + if (prefixToOffsetsMap == null) prefixToOffsetsMap = new Map(); animationMap.clear(); + prefixToOffsetsMap.clear(); for (anim in animations) { animationMap.set(anim.name, anim); + prefixToOffsetsMap.set(anim.prefix, anim.offsets); } } @@ -100,6 +120,15 @@ class PlayerFreeplayDJData return Paths.animateAtlas(assetPath); } + public function getFreeplayDJText(index:Int):String { + switch (index) { + case 1: return text1; + case 2: return text2; + case 3: return text3; + default: return ''; + } + } + public function getAnimationPrefix(name:String):Null { if (animationMap.size() == 0) mapAnimations(); @@ -109,13 +138,16 @@ class PlayerFreeplayDJData return anim.prefix; } - public function getAnimationOffsets(name:String):Null> + public function getAnimationOffsetsByPrefix(?prefix:String):Array { - if (animationMap.size() == 0) mapAnimations(); + if (prefixToOffsetsMap.size() == 0) mapAnimations(); + if (prefix == null) return [0, 0]; + return prefixToOffsetsMap.get(prefix); + } - var anim = animationMap.get(name); - if (anim == null) return null; - return anim.offsets; + public function getAnimationOffsets(name:String):Array + { + return getAnimationOffsetsByPrefix(getAnimationPrefix(name)); } // TODO: These should really be frame labels, ehe. diff --git a/source/funkin/play/scoring/Scoring.hx b/source/funkin/play/scoring/Scoring.hx index dc2c406475..02e5750bcb 100644 --- a/source/funkin/play/scoring/Scoring.hx +++ b/source/funkin/play/scoring/Scoring.hx @@ -590,7 +590,7 @@ enum abstract ScoringRank(String) } } - public function getFreeplayRankIconAsset():Null + public function getFreeplayRankIconAsset():String { switch (abstract) { @@ -607,7 +607,7 @@ enum abstract ScoringRank(String) case SHIT: return 'LOSS'; default: - return null; + return 'LOSS'; } } diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index f9effe793c..72eddd0ca2 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -135,8 +135,12 @@ class FreeplayDJ extends FlxAtlasSprite timeIdling = 0; case Cartoon: var animPrefix = playableCharData.getAnimationPrefix('cartoon'); - if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true); - timeIdling = 0; + if (animPrefix == null) { + currentState = IdleEasterEgg; + } else { + if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true); + timeIdling = 0; + } default: // I shit myself. } @@ -324,7 +328,7 @@ class FreeplayDJ extends FlxAtlasSprite function applyAnimOffset() { var AnimName = getCurrentAnimation(); - var daOffset = playableCharData.getAnimationOffsets(AnimName); + var daOffset = playableCharData.getAnimationOffsetsByPrefix(AnimName); if (daOffset != null) { var xValue = daOffset[0]; @@ -335,12 +339,12 @@ class FreeplayDJ extends FlxAtlasSprite yValue += offsetY; } - trace('Successfully applied offset: ' + xValue + ', ' + yValue); + trace('Successfully applied offset ($AnimName): ' + xValue + ', ' + yValue); offset.set(xValue, yValue); } else { - trace('No offset found, defaulting to: 0, 0'); + trace('No offset found ($AnimName), defaulting to: 0, 0'); offset.set(0, 0); } } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 98e48b3383..5725101cd4 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1,54 +1,55 @@ package funkin.ui.freeplay; -import funkin.graphics.adobeanimate.FlxAtlasSprite; import flixel.addons.transition.FlxTransitionableState; import flixel.addons.ui.FlxInputText; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.group.FlxGroup; -import funkin.graphics.shaders.GaussianBlurShader; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; import flixel.input.touch.FlxTouch; import flixel.math.FlxAngle; import flixel.math.FlxPoint; -import openfl.display.BlendMode; import flixel.system.debug.watch.Tracker.TrackerProfile; import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; +import flixel.tweens.misc.ShakeTween; import flixel.util.FlxColor; import flixel.util.FlxSpriteUtil; import flixel.util.FlxTimer; import funkin.audio.FunkinSound; -import funkin.data.story.level.LevelRegistry; -import funkin.data.song.SongRegistry; import funkin.data.freeplay.player.PlayerRegistry; +import funkin.data.song.SongRegistry; +import funkin.data.story.level.LevelRegistry; +import funkin.effects.IntervalShake; +import funkin.graphics.adobeanimate.FlxAtlasSprite; import funkin.graphics.FunkinCamera; import funkin.graphics.FunkinSprite; import funkin.graphics.shaders.AngleMask; +import funkin.graphics.shaders.GaussianBlurShader; import funkin.graphics.shaders.HSVShader; import funkin.graphics.shaders.PureColor; import funkin.graphics.shaders.StrokeShader; import funkin.input.Controls; import funkin.play.PlayStatePlaylist; +import funkin.play.scoring.Scoring; +import funkin.play.scoring.Scoring.ScoringRank; import funkin.play.song.Song; -import funkin.ui.story.Level; import funkin.save.Save; import funkin.save.Save.SaveScoreData; import funkin.ui.AtlasText; -import funkin.play.scoring.Scoring; -import funkin.play.scoring.Scoring.ScoringRank; +import funkin.ui.freeplay.charselect.PlayableCharacter; +import funkin.ui.freeplay.SongMenuItem.FreeplayRank; import funkin.ui.mainmenu.MainMenuState; import funkin.ui.MusicBeatSubState; +import funkin.ui.story.Level; import funkin.ui.transition.LoadingState; import funkin.ui.transition.StickerSubState; import funkin.util.MathUtil; +import funkin.util.SortUtil; import lime.utils.Assets; -import flixel.tweens.misc.ShakeTween; -import funkin.effects.IntervalShake; -import funkin.ui.freeplay.SongMenuItem.FreeplayRank; -import funkin.ui.freeplay.charselect.PlayableCharacter; +import openfl.display.BlendMode; /** * Parameters used to initialize the FreeplayState. @@ -94,6 +95,7 @@ typedef FromResultsParams = /** * The state for the freeplay menu, allowing the player to select any song to play. */ +@:nullSafety class FreeplayState extends MusicBeatSubState { // @@ -164,10 +166,9 @@ class FreeplayState extends MusicBeatSubState var grpSongs:FlxTypedGroup; var grpCapsules:FlxTypedGroup; - var curCapsule:SongMenuItem; var curPlaying:Bool = false; - var dj:FreeplayDJ; + var dj:Null = null; var ostName:FlxText; var albumRoll:AlbumRoll; @@ -175,7 +176,7 @@ class FreeplayState extends MusicBeatSubState var letterSort:LetterSort; var exitMovers:ExitMoverData = new Map(); - var stickerSubState:StickerSubState; + var stickerSubState:Null = null; public static var rememberedDifficulty:Null = Constants.DEFAULT_DIFFICULTY; public static var rememberedSongId:Null = 'tutorial'; @@ -210,8 +211,12 @@ class FreeplayState extends MusicBeatSubState public function new(?params:FreeplayStateParams, ?stickers:StickerSubState) { currentCharacterId = params?.character ?? Constants.DEFAULT_CHARACTER; - currentCharacter = PlayerRegistry.instance.fetchEntry(currentCharacterId); - if (currentCharacter == null) throw 'Could not build Freeplay state for character: $currentCharacterId'; + var fetchPlayableCharacter = function():PlayableCharacter { + var result = PlayerRegistry.instance.fetchEntry(params?.character ?? Constants.DEFAULT_CHARACTER); + if (result == null) throw 'No valid playable character with id ${params?.character}'; + return result; + }; + currentCharacter = fetchPlayableCharacter(); fromResultsParams = params?.fromResults; @@ -220,12 +225,54 @@ class FreeplayState extends MusicBeatSubState prepForNewRank = true; } + super(FlxColor.TRANSPARENT); + if (stickers?.members != null) { stickerSubState = stickers; } - super(FlxColor.TRANSPARENT); + // We build a bunch of sprites BEFORE create() so we can guarantee they aren't null later on. + albumRoll = new AlbumRoll(); + fp = new FreeplayScore(460, 60, 7, 100); + cardGlow = new FlxSprite(-30, -30).loadGraphic(Paths.image('freeplay/cardGlow')); + confirmGlow = new FlxSprite(-30, 240).loadGraphic(Paths.image('freeplay/confirmGlow')); + confirmTextGlow = new FlxSprite(-8, 115).loadGraphic(Paths.image('freeplay/glowingText')); + rankCamera = new FunkinCamera('rankCamera', 0, 0, FlxG.width, FlxG.height); + funnyCam = new FunkinCamera('freeplayFunny', 0, 0, FlxG.width, FlxG.height); + funnyScroll = new BGScrollingText(0, 220, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, false, 60); + funnyScroll2 = new BGScrollingText(0, 335, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, false, 60); + grpCapsules = new FlxTypedGroup(); + grpDifficulties = new FlxTypedSpriteGroup(-300, 80); + letterSort = new LetterSort(400, 75); + grpSongs = new FlxTypedGroup(); + moreWays = new BGScrollingText(0, 160, currentCharacter.getFreeplayDJText(2), FlxG.width, true, 43); + moreWays2 = new BGScrollingText(0, 397, currentCharacter.getFreeplayDJText(2), FlxG.width, true, 43); + pinkBack = FunkinSprite.create('freeplay/pinkBack'); + rankBg = new FunkinSprite(0, 0); + rankVignette = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/rankVignette')); + sparks = new FlxSprite(0, 0); + sparksADD = new FlxSprite(0, 0); + txtCompletion = new AtlasText(1185, 87, '69', AtlasFont.FREEPLAY_CLEAR); + txtNuts = new BGScrollingText(0, 285, currentCharacter.getFreeplayDJText(3), FlxG.width / 2, true, 43); + + ostName = new FlxText(8, 8, FlxG.width - 8 - 8, 'OFFICIAL OST', 48); + + orangeBackShit = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFFEDA00); + + bgDad = new FlxSprite(pinkBack.width * 0.74, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad')); + alsoOrangeLOL = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFFFD400); + confirmGlow2 = new FlxSprite(confirmGlow.x, confirmGlow.y).loadGraphic(Paths.image('freeplay/confirmGlow2')); + funnyScroll3 = new BGScrollingText(0, orangeBackShit.y + 10, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, 60); + backingTextYeah = new FlxAtlasSprite(640, 370, Paths.animateAtlas("freeplay/backing-text-yeah"), + { + FrameRate: 24.0, + Reversed: false, + // ?OnComplete:Void -> Void, + ShowPivot: false, + Antialiasing: true, + ScrollFactor: new FlxPoint(1, 1), + }); } override function create():Void @@ -236,12 +283,6 @@ class FreeplayState extends MusicBeatSubState FlxTransitionableState.skipNextTransIn = true; - // dedicated camera for the state so we don't need to fuk around with camera scrolls from the mainmenu / elsewhere - funnyCam = new FunkinCamera('freeplayFunny', 0, 0, FlxG.width, FlxG.height); - funnyCam.bgColor = FlxColor.TRANSPARENT; - FlxG.cameras.add(funnyCam, false); - this.cameras = [funnyCam]; - if (stickerSubState != null) { this.persistentUpdate = true; @@ -277,7 +318,7 @@ class FreeplayState extends MusicBeatSubState // programmatically adds the songs via LevelRegistry and SongRegistry for (levelId in LevelRegistry.instance.listSortedLevelIds()) { - var level:Level = LevelRegistry.instance.fetchEntry(levelId); + var level:Null = LevelRegistry.instance.fetchEntry(levelId); if (level == null) { @@ -287,7 +328,7 @@ class FreeplayState extends MusicBeatSubState for (songId in level.getSongs()) { - var song:Song = SongRegistry.instance.fetchEntry(songId); + var song:Null = SongRegistry.instance.fetchEntry(songId); if (song == null) { @@ -319,17 +360,14 @@ class FreeplayState extends MusicBeatSubState trace(FlxG.camera.initialZoom); trace(FlxCamera.defaultZoom); - pinkBack = FunkinSprite.create('freeplay/pinkBack'); pinkBack.color = 0xFFFFD4E9; // sets it to pink! pinkBack.x -= pinkBack.width; FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut}); add(pinkBack); - orangeBackShit = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFFEDA00); add(orangeBackShit); - alsoOrangeLOL = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFFFD400); add(alsoOrangeLOL); exitMovers.set([pinkBack, orangeBackShit, alsoOrangeLOL], @@ -344,15 +382,11 @@ class FreeplayState extends MusicBeatSubState orangeBackShit.visible = false; alsoOrangeLOL.visible = false; - confirmTextGlow = new FlxSprite(-8, 115).loadGraphic(Paths.image('freeplay/glowingText')); confirmTextGlow.blend = BlendMode.ADD; confirmTextGlow.visible = false; - confirmGlow = new FlxSprite(-30, 240).loadGraphic(Paths.image('freeplay/confirmGlow')); confirmGlow.blend = BlendMode.ADD; - confirmGlow2 = new FlxSprite(confirmGlow.x, confirmGlow.y).loadGraphic(Paths.image('freeplay/confirmGlow2')); - confirmGlow.visible = false; confirmGlow2.visible = false; @@ -367,7 +401,6 @@ class FreeplayState extends MusicBeatSubState FlxG.debugger.addTrackerProfile(new TrackerProfile(BGScrollingText, ['x', 'y', 'speed', 'size'])); - moreWays = new BGScrollingText(0, 160, 'HOT BLOODED IN MORE WAYS THAN ONE', FlxG.width, true, 43); moreWays.funnyColor = 0xFFFFF383; moreWays.speed = 6.8; grpTxtScrolls.add(moreWays); @@ -378,7 +411,6 @@ class FreeplayState extends MusicBeatSubState speed: 0.4, }); - funnyScroll = new BGScrollingText(0, 220, 'BOYFRIEND', FlxG.width / 2, false, 60); funnyScroll.funnyColor = 0xFFFF9963; funnyScroll.speed = -3.8; grpTxtScrolls.add(funnyScroll); @@ -391,7 +423,6 @@ class FreeplayState extends MusicBeatSubState wait: 0 }); - txtNuts = new BGScrollingText(0, 285, 'PROTECT YO NUTS', FlxG.width / 2, true, 43); txtNuts.speed = 3.5; grpTxtScrolls.add(txtNuts); exitMovers.set([txtNuts], @@ -400,7 +431,6 @@ class FreeplayState extends MusicBeatSubState speed: 0.4, }); - funnyScroll2 = new BGScrollingText(0, 335, 'BOYFRIEND', FlxG.width / 2, false, 60); funnyScroll2.funnyColor = 0xFFFF9963; funnyScroll2.speed = -3.8; grpTxtScrolls.add(funnyScroll2); @@ -411,7 +441,6 @@ class FreeplayState extends MusicBeatSubState speed: 0.5, }); - moreWays2 = new BGScrollingText(0, 397, 'HOT BLOODED IN MORE WAYS THAN ONE', FlxG.width, true, 43); moreWays2.funnyColor = 0xFFFFF383; moreWays2.speed = 6.8; grpTxtScrolls.add(moreWays2); @@ -422,7 +451,6 @@ class FreeplayState extends MusicBeatSubState speed: 0.4 }); - funnyScroll3 = new BGScrollingText(0, orangeBackShit.y + 10, 'BOYFRIEND', FlxG.width / 2, 60); funnyScroll3.funnyColor = 0xFFFEA400; funnyScroll3.speed = -3.8; grpTxtScrolls.add(funnyScroll3); @@ -433,19 +461,8 @@ class FreeplayState extends MusicBeatSubState speed: 0.3 }); - backingTextYeah = new FlxAtlasSprite(640, 370, Paths.animateAtlas("freeplay/backing-text-yeah"), - { - FrameRate: 24.0, - Reversed: false, - // ?OnComplete:Void -> Void, - ShowPivot: false, - Antialiasing: true, - ScrollFactor: new FlxPoint(1, 1), - }); - add(backingTextYeah); - cardGlow = new FlxSprite(-30, -30).loadGraphic(Paths.image('freeplay/cardGlow')); cardGlow.blend = BlendMode.ADD; cardGlow.visible = false; @@ -462,7 +479,6 @@ class FreeplayState extends MusicBeatSubState add(dj); } - bgDad = new FlxSprite(pinkBack.width * 0.74, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad')); bgDad.shader = new AngleMask(); bgDad.visible = false; @@ -488,17 +504,13 @@ class FreeplayState extends MusicBeatSubState blackOverlayBullshitLOLXD.shader = bgDad.shader; - rankBg = new FunkinSprite(0, 0); rankBg.makeSolidColor(FlxG.width, FlxG.height, 0xD3000000); add(rankBg); - grpSongs = new FlxTypedGroup(); add(grpSongs); - grpCapsules = new FlxTypedGroup(); add(grpCapsules); - grpDifficulties = new FlxTypedSpriteGroup(-300, 80); add(grpDifficulties); exitMovers.set([grpDifficulties], @@ -525,7 +537,6 @@ class FreeplayState extends MusicBeatSubState if (diffSprite.difficultyId == currentDifficulty) diffSprite.visible = true; } - albumRoll = new AlbumRoll(); albumRoll.albumId = null; add(albumRoll); @@ -540,7 +551,6 @@ class FreeplayState extends MusicBeatSubState fnfFreeplay.font = 'VCR OSD Mono'; fnfFreeplay.visible = false; - ostName = new FlxText(8, 8, FlxG.width - 8 - 8, 'OFFICIAL OST', 48); ostName.font = 'VCR OSD Mono'; ostName.alignment = RIGHT; ostName.visible = false; @@ -572,7 +582,6 @@ class FreeplayState extends MusicBeatSubState tmr.time = FlxG.random.float(20, 60); }, 0); - fp = new FreeplayScore(460, 60, 7, 100); fp.visible = false; add(fp); @@ -580,11 +589,9 @@ class FreeplayState extends MusicBeatSubState clearBoxSprite.visible = false; add(clearBoxSprite); - txtCompletion = new AtlasText(1185, 87, '69', AtlasFont.FREEPLAY_CLEAR); txtCompletion.visible = false; add(txtCompletion); - letterSort = new LetterSort(400, 75); add(letterSort); letterSort.visible = false; @@ -632,7 +639,7 @@ class FreeplayState extends MusicBeatSubState // be careful not to "add()" things in here unless it's to a group that's already added to the state // otherwise it won't be properly attatched to funnyCamera (relavent code should be at the bottom of create()) - dj.onIntroDone.add(function() { + var onDJIntroDone = function() { // when boyfriend hits dat shiii albumRoll.playIntro(); @@ -679,20 +686,27 @@ class FreeplayState extends MusicBeatSubState cardGlow.visible = true; FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.45, {ease: FlxEase.sineOut}); - if (prepForNewRank) + if (prepForNewRank && fromResultsParams != null) { rankAnimStart(fromResultsParams); } - }); + }; + + if (dj != null) + { + dj.onIntroDone.add(onDJIntroDone); + } + else + { + onDJIntroDone(); + } generateSongList(null, false); // dedicated camera for the state so we don't need to fuk around with camera scrolls from the mainmenu / elsewhere - funnyCam = new FunkinCamera('freeplayFunny', 0, 0, FlxG.width, FlxG.height); funnyCam.bgColor = FlxColor.TRANSPARENT; FlxG.cameras.add(funnyCam, false); - rankVignette = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/rankVignette')); rankVignette.scale.set(2, 2); rankVignette.updateHitbox(); rankVignette.blend = BlendMode.ADD; @@ -704,7 +718,6 @@ class FreeplayState extends MusicBeatSubState bs.cameras = [funnyCam]; }); - rankCamera = new FunkinCamera('rankCamera', 0, 0, FlxG.width, FlxG.height); rankCamera.bgColor = FlxColor.TRANSPARENT; FlxG.cameras.add(rankCamera, false); rankBg.cameras = [rankCamera]; @@ -716,8 +729,8 @@ class FreeplayState extends MusicBeatSubState } } - var currentFilter:SongFilter = null; - var currentFilteredSongs:Array = []; + var currentFilter:Null = null; + var currentFilteredSongs:Array> = []; /** * Given the current filter, rebuild the current song list. @@ -728,7 +741,7 @@ class FreeplayState extends MusicBeatSubState */ public function generateSongList(filterStuff:Null, force:Bool = false, onlyIfChanged:Bool = true):Void { - var tempSongs:Array = songs; + var tempSongs:Array> = songs; // Remember just the difficulty because it's important for song sorting. if (rememberedDifficulty != null) @@ -790,11 +803,12 @@ class FreeplayState extends MusicBeatSubState for (i in 0...tempSongs.length) { - if (tempSongs[i] == null) continue; + var tempSong = tempSongs[i]; + if (tempSong == null) continue; var funnyMenu:SongMenuItem = grpCapsules.recycle(SongMenuItem); - funnyMenu.init(FlxG.width, 0, tempSongs[i]); + funnyMenu.init(FlxG.width, 0, tempSong); funnyMenu.onConfirm = function() { capsuleOnConfirmDefault(funnyMenu); }; @@ -803,8 +817,8 @@ class FreeplayState extends MusicBeatSubState funnyMenu.ID = i; funnyMenu.capsule.alpha = 0.5; funnyMenu.songText.visible = false; - funnyMenu.favIcon.visible = tempSongs[i].isFav; - funnyMenu.favIconBlurred.visible = tempSongs[i].isFav; + funnyMenu.favIcon.visible = tempSong.isFav; + funnyMenu.favIconBlurred.visible = tempSong.isFav; funnyMenu.hsvShader = hsvShader; funnyMenu.newText.animation.curAnim.curFrame = 45 - ((i * 4) % 45); @@ -828,13 +842,10 @@ class FreeplayState extends MusicBeatSubState * @param songFilter The filter to apply * @return Array */ - public function sortSongs(songsToFilter:Array, songFilter:SongFilter):Array + public function sortSongs(songsToFilter:Array>, songFilter:SongFilter):Array> { - var filterAlphabetically = function(a:FreeplaySongData, b:FreeplaySongData):Int { - if (a?.songName.toLowerCase() < b?.songName.toLowerCase()) return -1; - else if (a?.songName.toLowerCase() > b?.songName.toLowerCase()) return 1; - else - return 0; + var filterAlphabetically = function(a:Null, b:Null):Int { + return SortUtil.alphabetically(a?.songName ?? '', b?.songName ?? ''); }; switch (songFilter.filterType) @@ -858,7 +869,7 @@ class FreeplayState extends MusicBeatSubState songsToFilter = songsToFilter.filter(str -> { if (str == null) return true; // Random - return str.songName.toLowerCase().startsWith(songFilter.filterData); + return str.songName.toLowerCase().startsWith(songFilter.filterData ?? ''); }); case ALL: // no filter! @@ -880,32 +891,28 @@ class FreeplayState extends MusicBeatSubState var sparks:FlxSprite; var sparksADD:FlxSprite; - function rankAnimStart(fromResults:Null):Void + function rankAnimStart(fromResults:FromResultsParams):Void { busy = true; grpCapsules.members[curSelected].sparkle.alpha = 0; // grpCapsules.members[curSelected].forcePosition(); - if (fromResults != null) - { - rememberedSongId = fromResults.songId; - rememberedDifficulty = fromResults.difficultyId; - changeSelection(); - changeDiff(); - } + rememberedSongId = fromResults.songId; + rememberedDifficulty = fromResults.difficultyId; + changeSelection(); + changeDiff(); - dj.fistPump(); + if (dj != null) dj.fistPump(); // rankCamera.fade(FlxColor.BLACK, 0.5, true); rankCamera.fade(0xFF000000, 0.5, true, null, true); if (FlxG.sound.music != null) FlxG.sound.music.volume = 0; rankBg.alpha = 1; - if (fromResults?.oldRank != null) + if (fromResults.oldRank != null) { grpCapsules.members[curSelected].fakeRanking.rank = fromResults.oldRank; grpCapsules.members[curSelected].fakeBlurredRanking.rank = fromResults.oldRank; - sparks = new FlxSprite(0, 0); sparks.frames = Paths.getSparrowAtlas('freeplay/sparks'); sparks.animation.addByPrefix('sparks', 'sparks', 24, false); sparks.visible = false; @@ -915,7 +922,6 @@ class FreeplayState extends MusicBeatSubState add(sparks); sparks.cameras = [rankCamera]; - sparksADD = new FlxSprite(0, 0); sparksADD.visible = false; sparksADD.frames = Paths.getSparrowAtlas('freeplay/sparksadd'); sparksADD.animation.addByPrefix('sparks add', 'sparks add', 24, false); @@ -980,14 +986,14 @@ class FreeplayState extends MusicBeatSubState grpCapsules.members[curSelected].ranking.scale.set(20, 20); grpCapsules.members[curSelected].blurredRanking.scale.set(20, 20); - if (fromResults?.newRank != null) + if (fromResults != null && fromResults.newRank != null) { grpCapsules.members[curSelected].ranking.animation.play(fromResults.newRank.getFreeplayRankIconAsset(), true); } FlxTween.tween(grpCapsules.members[curSelected].ranking, {"scale.x": 1, "scale.y": 1}, 0.1); - if (fromResults?.newRank != null) + if (fromResults != null && fromResults.newRank != null) { grpCapsules.members[curSelected].blurredRanking.animation.play(fromResults.newRank.getFreeplayRankIconAsset(), true); } @@ -1078,11 +1084,11 @@ class FreeplayState extends MusicBeatSubState if (fromResultsParams?.newRank == SHIT) { - dj.pumpFistBad(); + if (dj != null) dj.pumpFistBad(); } else { - dj.pumpFist(); + if (dj != null) dj.pumpFist(); } rankCamera.zoom = 0.8; @@ -1196,7 +1202,13 @@ class FreeplayState extends MusicBeatSubState #if debug if (FlxG.keys.justPressed.T) { - rankAnimStart(fromResultsParams); + rankAnimStart(fromResultsParams ?? + { + playRankAnim: true, + newRank: PERFECT_GOLD, + songId: "tutorial", + difficultyId: "hard" + }); } if (FlxG.keys.justPressed.P) @@ -1427,7 +1439,7 @@ class FreeplayState extends MusicBeatSubState } spamTimer += elapsed; - dj.resetAFKTimer(); + if (dj != null) dj.resetAFKTimer(); } else { @@ -1438,31 +1450,31 @@ class FreeplayState extends MusicBeatSubState #if !html5 if (FlxG.mouse.wheel != 0) { - dj.resetAFKTimer(); + if (dj != null) dj.resetAFKTimer(); changeSelection(-Math.round(FlxG.mouse.wheel)); } #else if (FlxG.mouse.wheel < 0) { - dj.resetAFKTimer(); + if (dj != null) dj.resetAFKTimer(); changeSelection(-Math.round(FlxG.mouse.wheel / 8)); } else if (FlxG.mouse.wheel > 0) { - dj.resetAFKTimer(); + if (dj != null) dj.resetAFKTimer(); changeSelection(-Math.round(FlxG.mouse.wheel / 8)); } #end if (controls.UI_LEFT_P) { - dj.resetAFKTimer(); + if (dj != null) dj.resetAFKTimer(); changeDiff(-1); generateSongList(currentFilter, true); } if (controls.UI_RIGHT_P) { - dj.resetAFKTimer(); + if (dj != null) dj.resetAFKTimer(); changeDiff(1); generateSongList(currentFilter, true); } @@ -1472,7 +1484,7 @@ class FreeplayState extends MusicBeatSubState busy = true; FlxTween.globalManager.clear(); FlxTimer.globalManager.clear(); - dj.onIntroDone.removeAll(); + if (dj != null) dj.onIntroDone.removeAll(); FunkinSound.playOnce(Paths.sound('cancelMenu')); @@ -1498,7 +1510,8 @@ class FreeplayState extends MusicBeatSubState for (grpSpr in exitMovers.keys()) { - var moveData:MoveData = exitMovers.get(grpSpr); + var moveData:Null = exitMovers.get(grpSpr); + if (moveData == null) continue; for (spr in grpSpr) { @@ -1506,14 +1519,14 @@ class FreeplayState extends MusicBeatSubState var funnyMoveShit:MoveData = moveData; - if (moveData.x == null) funnyMoveShit.x = spr.x; - if (moveData.y == null) funnyMoveShit.y = spr.y; - if (moveData.speed == null) funnyMoveShit.speed = 0.2; - if (moveData.wait == null) funnyMoveShit.wait = 0; + var moveDataX = funnyMoveShit.x ?? spr.x; + var moveDataY = funnyMoveShit.y ?? spr.y; + var moveDataSpeed = funnyMoveShit.speed ?? 0.2; + var moveDataWait = funnyMoveShit.wait ?? 0; - FlxTween.tween(spr, {x: funnyMoveShit.x, y: funnyMoveShit.y}, funnyMoveShit.speed, {ease: FlxEase.expoIn}); + FlxTween.tween(spr, {x: moveDataX, y: moveDataY}, moveDataSpeed, {ease: FlxEase.expoIn}); - longestTimer = Math.max(longestTimer, funnyMoveShit.speed + funnyMoveShit.wait); + longestTimer = Math.max(longestTimer, moveDataSpeed + moveDataWait); } } @@ -1586,19 +1599,18 @@ class FreeplayState extends MusicBeatSubState var daSong:Null = grpCapsules.members[curSelected].songData; if (daSong != null) { - // TODO: Make this actually be the variation you're focused on. We don't need to fetch the song metadata just to calculate it. - var targetSong:Song = SongRegistry.instance.fetchEntry(grpCapsules.members[curSelected].songData.songId); + var targetSong:Null = SongRegistry.instance.fetchEntry(daSong.songId); if (targetSong == null) { - FlxG.log.warn('WARN: could not find song with id (${grpCapsules.members[curSelected].songData.songId})'); + FlxG.log.warn('WARN: could not find song with id (${daSong.songId})'); return; } - var targetVariation:String = targetSong.getFirstValidVariation(currentDifficulty); + var targetVariation:String = targetSong.getFirstValidVariation(currentDifficulty) ?? ''; // TODO: This line of code makes me sad, but you can't really fix it without a breaking migration. var suffixedDifficulty = (targetVariation != Constants.DEFAULT_VARIATION && targetVariation != 'erect') ? '$currentDifficulty-${targetVariation}' : currentDifficulty; - var songScore:SaveScoreData = Save.instance.getSongScore(grpCapsules.members[curSelected].songData.songId, suffixedDifficulty); + var songScore:Null = Save.instance.getSongScore(daSong.songId, suffixedDifficulty); intendedScore = songScore?.score ?? 0; intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes); rememberedDifficulty = currentDifficulty; @@ -1660,7 +1672,7 @@ class FreeplayState extends MusicBeatSubState } // Set the album graphic and play the animation if relevant. - var newAlbumId:String = daSong?.albumId; + var newAlbumId:Null = daSong?.albumId; if (albumRoll.albumId != newAlbumId) { albumRoll.albumId = newAlbumId; @@ -1698,7 +1710,7 @@ class FreeplayState extends MusicBeatSubState }); trace('Available songs: ${availableSongCapsules.map(function(cap) { - return cap.songData.songName; + return cap?.songData?.songName; })}'); if (availableSongCapsules.length == 0) @@ -1727,17 +1739,20 @@ class FreeplayState extends MusicBeatSubState PlayStatePlaylist.isStoryMode = false; - var targetSong:Song = SongRegistry.instance.fetchEntry(cap.songData.songId); - if (targetSong == null) + var targetSongId:String = cap?.songData?.songId ?? 'unknown'; + var targetSongNullable:Null = SongRegistry.instance.fetchEntry(targetSongId); + if (targetSongNullable == null) { - FlxG.log.warn('WARN: could not find song with id (${cap.songData.songId})'); + FlxG.log.warn('WARN: could not find song with id (${targetSongId})'); return; } + var targetSong:Song = targetSongNullable; var targetDifficultyId:String = currentDifficulty; - var targetVariation:String = targetSong.getFirstValidVariation(targetDifficultyId, currentCharacter); - PlayStatePlaylist.campaignId = cap.songData.levelId; + var targetVariation:Null = targetSong.getFirstValidVariation(targetDifficultyId, currentCharacter); + var targetLevelId:Null = cap?.songData?.levelId; + PlayStatePlaylist.campaignId = targetLevelId ?? null; - var targetDifficulty:SongDifficulty = targetSong.getDifficulty(targetDifficultyId, targetVariation); + var targetDifficulty:Null = targetSong.getDifficulty(targetDifficultyId, targetVariation); if (targetDifficulty == null) { FlxG.log.warn('WARN: could not find difficulty with id (${targetDifficultyId})'); @@ -1759,7 +1774,7 @@ class FreeplayState extends MusicBeatSubState // Visual and audio effects. FunkinSound.playOnce(Paths.sound('confirmMenu')); - dj.confirm(); + if (dj != null) dj.confirm(); grpCapsules.members[curSelected].forcePosition(); grpCapsules.members[curSelected].confirm(); @@ -1801,7 +1816,7 @@ class FreeplayState extends MusicBeatSubState new FlxTimer().start(1, function(tmr:FlxTimer) { FunkinSound.emptyPartialQueue(); - Paths.setCurrentLevel(cap.songData.levelId); + Paths.setCurrentLevel(cap?.songData?.levelId); LoadingState.loadPlayState( { targetSong: targetSong, @@ -1856,7 +1871,7 @@ class FreeplayState extends MusicBeatSubState var daSongCapsule:SongMenuItem = grpCapsules.members[curSelected]; if (daSongCapsule.songData != null) { - var songScore:SaveScoreData = Save.instance.getSongScore(daSongCapsule.songData.songId, currentDifficulty); + var songScore:Null = Save.instance.getSongScore(daSongCapsule.songData.songId, currentDifficulty); intendedScore = songScore?.score ?? 0; intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes); diffIdsCurrent = daSongCapsule.songData.songDifficulties; @@ -1906,7 +1921,10 @@ class FreeplayState extends MusicBeatSubState } else { - var previewSong:Null = SongRegistry.instance.fetchEntry(daSongCapsule.songData.songId); + var previewSongId:Null = daSongCapsule?.songData?.songId; + if (previewSongId == null) return; + + var previewSong:Null = SongRegistry.instance.fetchEntry(previewSongId); var songDifficulty = previewSong?.getDifficulty(currentDifficulty, previewSong?.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST); var baseInstrumentalId:String = songDifficulty?.characters?.instrumental ?? ''; @@ -1924,7 +1942,9 @@ class FreeplayState extends MusicBeatSubState instSuffix = (instSuffix != '') ? '-$instSuffix' : ''; - FunkinSound.playMusic(daSongCapsule.songData.songId, + trace('Attempting to play partial preview: ${previewSongId}:${instSuffix}'); + + FunkinSound.playMusic(previewSongId, { startingVolume: 0.0, overrideExisting: true, @@ -1952,7 +1972,7 @@ class FreeplayState extends MusicBeatSubState public static function build(?params:FreeplayStateParams, ?stickers:StickerSubState):MusicBeatState { var result:MainMenuState; - if (params?.fromResults?.playRankAnim) result = new MainMenuState(true); + if (params?.fromResults?.playRankAnim ?? false) result = new MainMenuState(true); else result = new MainMenuState(false); diff --git a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx index 282e35d7a6..6d7b96c581 100644 --- a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx +++ b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx @@ -82,6 +82,11 @@ class PlayableCharacter implements IRegistryEntry return _data.freeplayDJ; } + public function getFreeplayDJText(index:Int):String + { + return _data.freeplayDJ.getFreeplayDJText(index); + } + /** * Returns whether this character is unlocked. */ diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 2eba406d9b..9bf4654847 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -117,7 +117,10 @@ class MainMenuState extends MusicBeatState FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - openSubState(new FreeplayState()); + openSubState(new FreeplayState( + { + character: FlxG.keys.pressed.SHIFT ? 'pico' : 'bf', + })); }); #if CAN_OPEN_LINKS diff --git a/source/funkin/util/SortUtil.hx b/source/funkin/util/SortUtil.hx index c5ac175be7..f6d3721f0a 100644 --- a/source/funkin/util/SortUtil.hx +++ b/source/funkin/util/SortUtil.hx @@ -97,7 +97,7 @@ class SortUtil * @param b The second string to compare. * @return 1 if `a` comes before `b`, -1 if `b` comes before `a`, 0 if they are equal */ - public static function alphabetically(a:String, b:String):Int + public static function alphabetically(?a:String, ?b:String):Int { a = a.toUpperCase(); b = b.toUpperCase(); From bd17d965f57ef1a62638def0f0f5ff35e14613a2 Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:53:33 +0300 Subject: [PATCH 101/469] [BUGFIX] Fixed Ranks not appearing in freeplay for custom variations Freeplay tries to access a song's rank using the current difficulty name alone, but custom variation ranks are being saved with a variation prefix. This PR makes freeplay look for the variation prefix when necessary. --- source/funkin/ui/freeplay/FreeplayState.hx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 5725101cd4..e36b6942f9 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -2158,8 +2158,13 @@ class FreeplaySongData { this.albumId = songDifficulty.album; } + + // TODO: This line of code makes me sad, but you can't really fix it without a breaking migration. + // `easy`, `erect`, `normal-pico`, etc. + var suffixedDifficulty = (songDifficulty.variation != Constants.DEFAULT_VARIATION + && songDifficulty.variation != 'erect') ? '$currentDifficulty-${songDifficulty.variation}' : currentDifficulty; - this.scoringRank = Save.instance.getSongRank(songId, currentDifficulty); + this.scoringRank = Save.instance.getSongRank(songId, suffixedDifficulty); this.isNew = song.isSongNew(currentDifficulty); } From e32fcee77c9b98e28d74f91b0be47b8c87b662bd Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 3 Jul 2024 22:50:39 -0400 Subject: [PATCH 102/469] Migrate results animations to JSON data so we can implement them per-character --- assets | 2 +- source/funkin/InitState.hx | 3 +- .../funkin/data/freeplay/player/PlayerData.hx | 66 +++- .../data/freeplay/player/PlayerRegistry.hx | 2 +- .../graphics/adobeanimate/FlxAtlasSprite.hx | 12 +- source/funkin/play/PlayState.hx | 1 + source/funkin/play/ResultState.hx | 342 ++++++------------ .../freeplay/charselect/PlayableCharacter.hx | 27 ++ 8 files changed, 200 insertions(+), 255 deletions(-) diff --git a/assets b/assets index bc1650ba78..0351610af0 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit bc1650ba789d675683a8c0cc27b1e2a42cb686cf +Subproject commit 0351610af02b45eb25e4bbfef0380f6f1d8cf737 diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index c2a56bdc2f..254165a957 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -223,6 +223,7 @@ class InitState extends FlxState storyMode: false, title: "Cum Song Erect by Kawai Sprite", songId: "cum", + characterId: "pico-playable", difficultyId: "nightmare", isNewHighscore: true, scoreData: @@ -238,7 +239,7 @@ class InitState extends FlxState combo: 69, maxCombo: 69, totalNotesHit: 140, - totalNotes: 200 // 0, + totalNotes: 240 // 0, } }, })); diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx index c461c9555d..caae2adc68 100644 --- a/source/funkin/data/freeplay/player/PlayerData.hx +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -38,6 +38,8 @@ class PlayerData @:optional public var freeplayDJ:Null = null; + public var results:Null = null; + /** * Whether this character is unlocked by default. * Use a ScriptedPlayableCharacter to add custom logic. @@ -86,7 +88,6 @@ class PlayerFreeplayDJData @:default("PROTECT YO NUTS") var text3:String; - @:jignored var animationMap:Map; @@ -120,12 +121,18 @@ class PlayerFreeplayDJData return Paths.animateAtlas(assetPath); } - public function getFreeplayDJText(index:Int):String { - switch (index) { - case 1: return text1; - case 2: return text2; - case 3: return text3; - default: return ''; + public function getFreeplayDJText(index:Int):String + { + switch (index) + { + case 1: + return text1; + case 2: + return text2; + case 3: + return text3; + default: + return ''; } } @@ -178,6 +185,51 @@ class PlayerFreeplayDJData } } +typedef PlayerResultsData = +{ + var perfect:Array; + var excellent:Array; + var great:Array; + var good:Array; + var loss:Array; +}; + +typedef PlayerResultsAnimationData = +{ + /** + * `sparrow` or `animate` or whatever + */ + var renderType:String; + + var assetPath:String; + + @:optional + @:default([0, 0]) + var offsets:Array; + + @:optional + @:default(500) + var zIndex:Int; + + @:optional + @:default(0.0) + var delay:Float; + + @:optional + @:default(1.0) + var scale:Float; + + @:optional + @:default('') + var startFrameLabel:Null; + + @:optional + var loopFrame:Null; + + @:optional + var loopFrameLabel:Null; +}; + typedef PlayerFreeplayDJCartoonData = { var soundClickFrame:Int; diff --git a/source/funkin/data/freeplay/player/PlayerRegistry.hx b/source/funkin/data/freeplay/player/PlayerRegistry.hx index 3de9efd411..4656a12869 100644 --- a/source/funkin/data/freeplay/player/PlayerRegistry.hx +++ b/source/funkin/data/freeplay/player/PlayerRegistry.hx @@ -58,7 +58,7 @@ class PlayerRegistry extends BaseRegistry * @param characterId The stage character ID. * @return The playable character. */ - public function getCharacterOwnerId(characterId:String):String + public function getCharacterOwnerId(characterId:String):Null { return ownedCharacterIds[characterId]; } diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index 8a77c1c859..1684e8b33a 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -131,12 +131,14 @@ class FlxAtlasSprite extends FlxAnimate anim.play('', false, false); } } - - // Skip if the animation doesn't exist - if (!hasAnimation(id)) + else { - trace('Animation ' + id + ' not found'); - return; + // Skip if the animation doesn't exist + if (!hasAnimation(id)) + { + trace('Animation ' + id + ' not found'); + return; + } } anim.callback = function(_, frame:Int) { diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index f55cef3888..61c2ad800e 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -3156,6 +3156,7 @@ class PlayState extends MusicBeatSubState storyMode: PlayStatePlaylist.isStoryMode, songId: currentChart.song.id, difficultyId: currentDifficulty, + characterId: currentChart.characters.player, title: PlayStatePlaylist.isStoryMode ? ('${PlayStatePlaylist.campaignTitle}') : ('${currentChart.songName} by ${currentChart.songArtist}'), prevScoreData: prevScoreData, scoreData: diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index a2c5f7e623..ae1a9bcf89 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -14,6 +14,9 @@ import flixel.math.FlxRect; import flixel.text.FlxBitmapText; import funkin.ui.freeplay.FreeplayScore; import flixel.text.FlxText; +import funkin.data.freeplay.player.PlayerRegistry; +import funkin.data.freeplay.player.PlayerData; +import funkin.ui.freeplay.charselect.PlayableCharacter; import flixel.util.FlxColor; import flixel.tweens.FlxEase; import funkin.graphics.FunkinCamera; @@ -55,14 +58,16 @@ class ResultState extends MusicBeatSubState final highscoreNew:FlxSprite; final score:ResultScore; - var bfPerfect:Null = null; - var heartsPerfect:Null = null; - var bfExcellent:Null = null; - var bfGreat:Null = null; - var gfGreat:Null = null; - var bfGood:Null = null; - var gfGood:Null = null; - var bfShit:Null = null; + var characterAtlasAnimations:Array< + { + sprite:FlxAtlasSprite, + delay:Float + }> = []; + var characterSparrowAnimations:Array< + { + sprite:FunkinSprite, + delay:Float + }> = []; var rankBg:FunkinSprite; final cameraBG:FunkinCamera; @@ -157,118 +162,84 @@ class ResultState extends MusicBeatSubState soundSystem.zIndex = 1100; add(soundSystem); - switch (rank) - { - case PERFECT | PERFECT_GOLD: - heartsPerfect = new FlxAtlasSprite(1342, 370, Paths.animateAtlas("resultScreen/results-bf/resultsPERFECT/hearts", "shared")); - heartsPerfect.visible = false; - heartsPerfect.zIndex = 501; - add(heartsPerfect); - - heartsPerfect.anim.onComplete = () -> { - if (heartsPerfect != null) - { - // bfPerfect.anim.curFrame = 137; - heartsPerfect.anim.curFrame = 43; - heartsPerfect.anim.play(); // unpauses this anim, since it's on PlayOnce! - } - }; + // Fetch playable character data. Default to BF on the results screen if we can't find it. + var playerCharacterId:Null = PlayerRegistry.instance.getCharacterOwnerId(params.characterId); + var playerCharacter:Null = PlayerRegistry.instance.fetchEntry(playerCharacterId ?? 'bf'); - bfPerfect = new FlxAtlasSprite(1342, 370, Paths.animateAtlas("resultScreen/results-bf/resultsPERFECT", "shared")); - bfPerfect.visible = false; - bfPerfect.zIndex = 500; - add(bfPerfect); + trace('Got playable character: ${playerCharacter?.getName()}'); + // Query JSON data based on the rank, then use that to build the animation(s) the player sees. + var playerAnimationDatas:Array = playerCharacter != null ? playerCharacter.getResultsAnimationDatas(rank) : []; - bfPerfect.anim.onComplete = () -> { - if (bfPerfect != null) - { - // bfPerfect.anim.curFrame = 137; - bfPerfect.anim.curFrame = 137; - bfPerfect.anim.play(); // unpauses this anim, since it's on PlayOnce! - } - }; + for (animData in playerAnimationDatas) + { + if (animData == null) continue; + + var animPath:String = Paths.stripLibrary(animData.assetPath); + var animLibrary:String = Paths.getLibrary(animData.assetPath); + var offsets = animData.offsets ?? [0, 0]; + switch (animData.renderType) + { + case 'animateatlas': + var animation:FlxAtlasSprite = new FlxAtlasSprite(offsets[0], offsets[1], Paths.animateAtlas(animPath, animLibrary)); + animation.zIndex = animData.zIndex ?? 500; - case EXCELLENT: - bfExcellent = new FlxAtlasSprite(1329, 429, Paths.animateAtlas("resultScreen/results-bf/resultsEXCELLENT", "shared")); - bfExcellent.visible = false; - bfExcellent.zIndex = 500; - add(bfExcellent); + animation.scale.set(animData.scale ?? 1.0, animData.scale ?? 1.0); - bfExcellent.anim.onComplete = () -> { - if (bfExcellent != null) + if (animData.loopFrameLabel != null) { - bfExcellent.anim.curFrame = 28; - bfExcellent.anim.play(); // unpauses this anim, since it's on PlayOnce! + animation.anim.onComplete = () -> { + if (animation != null) + { + animation.playAnimation(animData.loopFrameLabel ?? ''); // unpauses this anim, since it's on PlayOnce! + } + } } - }; - - case GREAT: - gfGreat = new FlxAtlasSprite(802, 331, Paths.animateAtlas("resultScreen/results-bf/resultsGREAT/gf", "shared")); - gfGreat.visible = false; - gfGreat.zIndex = 499; - add(gfGreat); - - gfGreat.scale.set(0.93, 0.93); - - gfGreat.anim.onComplete = () -> { - if (gfGreat != null) + else if (animData.loopFrame != null) { - gfGreat.anim.curFrame = 9; - gfGreat.anim.play(); // unpauses this anim, since it's on PlayOnce! + animation.anim.onComplete = () -> { + if (animation != null) + { + animation.anim.curFrame = animData.loopFrame ?? 0; + animation.anim.play(); // unpauses this anim, since it's on PlayOnce! + } + } } - }; - - bfGreat = new FlxAtlasSprite(929, 363, Paths.animateAtlas("resultScreen/results-bf/resultsGREAT/bf", "shared")); - bfGreat.visible = false; - bfGreat.zIndex = 500; - add(bfGreat); - bfGreat.scale.set(0.93, 0.93); + // Hide until ready to play. + animation.visible = false; + // Queue to play. + characterAtlasAnimations.push( + { + sprite: animation, + delay: animData.delay ?? 0.0 + }); + // Add to the scene. + add(animation); + case 'sparrow': + var animation:FunkinSprite = FunkinSprite.createSparrow(offsets[0], offsets[1], animPath); + animation.animation.addByPrefix('idle', '', 24, false, false, false); - bfGreat.anim.onComplete = () -> { - if (bfGreat != null) - { - bfGreat.anim.curFrame = 15; - bfGreat.anim.play(); // unpauses this anim, since it's on PlayOnce! - } - }; - - case GOOD: - gfGood = FunkinSprite.createSparrow(625, 325, 'resultScreen/results-bf/resultsGOOD/resultGirlfriendGOOD'); - gfGood.animation.addByPrefix("clap", "Girlfriend Good Anim", 24, false); - gfGood.visible = false; - gfGood.zIndex = 500; - gfGood.animation.finishCallback = _ -> { - if (gfGood != null) + if (animData.loopFrame != null) { - gfGood.animation.play('clap', true, false, 9); - } - }; - add(gfGood); - - bfGood = FunkinSprite.createSparrow(640, -200, 'resultScreen/results-bf/resultsGOOD/resultBoyfriendGOOD'); - bfGood.animation.addByPrefix("fall", "Boyfriend Good Anim0", 24, false); - bfGood.visible = false; - bfGood.zIndex = 501; - bfGood.animation.finishCallback = function(_) { - if (bfGood != null) - { - bfGood.animation.play('fall', true, false, 14); - } - }; - add(bfGood); - - case SHIT: - bfShit = new FlxAtlasSprite(0, 20, Paths.animateAtlas("resultScreen/results-bf/resultsSHIT", "shared")); - bfShit.visible = false; - bfShit.zIndex = 500; - add(bfShit); - bfShit.onAnimationFinish.add((animName) -> { - if (bfShit != null) - { - bfShit.playAnimation('Loop Start'); + animation.animation.finishCallback = (_name:String) -> { + if (animation != null) + { + animation.animation.play('idle', true, false, animData.loopFrame ?? 0); + } + } } - }); + + // Hide until ready to play. + animation.visible = false; + // Queue to play. + characterSparrowAnimations.push( + { + sprite: animation, + delay: animData.delay ?? 0.0 + }); + // Add to the scene. + add(animation); + } } var diffSpr:String = 'diff_${params?.difficultyId ?? 'Normal'}'; @@ -587,94 +558,22 @@ class ResultState extends MusicBeatSubState { showSmallClearPercent(); - switch (rank) + for (atlas in characterAtlasAnimations) { - case PERFECT | PERFECT_GOLD: - if (bfPerfect == null) - { - trace("Could not build PERFECT animation!"); - } - else - { - bfPerfect.visible = true; - bfPerfect.playAnimation(''); - } - new FlxTimer().start(106 / 24, _ -> { - if (heartsPerfect == null) - { - trace("Could not build heartsPerfect animation!"); - } - else - { - heartsPerfect.visible = true; - heartsPerfect.playAnimation(''); - } - }); - case EXCELLENT: - if (bfExcellent == null) - { - trace("Could not build EXCELLENT animation!"); - } - else - { - bfExcellent.visible = true; - bfExcellent.playAnimation(''); - } - case GREAT: - if (bfGreat == null) - { - trace("Could not build GREAT animation!"); - } - else - { - bfGreat.visible = true; - bfGreat.playAnimation(''); - } + new FlxTimer().start(atlas.delay, _ -> { + if (atlas.sprite == null) return; + atlas.sprite.visible = true; + atlas.sprite.playAnimation(''); + }); + } - new FlxTimer().start(6 / 24, _ -> { - if (gfGreat == null) - { - trace("Could not build GREAT animation for gf!"); - } - else - { - gfGreat.visible = true; - gfGreat.playAnimation(''); - } - }); - case SHIT: - if (bfShit == null) - { - trace("Could not build SHIT animation!"); - } - else - { - bfShit.visible = true; - bfShit.playAnimation('Intro'); - } - case GOOD: - if (bfGood == null) - { - trace("Could not build GOOD animation!"); - } - else - { - bfGood.animation.play('fall'); - bfGood.visible = true; - new FlxTimer().start((1 / 24) * 22, _ -> { - // plays about 22 frames (at 24fps timing) after bf spawns in - if (gfGood != null) - { - gfGood.animation.play('clap', true); - gfGood.visible = true; - } - else - { - trace("Could not build GOOD animation!"); - } - }); - } - default: + for (sprite in characterSparrowAnimations) + { + new FlxTimer().start(sprite.delay, _ -> { + if (sprite.sprite == null) return; + sprite.sprite.visible = true; + sprite.sprite.animation.play('idle', true); + }); } } @@ -776,52 +675,6 @@ class ResultState extends MusicBeatSubState // })); // } - // if(heartsPerfect != null){ - // if (FlxG.keys.justPressed.I) - // { - // heartsPerfect.y -= 1; - // trace(heartsPerfect.x, heartsPerfect.y); - // } - // if (FlxG.keys.justPressed.J) - // { - // heartsPerfect.x -= 1; - // trace(heartsPerfect.x, heartsPerfect.y); - // } - // if (FlxG.keys.justPressed.L) - // { - // heartsPerfect.x += 1; - // trace(heartsPerfect.x, heartsPerfect.y); - // } - // if (FlxG.keys.justPressed.K) - // { - // heartsPerfect.y += 1; - // trace(heartsPerfect.x, heartsPerfect.y); - // } - // } - - // if(bfGreat != null){ - // if (FlxG.keys.justPressed.W) - // { - // bfGreat.y -= 1; - // trace(bfGreat.x, bfGreat.y); - // } - // if (FlxG.keys.justPressed.A) - // { - // bfGreat.x -= 1; - // trace(bfGreat.x, bfGreat.y); - // } - // if (FlxG.keys.justPressed.D) - // { - // bfGreat.x += 1; - // trace(bfGreat.x, bfGreat.y); - // } - // if (FlxG.keys.justPressed.S) - // { - // bfGreat.y += 1; - // trace(bfGreat.x, bfGreat.y); - // } - // } - // maskShaderSongName.swagSprX = songName.x; maskShaderDifficulty.swagSprX = difficulty.x; @@ -922,12 +775,21 @@ typedef ResultsStateParams = var storyMode:Bool; /** + * A readable title for the song we just played. * Either "Song Name by Artist Name" or "Week Name" */ var title:String; + /** + * The internal song ID for the song we just played. + */ var songId:String; + /** + * The character ID for the song we just played. + */ + var characterId:String; + /** * Whether the displayed score is a new highscore */ diff --git a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx index 6d7b96c581..c46b4b9300 100644 --- a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx +++ b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx @@ -3,6 +3,7 @@ package funkin.ui.freeplay.charselect; import funkin.data.IRegistryEntry; import funkin.data.freeplay.player.PlayerData; import funkin.data.freeplay.player.PlayerRegistry; +import funkin.play.scoring.Scoring.ScoringRank; /** * An object used to retrieve data about a playable character (also known as "weeks"). @@ -87,6 +88,32 @@ class PlayableCharacter implements IRegistryEntry return _data.freeplayDJ.getFreeplayDJText(index); } + /** + * @param rank Which rank to get info for + * @return An array of animations. For example, BF Great has two animations, one for BF and one for GF + */ + public function getResultsAnimationDatas(rank:ScoringRank):Array + { + if (_data.results == null) + { + return []; + } + + switch (rank) + { + case PERFECT | PERFECT_GOLD: + return _data.results.perfect; + case EXCELLENT: + return _data.results.excellent; + case GREAT: + return _data.results.great; + case GOOD: + return _data.results.good; + case SHIT: + return _data.results.loss; + } + } + /** * Returns whether this character is unlocked. */ From 7fbc7654032f6ae46abcccb120105f0015209319 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 4 Jul 2024 03:32:50 -0400 Subject: [PATCH 103/469] Implement Pico results (good animation) --- assets | 2 +- source/funkin/play/ResultState.hx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assets b/assets index 0351610af0..859186eea4 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0351610af02b45eb25e4bbfef0380f6f1d8cf737 +Subproject commit 859186eea46c8b70deb3530c25304ba875b7b34a diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index ae1a9bcf89..042ab655df 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -187,22 +187,22 @@ class ResultState extends MusicBeatSubState if (animData.loopFrameLabel != null) { - animation.anim.onComplete = () -> { + animation.onAnimationFinish.add((_name:String) -> { if (animation != null) { animation.playAnimation(animData.loopFrameLabel ?? ''); // unpauses this anim, since it's on PlayOnce! } - } + }); } else if (animData.loopFrame != null) { - animation.anim.onComplete = () -> { + animation.onAnimationFinish.add((_name:String) -> { if (animation != null) { animation.anim.curFrame = animData.loopFrame ?? 0; animation.anim.play(); // unpauses this anim, since it's on PlayOnce! } - } + }); } // Hide until ready to play. From a7cfae85453adb0372141f211a5a9c85e8071e57 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 4 Jul 2024 14:48:24 -0400 Subject: [PATCH 104/469] mer --- .gitignore | 1 + .gitmodules | 4 +- assets | 2 +- hmm.json | 18 +- source/funkin/audio/FunkinSound.hx | 4 +- source/funkin/effects/RetroCameraFade.hx | 106 +++++ source/funkin/graphics/FunkinSprite.hx | 101 +++++ source/funkin/input/Controls.hx | 370 +++++++++++------- source/funkin/play/Countdown.hx | 19 +- source/funkin/play/GameOverSubState.hx | 26 +- source/funkin/play/ResultState.hx | 4 +- source/funkin/play/character/CharacterData.hx | 4 +- .../play/character/MultiSparrowCharacter.hx | 2 + .../funkin/play/character/PackerCharacter.hx | 2 + .../funkin/play/character/SparrowCharacter.hx | 2 + source/funkin/play/components/PopUpStuff.hx | 30 +- source/funkin/play/stage/Bopper.hx | 2 - source/funkin/play/stage/Stage.hx | 4 + source/funkin/ui/MenuItem.hx | 23 +- source/funkin/ui/freeplay/FreeplayState.hx | 17 +- source/funkin/ui/freeplay/SongMenuItem.hx | 46 ++- source/funkin/ui/mainmenu/MainMenuState.hx | 10 +- source/funkin/ui/story/LevelTitle.hx | 24 +- source/funkin/ui/story/StoryMenuState.hx | 2 +- source/funkin/ui/transition/LoadingState.hx | 2 +- source/funkin/util/logging/AnsiTrace.hx | 2 +- 26 files changed, 602 insertions(+), 225 deletions(-) create mode 100644 source/funkin/effects/RetroCameraFade.hx diff --git a/.gitignore b/.gitignore index 84585eee0f..143cf08c91 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ shitAudio/ node_modules/ package.json package-lock.json +.aider.* diff --git a/.gitmodules b/.gitmodules index be5e0aaa81..452c0089b7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "assets"] path = assets - url = https://github.com/FunkinCrew/funkin.assets + url = https://github.com/FunkinCrew/Funkin-Assets-secret [submodule "art"] path = art - url = https://github.com/FunkinCrew/funkin.art + url = https://github.com/FunkinCrew/Funkin-Art-secret diff --git a/assets b/assets index 8dd51cde0b..d7ecd602df 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8dd51cde0b9a3730abe9f97d0f50365c396ca784 +Subproject commit d7ecd602df733f0625763a2d7b6056f52147b9e6 diff --git a/hmm.json b/hmm.json index 68e0c5cb03..8eaf242122 100644 --- a/hmm.json +++ b/hmm.json @@ -5,7 +5,7 @@ "type": "git", "dir": null, "ref": "2d83fa863ef0c1eace5f1cf67c3ac315d1a3a8a5", - "url": "https://github.com/Aidan63/linc_discord-rpc" + "url": "https://github.com/FunkinCrew/linc_discord-rpc" }, { "name": "flixel", @@ -44,7 +44,7 @@ "name": "FlxPartialSound", "type": "git", "dir": null, - "ref": "f986332ba5ab02abd386ce662578baf04904604a", + "ref": "a1eab7b9bf507b87200a3341719054fe427f3b15", "url": "https://github.com/FunkinCrew/FlxPartialSound.git" }, { @@ -75,14 +75,14 @@ "name": "haxeui-core", "type": "git", "dir": null, - "ref": "0212d8fdfcafeb5f0d5a41e1ddba8ff21d0e183b", + "ref": "5dc4c933bdc029f6139a47962e3b8c754060f210", "url": "https://github.com/haxeui/haxeui-core" }, { "name": "haxeui-flixel", "type": "git", "dir": null, - "ref": "63a906a6148958dbfde8c7b48d90b0693767fd95", + "ref": "57c1604d6b5174839d7e0e012a4dd5dcbfc129da", "url": "https://github.com/haxeui/haxeui-flixel" }, { @@ -112,7 +112,7 @@ { "name": "hxp", "type": "haxelib", - "version": "1.2.2" + "version": "1.3.0" }, { "name": "json2object", @@ -174,15 +174,15 @@ "name": "thx.core", "type": "git", "dir": null, - "ref": "22605ff44f01971d599641790d6bae4869f7d9f4", - "url": "https://github.com/FunkinCrew/thx.core" + "ref": "6240b6e136f7490d9298edbe8c1891374bd7cdf2", + "url": "https://github.com/fponticelli/thx.core" }, { "name": "thx.semver", "type": "git", "dir": null, - "ref": "cf8d213589a2c7ce4a59b0fdba9e8ff36bc029fa", - "url": "https://github.com/FunkinCrew/thx.semver" + "ref": "bdb191fe7cf745c02a980749906dbf22719e200b", + "url": "https://github.com/fponticelli/thx.semver" } ] } diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index c70f195d2f..11b713f4db 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -491,8 +491,10 @@ class FunkinSound extends FlxSound implements ICloneable var promise:lime.app.Promise> = new lime.app.Promise>(); // split the path and get only after first : - // we are bypassing the openfl/lime asset library fuss + // we are bypassing the openfl/lime asset library fuss on web only + #if web path = Paths.stripLibrary(path); + #end var soundRequest = FlxPartialSound.partialLoadFromFile(path, start, end); diff --git a/source/funkin/effects/RetroCameraFade.hx b/source/funkin/effects/RetroCameraFade.hx new file mode 100644 index 0000000000..d4c1da5efc --- /dev/null +++ b/source/funkin/effects/RetroCameraFade.hx @@ -0,0 +1,106 @@ +package funkin.effects; + +import flixel.util.FlxTimer; +import flixel.FlxCamera; +import openfl.filters.ColorMatrixFilter; + +class RetroCameraFade +{ + // im lazy, but we only use this for week 6 + // and also sorta yoinked for djflixel, lol ! + public static function fadeWhite(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void + { + var steps:Int = 0; + var stepsTotal:Int = camSteps; + + new FlxTimer().start(time / stepsTotal, _ -> { + var V:Float = (1 / stepsTotal) * steps; + if (steps == stepsTotal) V = 1; + + var matrix = [ + 1, 0, 0, 0, V * 255, + 0, 1, 0, 0, V * 255, + 0, 0, 1, 0, V * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrix)]; + steps++; + }, stepsTotal + 1); + } + + public static function fadeFromWhite(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void + { + var steps:Int = camSteps; + var stepsTotal:Int = camSteps; + + var matrixDerp = [ + 1, 0, 0, 0, 1.0 * 255, + 0, 1, 0, 0, 1.0 * 255, + 0, 0, 1, 0, 1.0 * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrixDerp)]; + + new FlxTimer().start(time / stepsTotal, _ -> { + var V:Float = (1 / stepsTotal) * steps; + if (steps == stepsTotal) V = 1; + + var matrix = [ + 1, 0, 0, 0, V * 255, + 0, 1, 0, 0, V * 255, + 0, 0, 1, 0, V * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrix)]; + steps--; + }, camSteps); + } + + public static function fadeToBlack(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void + { + var steps:Int = 0; + var stepsTotal:Int = camSteps; + + new FlxTimer().start(time / stepsTotal, _ -> { + var V:Float = (1 / stepsTotal) * steps; + if (steps == stepsTotal) V = 1; + + var matrix = [ + 1, 0, 0, 0, -V * 255, + 0, 1, 0, 0, -V * 255, + 0, 0, 1, 0, -V * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrix)]; + steps++; + }, camSteps); + } + + public static function fadeBlack(camera:FlxCamera, camSteps:Int = 5, time:Float = 1):Void + { + var steps:Int = camSteps; + var stepsTotal:Int = camSteps; + + var matrixDerp = [ + 1, 0, 0, 0, -1.0 * 255, + 0, 1, 0, 0, -1.0 * 255, + 0, 0, 1, 0, -1.0 * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrixDerp)]; + + new FlxTimer().start(time / stepsTotal, _ -> { + var V:Float = (1 / stepsTotal) * steps; + if (steps == stepsTotal) V = 1; + + var matrix = [ + 1, 0, 0, 0, -V * 255, + 0, 1, 0, 0, -V * 255, + 0, 0, 1, 0, -V * 255, + 0, 0, 0, 1, 0 + ]; + camera.filters = [new ColorMatrixFilter(matrix)]; + steps--; + }, camSteps + 1); + } +} diff --git a/source/funkin/graphics/FunkinSprite.hx b/source/funkin/graphics/FunkinSprite.hx index bfd2e80285..521553527c 100644 --- a/source/funkin/graphics/FunkinSprite.hx +++ b/source/funkin/graphics/FunkinSprite.hx @@ -7,6 +7,10 @@ import flixel.tweens.FlxTween; import openfl.display3D.textures.TextureBase; import funkin.graphics.framebuffer.FixedBitmapData; import openfl.display.BitmapData; +import flixel.math.FlxRect; +import flixel.math.FlxPoint; +import flixel.graphics.frames.FlxFrame; +import flixel.FlxCamera; /** * An FlxSprite with additional functionality. @@ -269,6 +273,103 @@ class FunkinSprite extends FlxSprite return result; } + @:access(flixel.FlxCamera) + override function getBoundingBox(camera:FlxCamera):FlxRect + { + getScreenPosition(_point, camera); + + _rect.set(_point.x, _point.y, width, height); + _rect = camera.transformRect(_rect); + + if (isPixelPerfectRender(camera)) + { + _rect.width = _rect.width / this.scale.x; + _rect.height = _rect.height / this.scale.y; + _rect.x = _rect.x / this.scale.x; + _rect.y = _rect.y / this.scale.y; + _rect.floor(); + _rect.x = _rect.x * this.scale.x; + _rect.y = _rect.y * this.scale.y; + _rect.width = _rect.width * this.scale.x; + _rect.height = _rect.height * this.scale.y; + } + + return _rect; + } + + /** + * Returns the screen position of this object. + * + * @param result Optional arg for the returning point + * @param camera The desired "screen" coordinate space. If `null`, `FlxG.camera` is used. + * @return The screen position of this object. + */ + public override function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint + { + if (result == null) result = FlxPoint.get(); + + if (camera == null) camera = FlxG.camera; + + result.set(x, y); + if (pixelPerfectPosition) + { + _rect.width = _rect.width / this.scale.x; + _rect.height = _rect.height / this.scale.y; + _rect.x = _rect.x / this.scale.x; + _rect.y = _rect.y / this.scale.y; + _rect.round(); + _rect.x = _rect.x * this.scale.x; + _rect.y = _rect.y * this.scale.y; + _rect.width = _rect.width * this.scale.x; + _rect.height = _rect.height * this.scale.y; + } + + return result.subtract(camera.scroll.x * scrollFactor.x, camera.scroll.y * scrollFactor.y); + } + + override function drawSimple(camera:FlxCamera):Void + { + getScreenPosition(_point, camera).subtractPoint(offset); + if (isPixelPerfectRender(camera)) + { + _point.x = _point.x / this.scale.x; + _point.y = _point.y / this.scale.y; + _point.round(); + + _point.x = _point.x * this.scale.x; + _point.y = _point.y * this.scale.y; + } + + _point.copyToFlash(_flashPoint); + camera.copyPixels(_frame, framePixels, _flashRect, _flashPoint, colorTransform, blend, antialiasing); + } + + override function drawComplex(camera:FlxCamera):Void + { + _frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, checkFlipX(), checkFlipY()); + _matrix.translate(-origin.x, -origin.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.round(_matrix.tx / this.scale.x) * this.scale.x; + _matrix.ty = Math.round(_matrix.ty / this.scale.y) * this.scale.y; + } + + camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shader); + } + public override function destroy():Void { frames = null; diff --git a/source/funkin/input/Controls.hx b/source/funkin/input/Controls.hx index e2cae56130..f6c881f6df 100644 --- a/source/funkin/input/Controls.hx +++ b/source/funkin/input/Controls.hx @@ -31,6 +31,7 @@ class Controls extends FlxActionSet * Uses FlxActions to funnel various inputs to a single action. */ var _ui_up = new FunkinAction(Action.UI_UP); + var _ui_left = new FunkinAction(Action.UI_LEFT); var _ui_right = new FunkinAction(Action.UI_RIGHT); var _ui_down = new FunkinAction(Action.UI_DOWN); @@ -325,19 +326,18 @@ class Controls extends FlxActionSet add(_volume_down); add(_volume_mute); - for (action in digitalActions) { - if (Std.isOfType(action, FunkinAction)) { + for (action in digitalActions) + { + if (Std.isOfType(action, FunkinAction)) + { var funkinAction:FunkinAction = cast action; byName[funkinAction.name] = funkinAction; - if (funkinAction.namePressed != null) - byName[funkinAction.namePressed] = funkinAction; - if (funkinAction.nameReleased != null) - byName[funkinAction.nameReleased] = funkinAction; + if (funkinAction.namePressed != null) byName[funkinAction.namePressed] = funkinAction; + if (funkinAction.nameReleased != null) byName[funkinAction.nameReleased] = funkinAction; } } - if (scheme == null) - scheme = None; + if (scheme == null) scheme = None; setKeyboardScheme(scheme, false); } @@ -350,38 +350,38 @@ class Controls extends FlxActionSet public function check(name:Action, trigger:FlxInputState = JUST_PRESSED, gamepadOnly:Bool = false):Bool { #if debug - if (!byName.exists(name)) - throw 'Invalid name: $name'; + if (!byName.exists(name)) throw 'Invalid name: $name'; #end var action = byName[name]; - if (gamepadOnly) - return action.checkFiltered(trigger, GAMEPAD); + if (gamepadOnly) return action.checkFiltered(trigger, GAMEPAD); else return action.checkFiltered(trigger); } - public function getKeysForAction(name:Action):Array { + public function getKeysForAction(name:Action):Array + { #if debug - if (!byName.exists(name)) - throw 'Invalid name: $name'; + if (!byName.exists(name)) throw 'Invalid name: $name'; #end // TODO: Revert to `.map().filter()` once HashLink doesn't complain anymore. var result:Array = []; - for (input in byName[name].inputs) { + for (input in byName[name].inputs) + { if (input.device == KEYBOARD) result.push(input.inputID); } return result; } - public function getButtonsForAction(name:Action):Array { + public function getButtonsForAction(name:Action):Array + { #if debug - if (!byName.exists(name)) - throw 'Invalid name: $name'; + if (!byName.exists(name)) throw 'Invalid name: $name'; #end var result:Array = []; - for (input in byName[name].inputs) { + for (input in byName[name].inputs) + { if (input.device == GAMEPAD) result.push(input.inputID); } return result; @@ -405,7 +405,7 @@ class Controls extends FlxActionSet function getActionFromControl(control:Control):FlxActionDigital { - return switch(control) + return switch (control) { case UI_UP: _ui_up; case UI_DOWN: _ui_down; @@ -448,7 +448,7 @@ class Controls extends FlxActionSet */ function forEachBound(control:Control, func:FlxActionDigital->FlxInputState->Void) { - switch(control) + switch (control) { case UI_UP: func(_ui_up, PRESSED); @@ -519,10 +519,9 @@ class Controls extends FlxActionSet public function replaceBinding(control:Control, device:Device, toAdd:Int, toRemove:Int) { - if (toAdd == toRemove) - return; + if (toAdd == toRemove) return; - switch(device) + switch (device) { case Keys: forEachBound(control, function(action, state) replaceKey(action, toAdd, toRemove, state)); @@ -534,7 +533,8 @@ class Controls extends FlxActionSet function replaceKey(action:FlxActionDigital, toAdd:FlxKey, toRemove:FlxKey, state:FlxInputState) { - if (action.inputs.length == 0) { + if (action.inputs.length == 0) + { // Add the keybind, don't replace. addKeys(action, [toAdd], state); return; @@ -548,34 +548,44 @@ class Controls extends FlxActionSet if (input.device == KEYBOARD && input.inputID == toRemove) { - if (toAdd == FlxKey.NONE) { + if (toAdd == FlxKey.NONE) + { // Remove the keybind, don't replace. action.inputs.remove(input); - } else { + } + else + { // Replace the keybind. @:privateAccess action.inputs[i].inputID = toAdd; } hasReplaced = true; - } else if (input.device == KEYBOARD && input.inputID == toAdd) { + } + else if (input.device == KEYBOARD && input.inputID == toAdd) + { // This key is already bound! - if (hasReplaced) { + if (hasReplaced) + { // Remove the duplicate keybind, don't replace. action.inputs.remove(input); - } else { + } + else + { hasReplaced = true; } } } - if (!hasReplaced) { + if (!hasReplaced) + { addKeys(action, [toAdd], state); } } function replaceButton(action:FlxActionDigital, deviceID:Int, toAdd:FlxGamepadInputID, toRemove:FlxGamepadInputID, state:FlxInputState) { - if (action.inputs.length == 0) { + if (action.inputs.length == 0) + { addButtons(action, [toAdd], state, deviceID); return; } @@ -594,7 +604,8 @@ class Controls extends FlxActionSet } } - if (!hasReplaced) { + if (!hasReplaced) + { addButtons(action, [toAdd], state, deviceID); } } @@ -606,18 +617,16 @@ class Controls extends FlxActionSet var action = controls.byName[name]; for (input in action.inputs) { - if (device == null || isDevice(input, device)) - byName[name].add(cast input); + if (device == null || isDevice(input, device)) byName[name].add(cast input); } } - switch(device) + switch (device) { case null: // add all for (gamepad in controls.gamepadsAdded) - if (gamepadsAdded.indexOf(gamepad) == -1) - gamepadsAdded.push(gamepad); + if (gamepadsAdded.indexOf(gamepad) == -1) gamepadsAdded.push(gamepad); mergeKeyboardScheme(controls.keyboardScheme); @@ -637,7 +646,7 @@ class Controls extends FlxActionSet { if (scheme != None) { - switch(keyboardScheme) + switch (keyboardScheme) { case None: keyboardScheme = scheme; @@ -672,7 +681,8 @@ class Controls extends FlxActionSet static function addKeys(action:FlxActionDigital, keys:Array, state:FlxInputState) { - for (key in keys) { + for (key in keys) + { if (key == FlxKey.NONE) continue; // Ignore unbound keys. action.addKey(key, state); } @@ -684,15 +694,13 @@ class Controls extends FlxActionSet while (i-- > 0) { var input = action.inputs[i]; - if (input.device == KEYBOARD && keys.indexOf(cast input.inputID) != -1) - action.remove(input); + if (input.device == KEYBOARD && keys.indexOf(cast input.inputID) != -1) action.remove(input); } } public function setKeyboardScheme(scheme:KeyboardScheme, reset = true) { - if (reset) - removeKeyboard(); + if (reset) removeKeyboard(); keyboardScheme = scheme; @@ -724,10 +732,13 @@ class Controls extends FlxActionSet bindMobileLol(); } - function getDefaultKeybinds(scheme:KeyboardScheme, control:Control):Array { - switch (scheme) { + function getDefaultKeybinds(scheme:KeyboardScheme, control:Control):Array + { + switch (scheme) + { case Solo: - switch (control) { + switch (control) + { case Control.UI_UP: return [W, FlxKey.UP]; case Control.UI_DOWN: return [S, FlxKey.DOWN]; case Control.UI_LEFT: return [A, FlxKey.LEFT]; @@ -754,7 +765,8 @@ class Controls extends FlxActionSet case Control.VOLUME_MUTE: return [ZERO, NUMPADZERO]; } case Duo(true): - switch (control) { + switch (control) + { case Control.UI_UP: return [W]; case Control.UI_DOWN: return [S]; case Control.UI_LEFT: return [A]; @@ -779,10 +791,10 @@ class Controls extends FlxActionSet case Control.VOLUME_UP: return [PLUS]; case Control.VOLUME_DOWN: return [MINUS]; case Control.VOLUME_MUTE: return [ZERO]; - } case Duo(false): - switch (control) { + switch (control) + { case Control.UI_UP: return [FlxKey.UP]; case Control.UI_DOWN: return [FlxKey.DOWN]; case Control.UI_LEFT: return [FlxKey.LEFT]; @@ -807,7 +819,6 @@ class Controls extends FlxActionSet case Control.VOLUME_UP: return [NUMPADPLUS]; case Control.VOLUME_DOWN: return [NUMPADMINUS]; case Control.VOLUME_MUTE: return [NUMPADZERO]; - } default: // Fallthrough. @@ -834,8 +845,7 @@ class Controls extends FlxActionSet #end #if android - forEachBound(Control.BACK, function(action, pres) - { + forEachBound(Control.BACK, function(action, pres) { action.add(new FlxActionInputDigitalAndroid(FlxAndroidKey.BACK, JUST_PRESSED)); }); #end @@ -849,8 +859,7 @@ class Controls extends FlxActionSet while (i-- > 0) { var input = action.inputs[i]; - if (input.device == KEYBOARD) - action.remove(input); + if (input.device == KEYBOARD) action.remove(input); } } } @@ -862,11 +871,13 @@ class Controls extends FlxActionSet fromSaveData(padData, Gamepad(id)); } - public function getGamepadIds():Array { + public function getGamepadIds():Array + { return gamepadsAdded; } - public function getGamepads():Array { + public function getGamepads():Array + { return [for (id in gamepadsAdded) FlxG.gamepads.getByID(id)]; } @@ -886,8 +897,7 @@ class Controls extends FlxActionSet while (i-- > 0) { var input = action.inputs[i]; - if (isGamepad(input, deviceID)) - action.remove(input); + if (isGamepad(input, deviceID)) action.remove(input); } } @@ -924,32 +934,58 @@ class Controls extends FlxActionSet ]); } - function getDefaultGamepadBinds(control:Control):Array { - switch(control) { - case Control.ACCEPT: return [#if switch B #else A #end]; - case Control.BACK: return [#if switch A #else B #end]; - case Control.UI_UP: return [DPAD_UP, LEFT_STICK_DIGITAL_UP]; - case Control.UI_DOWN: return [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN]; - case Control.UI_LEFT: return [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT]; - case Control.UI_RIGHT: return [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT]; - case Control.NOTE_UP: return [DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP]; - case Control.NOTE_DOWN: return [DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN]; - case Control.NOTE_LEFT: return [DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT]; - case Control.NOTE_RIGHT: return [DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT]; - case Control.PAUSE: return [START]; - case Control.RESET: return [FlxGamepadInputID.BACK]; // Back (i.e. Select) - case Control.WINDOW_FULLSCREEN: []; - case Control.WINDOW_SCREENSHOT: []; - case Control.CUTSCENE_ADVANCE: return [A]; - case Control.FREEPLAY_FAVORITE: [FlxGamepadInputID.BACK]; // Back (i.e. Select) - case Control.FREEPLAY_LEFT: [LEFT_SHOULDER]; - case Control.FREEPLAY_RIGHT: [RIGHT_SHOULDER]; - case Control.VOLUME_UP: []; - case Control.VOLUME_DOWN: []; - case Control.VOLUME_MUTE: []; - case Control.DEBUG_MENU: []; - case Control.DEBUG_CHART: []; - case Control.DEBUG_STAGE: []; + function getDefaultGamepadBinds(control:Control):Array + { + switch (control) + { + case Control.ACCEPT: + return [#if switch B #else A #end]; + case Control.BACK: + return [#if switch A #else B #end]; + case Control.UI_UP: + return [DPAD_UP, LEFT_STICK_DIGITAL_UP]; + case Control.UI_DOWN: + return [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN]; + case Control.UI_LEFT: + return [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT]; + case Control.UI_RIGHT: + return [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT]; + case Control.NOTE_UP: + return [DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP]; + case Control.NOTE_DOWN: + return [DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN]; + case Control.NOTE_LEFT: + return [DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT]; + case Control.NOTE_RIGHT: + return [DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT]; + case Control.PAUSE: + return [START]; + case Control.RESET: + return [FlxGamepadInputID.BACK]; // Back (i.e. Select) + case Control.WINDOW_FULLSCREEN: + []; + case Control.WINDOW_SCREENSHOT: + []; + case Control.CUTSCENE_ADVANCE: + return [A]; + case Control.FREEPLAY_FAVORITE: + [FlxGamepadInputID.BACK]; // Back (i.e. Select) + case Control.FREEPLAY_LEFT: + [LEFT_SHOULDER]; + case Control.FREEPLAY_RIGHT: + [RIGHT_SHOULDER]; + case Control.VOLUME_UP: + []; + case Control.VOLUME_DOWN: + []; + case Control.VOLUME_MUTE: + []; + case Control.DEBUG_MENU: + []; + case Control.DEBUG_CHART: + []; + case Control.DEBUG_STAGE: + []; default: // Fallthrough. } @@ -967,8 +1003,7 @@ class Controls extends FlxActionSet public function touchShit(control:Control, id) { - forEachBound(control, function(action, state) - { + forEachBound(control, function(action, state) { // action }); } @@ -984,7 +1019,8 @@ class Controls extends FlxActionSet inline static function addButtons(action:FlxActionDigital, buttons:Array, state, id) { - for (button in buttons) { + for (button in buttons) + { if (button == FlxGamepadInputID.NONE) continue; // Ignore unbound keys. action.addGamepad(button, state, id); } @@ -996,29 +1032,25 @@ class Controls extends FlxActionSet while (i-- > 0) { var input = action.inputs[i]; - if (isGamepad(input, gamepadID) && buttons.indexOf(cast input.inputID) != -1) - action.remove(input); + if (isGamepad(input, gamepadID) && buttons.indexOf(cast input.inputID) != -1) action.remove(input); } } public function getInputsFor(control:Control, device:Device, ?list:Array):Array { - if (list == null) - list = []; + if (list == null) list = []; - switch(device) + switch (device) { case Keys: for (input in getActionFromControl(control).inputs) { - if (input.device == KEYBOARD) - list.push(input.inputID); + if (input.device == KEYBOARD) list.push(input.inputID); } case Gamepad(id): for (input in getActionFromControl(control).inputs) { - if (isGamepad(input, id)) - list.push(input.inputID); + if (isGamepad(input, id)) list.push(input.inputID); } } return list; @@ -1026,7 +1058,7 @@ class Controls extends FlxActionSet public function removeDevice(device:Device) { - switch(device) + switch (device) { case Keys: setKeyboardScheme(None); @@ -1040,27 +1072,32 @@ class Controls extends FlxActionSet * An EMPTY array means the control is uninitialized and needs to be reset to default. * An array with a single FlxKey.NONE means the control was intentionally unbound by the user. */ - public function fromSaveData(data:Dynamic, device:Device) + public function fromSaveData(data:Dynamic, device:Device):Void { for (control in Control.createAll()) { var inputs:Array = Reflect.field(data, control.getName()); - inputs = inputs.distinct(); + inputs = inputs?.distinct(); if (inputs != null) { - if (inputs.length == 0) { + if (inputs.length == 0) + { trace('Control ${control} is missing bindings, resetting to default.'); - switch(device) + switch (device) { case Keys: bindKeys(control, getDefaultKeybinds(Solo, control)); case Gamepad(id): bindButtons(control, id, getDefaultGamepadBinds(control)); } - } else if (inputs == [FlxKey.NONE]) { + } + else if (inputs == [FlxKey.NONE]) + { trace('Control ${control} is unbound, leaving it be.'); - } else { - switch(device) + } + else + { + switch (device) { case Keys: bindKeys(control, inputs.copy()); @@ -1068,9 +1105,11 @@ class Controls extends FlxActionSet bindButtons(control, id, inputs.copy()); } } - } else { + } + else + { trace('Control ${control} is missing bindings, resetting to default.'); - switch(device) + switch (device) { case Keys: bindKeys(control, getDefaultKeybinds(Solo, control)); @@ -1095,9 +1134,12 @@ class Controls extends FlxActionSet var inputs = getInputsFor(control, device); isEmpty = isEmpty && inputs.length == 0; - if (inputs.length == 0) { + if (inputs.length == 0) + { inputs = [FlxKey.NONE]; - } else { + } + else + { inputs = inputs.distinct(); } @@ -1109,7 +1151,7 @@ class Controls extends FlxActionSet static function isDevice(input:FlxActionInput, device:Device) { - return switch(device) + return switch (device) { case Keys: input.device == KEYBOARD; case Gamepad(id): isGamepad(input, id); @@ -1141,7 +1183,8 @@ typedef Swipes = * - Combining `pressed` and `released` inputs into one action. * - Filtering by input method (`KEYBOARD`, `MOUSE`, `GAMEPAD`, etc). */ -class FunkinAction extends FlxActionDigital { +class FunkinAction extends FlxActionDigital +{ public var namePressed(default, null):Null; public var nameReleased(default, null):Null; @@ -1158,83 +1201,102 @@ class FunkinAction extends FlxActionDigital { /** * Input checks default to whether the input was just pressed, on any input device. */ - public override function check():Bool { + public override function check():Bool + { return checkFiltered(JUST_PRESSED); } /** * Check whether the input is currently being held. */ - public function checkPressed():Bool { + public function checkPressed():Bool + { return checkFiltered(PRESSED); } /** * Check whether the input is currently being held, and was not held last frame. */ - public function checkJustPressed():Bool { + public function checkJustPressed():Bool + { return checkFiltered(JUST_PRESSED); } /** * Check whether the input is not currently being held. */ - public function checkReleased():Bool { + public function checkReleased():Bool + { return checkFiltered(RELEASED); } /** * Check whether the input is not currently being held, and was held last frame. */ - public function checkJustReleased():Bool { + public function checkJustReleased():Bool + { return checkFiltered(JUST_RELEASED); } /** * Check whether the input is currently being held by a gamepad device. */ - public function checkPressedGamepad():Bool { + public function checkPressedGamepad():Bool + { return checkFiltered(PRESSED, GAMEPAD); } /** * Check whether the input is currently being held by a gamepad device, and was not held last frame. */ - public function checkJustPressedGamepad():Bool { + public function checkJustPressedGamepad():Bool + { return checkFiltered(JUST_PRESSED, GAMEPAD); } /** * Check whether the input is not currently being held by a gamepad device. */ - public function checkReleasedGamepad():Bool { + public function checkReleasedGamepad():Bool + { return checkFiltered(RELEASED, GAMEPAD); } /** * Check whether the input is not currently being held by a gamepad device, and was held last frame. */ - public function checkJustReleasedGamepad():Bool { + public function checkJustReleasedGamepad():Bool + { return checkFiltered(JUST_RELEASED, GAMEPAD); } - public function checkMultiFiltered(?filterTriggers:Array, ?filterDevices:Array):Bool { - if (filterTriggers == null) { + public function checkMultiFiltered(?filterTriggers:Array, ?filterDevices:Array):Bool + { + if (filterTriggers == null) + { filterTriggers = [PRESSED, JUST_PRESSED]; } - if (filterDevices == null) { + if (filterDevices == null) + { filterDevices = []; } // Perform checkFiltered for each combination. - for (i in filterTriggers) { - if (filterDevices.length == 0) { - if (checkFiltered(i)) { + for (i in filterTriggers) + { + if (filterDevices.length == 0) + { + if (checkFiltered(i)) + { return true; } - } else { - for (j in filterDevices) { - if (checkFiltered(i, j)) { + } + else + { + for (j in filterDevices) + { + if (checkFiltered(i, j)) + { return true; } } @@ -1249,52 +1311,56 @@ class FunkinAction extends FlxActionDigital { * @param filterTrigger Optionally filter by trigger condition (`JUST_PRESSED`, `PRESSED`, `JUST_RELEASED`, `RELEASED`). * @param filterDevice Optionally filter by device (`KEYBOARD`, `MOUSE`, `GAMEPAD`, `OTHER`). */ - public function checkFiltered(?filterTrigger:FlxInputState, ?filterDevice:FlxInputDevice):Bool { + public function checkFiltered(?filterTrigger:FlxInputState, ?filterDevice:FlxInputDevice):Bool + { // The normal // Make sure we only update the inputs once per frame. var key = '${filterTrigger}:${filterDevice}'; var cacheEntry = cache.get(key); - if (cacheEntry != null && cacheEntry.timestamp == FlxG.game.ticks) { + if (cacheEntry != null && cacheEntry.timestamp == FlxG.game.ticks) + { return cacheEntry.value; } // Use a for loop instead so we can remove inputs while iterating. // We don't return early because we need to call check() on ALL inputs. var result = false; - var len = inputs != null ? inputs.length : 0; - for (i in 0...len) - { - var j = len - i - 1; - var input = inputs[j]; + var len = inputs != null ? inputs.length : 0; + for (i in 0...len) + { + var j = len - i - 1; + var input = inputs[j]; // Filter out dead inputs. - if (input.destroyed) - { - inputs.splice(j, 1); - continue; - } + if (input.destroyed) + { + inputs.splice(j, 1); + continue; + } // Update the input. input.update(); // Check whether the input is the right trigger. - if (filterTrigger != null && input.trigger != filterTrigger) { + if (filterTrigger != null && input.trigger != filterTrigger) + { continue; } // Check whether the input is the right device. - if (filterDevice != null && input.device != filterDevice) { + if (filterDevice != null && input.device != filterDevice) + { continue; } // Check whether the input has triggered. - if (input.check(this)) - { - result = true; - } - } + if (input.check(this)) + { + result = true; + } + } // We need to cache this result. cache.set(key, {timestamp: FlxG.game.ticks, value: result}); @@ -1391,12 +1457,12 @@ class FlxActionInputDigitalMobileSwipeGameplay extends FlxActionInputDigital { var degAngle = FlxAngle.asDegrees(swp.touchAngle); - switch(trigger) + switch (trigger) { case JUST_PRESSED: if (swp.touchLength >= activateLength) { - switch(inputID) + switch (inputID) { case FlxDirectionFlags.UP: if (degAngle >= 45 && degAngle <= 90 + 45) return properTouch(swp); @@ -1440,7 +1506,7 @@ class FlxActionInputDigitalAndroid extends FlxActionInputDigital override public function check(Action:FlxAction):Bool { - return switch(trigger) + return switch (trigger) { #if android case PRESSED: FlxG.android.checkStatus(inputID, PRESSED) || FlxG.android.checkStatus(inputID, PRESSED); diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 10636afdf9..55c2a8992a 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -9,6 +9,7 @@ import funkin.modding.module.ModuleHandler; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEvent.CountdownScriptEvent; import flixel.util.FlxTimer; +import funkin.util.EaseUtil; import funkin.audio.FunkinSound; class Countdown @@ -117,7 +118,7 @@ class Countdown * * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event. */ - public static function pauseCountdown() + public static function pauseCountdown():Void { if (countdownTimer != null && !countdownTimer.finished) { @@ -130,7 +131,7 @@ class Countdown * * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event. */ - public static function resumeCountdown() + public static function resumeCountdown():Void { if (countdownTimer != null && !countdownTimer.finished) { @@ -143,7 +144,7 @@ class Countdown * * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStart event. */ - public static function stopCountdown() + public static function stopCountdown():Void { if (countdownTimer != null) { @@ -156,7 +157,7 @@ class Countdown /** * Stops the current countdown, then starts the song for you. */ - public static function skipCountdown() + public static function skipCountdown():Void { stopCountdown(); // This will trigger PlayState.startSong() @@ -185,8 +186,11 @@ class Countdown { var spritePath:String = null; + var fadeEase = FlxEase.cubeInOut; + if (isPixelStyle) { + fadeEase = EaseUtil.stepped(8); switch (index) { case TWO: @@ -227,7 +231,7 @@ class Countdown countdownSprite.screenCenter(); // Fade sprite in, then out, then destroy it. - FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.instance.beatLengthMs / 1000, + FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100}, Conductor.instance.beatLengthMs / 1000, { ease: FlxEase.cubeInOut, onComplete: function(twn:FlxTween) { @@ -235,6 +239,11 @@ class Countdown } }); + FlxTween.tween(countdownSprite, {alpha: 0}, Conductor.instance.beatLengthMs / 1000, + { + ease: fadeEase + }); + PlayState.instance.add(countdownSprite); } diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index c84d5b154a..6eb954c0fa 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -16,6 +16,7 @@ import funkin.ui.MusicBeatSubState; import funkin.ui.story.StoryMenuState; import funkin.util.MathUtil; import openfl.utils.Assets; +import funkin.effects.RetroCameraFade; /** * A substate which renders over the PlayState when the player dies. @@ -144,6 +145,7 @@ class GameOverSubState extends MusicBeatSubState else { boyfriend = PlayState.instance.currentStage.getBoyfriend(true); + boyfriend.canPlayOtherAnims = true; boyfriend.isDead = true; add(boyfriend); boyfriend.resetCharacter(); @@ -331,9 +333,12 @@ class GameOverSubState extends MusicBeatSubState // After the animation finishes... new FlxTimer().start(0.7, function(tmr:FlxTimer) { // ...fade out the graphics. Then after that happens... - FlxG.camera.fade(FlxColor.BLACK, 2, false, function() { + + var resetPlaying = function(pixel:Bool = false) { // ...close the GameOverSubState. - FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true); + if (pixel) RetroCameraFade.fadeBlack(FlxG.camera, 10, 1); + else + FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true); PlayState.instance.needsReset = true; if (PlayState.instance.isMinimalMode || boyfriend == null) {} @@ -350,7 +355,22 @@ class GameOverSubState extends MusicBeatSubState // Close the substate. close(); - }); + }; + + if (musicSuffix == '-pixel') + { + RetroCameraFade.fadeToBlack(FlxG.camera, 10, 2); + new FlxTimer().start(2, _ -> { + FlxG.camera.filters = []; + resetPlaying(true); + }); + } + else + { + FlxG.camera.fade(FlxColor.BLACK, 2, false, function() { + resetPlaying(); + }); + } }); } } diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index 48fb3b04e9..a2c5f7e623 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -464,7 +464,9 @@ class ResultState extends MusicBeatSubState { bgFlash.visible = true; FlxTween.tween(bgFlash, {alpha: 0}, 5 / 24); - var clearPercentFloat = (params.scoreData.tallies.sick + params.scoreData.tallies.good) / params.scoreData.tallies.totalNotes * 100; + // NOTE: Only divide if totalNotes > 0 to prevent divide-by-zero errors. + var clearPercentFloat = params.scoreData.tallies.totalNotes == 0 ? 0.0 : (params.scoreData.tallies.sick + + params.scoreData.tallies.good) / params.scoreData.tallies.totalNotes * 100; clearPercentTarget = Math.floor(clearPercentFloat); // Prevent off-by-one errors. diff --git a/source/funkin/play/character/CharacterData.hx b/source/funkin/play/character/CharacterData.hx index 7d3d6cfb92..8b1649e26b 100644 --- a/source/funkin/play/character/CharacterData.hx +++ b/source/funkin/play/character/CharacterData.hx @@ -305,8 +305,8 @@ class CharacterDataParser icon = "darnell"; case "senpai-angry": icon = "senpai"; - case "tankman" | "tankman-atlas": - icon = "tankmen"; + case "tankman-atlas": + icon = "tankman"; } var path = Paths.image("freeplay/icons/" + icon + "pixel"); diff --git a/source/funkin/play/character/MultiSparrowCharacter.hx b/source/funkin/play/character/MultiSparrowCharacter.hx index 48c5afb58d..41c96fbfaa 100644 --- a/source/funkin/play/character/MultiSparrowCharacter.hx +++ b/source/funkin/play/character/MultiSparrowCharacter.hx @@ -41,6 +41,8 @@ class MultiSparrowCharacter extends BaseCharacter { this.isPixel = true; this.antialiasing = false; + pixelPerfectRender = true; + pixelPerfectPosition = true; } else { diff --git a/source/funkin/play/character/PackerCharacter.hx b/source/funkin/play/character/PackerCharacter.hx index 2bfac800ac..22edbe339d 100644 --- a/source/funkin/play/character/PackerCharacter.hx +++ b/source/funkin/play/character/PackerCharacter.hx @@ -43,6 +43,8 @@ class PackerCharacter extends BaseCharacter { this.isPixel = true; this.antialiasing = false; + pixelPerfectRender = true; + pixelPerfectPosition = true; } else { diff --git a/source/funkin/play/character/SparrowCharacter.hx b/source/funkin/play/character/SparrowCharacter.hx index a36aed84da..81d98b138b 100644 --- a/source/funkin/play/character/SparrowCharacter.hx +++ b/source/funkin/play/character/SparrowCharacter.hx @@ -46,6 +46,8 @@ class SparrowCharacter extends BaseCharacter { this.isPixel = true; this.antialiasing = false; + pixelPerfectRender = true; + pixelPerfectPosition = true; } else { diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index b7e206e977..1bdfd98a83 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -7,8 +7,9 @@ import flixel.util.FlxDirection; import funkin.graphics.FunkinSprite; import funkin.play.PlayState; import funkin.util.TimerUtil; +import funkin.util.EaseUtil; -class PopUpStuff extends FlxTypedGroup +class PopUpStuff extends FlxTypedGroup { public var offsets:Array = [0, 0]; @@ -17,7 +18,7 @@ class PopUpStuff extends FlxTypedGroup super(); } - public function displayRating(daRating:String) + public function displayRating(daRating:String):Void { var perfStart:Float = TimerUtil.start(); @@ -40,10 +41,15 @@ class PopUpStuff extends FlxTypedGroup add(rating); + var fadeEase = null; + if (PlayState.instance.currentStageId.startsWith('school')) { rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.7)); rating.antialiasing = false; + rating.pixelPerfectRender = true; + rating.pixelPerfectPosition = true; + fadeEase = EaseUtil.stepped(2); } else { @@ -61,7 +67,8 @@ class PopUpStuff extends FlxTypedGroup remove(rating, true); rating.destroy(); }, - startDelay: Conductor.instance.beatLengthMs * 0.001 + startDelay: Conductor.instance.beatLengthMs * 0.001, + ease: fadeEase }); trace('displayRating took: ${TimerUtil.seconds(perfStart)}'); @@ -92,10 +99,15 @@ class PopUpStuff extends FlxTypedGroup // add(comboSpr); + var fadeEase = null; + if (PlayState.instance.currentStageId.startsWith('school')) { - comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 0.7)); + comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 1)); comboSpr.antialiasing = false; + comboSpr.pixelPerfectRender = true; + comboSpr.pixelPerfectPosition = true; + fadeEase = EaseUtil.stepped(2); } else { @@ -110,7 +122,8 @@ class PopUpStuff extends FlxTypedGroup remove(comboSpr, true); comboSpr.destroy(); }, - startDelay: Conductor.instance.beatLengthMs * 0.001 + startDelay: Conductor.instance.beatLengthMs * 0.001, + ease: fadeEase }); var seperatedScore:Array = []; @@ -133,8 +146,10 @@ class PopUpStuff extends FlxTypedGroup if (PlayState.instance.currentStageId.startsWith('school')) { - numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 0.7)); + numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 1)); numScore.antialiasing = false; + numScore.pixelPerfectRender = true; + numScore.pixelPerfectPosition = true; } else { @@ -156,7 +171,8 @@ class PopUpStuff extends FlxTypedGroup remove(numScore, true); numScore.destroy(); }, - startDelay: Conductor.instance.beatLengthMs * 0.002 + startDelay: Conductor.instance.beatLengthMs * 0.002, + ease: fadeEase }); daLoop++; diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index 262aff7bca..11fb9e4999 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -200,12 +200,10 @@ class Bopper extends StageProp implements IPlayStateScriptedClass { if (hasDanced) { - trace('DanceRight (alternate)'); playAnimation('danceRight$idleSuffix', forceRestart); } else { - trace('DanceLeft (alternate)'); playAnimation('danceLeft$idleSuffix', forceRestart); } hasDanced = !hasDanced; diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index 4f8ab4434b..f4e22e380f 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -249,6 +249,10 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements // If pixel, disable antialiasing. propSprite.antialiasing = !dataProp.isPixel; + // If pixel, we render it pixel perfect so there's less "mixels" + propSprite.pixelPerfectRender = dataProp.isPixel; + propSprite.pixelPerfectPosition = dataProp.isPixel; + propSprite.scrollFactor.x = dataProp.scroll[0]; propSprite.scrollFactor.y = dataProp.scroll[1]; diff --git a/source/funkin/ui/MenuItem.hx b/source/funkin/ui/MenuItem.hx index ba5cc066bc..2a483ea789 100644 --- a/source/funkin/ui/MenuItem.hx +++ b/source/funkin/ui/MenuItem.hx @@ -11,7 +11,6 @@ class MenuItem extends FlxSpriteGroup { public var targetY:Float = 0; public var week:FlxSprite; - public var flashingInt:Int = 0; public function new(x:Float, y:Float, weekNum:Int = 0, weekType:WeekType) { @@ -30,28 +29,28 @@ class MenuItem extends FlxSpriteGroup } var isFlashing:Bool = false; + var flashTick:Float = 0; + final flashFramerate:Float = 20; public function startFlashing():Void { isFlashing = true; } - // if it runs at 60fps, fake framerate will be 6 - // if it runs at 144 fps, fake framerate will be like 14, and will update the graphic every 0.016666 * 3 seconds still??? - // so it runs basically every so many seconds, not dependant on framerate?? - // I'm still learning how math works thanks whoever is reading this lol - var fakeFramerate:Int = Math.round((1 / FlxG.elapsed) / 10); - override function update(elapsed:Float) { super.update(elapsed); y = MathUtil.coolLerp(y, (targetY * 120) + 480, 0.17); - if (isFlashing) flashingInt += 1; - - if (flashingInt % fakeFramerate >= Math.floor(fakeFramerate / 2)) week.color = 0xFF33ffff; - else - week.color = FlxColor.WHITE; + if (isFlashing) + { + flashTick += elapsed; + if (flashTick >= 1 / flashFramerate) + { + flashTick %= 1 / flashFramerate; + week.color = (week.color == FlxColor.WHITE) ? 0xFF33ffff : FlxColor.WHITE; + } + } } } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 637c23c952..abbe2d6bdf 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -227,7 +227,7 @@ class FreeplayState extends MusicBeatSubState super(FlxColor.TRANSPARENT); - if (stickers != null) + if (stickers?.members != null) { stickerSubState = stickers; } @@ -640,7 +640,6 @@ class FreeplayState extends MusicBeatSubState // be careful not to "add()" things in here unless it's to a group that's already added to the state // otherwise it won't be properly attatched to funnyCamera (relavent code should be at the bottom of create()) var onDJIntroDone = function() { - // when boyfriend hits dat shiii albumRoll.playIntro(); @@ -693,9 +692,12 @@ class FreeplayState extends MusicBeatSubState } }; - if (dj != null) { + if (dj != null) + { dj.onIntroDone.add(onDJIntroDone); - } else { + } + else + { onDJIntroDone(); } @@ -1775,7 +1777,7 @@ class FreeplayState extends MusicBeatSubState if (dj != null) dj.confirm(); grpCapsules.members[curSelected].forcePosition(); - grpCapsules.members[curSelected].songText.flickerText(); + grpCapsules.members[curSelected].confirm(); // FlxTween.color(bgDad, 0.33, 0xFFFFFFFF, 0xFF555555, {ease: FlxEase.quadOut}); FlxTween.color(pinkBack, 0.33, 0xFFFFD0D5, 0xFF171831, {ease: FlxEase.quadOut}); @@ -1947,6 +1949,7 @@ class FreeplayState extends MusicBeatSubState startingVolume: 0.0, overrideExisting: true, restartTrack: false, + mapTimeChanges: false, // The music metadata is not alongside the audio file so this won't work. pathsFunction: INST, suffix: instSuffix, partialParams: @@ -2097,6 +2100,8 @@ class FreeplaySongData function set_currentDifficulty(value:String):String { + if (currentDifficulty == value) return value; + currentDifficulty = value; updateValues(displayedVariations); return value; @@ -2153,7 +2158,7 @@ class FreeplaySongData { this.albumId = songDifficulty.album; } - + // TODO: This line of code makes me sad, but you can't really fix it without a breaking migration. // `easy`, `erect`, `normal-pico`, etc. var suffixedDifficulty = (songDifficulty.variation != Constants.DEFAULT_VARIATION diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx index 7708b3bcf9..68525a5f7a 100644 --- a/source/funkin/ui/freeplay/SongMenuItem.hx +++ b/source/funkin/ui/freeplay/SongMenuItem.hx @@ -213,6 +213,7 @@ class SongMenuItem extends FlxSpriteGroup favIconBlurred.frames = Paths.getSparrowAtlas('freeplay/favHeart'); favIconBlurred.animation.addByPrefix('fav', 'favorite heart', 24, false); favIconBlurred.animation.play('fav'); + favIconBlurred.setGraphicSize(50, 50); favIconBlurred.blend = BlendMode.ADD; favIconBlurred.shader = new GaussianBlurShader(1.2); @@ -516,6 +517,9 @@ class SongMenuItem extends FlxSpriteGroup updateDifficultyRating(songData?.difficultyRating ?? 0); updateScoringRank(songData?.scoringRank); newText.visible = songData?.isNew; + favIcon.animation.curAnim.curFrame = favIcon.animation.curAnim.numFrames - 1; + favIconBlurred.animation.curAnim.curFrame = favIconBlurred.animation.curAnim.numFrames - 1; + // Update opacity, offsets, etc. updateSelected(); @@ -539,8 +543,6 @@ class SongMenuItem extends FlxSpriteGroup charPath += 'monsterpixel'; case 'mom-car': charPath += 'mommypixel'; - case 'dad': - charPath += 'daddypixel'; case 'darnell-blazin': charPath += 'darnellpixel'; case 'senpai-angry': @@ -555,7 +557,17 @@ class SongMenuItem extends FlxSpriteGroup return; } - pixelIcon.loadGraphic(Paths.image(charPath)); + var isAnimated = openfl.utils.Assets.exists(Paths.file('images/$charPath.xml')); + + if (isAnimated) + { + pixelIcon.frames = Paths.getSparrowAtlas(charPath); + } + else + { + pixelIcon.loadGraphic(Paths.image(charPath)); + } + pixelIcon.scale.x = pixelIcon.scale.y = 2; switch (char) @@ -567,6 +579,22 @@ class SongMenuItem extends FlxSpriteGroup } // pixelIcon.origin.x = capsule.origin.x; // pixelIcon.offset.x -= pixelIcon.origin.x; + + if (isAnimated) + { + pixelIcon.active = true; + + pixelIcon.animation.addByPrefix('idle', 'idle0', 10, true); + pixelIcon.animation.addByPrefix('confirm', 'confirm0', 10, false); + pixelIcon.animation.addByPrefix('confirm-hold', 'confirm-hold0', 10, true); + + pixelIcon.animation.finishCallback = function(name:String):Void { + trace('Finish pixel animation: ${name}'); + if (name == 'confirm') pixelIcon.animation.play('confirm-hold'); + }; + + pixelIcon.animation.play('idle'); + } } var frameInTicker:Float = 0; @@ -707,6 +735,18 @@ class SongMenuItem extends FlxSpriteGroup super.update(elapsed); } + /** + * Play any animations associated with selecting this song. + */ + public function confirm():Void + { + if (songText != null) songText.flickerText(); + if (pixelIcon != null) + { + pixelIcon.animation.play('confirm'); + } + } + public function intendedY(index:Int):Float { return (index * ((height * realScaled) + 10)) + 120; diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 56ffc9a270..9bf4654847 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -156,6 +156,9 @@ class MainMenuState extends MusicBeatState resetCamStuff(); + // reset camera when debug menu is closed + subStateClosed.add(_ -> resetCamStuff(false)); + subStateOpened.add(sub -> { if (Type.getClass(sub) == FreeplayState) { @@ -185,10 +188,11 @@ class MainMenuState extends MusicBeatState }); } - function resetCamStuff():Void + function resetCamStuff(?snap:Bool = true):Void { FlxG.camera.follow(camFollow, null, 0.06); - FlxG.camera.snapToTarget(); + + if (snap) FlxG.camera.snapToTarget(); } function createMenuItem(name:String, atlas:String, callback:Void->Void, fireInstantly:Bool = false):Void @@ -347,8 +351,6 @@ class MainMenuState extends MusicBeatState persistentUpdate = false; FlxG.state.openSubState(new DebugMenuSubState()); - // reset camera when debug menu is closed - subStateClosed.addOnce(_ -> resetCamStuff()); } #end diff --git a/source/funkin/ui/story/LevelTitle.hx b/source/funkin/ui/story/LevelTitle.hx index e6f9890162..2be2da154b 100644 --- a/source/funkin/ui/story/LevelTitle.hx +++ b/source/funkin/ui/story/LevelTitle.hx @@ -13,13 +13,10 @@ class LevelTitle extends FlxSpriteGroup public final level:Level; public var targetY:Float; - public var isFlashing:Bool = false; var title:FlxSprite; var lock:FlxSprite; - var flashingInt:Int = 0; - public function new(x:Int, y:Int, level:Level) { super(x, y); @@ -46,20 +43,23 @@ class LevelTitle extends FlxSpriteGroup } } - // if it runs at 60fps, fake framerate will be 6 - // if it runs at 144 fps, fake framerate will be like 14, and will update the graphic every 0.016666 * 3 seconds still??? - // so it runs basically every so many seconds, not dependant on framerate?? - // I'm still learning how math works thanks whoever is reading this lol - var fakeFramerate:Int = Math.round((1 / FlxG.elapsed) / 10); + public var isFlashing:Bool = false; + var flashTick:Float = 0; + final flashFramerate:Float = 20; public override function update(elapsed:Float):Void { this.y = MathUtil.coolLerp(y, targetY, 0.17); - if (isFlashing) flashingInt += 1; - if (flashingInt % fakeFramerate >= Math.floor(fakeFramerate / 2)) title.color = 0xFF33ffff; - else - title.color = FlxColor.WHITE; + if (isFlashing) + { + flashTick += elapsed; + if (flashTick >= 1 / flashFramerate) + { + flashTick %= 1 / flashFramerate; + title.color = (title.color == FlxColor.WHITE) ? 0xFF33ffff : FlxColor.WHITE; + } + } } public function showLock():Void diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 06a83ab4db..7707850cea 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -113,7 +113,7 @@ class StoryMenuState extends MusicBeatState { super(); - if (stickers != null) + if (stickers?.members != null) { stickerSubState = stickers; } diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index bc26ad97a1..0f2ce10766 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -346,7 +346,7 @@ class LoadingState extends MusicBeatSubState return 'Done precaching ${path}'; }, true); - trace("Queued ${path} for precaching"); + trace('Queued ${path} for precaching'); // FunkinSprite.cacheTexture(path); } diff --git a/source/funkin/util/logging/AnsiTrace.hx b/source/funkin/util/logging/AnsiTrace.hx index 9fdc19e1be..2c18d494d2 100644 --- a/source/funkin/util/logging/AnsiTrace.hx +++ b/source/funkin/util/logging/AnsiTrace.hx @@ -51,7 +51,7 @@ class AnsiTrace public static function traceBF() { - #if sys + #if (sys && debug) if (colorSupported) { for (line in ansiBF) From c9c94a5fda5378effd0cf060696882a75d016b53 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 4 Jul 2024 15:55:19 -0400 Subject: [PATCH 105/469] note fadein fix --- .gitignore | 1 + source/funkin/play/PlayState.hx | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 143cf08c91..5c8cb5f55a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ node_modules/ package.json package-lock.json .aider.* +.aider* diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index de84928826..5ede97e6cf 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1795,11 +1795,8 @@ class PlayState extends MusicBeatSubState opponentStrumline.zIndex = 1000; opponentStrumline.cameras = [camHUD]; - if (!PlayStatePlaylist.isStoryMode) - { - playerStrumline.fadeInArrows(); - opponentStrumline.fadeInArrows(); - } + playerStrumline.fadeInArrows(); + opponentStrumline.fadeInArrows(); } /** From a9aa142b8de9113961854842a45f3a8767fc29ea Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sun, 7 Jul 2024 04:10:37 -0400 Subject: [PATCH 106/469] Added more pico results + charts --- Project.xml | 2 +- assets | 2 +- source/funkin/InitState.hx | 7 ++++++- .../funkin/data/freeplay/player/PlayerData.hx | 4 ++++ .../graphics/adobeanimate/FlxAtlasSprite.hx | 4 ++++ source/funkin/play/ResultState.hx | 18 +++++++++++++++--- 6 files changed, 31 insertions(+), 6 deletions(-) diff --git a/Project.xml b/Project.xml index fae9c768bd..8eb62bb1dd 100644 --- a/Project.xml +++ b/Project.xml @@ -2,7 +2,7 @@ - + diff --git a/assets b/assets index 859186eea4..94db49cd4a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 859186eea46c8b70deb3530c25304ba875b7b34a +Subproject commit 94db49cd4a2acacd5089628619104830a4f1ffce diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 254165a957..6e370b5ff7 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -239,8 +239,13 @@ class InitState extends FlxState combo: 69, maxCombo: 69, totalNotesHit: 140, - totalNotes: 240 // 0, + totalNotes: 190 } + // 2000 = loss + // 240 = good + // 230 = great + // 210 = excellent + // 190 = perfect }, })); #elseif ANIMDEBUG diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx index caae2adc68..f6c0850180 100644 --- a/source/funkin/data/freeplay/player/PlayerData.hx +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -223,6 +223,10 @@ typedef PlayerResultsAnimationData = @:default('') var startFrameLabel:Null; + @:optional + @:default(true) + var looped:Bool; + @:optional var loopFrame:Null; diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index 1684e8b33a..eb331b9c37 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -158,6 +158,10 @@ class FlxAtlasSprite extends FlxAnimate } }; + anim.onComplete = function() { + onAnimationFinish.dispatch(id); + }; + // Prevent other animations from playing if `ignoreOther` is true. if (ignoreOther) canPlayOtherAnims = false; diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index 042ab655df..c2d9d42b38 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -61,7 +61,8 @@ class ResultState extends MusicBeatSubState var characterAtlasAnimations:Array< { sprite:FlxAtlasSprite, - delay:Float + delay:Float, + forceLoop:Bool }> = []; var characterSparrowAnimations:Array< { @@ -185,7 +186,17 @@ class ResultState extends MusicBeatSubState animation.scale.set(animData.scale ?? 1.0, animData.scale ?? 1.0); - if (animData.loopFrameLabel != null) + if (!(animData.looped ?? true)) + { + // Animation is not looped. + animation.onAnimationFinish.add((_name:String) -> { + if (animation != null) + { + animation.anim.pause(); + } + }); + } + else if (animData.loopFrameLabel != null) { animation.onAnimationFinish.add((_name:String) -> { if (animation != null) @@ -211,7 +222,8 @@ class ResultState extends MusicBeatSubState characterAtlasAnimations.push( { sprite: animation, - delay: animData.delay ?? 0.0 + delay: animData.delay ?? 0.0, + forceLoop: (animData.loopFrame ?? -1) == 0 }); // Add to the scene. add(animation); From f82f0d3b05f7ec03c4d80fe7a8abdf01667c0973 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Mon, 8 Jul 2024 03:17:28 +0200 Subject: [PATCH 107/469] Remove SustainTrail forced alpha --- source/funkin/play/notes/Strumline.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index 0e4b6645f1..5784586ec1 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -598,7 +598,6 @@ class Strumline extends FlxSpriteGroup { note.holdNoteSprite.hitNote = true; note.holdNoteSprite.missedNote = false; - note.holdNoteSprite.alpha = 1.0; note.holdNoteSprite.sustainLength = (note.holdNoteSprite.strumTime + note.holdNoteSprite.fullSustainLength) - conductorInUse.songPosition; } From d7105e8b8e26e2fe1337c14862f7968f5dcd7893 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 8 Jul 2024 16:22:38 -0400 Subject: [PATCH 108/469] change debug menu and add key for freeplay --- source/funkin/ui/debug/DebugMenuSubState.hx | 2 ++ source/funkin/ui/freeplay/FreeplayState.hx | 5 +++++ source/funkin/ui/mainmenu/MainMenuState.hx | 4 +--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/source/funkin/ui/debug/DebugMenuSubState.hx b/source/funkin/ui/debug/DebugMenuSubState.hx index 22dc5dba66..590cce88b8 100644 --- a/source/funkin/ui/debug/DebugMenuSubState.hx +++ b/source/funkin/ui/debug/DebugMenuSubState.hx @@ -54,7 +54,9 @@ class DebugMenuSubState extends MusicBeatSubState // Create each menu item. // Call onMenuChange when the first item is created to move the camera . + #if CHART_EDITOR_SUPPORTED onMenuChange(createItem("CHART EDITOR", openChartEditor)); + #end // createItem("Input Offset Testing", openInputOffsetTesting); createItem("CHARACTER SELECT", openCharSelect, true); createItem("ANIMATION EDITOR", openAnimationEditor); diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 3b003cf89b..4b0ae78390 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1206,6 +1206,11 @@ class FreeplayState extends MusicBeatSubState // } #end + if (FlxG.keys.justPressed.TAB) + { + FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState()); + } + if (controls.FREEPLAY_FAVORITE && !busy) { var targetSong = grpCapsules.members[curSelected]?.songData; diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 2eba406d9b..eab116c864 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -341,17 +341,15 @@ class MainMenuState extends MusicBeatState } } + #if (debug || FORCE_DEBUG_VERSION) // Open the debug menu, defaults to ` / ~ - #if CHART_EDITOR_SUPPORTED if (controls.DEBUG_MENU) { persistentUpdate = false; FlxG.state.openSubState(new DebugMenuSubState()); } - #end - #if (debug || FORCE_DEBUG_VERSION) if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.W) { // Give the user a score of 1 point on Weekend 1 story mode. From 57c995e19d0024335c2244c5868e355847e70872 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 8 Jul 2024 16:30:17 -0400 Subject: [PATCH 109/469] char select screen controls stuff --- source/funkin/ui/charSelect/CharSelectSubState.hx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index d733663907..14a5b36e04 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -1,5 +1,6 @@ package funkin.ui.charSelect; +import funkin.ui.freeplay.FreeplayState; import flixel.text.FlxText; import funkin.ui.PixelatedIcon; import flixel.system.debug.watch.Tracker.TrackerProfile; @@ -451,7 +452,12 @@ class CharSelectSubState extends MusicBeatSubState pressedSelect = true; selectTimer.start(1.5, (_) -> { pressedSelect = false; - close(); + FlxG.switchState(FreeplayState.build( + { + { + character: curChar + } + })); }); } From 3a37b79a55e0ddb940f18988372b77e11a03a010 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 8 Jul 2024 16:40:10 -0400 Subject: [PATCH 110/469] add TAB to controls stuff --- source/funkin/input/Controls.hx | 16 ++++++++++++++++ source/funkin/ui/freeplay/FreeplayState.hx | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/source/funkin/input/Controls.hx b/source/funkin/input/Controls.hx index f6c881f6df..6b8e9aa3e6 100644 --- a/source/funkin/input/Controls.hx +++ b/source/funkin/input/Controls.hx @@ -64,6 +64,7 @@ class Controls extends FlxActionSet var _freeplay_favorite = new FunkinAction(Action.FREEPLAY_FAVORITE); var _freeplay_left = new FunkinAction(Action.FREEPLAY_LEFT); var _freeplay_right = new FunkinAction(Action.FREEPLAY_RIGHT); + var _freeplay_char_select = new FunkinAction(Action.FREEPLAY_CHAR_SELECT); var _cutscene_advance = new FunkinAction(Action.CUTSCENE_ADVANCE); var _debug_menu = new FunkinAction(Action.DEBUG_MENU); var _debug_chart = new FunkinAction(Action.DEBUG_CHART); @@ -262,6 +263,11 @@ class Controls extends FlxActionSet inline function get_FREEPLAY_RIGHT() return _freeplay_right.check(); + public var FREEPLAY_CHAR_SELECT(get, never):Bool; + + inline function get_FREEPLAY_CHAR_SELECT() + return _freeplay_char_select.check(); + public var CUTSCENE_ADVANCE(get, never):Bool; inline function get_CUTSCENE_ADVANCE() @@ -318,6 +324,7 @@ class Controls extends FlxActionSet add(_freeplay_favorite); add(_freeplay_left); add(_freeplay_right); + add(_freeplay_char_select); add(_cutscene_advance); add(_debug_menu); add(_debug_chart); @@ -424,6 +431,7 @@ class Controls extends FlxActionSet case FREEPLAY_FAVORITE: _freeplay_favorite; case FREEPLAY_LEFT: _freeplay_left; case FREEPLAY_RIGHT: _freeplay_right; + case FREEPLAY_CHAR_SELECT: _freeplay_char_select; case CUTSCENE_ADVANCE: _cutscene_advance; case DEBUG_MENU: _debug_menu; case DEBUG_CHART: _debug_chart; @@ -500,6 +508,8 @@ class Controls extends FlxActionSet func(_freeplay_left, JUST_PRESSED); case FREEPLAY_RIGHT: func(_freeplay_right, JUST_PRESSED); + case FREEPLAY_CHAR_SELECT: + func(_freeplay_char_select, JUST_PRESSED); case CUTSCENE_ADVANCE: func(_cutscene_advance, JUST_PRESSED); case DEBUG_MENU: @@ -721,6 +731,7 @@ class Controls extends FlxActionSet bindKeys(Control.FREEPLAY_FAVORITE, getDefaultKeybinds(scheme, Control.FREEPLAY_FAVORITE)); bindKeys(Control.FREEPLAY_LEFT, getDefaultKeybinds(scheme, Control.FREEPLAY_LEFT)); bindKeys(Control.FREEPLAY_RIGHT, getDefaultKeybinds(scheme, Control.FREEPLAY_RIGHT)); + bindKeys(Control.FREEPLAY_CHAR_SELECT, getDefaultKeybinds(scheme, Control.FREEPLAY_CHAR_SELECT)); bindKeys(Control.CUTSCENE_ADVANCE, getDefaultKeybinds(scheme, Control.CUTSCENE_ADVANCE)); bindKeys(Control.DEBUG_MENU, getDefaultKeybinds(scheme, Control.DEBUG_MENU)); bindKeys(Control.DEBUG_CHART, getDefaultKeybinds(scheme, Control.DEBUG_CHART)); @@ -756,6 +767,7 @@ class Controls extends FlxActionSet case Control.FREEPLAY_FAVORITE: return [F]; // Favorite a song on the menu case Control.FREEPLAY_LEFT: return [Q]; // Switch tabs on the menu case Control.FREEPLAY_RIGHT: return [E]; // Switch tabs on the menu + case Control.FREEPLAY_CHAR_SELECT: return [TAB]; case Control.CUTSCENE_ADVANCE: return [Z, ENTER]; case Control.DEBUG_MENU: return [GRAVEACCENT]; case Control.DEBUG_CHART: return []; @@ -784,6 +796,7 @@ class Controls extends FlxActionSet case Control.FREEPLAY_FAVORITE: return [F]; // Favorite a song on the menu case Control.FREEPLAY_LEFT: return [Q]; // Switch tabs on the menu case Control.FREEPLAY_RIGHT: return [E]; // Switch tabs on the menu + case Control.FREEPLAY_CHAR_SELECT: return [TAB]; case Control.CUTSCENE_ADVANCE: return [G, Z]; case Control.DEBUG_MENU: return [GRAVEACCENT]; case Control.DEBUG_CHART: return []; @@ -812,6 +825,7 @@ class Controls extends FlxActionSet case Control.FREEPLAY_FAVORITE: return []; case Control.FREEPLAY_LEFT: return []; case Control.FREEPLAY_RIGHT: return []; + case Control.FREEPLAY_CHAR_SELECT: return []; case Control.CUTSCENE_ADVANCE: return [ENTER]; case Control.DEBUG_MENU: return []; case Control.DEBUG_CHART: return []; @@ -1548,6 +1562,7 @@ enum Control FREEPLAY_FAVORITE; FREEPLAY_LEFT; FREEPLAY_RIGHT; + FREEPLAY_CHAR_SELECT; // WINDOW WINDOW_SCREENSHOT; WINDOW_FULLSCREEN; @@ -1602,6 +1617,7 @@ enum abstract Action(String) to String from String var FREEPLAY_FAVORITE = "freeplay_favorite"; var FREEPLAY_LEFT = "freeplay_left"; var FREEPLAY_RIGHT = "freeplay_right"; + var FREEPLAY_CHAR_SELECT = "freeplay_char_select"; // VOLUME var VOLUME_UP = "volume_up"; var VOLUME_DOWN = "volume_down"; diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index eef5d9e68a..dc42bd6510 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1232,7 +1232,7 @@ class FreeplayState extends MusicBeatSubState // } #end - if (FlxG.keys.justPressed.TAB) + if (controls.FREEPLAY_CHAR_SELECT && !busy) { FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState()); } From 96aa0b3a59399efe3e298c3879d2f61d19be52a3 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 8 Jul 2024 21:51:27 -0400 Subject: [PATCH 111/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 94db49cd4a..1b6502c3a4 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 94db49cd4a2acacd5089628619104830a4f1ffce +Subproject commit 1b6502c3a4200cedb1195f926b09307722cbd347 From b4acfe8d954b8bc96c51702c860c95e72ea84482 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 8 Jul 2024 22:10:40 -0400 Subject: [PATCH 112/469] assets submod --- art | 2 +- assets | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/art b/art index 55c1b56823..faeba700c5 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit 55c1b56823d4d7a74397bab9aeab30f15126499c +Subproject commit faeba700c5526bd4fd57ccc927d875c82b9d3553 diff --git a/assets b/assets index fe7960dac6..4e409880a2 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit fe7960dac67af26572376ded5df8eb4527e22095 +Subproject commit 4e409880a2e25357ba755e816e237519d6d6adfb From ae03537676f5a1b4ba19eb15ed7a420353a6c2eb Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 9 Jul 2024 03:20:51 -0400 Subject: [PATCH 113/469] Fix an issue with Pico's death animation spritesheet --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 4e409880a2..1c9c0986e5 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 4e409880a2e25357ba755e816e237519d6d6adfb +Subproject commit 1c9c0986e5eb0a7b5faa81ecacc158d20429720d From cb4620d33aaeb5b1eb88f0f9d1a29d1fe5c8c662 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 9 Jul 2024 04:20:42 -0400 Subject: [PATCH 114/469] Fix background props not bopping on Week 5 --- source/funkin/play/stage/Bopper.hx | 2 +- source/funkin/play/stage/Stage.hx | 147 +++++++++++++++++++++++++---- 2 files changed, 130 insertions(+), 19 deletions(-) diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index 0061e85fb0..7665d0de07 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -177,7 +177,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass */ public function onStepHit(event:SongTimeScriptEvent) { - if (danceEvery > 0) trace('step hit(${danceEvery}): ${event.step % (danceEvery * Constants.STEPS_PER_BEAT)} == 0?'); + if (danceEvery > 0) trace('step hit(${danceEvery}, ${shouldBop}): ${event.step % (danceEvery * Constants.STEPS_PER_BEAT)} == 0?'); if (danceEvery > 0 && (event.step % (danceEvery * Constants.STEPS_PER_BEAT)) == 0) { trace('dance onStepHit!'); diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index f4e22e380f..85b0056ca7 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -769,7 +769,16 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements * A function that gets called once per step in the song. * @param curStep The current step number. */ - public function onStepHit(event:SongTimeScriptEvent):Void {} + public function onStepHit(event:SongTimeScriptEvent):Void + { + // Override me in your scripted stage to perform custom behavior! + // Make sure to call super.onStepHit(event) if you want to keep the boppers dancing. + + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } /** * A function that gets called once per beat in the song (once every four steps). @@ -786,7 +795,13 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements } } - public function onUpdate(event:UpdateScriptEvent) {} + public function onUpdate(event:UpdateScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } public override function kill() { @@ -866,35 +881,131 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements return StageRegistry.instance.parseEntryDataWithMigration(id, StageRegistry.instance.fetchEntryVersion(id)); } - public function onScriptEvent(event:ScriptEvent) {} + public function onScriptEvent(event:ScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onPause(event:PauseScriptEvent) {} + public function onPause(event:PauseScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onResume(event:ScriptEvent) {} + public function onResume(event:ScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onSongStart(event:ScriptEvent) {} + public function onSongStart(event:ScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onSongEnd(event:ScriptEvent) {} + public function onSongEnd(event:ScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onGameOver(event:ScriptEvent) {} + public function onGameOver(event:ScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onCountdownStart(event:CountdownScriptEvent) {} + public function onCountdownStart(event:CountdownScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onCountdownStep(event:CountdownScriptEvent) {} + public function onCountdownStep(event:CountdownScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onCountdownEnd(event:CountdownScriptEvent) {} + public function onCountdownEnd(event:CountdownScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onNoteIncoming(event:NoteScriptEvent) {} + public function onNoteIncoming(event:NoteScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onNoteHit(event:HitNoteScriptEvent) {} + public function onNoteHit(event:HitNoteScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onNoteMiss(event:NoteScriptEvent) {} + public function onNoteMiss(event:NoteScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onSongEvent(event:SongEventScriptEvent) {} + public function onSongEvent(event:SongEventScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onNoteGhostMiss(event:GhostMissNoteScriptEvent) {} + public function onNoteGhostMiss(event:GhostMissNoteScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onSongLoaded(event:SongLoadScriptEvent) {} + public function onSongLoaded(event:SongLoadScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } - public function onSongRetry(event:ScriptEvent) {} + public function onSongRetry(event:ScriptEvent) + { + for (bopper in boppers) + { + ScriptEventDispatcher.callEvent(bopper, event); + } + } } From 98ed4bd843cd96162aa96d943c0779c3abd02ed8 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 9 Jul 2024 04:21:14 -0400 Subject: [PATCH 115/469] Implement Nene/GFXmas combo anims --- assets | 2 +- source/funkin/play/character/BaseCharacter.hx | 1 - source/funkin/play/stage/Bopper.hx | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/assets b/assets index 4e409880a2..240d0ff1da 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 4e409880a2e25357ba755e816e237519d6d6adfb +Subproject commit 240d0ff1dafd616551e6b6a2b048b7b6f602ac75 diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index c228d803a3..0dab2101a6 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -461,7 +461,6 @@ class BaseCharacter extends Bopper if (!currentAnimation.startsWith('dance') && !currentAnimation.startsWith('idle') && !isAnimationFinished()) return; } - trace('${characterId}: Actually dancing'); // Otherwise, fallback to the super dance() method, which handles playing the idle animation. super.dance(); } diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index 7665d0de07..87151de21b 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -177,10 +177,8 @@ class Bopper extends StageProp implements IPlayStateScriptedClass */ public function onStepHit(event:SongTimeScriptEvent) { - if (danceEvery > 0) trace('step hit(${danceEvery}, ${shouldBop}): ${event.step % (danceEvery * Constants.STEPS_PER_BEAT)} == 0?'); if (danceEvery > 0 && (event.step % (danceEvery * Constants.STEPS_PER_BEAT)) == 0) { - trace('dance onStepHit!'); dance(shouldBop); } } From 717894300d6041e1a24b10682122d9feffb7d18a Mon Sep 17 00:00:00 2001 From: AppleHair Date: Tue, 9 Jul 2024 19:59:31 +0300 Subject: [PATCH 116/469] [BUGFIX] Fixed `cancelMenu` sound not playing after switching state. --- source/funkin/ui/mainmenu/MainMenuState.hx | 2 +- source/funkin/ui/options/OptionsState.hx | 2 +- source/funkin/ui/story/StoryMenuState.hx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index d09536eea2..a2bdd23f84 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -409,8 +409,8 @@ class MainMenuState extends MusicBeatState if (controls.BACK && menuItems.enabled && !menuItems.busy) { - FunkinSound.playOnce(Paths.sound('cancelMenu')); FlxG.switchState(() -> new TitleState()); + FunkinSound.playOnce(Paths.sound('cancelMenu')); } } } diff --git a/source/funkin/ui/options/OptionsState.hx b/source/funkin/ui/options/OptionsState.hx index 40308d96bc..a2301e6a36 100644 --- a/source/funkin/ui/options/OptionsState.hx +++ b/source/funkin/ui/options/OptionsState.hx @@ -145,8 +145,8 @@ class Page extends FlxGroup { if (canExit && controls.BACK) { - FunkinSound.playOnce(Paths.sound('cancelMenu')); exit(); + FunkinSound.playOnce(Paths.sound('cancelMenu')); } } diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 06a83ab4db..04d0212565 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -374,9 +374,9 @@ class StoryMenuState extends MusicBeatState if (controls.BACK && !exitingMenu && !selectedLevel) { - FunkinSound.playOnce(Paths.sound('cancelMenu')); exitingMenu = true; FlxG.switchState(() -> new MainMenuState()); + FunkinSound.playOnce(Paths.sound('cancelMenu')); } } From 481f7ab61a6e206bc3aadfd2754cdc299116c087 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Wed, 10 Jul 2024 00:23:06 +0200 Subject: [PATCH 117/469] Fix F5 chart not reloading --- source/funkin/play/PlayState.hx | 59 ++----------------- source/funkin/ui/MusicBeatState.hx | 7 +-- source/funkin/ui/MusicBeatSubState.hx | 5 +- .../util/plugins/ReloadAssetsDebugPlugin.hx | 14 ++++- 4 files changed, 17 insertions(+), 68 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index f55cef3888..8b6753035f 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1335,64 +1335,13 @@ class PlayState extends MusicBeatSubState #end /** - * Removes any references to the current stage, then clears the stage cache, - * then reloads all the stages. - * - * This is useful for when you want to edit a stage without reloading the whole game. - * Reloading works on both the JSON and the HXC, if applicable. - * * Call this by pressing F5 on a debug build. */ - override function debug_refreshModules():Void + override function reloadAssets():Void { - // Prevent further gameplay updates, which will try to reference dead objects. - criticalFailure = true; - - // Remove the current stage. If the stage gets deleted while it's still in use, - // it'll probably crash the game or something. - if (this.currentStage != null) - { - remove(currentStage); - var event:ScriptEvent = new ScriptEvent(DESTROY, false); - ScriptEventDispatcher.callEvent(currentStage, event); - currentStage = null; - } - - if (!overrideMusic) - { - // Stop the instrumental. - if (FlxG.sound.music != null) - { - FlxG.sound.music.destroy(); - FlxG.sound.music = null; - } - - // Stop the vocals. - if (vocals != null && vocals.exists) - { - vocals.destroy(); - vocals = null; - } - } - else - { - // Stop the instrumental. - if (FlxG.sound.music != null) - { - FlxG.sound.music.stop(); - } - - // Stop the vocals. - if (vocals != null && vocals.exists) - { - vocals.stop(); - } - } - - super.debug_refreshModules(); - - var event:ScriptEvent = new ScriptEvent(CREATE, false); - ScriptEventDispatcher.callEvent(currentSong, event); + funkin.modding.PolymodHandler.forceReloadAssets(); + lastParams.targetSong = SongRegistry.instance.fetchEntry(currentSong.id); + LoadingState.loadPlayState(lastParams); } override function stepHit():Bool diff --git a/source/funkin/ui/MusicBeatState.hx b/source/funkin/ui/MusicBeatState.hx index 92169df751..8668b64c12 100644 --- a/source/funkin/ui/MusicBeatState.hx +++ b/source/funkin/ui/MusicBeatState.hx @@ -78,9 +78,6 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler { // Emergency exit button. if (FlxG.keys.justPressed.F4) FlxG.switchState(() -> new MainMenuState()); - - // This can now be used in EVERY STATE YAY! - if (FlxG.keys.justPressed.F5) debug_refreshModules(); } override function update(elapsed:Float) @@ -114,12 +111,10 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler ModuleHandler.callEvent(event); } - function debug_refreshModules() + function reloadAssets() { PolymodHandler.forceReloadAssets(); - this.destroy(); - // Create a new instance of the current state, so old data is cleared. FlxG.resetState(); } diff --git a/source/funkin/ui/MusicBeatSubState.hx b/source/funkin/ui/MusicBeatSubState.hx index 9035d12ff3..5c40b37bcb 100644 --- a/source/funkin/ui/MusicBeatSubState.hx +++ b/source/funkin/ui/MusicBeatSubState.hx @@ -72,9 +72,6 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler // Emergency exit button. if (FlxG.keys.justPressed.F4) FlxG.switchState(() -> new MainMenuState()); - // This can now be used in EVERY STATE YAY! - if (FlxG.keys.justPressed.F5) debug_refreshModules(); - // Display Conductor info in the watch window. FlxG.watch.addQuick("musicTime", FlxG.sound.music?.time ?? 0.0); Conductor.watchQuick(conductorInUse); @@ -82,7 +79,7 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler dispatchEvent(new UpdateScriptEvent(elapsed)); } - function debug_refreshModules() + function reloadAssets() { PolymodHandler.forceReloadAssets(); diff --git a/source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx b/source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx index f696095311..0e1e238ac2 100644 --- a/source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx +++ b/source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx @@ -1,6 +1,9 @@ package funkin.util.plugins; +import flixel.FlxG; import flixel.FlxBasic; +import funkin.ui.MusicBeatState; +import funkin.ui.MusicBeatSubState; /** * A plugin which adds functionality to press `F5` to reload all game assets, then reload the current state. @@ -28,10 +31,15 @@ class ReloadAssetsDebugPlugin extends FlxBasic if (FlxG.keys.justPressed.F5) #end { - funkin.modding.PolymodHandler.forceReloadAssets(); + var state:Dynamic = FlxG.state; + if (state is MusicBeatState || state is MusicBeatSubState) state.reloadAssets(); + else + { + funkin.modding.PolymodHandler.forceReloadAssets(); - // Create a new instance of the current state, so old data is cleared. - FlxG.resetState(); + // Create a new instance of the current state, so old data is cleared. + FlxG.resetState(); + } } } From 9ad2bb35f9b7f73b690a08b421fecd6a1eab0a8d Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Wed, 10 Jul 2024 01:26:16 +0200 Subject: [PATCH 118/469] ChartEditor Live Input Code Refactor + 6 key fix --- .../ui/debug/charting/ChartEditorState.hx | 59 ++++++------------- 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index f72cca77f6..5e74938402 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -282,6 +282,21 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ public static final WELCOME_MUSIC_FADE_IN_DURATION:Float = 10.0; + /** + * A map of the keys for every live input style. + */ + public static final LIVE_INPUT_KEYS:Map> = [ + NumberKeys => [ + FIVE, SIX, SEVEN, EIGHT, + ONE, TWO, THREE, FOUR + ], + WASDKeys => [ + LEFT, DOWN, UP, RIGHT, + A, S, W, D + ], + None => [] + ]; + /** * INSTANCE DATA */ @@ -5129,46 +5144,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState function handlePlayhead():Void { // Place notes at the playhead with the keyboard. - switch (currentLiveInputStyle) - { - case ChartEditorLiveInputStyle.WASDKeys: - if (FlxG.keys.justPressed.A) placeNoteAtPlayhead(4); - if (FlxG.keys.justReleased.A) finishPlaceNoteAtPlayhead(4); - if (FlxG.keys.justPressed.S) placeNoteAtPlayhead(5); - if (FlxG.keys.justReleased.S) finishPlaceNoteAtPlayhead(5); - if (FlxG.keys.justPressed.W) placeNoteAtPlayhead(6); - if (FlxG.keys.justReleased.W) finishPlaceNoteAtPlayhead(6); - if (FlxG.keys.justPressed.D) placeNoteAtPlayhead(7); - if (FlxG.keys.justReleased.D) finishPlaceNoteAtPlayhead(7); - - if (FlxG.keys.justPressed.LEFT) placeNoteAtPlayhead(0); - if (FlxG.keys.justReleased.LEFT) finishPlaceNoteAtPlayhead(0); - if (FlxG.keys.justPressed.DOWN) placeNoteAtPlayhead(1); - if (FlxG.keys.justReleased.DOWN) finishPlaceNoteAtPlayhead(1); - if (FlxG.keys.justPressed.UP) placeNoteAtPlayhead(2); - if (FlxG.keys.justReleased.UP) finishPlaceNoteAtPlayhead(2); - if (FlxG.keys.justPressed.RIGHT) placeNoteAtPlayhead(3); - if (FlxG.keys.justReleased.RIGHT) finishPlaceNoteAtPlayhead(3); - case ChartEditorLiveInputStyle.NumberKeys: - // Flipped because Dad is on the left but represents data 0-3. - if (FlxG.keys.justPressed.ONE) placeNoteAtPlayhead(4); - if (FlxG.keys.justReleased.ONE) finishPlaceNoteAtPlayhead(4); - if (FlxG.keys.justPressed.TWO) placeNoteAtPlayhead(5); - if (FlxG.keys.justReleased.TWO) finishPlaceNoteAtPlayhead(5); - if (FlxG.keys.justPressed.THREE) placeNoteAtPlayhead(6); - if (FlxG.keys.justReleased.THREE) finishPlaceNoteAtPlayhead(6); - if (FlxG.keys.justPressed.FOUR) placeNoteAtPlayhead(7); - if (FlxG.keys.justReleased.FOUR) finishPlaceNoteAtPlayhead(7); - - if (FlxG.keys.justPressed.FIVE) placeNoteAtPlayhead(0); - if (FlxG.keys.justReleased.FIVE) finishPlaceNoteAtPlayhead(0); - if (FlxG.keys.justPressed.SIX) placeNoteAtPlayhead(1); - if (FlxG.keys.justPressed.SEVEN) placeNoteAtPlayhead(2); - if (FlxG.keys.justReleased.SEVEN) finishPlaceNoteAtPlayhead(2); - if (FlxG.keys.justPressed.EIGHT) placeNoteAtPlayhead(3); - if (FlxG.keys.justReleased.EIGHT) finishPlaceNoteAtPlayhead(3); - case ChartEditorLiveInputStyle.None: - // Do nothing. + for (note => key in LIVE_INPUT_KEYS[currentLiveInputStyle]) + { + if (FlxG.keys.checkStatus(key, JUST_PRESSED)) placeNoteAtPlayhead(note) + else if (FlxG.keys.checkStatus(key, JUST_RELEASED)) finishPlaceNoteAtPlayhead(note); } // Place events at playhead. From a93af43d637d15ec55dbd1a541ace8e4e31d81d3 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 9 Jul 2024 19:37:06 -0400 Subject: [PATCH 119/469] Song title fixes. --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 4e409880a2..bc0beafa58 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 4e409880a2e25357ba755e816e237519d6d6adfb +Subproject commit bc0beafa58c484fe8d98ddadbbd963a43d895402 From d8572cd323d25044c78ddb8b5c870de63c01b7b1 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 10 Jul 2024 14:23:50 -0400 Subject: [PATCH 120/469] Thank you loggo --- source/funkin/play/PlayState.hx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index a4723611e4..ddf100bfc7 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -16,6 +16,7 @@ import flixel.tweens.FlxTween; import flixel.ui.FlxBar; import flixel.util.FlxColor; import flixel.util.FlxTimer; +import flixel.util.FlxStringUtil; import funkin.api.newgrounds.NGio; import funkin.audio.FunkinSound; import funkin.audio.VoicesGroup; @@ -2060,7 +2061,9 @@ class PlayState extends MusicBeatSubState } else { - scoreText.text = 'Score:' + songScore; + // TODO: Add an option for this maybe? + var commaSeparated:Bool = true; + scoreText.text = 'Score: ${FlxStringUtil.formatMoney(songScore, false, commaSeparated)}'; } } From 2ae68822415bd1a7309a3e81b69026c4a706ec0b Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 10 Jul 2024 17:25:52 -0400 Subject: [PATCH 121/469] Swappable album art and updated FlxAnimate --- assets | 2 +- hmm.json | 4 +- source/funkin/InitState.hx | 10 +- .../data/freeplay/player/PlayerRegistry.hx | 3 +- .../graphics/adobeanimate/FlxAtlasSprite.hx | 163 ++++++++++++++---- source/funkin/play/ResultState.hx | 14 +- .../play/character/AnimateAtlasCharacter.hx | 14 +- .../funkin/ui/charSelect/CharSelectPlayer.hx | 2 +- .../ui/charSelect/CharSelectSubState.hx | 4 +- source/funkin/ui/freeplay/AlbumRoll.hx | 66 ++++++- source/funkin/ui/freeplay/FreeplayDJ.hx | 37 ++-- source/funkin/ui/freeplay/FreeplayState.hx | 2 +- source/funkin/ui/freeplay/SongMenuItem.hx | 3 +- 13 files changed, 244 insertions(+), 80 deletions(-) diff --git a/assets b/assets index 4e409880a2..902b12a0cd 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 4e409880a2e25357ba755e816e237519d6d6adfb +Subproject commit 902b12a0cdd9b588cefe573e591530f088a77b4c diff --git a/hmm.json b/hmm.json index 8eaf242122..f1056c32cf 100644 --- a/hmm.json +++ b/hmm.json @@ -37,8 +37,8 @@ "name": "flxanimate", "type": "git", "dir": null, - "ref": "17e0d59fdbc2b6283a5c0e4df41f1c7f27b71c49", - "url": "https://github.com/FunkinCrew/flxanimate" + "ref": "768740a56b26aa0c072720e0d1236b94afe68e3e", + "url": "https://github.com/Dot-Stuff/flxanimate" }, { "name": "FlxPartialSound", diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 6e370b5ff7..43ef3c0a51 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -241,11 +241,11 @@ class InitState extends FlxState totalNotesHit: 140, totalNotes: 190 } - // 2000 = loss - // 240 = good - // 230 = great - // 210 = excellent - // 190 = perfect + // 2400 total notes = 7% = LOSS + // 240 total notes = 79% = GOOD + // 230 total notes = 82% = GREAT + // 210 total notes = 91% = EXCELLENT + // 190 total notes = PERFECT }, })); #elseif ANIMDEBUG diff --git a/source/funkin/data/freeplay/player/PlayerRegistry.hx b/source/funkin/data/freeplay/player/PlayerRegistry.hx index 4656a12869..be8730ccd2 100644 --- a/source/funkin/data/freeplay/player/PlayerRegistry.hx +++ b/source/funkin/data/freeplay/player/PlayerRegistry.hx @@ -58,8 +58,9 @@ class PlayerRegistry extends BaseRegistry * @param characterId The stage character ID. * @return The playable character. */ - public function getCharacterOwnerId(characterId:String):Null + public function getCharacterOwnerId(characterId:Null):Null { + if (characterId == null) return null; return ownedCharacterIds[characterId]; } diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index eb331b9c37..049c6e2064 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -4,8 +4,11 @@ import flixel.util.FlxSignal.FlxTypedSignal; import flxanimate.FlxAnimate; import flxanimate.FlxAnimate.Settings; import flxanimate.frames.FlxAnimateFrames; +import flixel.graphics.frames.FlxFrame; +import flixel.system.FlxAssets.FlxGraphicAsset; import openfl.display.BitmapData; import openfl.utils.Assets; +import flixel.math.FlxPoint; /** * A sprite which provides convenience functions for rendering a texture atlas with animations. @@ -25,9 +28,19 @@ class FlxAtlasSprite extends FlxAnimate }; /** - * Signal dispatched when an animation finishes playing. + * Signal dispatched when an animation advances to the next frame. */ - public var onAnimationFinish:FlxTypedSignalVoid> = new FlxTypedSignalVoid>(); + public var onAnimationFrame:FlxTypedSignalInt->Void> = new FlxTypedSignal(); + + /** + * Signal dispatched when a non-looping animation finishes playing. + */ + public var onAnimationComplete:FlxTypedSignalVoid> = new FlxTypedSignal(); + + /** + * Signal dispatched when a looping animation finishes playing + */ + public var onAnimationLoopComplete:FlxTypedSignalVoid> = new FlxTypedSignal(); var currentAnimation:String; @@ -44,17 +57,20 @@ class FlxAtlasSprite extends FlxAnimate super(x, y, path, settings); - if (this.anim.curInstance == null) + if (this.anim.stageInstance == null) { throw 'FlxAtlasSprite not initialized properly. Are you sure the path (${path}) exists?'; } - onAnimationFinish.add(cleanupAnimation); + onAnimationComplete.add(cleanupAnimation); // This defaults the sprite to play the first animation in the atlas, // then pauses it. This ensures symbols are intialized properly. this.anim.play(''); this.anim.pause(); + + this.anim.onComplete.add(_onAnimationComplete); + this.anim.onFrame.add(_onAnimationFrame); } /** @@ -62,9 +78,13 @@ class FlxAtlasSprite extends FlxAnimate */ public function listAnimations():Array { - if (this.anim == null) return []; - return this.anim.getFrameLabels(); - // return [""]; + var mainSymbol = this.anim.symbolDictionary[this.anim.stageInstance.symbol.name]; + if (mainSymbol == null) + { + FlxG.log.error('FlxAtlasSprite does not have its main symbol!'); + return []; + } + return mainSymbol.getFrameLabels().map(keyFrame -> keyFrame.name).filterNull(); } /** @@ -107,12 +127,11 @@ class FlxAtlasSprite extends FlxAnimate * @param restart Whether to restart the animation if it is already playing. * @param ignoreOther Whether to ignore all other animation inputs, until this one is done playing * @param loop Whether to loop the animation + * @param startFrame The frame to start the animation on * NOTE: `loop` and `ignoreOther` are not compatible with each other! */ - public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false, ?loop:Bool = false):Void + public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false, loop:Bool = false, startFrame:Int = 0):Void { - if (loop == null) loop = false; - // Skip if not allowed to play animations. if ((!canPlayOtherAnims && !ignoreOther)) return; @@ -128,7 +147,7 @@ class FlxAtlasSprite extends FlxAnimate else { // Resume animation if it's paused. - anim.play('', false, false); + anim.play('', restart, false, startFrame); } } else @@ -141,31 +160,27 @@ class FlxAtlasSprite extends FlxAnimate } } - anim.callback = function(_, frame:Int) { - var offset = loop ? 0 : -1; - - var frameLabel = anim.getFrameLabel(id); - if (frame == (frameLabel.duration + offset) + frameLabel.index) + anim.onComplete.removeAll(); + anim.onComplete.add(function() { + if (loop) { - if (loop) - { - playAnimation(id, true, false, true); - } - else - { - onAnimationFinish.dispatch(id); - } + onAnimationLoopComplete.dispatch(id); + this.anim.play(id, restart, false, startFrame); + this.currentAnimation = id; } - }; - - anim.onComplete = function() { - onAnimationFinish.dispatch(id); - }; + else + { + onAnimationComplete.dispatch(id); + } + }); // Prevent other animations from playing if `ignoreOther` is true. if (ignoreOther) canPlayOtherAnims = false; // Move to the first frame of the animation. + // goToFrameLabel(id); + trace('Playing animation $id'); + this.anim.play(id, restart, false, startFrame); goToFrameLabel(id); this.currentAnimation = id; } @@ -175,6 +190,24 @@ class FlxAtlasSprite extends FlxAnimate super.update(elapsed); } + /** + * Returns true if the animation has finished playing. + * Never true if animation is configured to loop. + */ + public function isAnimationFinished():Bool + { + return this.anim.finished; + } + + /** + * Returns true if the animation has reached the last frame. + * Can be true even if animation is configured to loop. + */ + public function isLoopComplete():Bool + { + return (anim.reversed && anim.curFrame == 0 || !(anim.reversed) && (anim.curFrame) >= (anim.length - 1)); + } + /** * Stops the current animation. */ @@ -219,4 +252,76 @@ class FlxAtlasSprite extends FlxAnimate // this.currentAnimation = null; this.anim.pause(); } + + function _onAnimationFrame(frame:Int):Void + { + if (currentAnimation != null) + { + onAnimationFrame.dispatch(currentAnimation, frame); + if (isLoopComplete()) onAnimationLoopComplete.dispatch(currentAnimation); + } + } + + function _onAnimationComplete():Void + { + if (currentAnimation != null) + { + onAnimationComplete.dispatch(currentAnimation); + } + } + + var prevFrames:Map = []; + + public function replaceFrameGraphic(index:Int, ?graphic:FlxGraphicAsset):Void + { + if (graphic == null || !Assets.exists(graphic)) + { + var prevFrame:Null = prevFrames.get(index); + if (prevFrame == null) return; + + prevFrame.copyTo(frames.getByIndex(index)); + return; + } + + var prevFrame:FlxFrame = prevFrames.get(index) ?? frames.getByIndex(index).copyTo(); + prevFrames.set(index, prevFrame); + + var frame = FlxG.bitmap.add(graphic).imageFrame.frame; + frame.copyTo(frames.getByIndex(index)); + + // Additional sizing fix. + @:privateAccess + if (true) + { + var frame = frames.getByIndex(index); + frame.tileMatrix[0] = prevFrame.frame.width / frame.frame.width; + frame.tileMatrix[3] = prevFrame.frame.height / frame.frame.height; + } + } + + public function getBasePosition():Null + { + var stagePos = new FlxPoint(anim.stageInstance.matrix.tx, anim.stageInstance.matrix.ty); + var instancePos = new FlxPoint(anim.curInstance.matrix.tx, anim.curInstance.matrix.ty); + var firstElement = anim.curSymbol.timeline?.get(0)?.get(0)?.get(0); + if (firstElement == null) return instancePos; + var firstElementPos = new FlxPoint(firstElement.matrix.tx, firstElement.matrix.ty); + + return instancePos + firstElementPos; + } + + public function getPivotPosition():Null + { + return anim.curInstance.symbol.transformationPoint; + } + + public override function destroy():Void + { + for (prevFrameId in prevFrames.keys()) + { + replaceFrameGraphic(prevFrameId, null); + } + + super.destroy(); + } } diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index c2d9d42b38..b1ff69a3a9 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -70,6 +70,8 @@ class ResultState extends MusicBeatSubState delay:Float }> = []; + var playerCharacterId:Null; + var rankBg:FunkinSprite; final cameraBG:FunkinCamera; final cameraScroll:FunkinCamera; @@ -164,7 +166,7 @@ class ResultState extends MusicBeatSubState add(soundSystem); // Fetch playable character data. Default to BF on the results screen if we can't find it. - var playerCharacterId:Null = PlayerRegistry.instance.getCharacterOwnerId(params.characterId); + playerCharacterId = PlayerRegistry.instance.getCharacterOwnerId(params.characterId); var playerCharacter:Null = PlayerRegistry.instance.fetchEntry(playerCharacterId ?? 'bf'); trace('Got playable character: ${playerCharacter?.getName()}'); @@ -189,7 +191,7 @@ class ResultState extends MusicBeatSubState if (!(animData.looped ?? true)) { // Animation is not looped. - animation.onAnimationFinish.add((_name:String) -> { + animation.onAnimationComplete.add((_name:String) -> { if (animation != null) { animation.anim.pause(); @@ -198,7 +200,7 @@ class ResultState extends MusicBeatSubState } else if (animData.loopFrameLabel != null) { - animation.onAnimationFinish.add((_name:String) -> { + animation.onAnimationComplete.add((_name:String) -> { if (animation != null) { animation.playAnimation(animData.loopFrameLabel ?? ''); // unpauses this anim, since it's on PlayOnce! @@ -207,7 +209,7 @@ class ResultState extends MusicBeatSubState } else if (animData.loopFrame != null) { - animation.onAnimationFinish.add((_name:String) -> { + animation.onAnimationComplete.add((_name:String) -> { if (animation != null) { animation.anim.curFrame = animData.loopFrame ?? 0; @@ -742,6 +744,7 @@ class ResultState extends MusicBeatSubState FlxG.switchState(FreeplayState.build( { { + character: playerCharacterId ?? "bf", fromResults: { oldRank: Scoring.calculateRank(params?.prevScoreData), @@ -799,8 +802,9 @@ typedef ResultsStateParams = /** * The character ID for the song we just played. + * @default `bf` */ - var characterId:String; + var ?characterId:String; /** * Whether the displayed score is a new highscore diff --git a/source/funkin/play/character/AnimateAtlasCharacter.hx b/source/funkin/play/character/AnimateAtlasCharacter.hx index ed58b92b55..1d1672c9d3 100644 --- a/source/funkin/play/character/AnimateAtlasCharacter.hx +++ b/source/funkin/play/character/AnimateAtlasCharacter.hx @@ -109,8 +109,6 @@ class AnimateAtlasCharacter extends BaseCharacter var loop:Bool = animData.looped; this.mainSprite.playAnimation(prefix, restart, ignoreOther, loop); - - animFinished = false; } public override function hasAnimation(name:String):Bool @@ -124,7 +122,7 @@ class AnimateAtlasCharacter extends BaseCharacter */ public override function isAnimationFinished():Bool { - return animFinished; + return mainSprite.isAnimationFinished(); } function loadAtlasSprite():FlxAtlasSprite @@ -133,8 +131,8 @@ class AnimateAtlasCharacter extends BaseCharacter var sprite:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas(_data.assetPath, 'shared')); - sprite.onAnimationFinish.removeAll(); - sprite.onAnimationFinish.add(this.onAnimationFinished); + // sprite.onAnimationComplete.removeAll(); + sprite.onAnimationComplete.add(this.onAnimationFinished); return sprite; } @@ -152,7 +150,6 @@ class AnimateAtlasCharacter extends BaseCharacter // Make the game hold on the last frame. this.mainSprite.cleanupAnimation(prefix); // currentAnimName = null; - animFinished = true; // Fallback to idle! // playAnimation('idle', true, false); @@ -165,6 +162,11 @@ class AnimateAtlasCharacter extends BaseCharacter this.mainSprite = sprite; + // This forces the atlas to recalcuate its width and height + this.mainSprite.alpha = 0.0001; + this.mainSprite.draw(); + this.mainSprite.alpha = 1.0; + var feetPos:FlxPoint = feetPosition; this.updateHitbox(); diff --git a/source/funkin/ui/charSelect/CharSelectPlayer.hx b/source/funkin/ui/charSelect/CharSelectPlayer.hx index 9322369baf..9052c60e90 100644 --- a/source/funkin/ui/charSelect/CharSelectPlayer.hx +++ b/source/funkin/ui/charSelect/CharSelectPlayer.hx @@ -9,7 +9,7 @@ class CharSelectPlayer extends FlxAtlasSprite { super(x, y, Paths.animateAtlas("charSelect/bfChill")); - onAnimationFinish.add(function(animLabel:String) { + onAnimationComplete.add(function(animLabel:String) { switch (animLabel) { case "slidein": diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 14a5b36e04..8b1f050f54 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -600,7 +600,7 @@ class CharSelectSubState extends MusicBeatSubState playerChill.visible = false; playerChillOut.visible = true; playerChillOut.anim.goToFrameLabel("slideout"); - playerChillOut.anim.callback = (_, frame:Int) -> { + playerChillOut.onAnimationFrame.add((_, frame:Int) -> { if (frame == playerChillOut.anim.getFrameLabel("slideout").index + 1) { playerChill.visible = true; @@ -612,7 +612,7 @@ class CharSelectSubState extends MusicBeatSubState playerChillOut.switchChar(value); playerChillOut.visible = false; } - }; + }); return value; } diff --git a/source/funkin/ui/freeplay/AlbumRoll.hx b/source/funkin/ui/freeplay/AlbumRoll.hx index 49c5887225..36dba0054a 100644 --- a/source/funkin/ui/freeplay/AlbumRoll.hx +++ b/source/funkin/ui/freeplay/AlbumRoll.hx @@ -37,6 +37,7 @@ class AlbumRoll extends FlxSpriteGroup } var newAlbumArt:FlxAtlasSprite; + var albumTitle:FunkinSprite; var difficultyStars:DifficultyStars; var _exitMovers:Null; @@ -59,24 +60,27 @@ class AlbumRoll extends FlxSpriteGroup { super(); - newAlbumArt = new FlxAtlasSprite(0, 0, Paths.animateAtlas("freeplay/albumRoll/freeplayAlbum")); + newAlbumArt = new FlxAtlasSprite(640, 350, Paths.animateAtlas("freeplay/albumRoll/freeplayAlbum")); newAlbumArt.visible = false; - newAlbumArt.onAnimationFinish.add(onAlbumFinish); + newAlbumArt.onAnimationComplete.add(onAlbumFinish); add(newAlbumArt); difficultyStars = new DifficultyStars(140, 39); difficultyStars.visible = false; add(difficultyStars); + + buildAlbumTitle("freeplay/albumRoll/volume1-text"); + albumTitle.visible = false; } function onAlbumFinish(animName:String):Void { // Play the idle animation for the current album. - newAlbumArt.playAnimation(animNames.get('$albumId-idle'), false, false, true); - - // End on the last frame and don't continue until playAnimation is called again. - // newAlbumArt.anim.pause(); + if (animName != "idle") + { + // newAlbumArt.playAnimation('idle', true); + } } /** @@ -104,6 +108,12 @@ class AlbumRoll extends FlxSpriteGroup return; }; + // Update the album art. + var albumGraphic = Paths.image(albumData.getAlbumArtAssetKey()); + newAlbumArt.replaceFrameGraphic(0, albumGraphic); + + buildAlbumTitle(albumData.getAlbumTitleAssetKey()); + applyExitMovers(); refresh(); @@ -146,19 +156,57 @@ class AlbumRoll extends FlxSpriteGroup */ public function playIntro():Void { + albumTitle.visible = false; newAlbumArt.visible = true; - newAlbumArt.playAnimation(animNames.get('$albumId-active'), false, false, false); + newAlbumArt.playAnimation('intro', true); difficultyStars.visible = false; new FlxTimer().start(0.75, function(_) { - // showTitle(); + showTitle(); showStars(); + albumTitle.animation.play('switch'); }); } public function skipIntro():Void { - newAlbumArt.playAnimation(animNames.get('$albumId-trans'), false, false, false); + // Weird workaround + newAlbumArt.playAnimation('switch', true); + albumTitle.animation.play('switch'); + } + + public function showTitle():Void + { + albumTitle.visible = true; + } + + public function buildAlbumTitle(assetKey:String):Void + { + if (albumTitle != null) + { + remove(albumTitle); + albumTitle = null; + } + + albumTitle = FunkinSprite.createSparrow(925, 500, assetKey); + albumTitle.visible = albumTitle.frames != null && newAlbumArt.visible; + albumTitle.animation.addByPrefix('idle', 'idle0', 24, true); + albumTitle.animation.addByPrefix('switch', 'switch0', 24, false); + add(albumTitle); + + albumTitle.animation.finishCallback = (function(name) { + if (name == 'switch') albumTitle.animation.play('idle'); + }); + albumTitle.animation.play('idle'); + + albumTitle.zIndex = 1000; + + if (_exitMovers != null) _exitMovers.set([albumTitle], + { + x: FlxG.width, + speed: 0.4, + wait: 0 + }); } public function setDifficultyStars(?difficulty:Int):Void diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index 72eddd0ca2..317a523084 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -43,7 +43,7 @@ class FreeplayDJ extends FlxAtlasSprite super(x, y, playableCharData.getAtlasPath()); - anim.callback = function(name, number) { + onAnimationFrame.add(function(name, number) { if (name == playableCharData.getAnimationPrefix('cartoon')) { if (number == playableCharData.getCartoonSoundClickFrame()) @@ -55,12 +55,12 @@ class FreeplayDJ extends FlxAtlasSprite runTvLogic(); } } - }; + }); FlxG.debugger.track(this); FlxG.console.registerObject("dj", this); - anim.onComplete = onFinishAnim; + onAnimationComplete.add(onFinishAnim); FlxG.console.registerFunction("freeplayCartoon", function() { currentState = Cartoon; @@ -96,7 +96,7 @@ class FreeplayDJ extends FlxAtlasSprite var animPrefix = playableCharData.getAnimationPrefix('idle'); if (getCurrentAnimation() != animPrefix) { - playFlashAnimation(animPrefix, true); + playFlashAnimation(animPrefix, true, false, true); } if (getCurrentAnimation() == animPrefix && this.isLoopFinished()) @@ -120,7 +120,7 @@ class FreeplayDJ extends FlxAtlasSprite if (getCurrentAnimation() != animPrefix) playFlashAnimation('Boyfriend DJ fist pump', false); if (getCurrentAnimation() == animPrefix && anim.curFrame >= 4) { - anim.play("Boyfriend DJ fist pump", true, false, 0); + playAnimation("Boyfriend DJ fist pump", true, false, false, 0); } case FistPump: @@ -135,9 +135,12 @@ class FreeplayDJ extends FlxAtlasSprite timeIdling = 0; case Cartoon: var animPrefix = playableCharData.getAnimationPrefix('cartoon'); - if (animPrefix == null) { + if (animPrefix == null) + { currentState = IdleEasterEgg; - } else { + } + else + { if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true); timeIdling = 0; } @@ -145,6 +148,7 @@ class FreeplayDJ extends FlxAtlasSprite // I shit myself. } + #if debug if (FlxG.keys.pressed.CONTROL) { if (FlxG.keys.justPressed.LEFT) @@ -167,16 +171,17 @@ class FreeplayDJ extends FlxAtlasSprite this.offsetY += FlxG.keys.pressed.ALT ? 0.1 : (FlxG.keys.pressed.SHIFT ? 10.0 : 1.0); } - if (FlxG.keys.justPressed.SPACE) + if (FlxG.keys.justPressed.C) { currentState = (currentState == Idle ? Cartoon : Idle); } } + #end } - function onFinishAnim():Void + function onFinishAnim(name:String):Void { - var name = anim.curSymbol.name; + // var name = anim.curSymbol.name; if (name == playableCharData.getAnimationPrefix('intro')) { @@ -220,7 +225,7 @@ class FreeplayDJ extends FlxAtlasSprite // runTvLogic(); } trace('Replay idle: ${frame}'); - anim.play(playableCharData.getAnimationPrefix('cartoon'), true, false, frame); + playAnimation(playableCharData.getAnimationPrefix('cartoon'), true, false, false, frame); // trace('Finished confirm'); } else @@ -266,7 +271,7 @@ class FreeplayDJ extends FlxAtlasSprite function loadCartoon() { cartoonSnd = FunkinSound.load(Paths.sound(getRandomFlashToon()), 1.0, false, true, true, function() { - anim.play("Boyfriend DJ watchin tv OG", true, false, 60); + playAnimation("Boyfriend DJ watchin tv OG", true, false, false, 60); }); // Fade out music to 40% volume over 1 second. @@ -304,13 +309,13 @@ class FreeplayDJ extends FlxAtlasSprite public function pumpFist():Void { currentState = FistPump; - anim.play("Boyfriend DJ fist pump", true, false, 4); + playAnimation("Boyfriend DJ fist pump", true, false, false, 4); } public function pumpFistBad():Void { currentState = FistPump; - anim.play("Boyfriend DJ loss reaction 1", true, false, 4); + playAnimation("Boyfriend DJ loss reaction 1", true, false, false, 4); } override public function getCurrentAnimation():String @@ -319,9 +324,9 @@ class FreeplayDJ extends FlxAtlasSprite return this.anim.curSymbol.name; } - public function playFlashAnimation(id:String, ?Force:Bool = false, ?Reverse:Bool = false, ?Frame:Int = 0):Void + public function playFlashAnimation(id:String, Force:Bool = false, Reverse:Bool = false, Loop:Bool = false, Frame:Int = 0):Void { - anim.play(id, Force, Reverse, Frame); + playAnimation(id, Force, Reverse, Loop, Frame); applyAnimOffset(); } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index dc42bd6510..284f41bb6c 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1792,7 +1792,7 @@ class FreeplayState extends MusicBeatSubState confirmGlow.visible = true; confirmGlow2.visible = true; - backingTextYeah.anim.play("BF back card confirm raw", false, false, 0); + backingTextYeah.playAnimation("BF back card confirm raw", false, false, false, 0); confirmGlow2.alpha = 0; confirmGlow.alpha = 0; diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx index 2eec83223e..b4409d377b 100644 --- a/source/funkin/ui/freeplay/SongMenuItem.hx +++ b/source/funkin/ui/freeplay/SongMenuItem.hx @@ -162,7 +162,7 @@ class SongMenuItem extends FlxSpriteGroup sparkle = new FlxSprite(ranking.x, ranking.y); sparkle.frames = Paths.getSparrowAtlas('freeplay/sparkle'); - sparkle.animation.addByPrefix('sparkle', 'sparkle', 24, false); + sparkle.animation.addByPrefix('sparkle', 'sparkle Export0', 24, false); sparkle.animation.play('sparkle', true); sparkle.scale.set(0.8, 0.8); sparkle.blend = BlendMode.ADD; @@ -523,7 +523,6 @@ class SongMenuItem extends FlxSpriteGroup checkWeek(songData?.songId); } - var frameInTicker:Float = 0; var frameInTypeBeat:Int = 0; From 81680694e3a1bd7d020de70c9980736d2d5f5e97 Mon Sep 17 00:00:00 2001 From: Hundrec Date: Fri, 14 Jun 2024 21:52:52 -0700 Subject: [PATCH 122/469] Reorder download Git step in compiling guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved “download Git” from the middle of the guide to the setup step Should prevent errors with Git before installing Git --- docs/COMPILING.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index e7c19875a7..b8ddee4a70 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -2,14 +2,14 @@ 0. Setup - Download Haxe from [Haxe.org](https://haxe.org) + - Download Git from [git-scm.com](https://www.git-scm.com) 1. Cloning the Repository: Make sure when you clone, you clone the submodules to get the assets repo: - `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` - If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way. 2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`) -3. Download Git from [git-scm.com](https://www.git-scm.com) -4. Install all haxelibs of the current branch by running `hmm install` -5. Setup lime: `haxelib run lime setup` -6. Platform setup +3. Install all haxelibs of the current branch by running `hmm install` +4. Setup lime: `haxelib run lime setup` +5. Platform setup - For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe) - When prompted, select "Individual Components" and make sure to download the following: - MSVC v143 VS 2022 C++ x64/x86 build tools @@ -17,8 +17,8 @@ - Mac: [`lime setup mac` Documentation](https://lime.openfl.org/docs/advanced-setup/macos/) - Linux: [`lime setup linux` Documentation](https://lime.openfl.org/docs/advanced-setup/linux/) - HTML5: Compiles without any extra setup -7. If you are targeting for native, you may need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug` -8. `lime test PLATFORM` ! Add `-debug` to enable several debug features such as time travel (`PgUp`/`PgDn` in Play State). +6. If you are targeting for native, you may need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug` +7. `lime test PLATFORM` ! Add `-debug` to enable several debug features such as time travel (`PgUp`/`PgDn` in Play State). # Troubleshooting From 9f17478a3afd007e4d164adc14cf758675e620c6 Mon Sep 17 00:00:00 2001 From: Hundrec Date: Sat, 15 Jun 2024 13:37:21 -0400 Subject: [PATCH 123/469] Add ZIP button warning and restructured sentences Each step should be easier to follow with this structure --- docs/COMPILING.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index b8ddee4a70..b2a106c867 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -3,12 +3,13 @@ 0. Setup - Download Haxe from [Haxe.org](https://haxe.org) - Download Git from [git-scm.com](https://www.git-scm.com) -1. Cloning the Repository: Make sure when you clone, you clone the submodules to get the assets repo: - - `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` - - If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way. -2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`) -3. Install all haxelibs of the current branch by running `hmm install` -4. Setup lime: `haxelib run lime setup` + - Do NOT download the repository using the Download ZIP button on GitHub or you may run into errors! + - Instead, open a command prompt and do the following steps... +1. Run `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` to clone the repository with the necessary assets submodule + - _If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way._ +2. Run `haxelib --global install hmm` and then `haxelib --global run hmm setup` to install hmm.json +3. Run `hmm install` to install all haxelibs of the current branch +4. Run `haxelib run lime setup` to set up lime 5. Platform setup - For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe) - When prompted, select "Individual Components" and make sure to download the following: From 87d8d4043e39e2302479a6f384d8cd289e4c8b79 Mon Sep 17 00:00:00 2001 From: Eric Date: Sun, 16 Jun 2024 18:48:01 -0400 Subject: [PATCH 124/469] Make downloading the assets submodule a separate step. --- docs/COMPILING.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index b2a106c867..cc90bd348e 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -5,8 +5,9 @@ - Download Git from [git-scm.com](https://www.git-scm.com) - Do NOT download the repository using the Download ZIP button on GitHub or you may run into errors! - Instead, open a command prompt and do the following steps... -1. Run `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` to clone the repository with the necessary assets submodule - - _If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way._ +1. Run `git clone https://github.com/FunkinCrew/funkin.git` to clone the base repository. +2. Run `git submodule update --init --recursive` to download the game's assets. + - NOTE: By performing this operation, you are downloading Content which is proprietary and protected by national and international copyright and trademark laws. See [the LICENSE.md file for the Funkin.assets](https://github.com/FunkinCrew/funkin.assets/blob/main/LICENSE.md) repo for more information. 2. Run `haxelib --global install hmm` and then `haxelib --global run hmm setup` to install hmm.json 3. Run `hmm install` to install all haxelibs of the current branch 4. Run `haxelib run lime setup` to set up lime From 242ab6e922ede547bb6c6babefccd41ed0f10962 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 25 Jun 2024 18:22:30 -0400 Subject: [PATCH 125/469] Add change counts labels to Actions labeler --- .github/changed-lines-count-labeler.yml | 12 ++++++++++++ .github/workflows/labeler.yml | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .github/changed-lines-count-labeler.yml diff --git a/.github/changed-lines-count-labeler.yml b/.github/changed-lines-count-labeler.yml new file mode 100644 index 0000000000..6f890f5342 --- /dev/null +++ b/.github/changed-lines-count-labeler.yml @@ -0,0 +1,12 @@ +# Add 'small' to any changes below 10 lines +small: + max: 9 + +# Add 'medium' to any changes between 10 and 100 lines +medium: + min: 10 + max: 99 + +# Add 'large' to any changes for more than 100 lines +large: + min: 100 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 0bcc420d34..a861af5781 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -9,6 +9,19 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v5 + - name: Set basic labels + uses: actions/labeler@v5 with: sync-labels: true + changed-lines-count-labeler: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + name: An action for automatically labelling pull requests based on the changed lines count + steps: + - name: Set change count labels + uses: vkirilichev/changed-lines-count-labeler@v0.2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + configuration-path: .github/changed-lines-count-labeler.yml From 34a19ef5832aa98c9edae98823faf2537f2abc59 Mon Sep 17 00:00:00 2001 From: AppleHair Date: Tue, 9 Jul 2024 19:59:31 +0300 Subject: [PATCH 126/469] [BUGFIX] Fixed `cancelMenu` sound not playing after switching state. --- source/funkin/ui/mainmenu/MainMenuState.hx | 2 +- source/funkin/ui/options/OptionsState.hx | 2 +- source/funkin/ui/story/StoryMenuState.hx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 1dcf577439..d219bfbcfa 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -412,8 +412,8 @@ class MainMenuState extends MusicBeatState if (controls.BACK && menuItems.enabled && !menuItems.busy) { - FunkinSound.playOnce(Paths.sound('cancelMenu')); FlxG.switchState(() -> new TitleState()); + FunkinSound.playOnce(Paths.sound('cancelMenu')); } } } diff --git a/source/funkin/ui/options/OptionsState.hx b/source/funkin/ui/options/OptionsState.hx index 40308d96bc..a2301e6a36 100644 --- a/source/funkin/ui/options/OptionsState.hx +++ b/source/funkin/ui/options/OptionsState.hx @@ -145,8 +145,8 @@ class Page extends FlxGroup { if (canExit && controls.BACK) { - FunkinSound.playOnce(Paths.sound('cancelMenu')); exit(); + FunkinSound.playOnce(Paths.sound('cancelMenu')); } } diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 4da467db72..4e51fb229e 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -390,9 +390,9 @@ class StoryMenuState extends MusicBeatState if (controls.BACK && !exitingMenu && !selectedLevel) { - FunkinSound.playOnce(Paths.sound('cancelMenu')); exitingMenu = true; FlxG.switchState(() -> new MainMenuState()); + FunkinSound.playOnce(Paths.sound('cancelMenu')); } } From 2d5be92882698336194696a2f20ee1984a756aa5 Mon Sep 17 00:00:00 2001 From: Hundrec Date: Fri, 14 Jun 2024 21:52:52 -0700 Subject: [PATCH 127/469] Reorder download Git step in compiling guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved “download Git” from the middle of the guide to the setup step Should prevent errors with Git before installing Git --- docs/COMPILING.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index e7c19875a7..b8ddee4a70 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -2,14 +2,14 @@ 0. Setup - Download Haxe from [Haxe.org](https://haxe.org) + - Download Git from [git-scm.com](https://www.git-scm.com) 1. Cloning the Repository: Make sure when you clone, you clone the submodules to get the assets repo: - `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` - If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way. 2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`) -3. Download Git from [git-scm.com](https://www.git-scm.com) -4. Install all haxelibs of the current branch by running `hmm install` -5. Setup lime: `haxelib run lime setup` -6. Platform setup +3. Install all haxelibs of the current branch by running `hmm install` +4. Setup lime: `haxelib run lime setup` +5. Platform setup - For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe) - When prompted, select "Individual Components" and make sure to download the following: - MSVC v143 VS 2022 C++ x64/x86 build tools @@ -17,8 +17,8 @@ - Mac: [`lime setup mac` Documentation](https://lime.openfl.org/docs/advanced-setup/macos/) - Linux: [`lime setup linux` Documentation](https://lime.openfl.org/docs/advanced-setup/linux/) - HTML5: Compiles without any extra setup -7. If you are targeting for native, you may need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug` -8. `lime test PLATFORM` ! Add `-debug` to enable several debug features such as time travel (`PgUp`/`PgDn` in Play State). +6. If you are targeting for native, you may need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug` +7. `lime test PLATFORM` ! Add `-debug` to enable several debug features such as time travel (`PgUp`/`PgDn` in Play State). # Troubleshooting From 33bfe34f098cd7e7f0d6e7c8af6f9b6161344537 Mon Sep 17 00:00:00 2001 From: Hundrec Date: Sat, 15 Jun 2024 13:37:21 -0400 Subject: [PATCH 128/469] Add ZIP button warning and restructured sentences Each step should be easier to follow with this structure --- docs/COMPILING.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index b8ddee4a70..b2a106c867 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -3,12 +3,13 @@ 0. Setup - Download Haxe from [Haxe.org](https://haxe.org) - Download Git from [git-scm.com](https://www.git-scm.com) -1. Cloning the Repository: Make sure when you clone, you clone the submodules to get the assets repo: - - `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` - - If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way. -2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`) -3. Install all haxelibs of the current branch by running `hmm install` -4. Setup lime: `haxelib run lime setup` + - Do NOT download the repository using the Download ZIP button on GitHub or you may run into errors! + - Instead, open a command prompt and do the following steps... +1. Run `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` to clone the repository with the necessary assets submodule + - _If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way._ +2. Run `haxelib --global install hmm` and then `haxelib --global run hmm setup` to install hmm.json +3. Run `hmm install` to install all haxelibs of the current branch +4. Run `haxelib run lime setup` to set up lime 5. Platform setup - For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe) - When prompted, select "Individual Components" and make sure to download the following: From abf898ff01a5981778143a1aed75264d7d7bdcdb Mon Sep 17 00:00:00 2001 From: Eric Date: Sun, 16 Jun 2024 18:48:01 -0400 Subject: [PATCH 129/469] Make downloading the assets submodule a separate step. --- docs/COMPILING.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index b2a106c867..cc90bd348e 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -5,8 +5,9 @@ - Download Git from [git-scm.com](https://www.git-scm.com) - Do NOT download the repository using the Download ZIP button on GitHub or you may run into errors! - Instead, open a command prompt and do the following steps... -1. Run `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` to clone the repository with the necessary assets submodule - - _If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way._ +1. Run `git clone https://github.com/FunkinCrew/funkin.git` to clone the base repository. +2. Run `git submodule update --init --recursive` to download the game's assets. + - NOTE: By performing this operation, you are downloading Content which is proprietary and protected by national and international copyright and trademark laws. See [the LICENSE.md file for the Funkin.assets](https://github.com/FunkinCrew/funkin.assets/blob/main/LICENSE.md) repo for more information. 2. Run `haxelib --global install hmm` and then `haxelib --global run hmm setup` to install hmm.json 3. Run `hmm install` to install all haxelibs of the current branch 4. Run `haxelib run lime setup` to set up lime From 49b7876b5a60cf5627fd137a75c773c508aa4513 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 25 Jun 2024 18:22:30 -0400 Subject: [PATCH 130/469] Add change counts labels to Actions labeler --- .github/changed-lines-count-labeler.yml | 12 ++++++++++++ .github/workflows/labeler.yml | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .github/changed-lines-count-labeler.yml diff --git a/.github/changed-lines-count-labeler.yml b/.github/changed-lines-count-labeler.yml new file mode 100644 index 0000000000..6f890f5342 --- /dev/null +++ b/.github/changed-lines-count-labeler.yml @@ -0,0 +1,12 @@ +# Add 'small' to any changes below 10 lines +small: + max: 9 + +# Add 'medium' to any changes between 10 and 100 lines +medium: + min: 10 + max: 99 + +# Add 'large' to any changes for more than 100 lines +large: + min: 100 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 0bcc420d34..a861af5781 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -9,6 +9,19 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v5 + - name: Set basic labels + uses: actions/labeler@v5 with: sync-labels: true + changed-lines-count-labeler: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + name: An action for automatically labelling pull requests based on the changed lines count + steps: + - name: Set change count labels + uses: vkirilichev/changed-lines-count-labeler@v0.2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + configuration-path: .github/changed-lines-count-labeler.yml From 8107d1a29096467acf207d4b12760c41bd17495c Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Mon, 8 Jul 2024 03:17:28 +0200 Subject: [PATCH 131/469] Remove SustainTrail forced alpha --- source/funkin/play/notes/Strumline.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index 3c114b5e0d..fdb32bb857 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -598,7 +598,6 @@ class Strumline extends FlxSpriteGroup { note.holdNoteSprite.hitNote = true; note.holdNoteSprite.missedNote = false; - note.holdNoteSprite.alpha = 1.0; note.holdNoteSprite.sustainLength = (note.holdNoteSprite.strumTime + note.holdNoteSprite.fullSustainLength) - conductorInUse.songPosition; } From 2fb55ba3edf4e84a46dc6e451922af0a2ca63326 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Thu, 11 Jul 2024 06:59:22 +0200 Subject: [PATCH 132/469] Fix default health icon not used --- source/funkin/play/components/HealthIcon.hx | 29 +++++++-------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/source/funkin/play/components/HealthIcon.hx b/source/funkin/play/components/HealthIcon.hx index 2442b0dc57..682334db30 100644 --- a/source/funkin/play/components/HealthIcon.hx +++ b/source/funkin/play/components/HealthIcon.hx @@ -33,7 +33,7 @@ class HealthIcon extends FunkinSprite * The character this icon is representing. * Setting this variable will automatically update the graphic. */ - public var characterId(default, set):Null; + public var characterId(default, set):String = Constants.DEFAULT_HEALTH_ICON; /** * Whether this health icon should automatically update its state based on the character's health. @@ -116,7 +116,7 @@ class HealthIcon extends FunkinSprite */ static final POSITION_OFFSET:Int = 26; - public function new(char:String = 'bf', playerId:Int = 0) + public function new(char:Null, playerId:Int = 0) { super(0, 0); this.playerId = playerId; @@ -127,7 +127,7 @@ class HealthIcon extends FunkinSprite initTargetSize(); } - function set_characterId(value:Null):Null + function set_characterId(value:Null):String { if (value == characterId) return value; @@ -380,20 +380,9 @@ class HealthIcon extends FunkinSprite } } - function correctCharacterId(charId:Null):String + function iconExists(charId:String):Bool { - if (charId == null) - { - return Constants.DEFAULT_HEALTH_ICON; - } - - if (!Assets.exists(Paths.image('icons/icon-$charId'))) - { - FlxG.log.warn('No icon for character: $charId : using default placeholder face instead!'); - return Constants.DEFAULT_HEALTH_ICON; - } - - return charId; + return Assets.exists(Paths.image('icons/icon-$charId')); } function isNewSpritesheet(charId:String):Bool @@ -403,11 +392,11 @@ class HealthIcon extends FunkinSprite function loadCharacter(charId:Null):Void { - if (charId == null || correctCharacterId(charId) != charId) + if (charId == null || !iconExists(charId)) { - // This will recursively trigger loadCharacter to be called again. - characterId = correctCharacterId(charId); - return; + FlxG.log.warn('No icon for character: $charId : using default placeholder face instead!'); + characterId = Constants.DEFAULT_HEALTH_ICON; + charId = characterId; } isLegacyStyle = !isNewSpritesheet(charId); From 4044a7d9360a391c59878bbca65ab65e5b69e16d Mon Sep 17 00:00:00 2001 From: Hundrec Date: Fri, 14 Jun 2024 21:52:52 -0700 Subject: [PATCH 133/469] Reorder download Git step in compiling guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved “download Git” from the middle of the guide to the setup step Should prevent errors with Git before installing Git --- docs/COMPILING.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index e7c19875a7..b8ddee4a70 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -2,14 +2,14 @@ 0. Setup - Download Haxe from [Haxe.org](https://haxe.org) + - Download Git from [git-scm.com](https://www.git-scm.com) 1. Cloning the Repository: Make sure when you clone, you clone the submodules to get the assets repo: - `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` - If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way. 2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`) -3. Download Git from [git-scm.com](https://www.git-scm.com) -4. Install all haxelibs of the current branch by running `hmm install` -5. Setup lime: `haxelib run lime setup` -6. Platform setup +3. Install all haxelibs of the current branch by running `hmm install` +4. Setup lime: `haxelib run lime setup` +5. Platform setup - For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe) - When prompted, select "Individual Components" and make sure to download the following: - MSVC v143 VS 2022 C++ x64/x86 build tools @@ -17,8 +17,8 @@ - Mac: [`lime setup mac` Documentation](https://lime.openfl.org/docs/advanced-setup/macos/) - Linux: [`lime setup linux` Documentation](https://lime.openfl.org/docs/advanced-setup/linux/) - HTML5: Compiles without any extra setup -7. If you are targeting for native, you may need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug` -8. `lime test PLATFORM` ! Add `-debug` to enable several debug features such as time travel (`PgUp`/`PgDn` in Play State). +6. If you are targeting for native, you may need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug` +7. `lime test PLATFORM` ! Add `-debug` to enable several debug features such as time travel (`PgUp`/`PgDn` in Play State). # Troubleshooting From 562136ed554d73e04a015c32f9cb34b790b9cc07 Mon Sep 17 00:00:00 2001 From: Hundrec Date: Sat, 15 Jun 2024 13:37:21 -0400 Subject: [PATCH 134/469] Add ZIP button warning and restructured sentences Each step should be easier to follow with this structure --- docs/COMPILING.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index b8ddee4a70..b2a106c867 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -3,12 +3,13 @@ 0. Setup - Download Haxe from [Haxe.org](https://haxe.org) - Download Git from [git-scm.com](https://www.git-scm.com) -1. Cloning the Repository: Make sure when you clone, you clone the submodules to get the assets repo: - - `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` - - If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way. -2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`) -3. Install all haxelibs of the current branch by running `hmm install` -4. Setup lime: `haxelib run lime setup` + - Do NOT download the repository using the Download ZIP button on GitHub or you may run into errors! + - Instead, open a command prompt and do the following steps... +1. Run `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` to clone the repository with the necessary assets submodule + - _If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way._ +2. Run `haxelib --global install hmm` and then `haxelib --global run hmm setup` to install hmm.json +3. Run `hmm install` to install all haxelibs of the current branch +4. Run `haxelib run lime setup` to set up lime 5. Platform setup - For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe) - When prompted, select "Individual Components" and make sure to download the following: From 6e65996180de85c42b74f6730773975f819e2efa Mon Sep 17 00:00:00 2001 From: Eric Date: Sun, 16 Jun 2024 18:48:01 -0400 Subject: [PATCH 135/469] Make downloading the assets submodule a separate step. --- docs/COMPILING.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index b2a106c867..cc90bd348e 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -5,8 +5,9 @@ - Download Git from [git-scm.com](https://www.git-scm.com) - Do NOT download the repository using the Download ZIP button on GitHub or you may run into errors! - Instead, open a command prompt and do the following steps... -1. Run `git clone --recurse-submodules https://github.com/FunkinCrew/funkin.git` to clone the repository with the necessary assets submodule - - _If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way._ +1. Run `git clone https://github.com/FunkinCrew/funkin.git` to clone the base repository. +2. Run `git submodule update --init --recursive` to download the game's assets. + - NOTE: By performing this operation, you are downloading Content which is proprietary and protected by national and international copyright and trademark laws. See [the LICENSE.md file for the Funkin.assets](https://github.com/FunkinCrew/funkin.assets/blob/main/LICENSE.md) repo for more information. 2. Run `haxelib --global install hmm` and then `haxelib --global run hmm setup` to install hmm.json 3. Run `hmm install` to install all haxelibs of the current branch 4. Run `haxelib run lime setup` to set up lime From 3f63bc35a4f27a51f07a5c1ac8053de2512d1757 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 25 Jun 2024 18:22:30 -0400 Subject: [PATCH 136/469] Add change counts labels to Actions labeler --- .github/changed-lines-count-labeler.yml | 12 ++++++++++++ .github/workflows/labeler.yml | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .github/changed-lines-count-labeler.yml diff --git a/.github/changed-lines-count-labeler.yml b/.github/changed-lines-count-labeler.yml new file mode 100644 index 0000000000..6f890f5342 --- /dev/null +++ b/.github/changed-lines-count-labeler.yml @@ -0,0 +1,12 @@ +# Add 'small' to any changes below 10 lines +small: + max: 9 + +# Add 'medium' to any changes between 10 and 100 lines +medium: + min: 10 + max: 99 + +# Add 'large' to any changes for more than 100 lines +large: + min: 100 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 0bcc420d34..a861af5781 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -9,6 +9,19 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v5 + - name: Set basic labels + uses: actions/labeler@v5 with: sync-labels: true + changed-lines-count-labeler: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + name: An action for automatically labelling pull requests based on the changed lines count + steps: + - name: Set change count labels + uses: vkirilichev/changed-lines-count-labeler@v0.2 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + configuration-path: .github/changed-lines-count-labeler.yml From 7b9e4a054284dbe6e69c1659e04571247276a599 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Wed, 10 Jul 2024 00:23:06 +0200 Subject: [PATCH 137/469] Fix F5 chart not reloading --- source/funkin/play/PlayState.hx | 59 ++----------------- source/funkin/ui/MusicBeatState.hx | 7 +-- source/funkin/ui/MusicBeatSubState.hx | 5 +- .../util/plugins/ReloadAssetsDebugPlugin.hx | 14 ++++- 4 files changed, 17 insertions(+), 68 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index a4723611e4..86e8e02418 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1347,64 +1347,13 @@ class PlayState extends MusicBeatSubState } /** - * Removes any references to the current stage, then clears the stage cache, - * then reloads all the stages. - * - * This is useful for when you want to edit a stage without reloading the whole game. - * Reloading works on both the JSON and the HXC, if applicable. - * * Call this by pressing F5 on a debug build. */ - override function debug_refreshModules():Void + override function reloadAssets():Void { - // Prevent further gameplay updates, which will try to reference dead objects. - criticalFailure = true; - - // Remove the current stage. If the stage gets deleted while it's still in use, - // it'll probably crash the game or something. - if (this.currentStage != null) - { - remove(currentStage); - var event:ScriptEvent = new ScriptEvent(DESTROY, false); - ScriptEventDispatcher.callEvent(currentStage, event); - currentStage = null; - } - - if (!overrideMusic) - { - // Stop the instrumental. - if (FlxG.sound.music != null) - { - FlxG.sound.music.destroy(); - FlxG.sound.music = null; - } - - // Stop the vocals. - if (vocals != null && vocals.exists) - { - vocals.destroy(); - vocals = null; - } - } - else - { - // Stop the instrumental. - if (FlxG.sound.music != null) - { - FlxG.sound.music.stop(); - } - - // Stop the vocals. - if (vocals != null && vocals.exists) - { - vocals.stop(); - } - } - - super.debug_refreshModules(); - - var event:ScriptEvent = new ScriptEvent(CREATE, false); - ScriptEventDispatcher.callEvent(currentSong, event); + funkin.modding.PolymodHandler.forceReloadAssets(); + lastParams.targetSong = SongRegistry.instance.fetchEntry(currentSong.id); + LoadingState.loadPlayState(lastParams); } override function stepHit():Bool diff --git a/source/funkin/ui/MusicBeatState.hx b/source/funkin/ui/MusicBeatState.hx index 92169df751..8668b64c12 100644 --- a/source/funkin/ui/MusicBeatState.hx +++ b/source/funkin/ui/MusicBeatState.hx @@ -78,9 +78,6 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler { // Emergency exit button. if (FlxG.keys.justPressed.F4) FlxG.switchState(() -> new MainMenuState()); - - // This can now be used in EVERY STATE YAY! - if (FlxG.keys.justPressed.F5) debug_refreshModules(); } override function update(elapsed:Float) @@ -114,12 +111,10 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler ModuleHandler.callEvent(event); } - function debug_refreshModules() + function reloadAssets() { PolymodHandler.forceReloadAssets(); - this.destroy(); - // Create a new instance of the current state, so old data is cleared. FlxG.resetState(); } diff --git a/source/funkin/ui/MusicBeatSubState.hx b/source/funkin/ui/MusicBeatSubState.hx index 9035d12ff3..5c40b37bcb 100644 --- a/source/funkin/ui/MusicBeatSubState.hx +++ b/source/funkin/ui/MusicBeatSubState.hx @@ -72,9 +72,6 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler // Emergency exit button. if (FlxG.keys.justPressed.F4) FlxG.switchState(() -> new MainMenuState()); - // This can now be used in EVERY STATE YAY! - if (FlxG.keys.justPressed.F5) debug_refreshModules(); - // Display Conductor info in the watch window. FlxG.watch.addQuick("musicTime", FlxG.sound.music?.time ?? 0.0); Conductor.watchQuick(conductorInUse); @@ -82,7 +79,7 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler dispatchEvent(new UpdateScriptEvent(elapsed)); } - function debug_refreshModules() + function reloadAssets() { PolymodHandler.forceReloadAssets(); diff --git a/source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx b/source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx index f696095311..0e1e238ac2 100644 --- a/source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx +++ b/source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx @@ -1,6 +1,9 @@ package funkin.util.plugins; +import flixel.FlxG; import flixel.FlxBasic; +import funkin.ui.MusicBeatState; +import funkin.ui.MusicBeatSubState; /** * A plugin which adds functionality to press `F5` to reload all game assets, then reload the current state. @@ -28,10 +31,15 @@ class ReloadAssetsDebugPlugin extends FlxBasic if (FlxG.keys.justPressed.F5) #end { - funkin.modding.PolymodHandler.forceReloadAssets(); + var state:Dynamic = FlxG.state; + if (state is MusicBeatState || state is MusicBeatSubState) state.reloadAssets(); + else + { + funkin.modding.PolymodHandler.forceReloadAssets(); - // Create a new instance of the current state, so old data is cleared. - FlxG.resetState(); + // Create a new instance of the current state, so old data is cleared. + FlxG.resetState(); + } } } From a12b0e6ec36c45f0ec2c1516826cdfde2b8899e6 Mon Sep 17 00:00:00 2001 From: anysad Date: Thu, 11 Jul 2024 18:10:45 +0300 Subject: [PATCH 138/469] Add HEY! song events to Tutorial --- source/funkin/play/PlayState.hx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index f55cef3888..ff55a79fdc 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1489,7 +1489,7 @@ class PlayState extends MusicBeatSubState if (opponentStrumline != null) opponentStrumline.onBeatHit(); // Make the characters dance on the beat - danceOnBeat(); + //danceOnBeat(); return true; } @@ -1509,16 +1509,6 @@ class PlayState extends MusicBeatSubState function danceOnBeat():Void { if (currentStage == null) return; - - // TODO: Add HEY! song events to Tutorial. - if (Conductor.instance.currentBeat % 16 == 15 - && currentStage.getDad().characterId == 'gf' - && Conductor.instance.currentBeat > 16 - && Conductor.instance.currentBeat < 48) - { - currentStage.getBoyfriend().playAnimation('hey', true); - currentStage.getDad().playAnimation('cheer', true); - } } /** From 47a2cf4e5db9a36fb39e49500d85d2aca3961a06 Mon Sep 17 00:00:00 2001 From: anysad Date: Thu, 11 Jul 2024 18:12:50 +0300 Subject: [PATCH 139/469] Add HEY! song events to Tutorial --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 2e1594ee4c..9ef18cde0b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 2e1594ee4c04c7148628bae471bdd061c9deb6b7 +Subproject commit 9ef18cde0b9802295118b922b76b851e96af2fbf From cfb2cf36de0eb77f14412506f090cf16250ecdc2 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 11 Jul 2024 18:38:29 -0400 Subject: [PATCH 140/469] assets submod merge --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 1c9c0986e5..e0d719c18f 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 1c9c0986e5eb0a7b5faa81ecacc158d20429720d +Subproject commit e0d719c18f87c762e09b054123bb99e4b5f7fcd3 From 66b103c65030706d7a6b3f586df5026a19314450 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 11 Jul 2024 19:34:34 -0400 Subject: [PATCH 141/469] [PUBLIC PR] Remove 5 duplicate audio files from the DadBattle folder. (#657) * Remove 5 duplicate audio files from the DadBattle folder. * assets submod merge --------- Co-authored-by: Cameron Taylor --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index e0d719c18f..361f696cec 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit e0d719c18f87c762e09b054123bb99e4b5f7fcd3 +Subproject commit 361f696cec5c4027ebcfa6f7cec5ba718eaab0d2 From 71947c581e78891233dd2c97cbe60d5cdc23d8b5 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 11 Jul 2024 19:35:41 -0400 Subject: [PATCH 142/469] [PUBLIC PR] Fix a bug where the memory counter would overflow into the negatives (#662) * get Float instead of Int in cpp (Float is 64-bit in so it works easier than Int64) * use Math.fround instead of Math.round --------- Co-authored-by: Karim Akra <144803230+KarimAkra@users.noreply.github.com> --- source/funkin/ui/debug/MemoryCounter.hx | 2 +- source/funkin/util/MemoryUtil.hx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/funkin/ui/debug/MemoryCounter.hx b/source/funkin/ui/debug/MemoryCounter.hx index b25b556451..50421f3987 100644 --- a/source/funkin/ui/debug/MemoryCounter.hx +++ b/source/funkin/ui/debug/MemoryCounter.hx @@ -36,7 +36,7 @@ class MemoryCounter extends TextField @:noCompletion #if !flash override #end function __enterFrame(deltaTime:Float):Void { - var mem:Float = Math.round(MemoryUtil.getMemoryUsed() / BYTES_PER_MEG / ROUND_TO) * ROUND_TO; + var mem:Float = Math.fround(MemoryUtil.getMemoryUsed() / BYTES_PER_MEG / ROUND_TO) * ROUND_TO; if (mem > memPeak) memPeak = mem; diff --git a/source/funkin/util/MemoryUtil.hx b/source/funkin/util/MemoryUtil.hx index f5935ed672..18fd41472c 100644 --- a/source/funkin/util/MemoryUtil.hx +++ b/source/funkin/util/MemoryUtil.hx @@ -48,11 +48,11 @@ class MemoryUtil * Calculate the total memory usage of the program, in bytes. * @return Int */ - public static function getMemoryUsed():Int + public static function getMemoryUsed():#if cpp Float #else Int #end { #if cpp // There is also Gc.MEM_INFO_RESERVED, MEM_INFO_CURRENT, and MEM_INFO_LARGE. - return cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_USAGE); + return cpp.vm.Gc.memInfo64(cpp.vm.Gc.MEM_INFO_USAGE); #else return openfl.system.System.totalMemory; #end From 261d68c410777e97a44b4ba13ca0495957765c13 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 11 Jul 2024 19:51:00 -0400 Subject: [PATCH 143/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 240d0ff1da..005c96f85f 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 240d0ff1dafd616551e6b6a2b048b7b6f602ac75 +Subproject commit 005c96f85f4304865acb196e7cc4d6d83f9d76d8 From 18b795d4f74e81e8f9bbabd3e3e3e0fe7930a167 Mon Sep 17 00:00:00 2001 From: anysad Date: Thu, 11 Jul 2024 18:10:45 +0300 Subject: [PATCH 144/469] Add HEY! song events to Tutorial --- source/funkin/play/PlayState.hx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 8d7d82aabe..216acca715 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1502,7 +1502,7 @@ class PlayState extends MusicBeatSubState if (opponentStrumline != null) opponentStrumline.onBeatHit(); // Make the characters dance on the beat - danceOnBeat(); + //danceOnBeat(); return true; } @@ -1522,16 +1522,6 @@ class PlayState extends MusicBeatSubState function danceOnBeat():Void { if (currentStage == null) return; - - // TODO: Add HEY! song events to Tutorial. - if (Conductor.instance.currentBeat % 16 == 15 - && currentStage.getDad().characterId == 'gf' - && Conductor.instance.currentBeat > 16 - && Conductor.instance.currentBeat < 48) - { - currentStage.getBoyfriend().playAnimation('hey', true); - currentStage.getDad().playAnimation('cheer', true); - } } /** From 305ab3146fe8891e925d34f5f2aafd9006ce1288 Mon Sep 17 00:00:00 2001 From: anysad Date: Thu, 11 Jul 2024 18:12:50 +0300 Subject: [PATCH 145/469] Add HEY! song events to Tutorial --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 005c96f85f..81e61c2876 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 005c96f85f4304865acb196e7cc4d6d83f9d76d8 +Subproject commit 81e61c287670f6aa8b7faf2c27561df57361f1ad From 4a7545a0dbdf7e8e1f1acca59940c87d59151646 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 11 Jul 2024 21:45:06 -0400 Subject: [PATCH 146/469] Remove empty function. --- source/funkin/play/PlayState.hx | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 216acca715..309c21438c 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1501,9 +1501,6 @@ class PlayState extends MusicBeatSubState if (playerStrumline != null) playerStrumline.onBeatHit(); if (opponentStrumline != null) opponentStrumline.onBeatHit(); - // Make the characters dance on the beat - //danceOnBeat(); - return true; } @@ -1514,16 +1511,6 @@ class PlayState extends MusicBeatSubState super.destroy(); } - /** - * Handles characters dancing to the beat of the current song. - * - * TODO: Move some of this logic into `Bopper.hx`, or individual character scripts. - */ - function danceOnBeat():Void - { - if (currentStage == null) return; - } - /** * Initializes the game and HUD cameras. */ From 90155bcfbfa86561571b7c556b1f63486c18877d Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Wed, 10 Jul 2024 01:26:16 +0200 Subject: [PATCH 147/469] ChartEditor Live Input Code Refactor + 6 key fix --- .../ui/debug/charting/ChartEditorState.hx | 59 ++++++------------- 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index f72cca77f6..5e74938402 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -282,6 +282,21 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ public static final WELCOME_MUSIC_FADE_IN_DURATION:Float = 10.0; + /** + * A map of the keys for every live input style. + */ + public static final LIVE_INPUT_KEYS:Map> = [ + NumberKeys => [ + FIVE, SIX, SEVEN, EIGHT, + ONE, TWO, THREE, FOUR + ], + WASDKeys => [ + LEFT, DOWN, UP, RIGHT, + A, S, W, D + ], + None => [] + ]; + /** * INSTANCE DATA */ @@ -5129,46 +5144,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState function handlePlayhead():Void { // Place notes at the playhead with the keyboard. - switch (currentLiveInputStyle) - { - case ChartEditorLiveInputStyle.WASDKeys: - if (FlxG.keys.justPressed.A) placeNoteAtPlayhead(4); - if (FlxG.keys.justReleased.A) finishPlaceNoteAtPlayhead(4); - if (FlxG.keys.justPressed.S) placeNoteAtPlayhead(5); - if (FlxG.keys.justReleased.S) finishPlaceNoteAtPlayhead(5); - if (FlxG.keys.justPressed.W) placeNoteAtPlayhead(6); - if (FlxG.keys.justReleased.W) finishPlaceNoteAtPlayhead(6); - if (FlxG.keys.justPressed.D) placeNoteAtPlayhead(7); - if (FlxG.keys.justReleased.D) finishPlaceNoteAtPlayhead(7); - - if (FlxG.keys.justPressed.LEFT) placeNoteAtPlayhead(0); - if (FlxG.keys.justReleased.LEFT) finishPlaceNoteAtPlayhead(0); - if (FlxG.keys.justPressed.DOWN) placeNoteAtPlayhead(1); - if (FlxG.keys.justReleased.DOWN) finishPlaceNoteAtPlayhead(1); - if (FlxG.keys.justPressed.UP) placeNoteAtPlayhead(2); - if (FlxG.keys.justReleased.UP) finishPlaceNoteAtPlayhead(2); - if (FlxG.keys.justPressed.RIGHT) placeNoteAtPlayhead(3); - if (FlxG.keys.justReleased.RIGHT) finishPlaceNoteAtPlayhead(3); - case ChartEditorLiveInputStyle.NumberKeys: - // Flipped because Dad is on the left but represents data 0-3. - if (FlxG.keys.justPressed.ONE) placeNoteAtPlayhead(4); - if (FlxG.keys.justReleased.ONE) finishPlaceNoteAtPlayhead(4); - if (FlxG.keys.justPressed.TWO) placeNoteAtPlayhead(5); - if (FlxG.keys.justReleased.TWO) finishPlaceNoteAtPlayhead(5); - if (FlxG.keys.justPressed.THREE) placeNoteAtPlayhead(6); - if (FlxG.keys.justReleased.THREE) finishPlaceNoteAtPlayhead(6); - if (FlxG.keys.justPressed.FOUR) placeNoteAtPlayhead(7); - if (FlxG.keys.justReleased.FOUR) finishPlaceNoteAtPlayhead(7); - - if (FlxG.keys.justPressed.FIVE) placeNoteAtPlayhead(0); - if (FlxG.keys.justReleased.FIVE) finishPlaceNoteAtPlayhead(0); - if (FlxG.keys.justPressed.SIX) placeNoteAtPlayhead(1); - if (FlxG.keys.justPressed.SEVEN) placeNoteAtPlayhead(2); - if (FlxG.keys.justReleased.SEVEN) finishPlaceNoteAtPlayhead(2); - if (FlxG.keys.justPressed.EIGHT) placeNoteAtPlayhead(3); - if (FlxG.keys.justReleased.EIGHT) finishPlaceNoteAtPlayhead(3); - case ChartEditorLiveInputStyle.None: - // Do nothing. + for (note => key in LIVE_INPUT_KEYS[currentLiveInputStyle]) + { + if (FlxG.keys.checkStatus(key, JUST_PRESSED)) placeNoteAtPlayhead(note) + else if (FlxG.keys.checkStatus(key, JUST_RELEASED)) finishPlaceNoteAtPlayhead(note); } // Place events at playhead. From 17f5a06256b34ff5224fde5b893cdac7c5086a5f Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Mon, 20 May 2024 02:56:57 +0200 Subject: [PATCH 148/469] Add camOther to fix zooms on pause and stickers --- source/funkin/play/PlayState.hx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 8d7d82aabe..e87fde90c0 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -503,9 +503,9 @@ class PlayState extends MusicBeatSubState public var camGame:FlxCamera; /** - * The camera which contains, and controls visibility of, a video cutscene. + * The camera which contains, and controls visibility of, a video cutscene, dialogue, pause menu and sticker transition. */ - public var camCutscene:FlxCamera; + public var camOther:FlxCamera; /** * The combo popups. Includes the real-time combo counter and the rating. @@ -975,7 +975,7 @@ class PlayState extends MusicBeatSubState FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - pauseSubState.camera = camHUD; + pauseSubState.camera = camOther; openSubState(pauseSubState); // boyfriendPos.put(); // TODO: Why is this here? } @@ -1543,12 +1543,12 @@ class PlayState extends MusicBeatSubState camGame.bgColor = BACKGROUND_COLOR; // Show a pink background behind the stage. camHUD = new FlxCamera(); camHUD.bgColor.alpha = 0; // Show the game scene behind the camera. - camCutscene = new FlxCamera(); - camCutscene.bgColor.alpha = 0; // Show the game scene behind the camera. + camOther = new FlxCamera(); + camOther.bgColor.alpha = 0; // Show the game scene behind the camera. FlxG.cameras.reset(camGame); FlxG.cameras.add(camHUD, false); - FlxG.cameras.add(camCutscene, false); + FlxG.cameras.add(camOther, false); // Configure camera follow point. if (previousCameraFollowPoint != null) @@ -1934,7 +1934,6 @@ class PlayState extends MusicBeatSubState if (!result) return; isInCutscene = false; - camCutscene.visible = false; // TODO: Maybe tween in the camera after any cutscenes. camHUD.visible = true; @@ -1953,7 +1952,7 @@ class PlayState extends MusicBeatSubState if (!currentConversation.alive) currentConversation.revive(); currentConversation.completeCallback = onConversationComplete; - currentConversation.cameras = [camCutscene]; + currentConversation.cameras = [camOther]; currentConversation.zIndex = 1000; add(currentConversation); refresh(); @@ -2788,7 +2787,7 @@ class PlayState extends MusicBeatSubState persistentUpdate = false; FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - pauseSubState.camera = camCutscene; + pauseSubState.camera = camOther; openSubState(pauseSubState); } } @@ -2804,7 +2803,7 @@ class PlayState extends MusicBeatSubState persistentUpdate = false; FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - pauseSubState.camera = camCutscene; + pauseSubState.camera = camOther; openSubState(pauseSubState); } } From e23e6c160d9ed9243a380c4b6842a059a3954768 Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Mon, 20 May 2024 23:52:45 +0200 Subject: [PATCH 149/469] Fix references to camCutscene --- source/funkin/play/cutscene/VideoCutscene.hx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index abbcd4f540..7dac978853 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -81,12 +81,11 @@ class VideoCutscene // Trigger the cutscene. Don't play the song in the background. PlayState.instance.isInCutscene = true; PlayState.instance.camHUD.visible = false; - PlayState.instance.camCutscene.visible = true; // Display a black screen to hide the game while the video is playing. blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK); blackScreen.scrollFactor.set(0, 0); - blackScreen.cameras = [PlayState.instance.camCutscene]; + blackScreen.cameras = [PlayState.instance.camOther]; PlayState.instance.add(blackScreen); VideoCutscene.cutsceneType = cutsceneType; @@ -120,7 +119,7 @@ class VideoCutscene vid.finishCallback = finishVideo.bind(0.5); - vid.cameras = [PlayState.instance.camCutscene]; + vid.cameras = [PlayState.instance.camOther]; PlayState.instance.add(vid); @@ -147,7 +146,7 @@ class VideoCutscene vid.bitmap.onEndReached.add(finishVideo.bind(0.5)); vid.autoPause = FlxG.autoPause; - vid.cameras = [PlayState.instance.camCutscene]; + vid.cameras = [PlayState.instance.camOther]; PlayState.instance.add(vid); @@ -305,7 +304,6 @@ class VideoCutscene vid = null; #end - PlayState.instance.camCutscene.visible = true; PlayState.instance.camHUD.visible = true; FlxTween.tween(blackScreen, {alpha: 0}, transitionTime, From e6c97678002e1b4ba823b71471ec0d6052d96c0a Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Wed, 12 Jun 2024 00:34:04 +0200 Subject: [PATCH 150/469] Revert camCutscene rename --- source/funkin/play/PlayState.hx | 16 ++++++++-------- source/funkin/play/cutscene/VideoCutscene.hx | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index e87fde90c0..0bb57b8cb3 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -505,7 +505,7 @@ class PlayState extends MusicBeatSubState /** * The camera which contains, and controls visibility of, a video cutscene, dialogue, pause menu and sticker transition. */ - public var camOther:FlxCamera; + public var camCutscene:FlxCamera; /** * The combo popups. Includes the real-time combo counter and the rating. @@ -975,7 +975,7 @@ class PlayState extends MusicBeatSubState FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - pauseSubState.camera = camOther; + pauseSubState.camera = camCutscene; openSubState(pauseSubState); // boyfriendPos.put(); // TODO: Why is this here? } @@ -1543,12 +1543,12 @@ class PlayState extends MusicBeatSubState camGame.bgColor = BACKGROUND_COLOR; // Show a pink background behind the stage. camHUD = new FlxCamera(); camHUD.bgColor.alpha = 0; // Show the game scene behind the camera. - camOther = new FlxCamera(); - camOther.bgColor.alpha = 0; // Show the game scene behind the camera. + camCutscene = new FlxCamera(); + camCutscene.bgColor.alpha = 0; // Show the game scene behind the camera. FlxG.cameras.reset(camGame); FlxG.cameras.add(camHUD, false); - FlxG.cameras.add(camOther, false); + FlxG.cameras.add(camCutscene, false); // Configure camera follow point. if (previousCameraFollowPoint != null) @@ -1952,7 +1952,7 @@ class PlayState extends MusicBeatSubState if (!currentConversation.alive) currentConversation.revive(); currentConversation.completeCallback = onConversationComplete; - currentConversation.cameras = [camOther]; + currentConversation.cameras = [camCutscene]; currentConversation.zIndex = 1000; add(currentConversation); refresh(); @@ -2787,7 +2787,7 @@ class PlayState extends MusicBeatSubState persistentUpdate = false; FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - pauseSubState.camera = camOther; + pauseSubState.camera = camCutscene; openSubState(pauseSubState); } } @@ -2803,7 +2803,7 @@ class PlayState extends MusicBeatSubState persistentUpdate = false; FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - pauseSubState.camera = camOther; + pauseSubState.camera = camCutscene; openSubState(pauseSubState); } } diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index 7dac978853..60454b8811 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -85,7 +85,7 @@ class VideoCutscene // Display a black screen to hide the game while the video is playing. blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK); blackScreen.scrollFactor.set(0, 0); - blackScreen.cameras = [PlayState.instance.camOther]; + blackScreen.cameras = [PlayState.instance.camCutscene]; PlayState.instance.add(blackScreen); VideoCutscene.cutsceneType = cutsceneType; @@ -119,7 +119,7 @@ class VideoCutscene vid.finishCallback = finishVideo.bind(0.5); - vid.cameras = [PlayState.instance.camOther]; + vid.cameras = [PlayState.instance.camCutscene]; PlayState.instance.add(vid); @@ -146,7 +146,7 @@ class VideoCutscene vid.bitmap.onEndReached.add(finishVideo.bind(0.5)); vid.autoPause = FlxG.autoPause; - vid.cameras = [PlayState.instance.camOther]; + vid.cameras = [PlayState.instance.camCutscene]; PlayState.instance.add(vid); From c3f8075b5f655b68e8dc7fb8615be3adc869ab7f Mon Sep 17 00:00:00 2001 From: gamerbross <55158797+gamerbross@users.noreply.github.com> Date: Sat, 15 Jun 2024 20:23:55 +0200 Subject: [PATCH 151/469] Few animation editor bugfixes --- hmm.json | 2 +- .../ui/debug/anim/DebugBoundingState.hx | 23 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/hmm.json b/hmm.json index 8eaf242122..324c6d2465 100644 --- a/hmm.json +++ b/hmm.json @@ -30,7 +30,7 @@ "name": "flixel-ui", "type": "git", "dir": null, - "ref": "719b4f10d94186ed55f6fef1b6618d32abec8c15", + "ref": "d0afed7293c71ffdb1184751317fc709b44c9056", "url": "https://github.com/HaxeFlixel/flixel-ui" }, { diff --git a/source/funkin/ui/debug/anim/DebugBoundingState.hx b/source/funkin/ui/debug/anim/DebugBoundingState.hx index 04784a5b79..d82bcc612f 100644 --- a/source/funkin/ui/debug/anim/DebugBoundingState.hx +++ b/source/funkin/ui/debug/anim/DebugBoundingState.hx @@ -1,6 +1,7 @@ package funkin.ui.debug.anim; import flixel.addons.display.FlxGridOverlay; +import flixel.addons.display.FlxBackdrop; import flixel.addons.ui.FlxInputText; import flixel.addons.ui.FlxUIDropDownMenu; import flixel.FlxCamera; @@ -55,7 +56,7 @@ class DebugBoundingState extends FlxState TODAY'S TO-DO - Cleaner UI */ - var bg:FlxSprite; + var bg:FlxBackdrop; var fileInfo:FlxText; var txtGrp:FlxGroup; @@ -80,7 +81,7 @@ class DebugBoundingState extends FlxState { // get the screen position, according to the HUD camera, temp default to FlxG.camera juuust in case? var hudMousePos:FlxPoint = FlxG.mouse.getScreenPosition(hudCam ?? FlxG.camera); - return Screen.instance.hasSolidComponentUnderPoint(hudMousePos.x, hudMousePos.y); + return Screen.instance.hasSolidComponentUnderPoint(hudMousePos.x, hudMousePos.y) || FlxG.mouse.overlaps(animDropDownMenu, hudCam); } override function create() @@ -103,10 +104,7 @@ class DebugBoundingState extends FlxState hudCam = new FlxCamera(); hudCam.bgColor.alpha = 0; - bg = FlxGridOverlay.create(10, 10); - // bg = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.GREEN); - - bg.scrollFactor.set(); + bg = new FlxBackdrop(FlxGridOverlay.createGrid(10, 10, FlxG.width, FlxG.height, true, 0xffe7e6e6, 0xffd9d5d5)); add(bg); // we are setting this as the default draw camera only temporarily, to trick haxeui @@ -289,16 +287,20 @@ class DebugBoundingState extends FlxState public var mouseOffset:FlxPoint = FlxPoint.get(0, 0); public var oldPos:FlxPoint = FlxPoint.get(0, 0); + public var movingCharacter:Bool = false; function mouseOffsetMovement() { if (swagChar != null) { - if (FlxG.mouse.justPressed) + if (FlxG.mouse.justPressed && !haxeUIFocused) { + movingCharacter = true; mouseOffset.set(FlxG.mouse.x - -swagChar.animOffsets[0], FlxG.mouse.y - -swagChar.animOffsets[1]); } + if (!movingCharacter) return; + if (FlxG.mouse.pressed) { swagChar.animOffsets = [(FlxG.mouse.x - mouseOffset.x) * -1, (FlxG.mouse.y - mouseOffset.y) * -1]; @@ -307,6 +309,11 @@ class DebugBoundingState extends FlxState txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets; } + + if (FlxG.mouse.justReleased) + { + movingCharacter = false; + } } } @@ -373,7 +380,7 @@ class DebugBoundingState extends FlxState offsetView.visible = true; offsetView.active = true; offsetControls(); - if (!haxeUIFocused) mouseOffsetMovement(); + mouseOffsetMovement(); } if (FlxG.keys.justPressed.H) hudCam.visible = !hudCam.visible; From 03d1f858ec19a8025cbcf3e63f7bf6e23c8ef46b Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 12 Jul 2024 00:57:08 -0400 Subject: [PATCH 152/469] Remove flixel-ui and add a new dropdown to the animation offset editor --- Project.xml | 3 - assets | 2 +- hmm.json | 9 +- .../funkin/modding/base/ScriptedFlxUIState.hx | 8 - .../ui/debug/anim/DebugBoundingState.hx | 162 +++++++++++------- source/funkin/ui/freeplay/FreeplayState.hx | 1 - 6 files changed, 101 insertions(+), 84 deletions(-) delete mode 100644 source/funkin/modding/base/ScriptedFlxUIState.hx diff --git a/Project.xml b/Project.xml index 8eb62bb1dd..656590aec9 100644 --- a/Project.xml +++ b/Project.xml @@ -114,11 +114,8 @@ xsi:schemaLocation="http://lime.openfl.org/project/1.0.4 http://lime.openfl.org/ - - - diff --git a/assets b/assets index 005c96f85f..f3231b1404 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 005c96f85f4304865acb196e7cc4d6d83f9d76d8 +Subproject commit f3231b1404f733c909f970d639fb11c56a7ca7f0 diff --git a/hmm.json b/hmm.json index 324c6d2465..d7171dfb2a 100644 --- a/hmm.json +++ b/hmm.json @@ -23,15 +23,10 @@ }, { "name": "flixel-text-input", - "type": "haxelib", - "version": "1.1.0" - }, - { - "name": "flixel-ui", "type": "git", "dir": null, - "ref": "d0afed7293c71ffdb1184751317fc709b44c9056", - "url": "https://github.com/HaxeFlixel/flixel-ui" + "ref": "951a0103a17bfa55eed86703ce50b4fb0d7590bc", + "url": "https://github.com/Starmapo/flixel-text-input" }, { "name": "flxanimate", diff --git a/source/funkin/modding/base/ScriptedFlxUIState.hx b/source/funkin/modding/base/ScriptedFlxUIState.hx deleted file mode 100644 index c58fc294fd..0000000000 --- a/source/funkin/modding/base/ScriptedFlxUIState.hx +++ /dev/null @@ -1,8 +0,0 @@ -package funkin.modding.base; - -/** - * A script that can be tied to an FlxUIState. - * Create a scripted class that extends FlxUIState to use this. - */ -@:hscriptClass -class ScriptedFlxUIState extends flixel.addons.ui.FlxUIState implements HScriptedClass {} diff --git a/source/funkin/ui/debug/anim/DebugBoundingState.hx b/source/funkin/ui/debug/anim/DebugBoundingState.hx index d82bcc612f..ed2f5870f3 100644 --- a/source/funkin/ui/debug/anim/DebugBoundingState.hx +++ b/source/funkin/ui/debug/anim/DebugBoundingState.hx @@ -1,9 +1,7 @@ package funkin.ui.debug.anim; -import flixel.addons.display.FlxGridOverlay; import flixel.addons.display.FlxBackdrop; -import flixel.addons.ui.FlxInputText; -import flixel.addons.ui.FlxUIDropDownMenu; +import flixel.addons.display.FlxGridOverlay; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.FlxState; @@ -26,6 +24,7 @@ import funkin.util.MouseUtil; import funkin.util.SerializerUtil; import funkin.util.SortUtil; import haxe.ui.components.DropDown; +import haxe.ui.containers.dialogs.CollapsibleDialog; import haxe.ui.core.Component; import haxe.ui.core.Screen; import haxe.ui.events.ItemEvent; @@ -59,7 +58,7 @@ class DebugBoundingState extends FlxState var bg:FlxBackdrop; var fileInfo:FlxText; - var txtGrp:FlxGroup; + var txtGrp:FlxTypedGroup; var hudCam:FlxCamera; @@ -67,40 +66,34 @@ class DebugBoundingState extends FlxState var spriteSheetView:FlxGroup; var offsetView:FlxGroup; - var animDropDownMenu:FlxUIDropDownMenu; var dropDownSetup:Bool = false; var onionSkinChar:FlxSprite; var txtOffsetShit:FlxText; - var uiStuff:Component; + var offsetEditorDialog:CollapsibleDialog; + var offsetAnimationDropdown:DropDown; var haxeUIFocused(get, default):Bool = false; + var currentAnimationName(get, never):String; + + function get_currentAnimationName():String + { + return offsetAnimationDropdown?.value?.id ?? "idle"; + } + function get_haxeUIFocused():Bool { // get the screen position, according to the HUD camera, temp default to FlxG.camera juuust in case? var hudMousePos:FlxPoint = FlxG.mouse.getScreenPosition(hudCam ?? FlxG.camera); - return Screen.instance.hasSolidComponentUnderPoint(hudMousePos.x, hudMousePos.y) || FlxG.mouse.overlaps(animDropDownMenu, hudCam); + return Screen.instance.hasSolidComponentUnderPoint(hudMousePos.x, hudMousePos.y); } override function create() { Paths.setCurrentLevel('week1'); - // lv. - // lv.onChange = function(e:UIEvent) - // { - // trace(e.type); - // // trace(e.data.curView); - // // var item:haxe.ui.core.ItemRenderer = cast e.target; - // trace(e.target); - // // if (e.type == "change") - // // { - // // curView = cast e.data; - // // } - // }; - hudCam = new FlxCamera(); hudCam.bgColor.alpha = 0; @@ -111,20 +104,25 @@ class DebugBoundingState extends FlxState FlxG.cameras.add(hudCam); var str = Paths.xml('ui/animation-editor/offset-editor-view'); - uiStuff = RuntimeComponentBuilder.fromAsset(str); + offsetEditorDialog = cast RuntimeComponentBuilder.fromAsset(str); - // uiStuff.findComponent("btnViewSpriteSheet").onClick = _ -> curView = SPRITESHEET; - var dropdown:DropDown = cast uiStuff.findComponent("swapper"); - dropdown.onChange = function(e:UIEvent) { + // offsetEditorDialog.findComponent("btnViewSpriteSheet").onClick = _ -> curView = SPRITESHEET; + var viewDropdown:DropDown = offsetEditorDialog.findComponent("swapper", DropDown); + viewDropdown.onChange = function(e:UIEvent) { trace(e.type); curView = cast e.data.curView; trace(e.data); // trace(e.data); }; - uiStuff.cameras = [hudCam]; + offsetAnimationDropdown = offsetEditorDialog.findComponent("animationDropdown", DropDown); + + offsetEditorDialog.cameras = [hudCam]; + + add(offsetEditorDialog); - add(uiStuff); + // Anchor to the right side by default + // offsetEditorDialog.x = FlxG.width - offsetEditorDialog.width; // sets the default camera back to FlxG.camera, since we set it to hudCamera for haxeui stuf FlxG.cameras.setDefaultDrawTarget(FlxG.camera, true); @@ -157,7 +155,7 @@ class DebugBoundingState extends FlxState generateOutlines(tex.frames); - txtGrp = new FlxGroup(); + txtGrp = new FlxTypedGroup(); txtGrp.cameras = [hudCam]; spriteSheetView.add(txtGrp); @@ -258,15 +256,9 @@ class DebugBoundingState extends FlxState txtOffsetShit = new FlxText(20, 20, 0, "", 20); txtOffsetShit.setFormat(Paths.font("vcr.ttf"), 26, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); txtOffsetShit.cameras = [hudCam]; + txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height; offsetView.add(txtOffsetShit); - animDropDownMenu = new FlxUIDropDownMenu(0, 0, FlxUIDropDownMenu.makeStrIdLabelArray(['weed'], true)); - animDropDownMenu.cameras = [hudCam]; - // Move to bottom right corner - animDropDownMenu.x = FlxG.width - animDropDownMenu.width - 20; - animDropDownMenu.y = FlxG.height - animDropDownMenu.height - 20; - offsetView.add(animDropDownMenu); - var characters:Array = CharacterDataParser.listCharacterIds(); characters = characters.filter(function(charId:String) { var char = CharacterDataParser.fetchCharacterData(charId); @@ -274,7 +266,7 @@ class DebugBoundingState extends FlxState }); characters.sort(SortUtil.alphabetically); - var charDropdown:DropDown = cast uiStuff.findComponent('characterDropdown'); + var charDropdown:DropDown = offsetEditorDialog.findComponent('characterDropdown', DropDown); for (char in characters) { charDropdown.dataSource.add({text: char}); @@ -305,9 +297,10 @@ class DebugBoundingState extends FlxState { swagChar.animOffsets = [(FlxG.mouse.x - mouseOffset.x) * -1, (FlxG.mouse.y - mouseOffset.y) * -1]; - swagChar.animationOffsets.set(animDropDownMenu.selectedLabel, swagChar.animOffsets); + swagChar.animationOffsets.set(offsetAnimationDropdown.value.id, swagChar.animOffsets); txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets; + txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height; } if (FlxG.mouse.justReleased) @@ -319,9 +312,14 @@ class DebugBoundingState extends FlxState function addInfo(str:String, value:Dynamic) { - var swagText:FlxText = new FlxText(10, 10 + (28 * txtGrp.length)); + var swagText:FlxText = new FlxText(10, FlxG.height - 32); swagText.setFormat(Paths.font("vcr.ttf"), 26, FlxColor.WHITE, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); swagText.scrollFactor.set(); + + for (text in txtGrp.members) + { + text.y -= swagText.height; + } txtGrp.add(swagText); swagText.text = str + ": " + Std.string(value); @@ -352,14 +350,14 @@ class DebugBoundingState extends FlxState { if (FlxG.keys.justPressed.ONE) { - var lv:DropDown = cast uiStuff.findComponent("swapper"); + var lv:DropDown = offsetEditorDialog.findComponent("swapper", DropDown); lv.selectedIndex = 0; curView = SPRITESHEET; } if (FlxG.keys.justReleased.TWO) { - var lv:DropDown = cast uiStuff.findComponent("swapper"); + var lv:DropDown = offsetEditorDialog.findComponent("swapper", DropDown); lv.selectedIndex = 1; curView = ANIMATIONS; if (swagChar != null) @@ -375,10 +373,12 @@ class DebugBoundingState extends FlxState spriteSheetView.visible = true; offsetView.visible = false; offsetView.active = false; + offsetAnimationDropdown.visible = false; case ANIMATIONS: spriteSheetView.visible = false; offsetView.visible = true; offsetView.active = true; + offsetAnimationDropdown.visible = true; offsetControls(); mouseOffsetMovement(); } @@ -402,24 +402,36 @@ class DebugBoundingState extends FlxState { if (FlxG.keys.justPressed.RBRACKET || FlxG.keys.justPressed.E) { - if (Std.parseInt(animDropDownMenu.selectedId) + 1 <= animDropDownMenu.length) - animDropDownMenu.selectedId = Std.string(Std.parseInt(animDropDownMenu.selectedId) - + 1); + if (offsetAnimationDropdown.selectedIndex + 1 <= offsetAnimationDropdown.dataSource.size) + { + offsetAnimationDropdown.selectedIndex += 1; + } else - animDropDownMenu.selectedId = Std.string(0); - playCharacterAnimation(animDropDownMenu.selectedId, true); + { + offsetAnimationDropdown.selectedIndex = 0; + } + trace(offsetAnimationDropdown.selectedIndex); + trace(offsetAnimationDropdown.dataSource.size); + trace(offsetAnimationDropdown.value); + trace(currentAnimationName); + playCharacterAnimation(currentAnimationName, true); } if (FlxG.keys.justPressed.LBRACKET || FlxG.keys.justPressed.Q) { - if (Std.parseInt(animDropDownMenu.selectedId) - 1 >= 0) animDropDownMenu.selectedId = Std.string(Std.parseInt(animDropDownMenu.selectedId) - 1); + if (offsetAnimationDropdown.selectedIndex - 1 >= 0) + { + offsetAnimationDropdown.selectedIndex -= 1; + } else - animDropDownMenu.selectedId = Std.string(animDropDownMenu.length - 1); - playCharacterAnimation(animDropDownMenu.selectedId, true); + { + offsetAnimationDropdown.selectedIndex = offsetAnimationDropdown.dataSource.size - 1; + } + playCharacterAnimation(currentAnimationName, true); } // Keyboards controls for general WASD "movement" - // modifies the animDropDownMenu so that it's properly updated and shit - // and then it's just played and updated from the animDropDownMenu callback, which is set in the loadAnimShit() function probabbly + // modifies the animDrooffsetAnimationDropdownpDownMenu so that it's properly updated and shit + // and then it's just played and updated from the offsetAnimationDropdown callback, which is set in the loadAnimShit() function probabbly if (FlxG.keys.justPressed.W || FlxG.keys.justPressed.S || FlxG.keys.justPressed.D || FlxG.keys.justPressed.A) { var suffix:String = ''; @@ -432,18 +444,19 @@ class DebugBoundingState extends FlxState if (FlxG.keys.justPressed.A) targetLabel = 'singLEFT$suffix'; if (FlxG.keys.justPressed.D) targetLabel = 'singRIGHT$suffix'; - if (targetLabel != animDropDownMenu.selectedLabel) + if (targetLabel != currentAnimationName) { + offsetAnimationDropdown.value = {id: targetLabel, text: targetLabel}; + // Play the new animation if the IDs are the different. // Override the onion skin. - animDropDownMenu.selectedLabel = targetLabel; - playCharacterAnimation(animDropDownMenu.selectedId, true); + playCharacterAnimation(currentAnimationName, true); } else { // Replay the current animation if the IDs are the same. // Don't override the onion skin. - playCharacterAnimation(animDropDownMenu.selectedId, false); + playCharacterAnimation(currentAnimationName, false); } } @@ -455,16 +468,20 @@ class DebugBoundingState extends FlxState // Plays the idle animation if (FlxG.keys.justPressed.SPACE) { - animDropDownMenu.selectedLabel = 'idle'; - playCharacterAnimation(animDropDownMenu.selectedId, true); + offsetAnimationDropdown.value = {id: 'idle', text: 'idle'}; + + playCharacterAnimation(currentAnimationName, true); } // Playback the animation - if (FlxG.keys.justPressed.ENTER) playCharacterAnimation(animDropDownMenu.selectedId, false); + if (FlxG.keys.justPressed.ENTER) + { + playCharacterAnimation(currentAnimationName, false); + } if (FlxG.keys.justPressed.RIGHT || FlxG.keys.justPressed.LEFT || FlxG.keys.justPressed.UP || FlxG.keys.justPressed.DOWN) { - var animName = animDropDownMenu.selectedLabel; + var animName = currentAnimationName; var coolValues:Array = swagChar.animationOffsets.get(animName).copy(); var multiplier:Int = 5; @@ -478,10 +495,11 @@ class DebugBoundingState extends FlxState else if (FlxG.keys.justPressed.UP) coolValues[1] += 1 * multiplier; else if (FlxG.keys.justPressed.DOWN) coolValues[1] -= 1 * multiplier; - swagChar.animationOffsets.set(animDropDownMenu.selectedLabel, coolValues); + swagChar.animationOffsets.set(currentAnimationName, coolValues); swagChar.playAnimation(animName); txtOffsetShit.text = 'Offset: ' + coolValues; + txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height; trace(animName); } @@ -536,7 +554,7 @@ class DebugBoundingState extends FlxState swagChar = CharacterDataParser.fetchCharacter(char); swagChar.x = 100; swagChar.y = 100; - // swagChar.debugMode = true; + swagChar.debug = true; offsetView.add(swagChar); if (swagChar == null || swagChar.frames == null) @@ -561,11 +579,25 @@ class DebugBoundingState extends FlxState trace(swagChar.animationOffsets[i]); } - animDropDownMenu.setData(FlxUIDropDownMenu.makeStrIdLabelArray(characterAnimNames, true)); - animDropDownMenu.callback = function(str:String) { - playCharacterAnimation(str, true); - }; + offsetAnimationDropdown.dataSource.clear(); + + for (charAnim in characterAnimNames) + { + trace('Adding ${charAnim} to HaxeUI dropdown'); + offsetAnimationDropdown.dataSource.add({id: charAnim, text: charAnim}); + } + + offsetAnimationDropdown.selectedIndex = 0; + + trace('Added ${offsetAnimationDropdown.dataSource.size} to HaxeUI dropdown'); + + offsetAnimationDropdown.onChange = function(event:UIEvent) { + trace('Selected animation ${event?.data?.id}'); + playCharacterAnimation(event.data.id, true); + } + txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets; + txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height; dropDownSetup = true; } @@ -582,11 +614,13 @@ class DebugBoundingState extends FlxState onionSkinChar.alpha = 0.6; } - var animName = characterAnimNames[Std.parseInt(str)]; + // var animName = characterAnimNames[Std.parseInt(str)]; + var animName = str; swagChar.playAnimation(animName, true); // trace(); trace(swagChar.animationOffsets.get(animName)); txtOffsetShit.text = 'Offset: ' + swagChar.animOffsets; + txtOffsetShit.y = FlxG.height - 20 - txtOffsetShit.height; } var _file:FileReference; diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index dc42bd6510..f9d5a36d49 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1,7 +1,6 @@ package funkin.ui.freeplay; import flixel.addons.transition.FlxTransitionableState; -import flixel.addons.ui.FlxInputText; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.group.FlxGroup; From a7482410b91e82fbd7b7cf37a123b0e7aaa72fec Mon Sep 17 00:00:00 2001 From: lemz Date: Fri, 31 May 2024 14:02:28 +0200 Subject: [PATCH 153/469] add note kind scripts --- source/funkin/InitState.hx | 3 ++ source/funkin/play/PlayState.hx | 8 +++- .../play/notes/notekind/NoteKindScript.hx | 45 ++++++++++++++++++ .../notes/notekind/NoteKindScriptManager.hx | 46 +++++++++++++++++++ .../notes/notekind/ScriptedNoteKindScript.hx | 9 ++++ .../charting/util/ChartEditorDropdowns.hx | 2 +- 6 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 source/funkin/play/notes/notekind/NoteKindScript.hx create mode 100644 source/funkin/play/notes/notekind/NoteKindScriptManager.hx create mode 100644 source/funkin/play/notes/notekind/ScriptedNoteKindScript.hx diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 6e370b5ff7..a339d26559 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -27,6 +27,7 @@ import funkin.data.dialogue.speaker.SpeakerRegistry; import funkin.data.freeplay.album.AlbumRegistry; import funkin.data.song.SongRegistry; import funkin.play.character.CharacterData.CharacterDataParser; +import funkin.play.notes.notekind.NoteKindScriptManager; import funkin.modding.module.ModuleHandler; import funkin.ui.title.TitleState; import funkin.util.CLIUtil; @@ -176,6 +177,8 @@ class InitState extends FlxState // Move it to use a BaseRegistry. CharacterDataParser.loadCharacterCache(); + NoteKindScriptManager.loadScripts(); + ModuleHandler.buildModuleCallbacks(); ModuleHandler.loadModuleCache(); ModuleHandler.callOnCreate(); diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 8d7d82aabe..93306e9d53 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -49,6 +49,7 @@ import funkin.play.notes.NoteSprite; import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.Strumline; import funkin.play.notes.SustainTrail; +import funkin.play.notes.notekind.NoteKindScriptManager; import funkin.play.scoring.Scoring; import funkin.play.song.Song; import funkin.play.stage.Stage; @@ -1177,7 +1178,12 @@ class PlayState extends MusicBeatSubState // Dispatch event to conversation script. ScriptEventDispatcher.callEvent(currentConversation, event); - // TODO: Dispatch event to note scripts + // Dispatch event to note script + if (Std.isOfType(event, NoteScriptEvent)) + { + var noteEvent:NoteScriptEvent = cast(event, NoteScriptEvent); + NoteKindScriptManager.callEvent(noteEvent.note.noteData.kind, noteEvent); + } } /** diff --git a/source/funkin/play/notes/notekind/NoteKindScript.hx b/source/funkin/play/notes/notekind/NoteKindScript.hx new file mode 100644 index 0000000000..baa57b1461 --- /dev/null +++ b/source/funkin/play/notes/notekind/NoteKindScript.hx @@ -0,0 +1,45 @@ +package funkin.play.notes.notekind; + +import funkin.modding.IScriptedClass.INoteScriptedClass; +import funkin.modding.events.ScriptEvent; + +/** + * Class for note scripts + */ +class NoteKindScript implements INoteScriptedClass +{ + /** + * the name of the note kind + */ + public var noteKind:String; + + /** + * description used in chart editor + */ + public var description:String = ""; + + public function new(noteKind:String, description:String = "") + { + this.noteKind = noteKind; + this.description = description; + } + + public function toString():String + { + return noteKind; + } + + public function onScriptEvent(event:ScriptEvent):Void {} + + public function onCreate(event:ScriptEvent):Void {} + + public function onDestroy(event:ScriptEvent):Void {} + + public function onUpdate(event:UpdateScriptEvent):Void {} + + public function onNoteIncoming(event:NoteScriptEvent):Void {} + + public function onNoteHit(event:HitNoteScriptEvent):Void {} + + public function onNoteMiss(event:NoteScriptEvent):Void {} +} diff --git a/source/funkin/play/notes/notekind/NoteKindScriptManager.hx b/source/funkin/play/notes/notekind/NoteKindScriptManager.hx new file mode 100644 index 0000000000..dc22732b68 --- /dev/null +++ b/source/funkin/play/notes/notekind/NoteKindScriptManager.hx @@ -0,0 +1,46 @@ +package funkin.play.notes.notekind; + +import funkin.modding.events.ScriptEventDispatcher; +import funkin.modding.events.ScriptEvent; +import funkin.ui.debug.charting.util.ChartEditorDropdowns; + +class NoteKindScriptManager +{ + static var noteKindScripts:Map = []; + + public static function loadScripts():Void + { + var scriptedClassName:Array = ScriptedNoteKindScript.listScriptClasses(); + if (scriptedClassName.length > 0) + { + trace('Instantiating ${scriptedClassName.length} scripted note kind...'); + for (scriptedClass in scriptedClassName) + { + try + { + var script:NoteKindScript = ScriptedNoteKindScript.init(scriptedClass, "unknown"); + trace(' Initialized scripted note kind: ${script.noteKind}'); + noteKindScripts.set(script.noteKind, script); + ChartEditorDropdowns.NOTE_KINDS.set(script.noteKind, script.description); + } + catch (e) + { + trace(' FAILED to instantiate scripted note kind: ${scriptedClass}'); + trace(e); + } + } + } + } + + public static function callEvent(noteKind:String, event:ScriptEvent):Void + { + var noteKindScript:NoteKindScript = noteKindScripts.get(noteKind); + + if (noteKindScript == null) + { + return; + } + + ScriptEventDispatcher.callEvent(noteKindScript, event); + } +} diff --git a/source/funkin/play/notes/notekind/ScriptedNoteKindScript.hx b/source/funkin/play/notes/notekind/ScriptedNoteKindScript.hx new file mode 100644 index 0000000000..d54a0cde2f --- /dev/null +++ b/source/funkin/play/notes/notekind/ScriptedNoteKindScript.hx @@ -0,0 +1,9 @@ +package funkin.play.notes.notekind; + +/** + * A script that can be tied to a NoteKindScript. + * Create a scripted class that extends NoteKindScript, + * then call `super('noteKind')` in the constructor to use this. + */ +@:hscriptClass +class ScriptedNoteKindScript extends NoteKindScript implements polymod.hscript.HScriptedClass {} diff --git a/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx b/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx index 55aab0ab00..f20b756506 100644 --- a/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx +++ b/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx @@ -146,7 +146,7 @@ class ChartEditorDropdowns return returnValue; } - static final NOTE_KINDS:Map = [ + public static final NOTE_KINDS:Map = [ // Base "" => "Default", "~CUSTOM~" => "Custom", From 134b4678769de803b245c182fed9afe07fc29049 Mon Sep 17 00:00:00 2001 From: lemz Date: Fri, 31 May 2024 16:55:42 +0200 Subject: [PATCH 154/469] rename stuff --- source/funkin/InitState.hx | 4 ++-- source/funkin/play/PlayState.hx | 4 ++-- .../notekind/{NoteKindScript.hx => NoteKind.hx} | 2 +- ...teKindScriptManager.hx => NoteKindManager.hx} | 16 ++++++++-------- .../play/notes/notekind/ScriptedNoteKind.hx | 9 +++++++++ .../notes/notekind/ScriptedNoteKindScript.hx | 9 --------- 6 files changed, 22 insertions(+), 22 deletions(-) rename source/funkin/play/notes/notekind/{NoteKindScript.hx => NoteKind.hx} (94%) rename source/funkin/play/notes/notekind/{NoteKindScriptManager.hx => NoteKindManager.hx} (64%) create mode 100644 source/funkin/play/notes/notekind/ScriptedNoteKind.hx delete mode 100644 source/funkin/play/notes/notekind/ScriptedNoteKindScript.hx diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index a339d26559..34516dee19 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -27,7 +27,7 @@ import funkin.data.dialogue.speaker.SpeakerRegistry; import funkin.data.freeplay.album.AlbumRegistry; import funkin.data.song.SongRegistry; import funkin.play.character.CharacterData.CharacterDataParser; -import funkin.play.notes.notekind.NoteKindScriptManager; +import funkin.play.notes.notekind.NoteKindManager; import funkin.modding.module.ModuleHandler; import funkin.ui.title.TitleState; import funkin.util.CLIUtil; @@ -177,7 +177,7 @@ class InitState extends FlxState // Move it to use a BaseRegistry. CharacterDataParser.loadCharacterCache(); - NoteKindScriptManager.loadScripts(); + NoteKindManager.loadScripts(); ModuleHandler.buildModuleCallbacks(); ModuleHandler.loadModuleCache(); diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 93306e9d53..bc441a7d5c 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -49,7 +49,7 @@ import funkin.play.notes.NoteSprite; import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.Strumline; import funkin.play.notes.SustainTrail; -import funkin.play.notes.notekind.NoteKindScriptManager; +import funkin.play.notes.notekind.NoteKindManager; import funkin.play.scoring.Scoring; import funkin.play.song.Song; import funkin.play.stage.Stage; @@ -1182,7 +1182,7 @@ class PlayState extends MusicBeatSubState if (Std.isOfType(event, NoteScriptEvent)) { var noteEvent:NoteScriptEvent = cast(event, NoteScriptEvent); - NoteKindScriptManager.callEvent(noteEvent.note.noteData.kind, noteEvent); + NoteKindManager.callEvent(noteEvent.note.noteData.kind, noteEvent); } } diff --git a/source/funkin/play/notes/notekind/NoteKindScript.hx b/source/funkin/play/notes/notekind/NoteKind.hx similarity index 94% rename from source/funkin/play/notes/notekind/NoteKindScript.hx rename to source/funkin/play/notes/notekind/NoteKind.hx index baa57b1461..77b2bbc456 100644 --- a/source/funkin/play/notes/notekind/NoteKindScript.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -6,7 +6,7 @@ import funkin.modding.events.ScriptEvent; /** * Class for note scripts */ -class NoteKindScript implements INoteScriptedClass +class NoteKind implements INoteScriptedClass { /** * the name of the note kind diff --git a/source/funkin/play/notes/notekind/NoteKindScriptManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx similarity index 64% rename from source/funkin/play/notes/notekind/NoteKindScriptManager.hx rename to source/funkin/play/notes/notekind/NoteKindManager.hx index dc22732b68..eaee0d319d 100644 --- a/source/funkin/play/notes/notekind/NoteKindScriptManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -4,13 +4,13 @@ import funkin.modding.events.ScriptEventDispatcher; import funkin.modding.events.ScriptEvent; import funkin.ui.debug.charting.util.ChartEditorDropdowns; -class NoteKindScriptManager +class NoteKindManager { - static var noteKindScripts:Map = []; + static var noteKinds:Map = []; public static function loadScripts():Void { - var scriptedClassName:Array = ScriptedNoteKindScript.listScriptClasses(); + var scriptedClassName:Array = ScriptedNoteKind.listScriptClasses(); if (scriptedClassName.length > 0) { trace('Instantiating ${scriptedClassName.length} scripted note kind...'); @@ -18,9 +18,9 @@ class NoteKindScriptManager { try { - var script:NoteKindScript = ScriptedNoteKindScript.init(scriptedClass, "unknown"); + var script:NoteKind = ScriptedNoteKind.init(scriptedClass, "unknown"); trace(' Initialized scripted note kind: ${script.noteKind}'); - noteKindScripts.set(script.noteKind, script); + noteKinds.set(script.noteKind, script); ChartEditorDropdowns.NOTE_KINDS.set(script.noteKind, script.description); } catch (e) @@ -34,13 +34,13 @@ class NoteKindScriptManager public static function callEvent(noteKind:String, event:ScriptEvent):Void { - var noteKindScript:NoteKindScript = noteKindScripts.get(noteKind); + var noteKind:NoteKind = noteKinds.get(noteKind); - if (noteKindScript == null) + if (noteKind == null) { return; } - ScriptEventDispatcher.callEvent(noteKindScript, event); + ScriptEventDispatcher.callEvent(noteKind, event); } } diff --git a/source/funkin/play/notes/notekind/ScriptedNoteKind.hx b/source/funkin/play/notes/notekind/ScriptedNoteKind.hx new file mode 100644 index 0000000000..cd17813941 --- /dev/null +++ b/source/funkin/play/notes/notekind/ScriptedNoteKind.hx @@ -0,0 +1,9 @@ +package funkin.play.notes.notekind; + +/** + * A script that can be tied to a NoteKind. + * Create a scripted class that extends NoteKind, + * then call `super('noteKind')` in the constructor to use this. + */ +@:hscriptClass +class ScriptedNoteKind extends NoteKind implements polymod.hscript.HScriptedClass {} diff --git a/source/funkin/play/notes/notekind/ScriptedNoteKindScript.hx b/source/funkin/play/notes/notekind/ScriptedNoteKindScript.hx deleted file mode 100644 index d54a0cde2f..0000000000 --- a/source/funkin/play/notes/notekind/ScriptedNoteKindScript.hx +++ /dev/null @@ -1,9 +0,0 @@ -package funkin.play.notes.notekind; - -/** - * A script that can be tied to a NoteKindScript. - * Create a scripted class that extends NoteKindScript, - * then call `super('noteKind')` in the constructor to use this. - */ -@:hscriptClass -class ScriptedNoteKindScript extends NoteKindScript implements polymod.hscript.HScriptedClass {} From 606d9d4af47ea50a89e921ef369aaa73b5dc3c7b Mon Sep 17 00:00:00 2001 From: lemz Date: Fri, 31 May 2024 17:24:51 +0200 Subject: [PATCH 155/469] Update NoteKindManager.hx --- source/funkin/play/notes/notekind/NoteKindManager.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/play/notes/notekind/NoteKindManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx index eaee0d319d..99502af086 100644 --- a/source/funkin/play/notes/notekind/NoteKindManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -13,7 +13,7 @@ class NoteKindManager var scriptedClassName:Array = ScriptedNoteKind.listScriptClasses(); if (scriptedClassName.length > 0) { - trace('Instantiating ${scriptedClassName.length} scripted note kind...'); + trace('Instantiating ${scriptedClassName.length} scripted note kind(s)...'); for (scriptedClass in scriptedClassName) { try From c83e505f5bae0e1a61a1d010aec8765d53cab36d Mon Sep 17 00:00:00 2001 From: lemz Date: Fri, 31 May 2024 17:49:25 +0200 Subject: [PATCH 156/469] onUpdate, etc. works now too --- source/funkin/play/PlayState.hx | 6 +++++- source/funkin/play/notes/notekind/NoteKindManager.hx | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index bc441a7d5c..3b098be14f 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1178,12 +1178,16 @@ class PlayState extends MusicBeatSubState // Dispatch event to conversation script. ScriptEventDispatcher.callEvent(currentConversation, event); - // Dispatch event to note script + // Dispatch event to only the specific note script if (Std.isOfType(event, NoteScriptEvent)) { var noteEvent:NoteScriptEvent = cast(event, NoteScriptEvent); NoteKindManager.callEvent(noteEvent.note.noteData.kind, noteEvent); } + else // Dispatch event to all note scripts + { + NoteKindManager.callEventForAll(event); + } } /** diff --git a/source/funkin/play/notes/notekind/NoteKindManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx index 99502af086..0de1a0a33a 100644 --- a/source/funkin/play/notes/notekind/NoteKindManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -43,4 +43,12 @@ class NoteKindManager ScriptEventDispatcher.callEvent(noteKind, event); } + + public static function callEventForAll(event:ScriptEvent):Void + { + for (noteKind in noteKinds.iterator()) + { + ScriptEventDispatcher.callEvent(noteKind, event); + } + } } From e3e4e9fac01844a807ecd54e34fc162205c59ed9 Mon Sep 17 00:00:00 2001 From: lemz Date: Fri, 31 May 2024 20:28:56 +0200 Subject: [PATCH 157/469] helper function --- source/funkin/play/notes/notekind/NoteKind.hx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index 77b2bbc456..8b87e0442d 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -18,6 +18,16 @@ class NoteKind implements INoteScriptedClass */ public var description:String = ""; + /** + * this only exists for people that don't like calling functions + */ + var notes(get, never):Array; + + function get_notes():Array + { + return this.getNotes(); + } + public function new(noteKind:String, description:String = "") { this.noteKind = noteKind; @@ -29,6 +39,18 @@ class NoteKind implements INoteScriptedClass return noteKind; } + /** + * Retrieve all notes of this kind + * @return Array + */ + function getNotes():Array + { + var allNotes:Array = PlayState.instance.playerStrumline.notes.members.concat(PlayState.instance.opponentStrumline.notes.members); + return allNotes.filter(function(note:NoteSprite) { + return note != null && note.noteData.kind == this.noteKind; + }); + } + public function onScriptEvent(event:ScriptEvent):Void {} public function onCreate(event:ScriptEvent):Void {} From 3f39d9509c1e2a02d4ace85553991b6679a83d14 Mon Sep 17 00:00:00 2001 From: lemz Date: Fri, 31 May 2024 23:56:57 +0200 Subject: [PATCH 158/469] make custom note style less clunky --- source/funkin/play/notes/notekind/NoteKind.hx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index 8b87e0442d..f4936c59e9 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -1,5 +1,7 @@ package funkin.play.notes.notekind; +import funkin.data.notestyle.NoteStyleRegistry; +import funkin.play.notes.notestyle.NoteStyle; import funkin.modding.IScriptedClass.INoteScriptedClass; import funkin.modding.events.ScriptEvent; @@ -39,6 +41,23 @@ class NoteKind implements INoteScriptedClass return noteKind; } + /** + * Changes the note style of the given note. Use this in `onNoteIncoming` + * @param note + * @param noteStyle + */ + function setNoteStyle(note:NoteSprite, noteStyleId:String):Void + { + var noteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyleId); + noteStyle.buildNoteSprite(note); + + note.setGraphicSize(Strumline.STRUMLINE_SIZE); + note.updateHitbox(); + + // this calls the setter for playing the correct animation + note.direction = note.direction; + } + /** * Retrieve all notes of this kind * @return Array From 14771e72deb7f08b9658e0801c9e088bb5d1aa96 Mon Sep 17 00:00:00 2001 From: lemz Date: Fri, 31 May 2024 23:59:40 +0200 Subject: [PATCH 159/469] no more note getter --- source/funkin/play/notes/notekind/NoteKind.hx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index f4936c59e9..262660eea9 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -20,16 +20,6 @@ class NoteKind implements INoteScriptedClass */ public var description:String = ""; - /** - * this only exists for people that don't like calling functions - */ - var notes(get, never):Array; - - function get_notes():Array - { - return this.getNotes(); - } - public function new(noteKind:String, description:String = "") { this.noteKind = noteKind; From b6eda8e498be12f40c6086678a48c7c038e4b69e Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 1 Jun 2024 18:25:46 +0200 Subject: [PATCH 160/469] remove note kind logic from playstate --- source/funkin/play/PlayState.hx | 12 ++----- .../play/notes/notekind/NoteKindManager.hx | 32 ++++++++++++------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 3b098be14f..da343f43f6 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1178,16 +1178,8 @@ class PlayState extends MusicBeatSubState // Dispatch event to conversation script. ScriptEventDispatcher.callEvent(currentConversation, event); - // Dispatch event to only the specific note script - if (Std.isOfType(event, NoteScriptEvent)) - { - var noteEvent:NoteScriptEvent = cast(event, NoteScriptEvent); - NoteKindManager.callEvent(noteEvent.note.noteData.kind, noteEvent); - } - else // Dispatch event to all note scripts - { - NoteKindManager.callEventForAll(event); - } + // Dispatch event to note kind scripts + NoteKindManager.callEvent(event); } /** diff --git a/source/funkin/play/notes/notekind/NoteKindManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx index 0de1a0a33a..6efd601c75 100644 --- a/source/funkin/play/notes/notekind/NoteKindManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -32,23 +32,31 @@ class NoteKindManager } } - public static function callEvent(noteKind:String, event:ScriptEvent):Void + /** + * Calls the given event for note kind scripts + * @param event The event + */ + public static function callEvent(event:ScriptEvent):Void { - var noteKind:NoteKind = noteKinds.get(noteKind); - - if (noteKind == null) + // if it is a note script event, + // then only call the event for the specific note kind script + if (Std.isOfType(event, NoteScriptEvent)) { - return; - } + var noteEvent:NoteScriptEvent = cast(event, NoteScriptEvent); - ScriptEventDispatcher.callEvent(noteKind, event); - } + var noteKind:NoteKind = noteKinds.get(noteEvent.note.noteData.kind); - public static function callEventForAll(event:ScriptEvent):Void - { - for (noteKind in noteKinds.iterator()) + if (noteKind != null) + { + ScriptEventDispatcher.callEvent(noteKind, event); + } + } + else // call the event for all note kind scripts { - ScriptEventDispatcher.callEvent(noteKind, event); + for (noteKind in noteKinds.iterator()) + { + ScriptEventDispatcher.callEvent(noteKind, event); + } } } } From 328e590f92901f3dab711a725af02cc40763f359 Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 1 Jun 2024 19:47:45 +0200 Subject: [PATCH 161/469] hold assets are updated aswell --- source/funkin/play/notes/NoteSprite.hx | 6 ++- source/funkin/play/notes/Strumline.hx | 7 ++++ source/funkin/play/notes/SustainTrail.hx | 38 ++++++++++++++----- source/funkin/play/notes/notekind/NoteKind.hx | 33 +++++----------- .../play/notes/notekind/NoteKindManager.hx | 16 +++++++- 5 files changed, 65 insertions(+), 35 deletions(-) diff --git a/source/funkin/play/notes/NoteSprite.hx b/source/funkin/play/notes/NoteSprite.hx index b16b88466b..17a5e57fcf 100644 --- a/source/funkin/play/notes/NoteSprite.hx +++ b/source/funkin/play/notes/NoteSprite.hx @@ -140,7 +140,11 @@ class NoteSprite extends FunkinSprite this.active = false; } - function setupNoteGraphic(noteStyle:NoteStyle):Void + /** + * Creates frames and animations + * @param noteStyle The `NoteStyle` instance + */ + public function setupNoteGraphic(noteStyle:NoteStyle):Void { noteStyle.buildNoteSprite(this); diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index fdb32bb857..ebc48a8c74 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -16,6 +16,7 @@ import funkin.data.song.SongData.SongNoteData; import funkin.ui.options.PreferencesMenu; import funkin.util.SortUtil; import funkin.modding.events.ScriptEvent; +import funkin.play.notes.notekind.NoteKindManager; /** * A group of sprites which handles the receptor, the note splashes, and the notes (with sustains) for a given player. @@ -708,6 +709,9 @@ class Strumline extends FlxSpriteGroup if (noteSprite != null) { + var noteKindStyle:NoteStyle = NoteKindManager.getNoteStyle(note.kind) ?? this.noteStyle; + noteSprite.setupNoteGraphic(noteKindStyle); + noteSprite.direction = note.getDirection(); noteSprite.noteData = note; @@ -727,6 +731,9 @@ class Strumline extends FlxSpriteGroup if (holdNoteSprite != null) { + var noteKindStyle:NoteStyle = NoteKindManager.getNoteStyle(note.kind) ?? this.noteStyle; + holdNoteSprite.setupHoldNoteGraphic(noteKindStyle); + holdNoteSprite.parentStrumline = this; holdNoteSprite.noteData = note; holdNoteSprite.strumTime = note.time; diff --git a/source/funkin/play/notes/SustainTrail.hx b/source/funkin/play/notes/SustainTrail.hx index f6d43b33f6..570c05190b 100644 --- a/source/funkin/play/notes/SustainTrail.hx +++ b/source/funkin/play/notes/SustainTrail.hx @@ -99,7 +99,27 @@ class SustainTrail extends FlxSprite */ public function new(noteDirection:NoteDirection, sustainLength:Float, noteStyle:NoteStyle) { - super(0, 0, noteStyle.getHoldNoteAssetPath()); + super(0, 0); + + // BASIC SETUP + this.sustainLength = sustainLength; + this.fullSustainLength = sustainLength; + this.noteDirection = noteDirection; + + setupHoldNoteGraphic(noteStyle); + + indices = new DrawData(12, true, TRIANGLE_VERTEX_INDICES); + + this.active = true; // This NEEDS to be true for the note to be drawn! + } + + /** + * Creates hold note graphic and applies correct zooming + * @param noteStyle The note style + */ + public function setupHoldNoteGraphic(noteStyle:NoteStyle):Void + { + loadGraphic(noteStyle.getHoldNoteAssetPath()); antialiasing = true; @@ -109,13 +129,9 @@ class SustainTrail extends FlxSprite endOffset = bottomClip = 1; antialiasing = false; } - zoom *= noteStyle.fetchHoldNoteScale(); - - // BASIC SETUP - this.sustainLength = sustainLength; - this.fullSustainLength = sustainLength; - this.noteDirection = noteDirection; + zoom = 1.0; + zoom *= noteStyle.fetchHoldNoteScale(); zoom *= 0.7; // CALCULATE SIZE @@ -131,9 +147,6 @@ class SustainTrail extends FlxSprite updateColorTransform(); updateClipping(); - indices = new DrawData(12, true, TRIANGLE_VERTEX_INDICES); - - this.active = true; // This NEEDS to be true for the note to be drawn! } function getBaseScrollSpeed() @@ -195,6 +208,11 @@ class SustainTrail extends FlxSprite */ public function updateClipping(songTime:Float = 0):Void { + if (graphic == null) + { + return; + } + var clipHeight:Float = FlxMath.bound(sustainHeight(sustainLength - (songTime - strumTime), parentStrumline?.scrollSpeed ?? 1.0), 0, graphicHeight); if (clipHeight <= 0.1) { diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index 262660eea9..53393623af 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -1,7 +1,5 @@ package funkin.play.notes.notekind; -import funkin.data.notestyle.NoteStyleRegistry; -import funkin.play.notes.notestyle.NoteStyle; import funkin.modding.IScriptedClass.INoteScriptedClass; import funkin.modding.events.ScriptEvent; @@ -11,19 +9,25 @@ import funkin.modding.events.ScriptEvent; class NoteKind implements INoteScriptedClass { /** - * the name of the note kind + * The name of the note kind */ public var noteKind:String; /** - * description used in chart editor + * Description used in chart editor */ - public var description:String = ""; + public var description:String; - public function new(noteKind:String, description:String = "") + /** + * Custom note style + */ + public var noteStyleId:String; + + public function new(noteKind:String, description:String = "", noteStyleId:String = "") { this.noteKind = noteKind; this.description = description; + this.noteStyleId = noteStyleId; } public function toString():String @@ -31,23 +35,6 @@ class NoteKind implements INoteScriptedClass return noteKind; } - /** - * Changes the note style of the given note. Use this in `onNoteIncoming` - * @param note - * @param noteStyle - */ - function setNoteStyle(note:NoteSprite, noteStyleId:String):Void - { - var noteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyleId); - noteStyle.buildNoteSprite(note); - - note.setGraphicSize(Strumline.STRUMLINE_SIZE); - note.updateHitbox(); - - // this calls the setter for playing the correct animation - note.direction = note.direction; - } - /** * Retrieve all notes of this kind * @return Array diff --git a/source/funkin/play/notes/notekind/NoteKindManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx index 6efd601c75..849034fc43 100644 --- a/source/funkin/play/notes/notekind/NoteKindManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -3,6 +3,8 @@ package funkin.play.notes.notekind; import funkin.modding.events.ScriptEventDispatcher; import funkin.modding.events.ScriptEvent; import funkin.ui.debug.charting.util.ChartEditorDropdowns; +import funkin.data.notestyle.NoteStyleRegistry; +import funkin.play.notes.notestyle.NoteStyle; class NoteKindManager { @@ -44,7 +46,7 @@ class NoteKindManager { var noteEvent:NoteScriptEvent = cast(event, NoteScriptEvent); - var noteKind:NoteKind = noteKinds.get(noteEvent.note.noteData.kind); + var noteKind:NoteKind = noteKinds.get(noteEvent.note.kind); if (noteKind != null) { @@ -59,4 +61,16 @@ class NoteKindManager } } } + + /** + * Retrieve the note style from the given note kind + * @param noteKind note kind name + * @return NoteStyle + */ + public static function getNoteStyle(noteKind:String):Null + { + var noteStyleId:String = noteKinds.get(noteKind)?.noteStyleId ?? ""; + + return NoteStyleRegistry.instance.fetchEntry(noteStyleId); + } } From ca69e7b850332e0aa4a35574566867cca649cdbf Mon Sep 17 00:00:00 2001 From: lemz Date: Sun, 2 Jun 2024 02:44:16 +0200 Subject: [PATCH 162/469] custom note styles in chart editor --- .../ui/debug/charting/ChartEditorState.hx | 2 + .../components/ChartEditorNoteSprite.hx | 106 ++++++++++-------- .../toolboxes/ChartEditorNoteDataToolbox.hx | 5 + 3 files changed, 64 insertions(+), 49 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index f72cca77f6..0117d8a515 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -1663,6 +1663,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState return currentSongMetadata.playData.characters.instrumental = value; } + var currentCustomNoteKindStyle:Null; + /** * HAXEUI COMPONENTS */ diff --git a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx index 98f5a47aa2..c97aee1f82 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx @@ -7,7 +7,11 @@ import flixel.graphics.frames.FlxAtlasFrames; import flixel.graphics.frames.FlxFrame; import flixel.graphics.frames.FlxTileFrames; import flixel.math.FlxPoint; +import funkin.data.animation.AnimationData; import funkin.data.song.SongData.SongNoteData; +import funkin.data.notestyle.NoteStyleRegistry; +import funkin.play.notes.notestyle.NoteStyle; +import funkin.play.notes.NoteDirection; /** * A sprite that can be used to display a note in a chart. @@ -68,68 +72,62 @@ class ChartEditorNoteSprite extends FlxSprite if (noteFrameCollection == null) { - initFrameCollection(); + buildEmptyFrameCollection(); + + addNoteStyleFrames(fetchNoteStyle('funkin')); + addNoteStyleFrames(fetchNoteStyle('pixel')); } if (noteFrameCollection == null) throw 'ERROR: Could not initialize note sprite animations.'; this.frames = noteFrameCollection; - // Initialize all the animations, not just the one we're going to use immediately, - // so that later we can reuse the sprite without having to initialize more animations during scrolling. - this.animation.addByPrefix('tapLeftFunkin', 'purple instance'); - this.animation.addByPrefix('tapDownFunkin', 'blue instance'); - this.animation.addByPrefix('tapUpFunkin', 'green instance'); - this.animation.addByPrefix('tapRightFunkin', 'red instance'); - - this.animation.addByPrefix('holdLeftFunkin', 'LeftHoldPiece'); - this.animation.addByPrefix('holdDownFunkin', 'DownHoldPiece'); - this.animation.addByPrefix('holdUpFunkin', 'UpHoldPiece'); - this.animation.addByPrefix('holdRightFunkin', 'RightHoldPiece'); - - this.animation.addByPrefix('holdEndLeftFunkin', 'LeftHoldEnd'); - this.animation.addByPrefix('holdEndDownFunkin', 'DownHoldEnd'); - this.animation.addByPrefix('holdEndUpFunkin', 'UpHoldEnd'); - this.animation.addByPrefix('holdEndRightFunkin', 'RightHoldEnd'); - - this.animation.addByPrefix('tapLeftPixel', 'pixel4'); - this.animation.addByPrefix('tapDownPixel', 'pixel5'); - this.animation.addByPrefix('tapUpPixel', 'pixel6'); - this.animation.addByPrefix('tapRightPixel', 'pixel7'); + addNoteStyleAnimations(fetchNoteStyle('funkin')); + addNoteStyleAnimations(fetchNoteStyle('pixel')); } static var noteFrameCollection:Null = null; - /** - * We load all the note frames once, then reuse them. - */ - static function initFrameCollection():Void + function fetchNoteStyle(noteStyleId:String):NoteStyle { - buildEmptyFrameCollection(); - if (noteFrameCollection == null) return; - - // TODO: Automatically iterate over the list of note skins. + return NoteStyleRegistry.instance.fetchEntry(noteStyleId) ?? NoteStyleRegistry.instance.fetchDefault(); + } - // Normal notes - var frameCollectionNormal:FlxAtlasFrames = Paths.getSparrowAtlas('NOTE_assets'); + @:access(funkin.play.notes.notestyle.NoteStyle) + @:nullSafety(Off) + static function addNoteStyleFrames(noteStyle:NoteStyle):Void + { + var prefix:String = noteStyle.id.toTitleCase(); - for (frame in frameCollectionNormal.frames) + var frameCollection:FlxAtlasFrames = Paths.getSparrowAtlas(noteStyle.getNoteAssetPath(), noteStyle.getNoteAssetLibrary()); + for (frame in frameCollection.frames) { - noteFrameCollection.pushFrame(frame); + // cloning the frame because else + // we will fuck up the frame data used in game + var clonedFrame:FlxFrame = frame.copyTo(); + clonedFrame.name = '$prefix${clonedFrame.name}'; + noteFrameCollection.pushFrame(clonedFrame); } + } - // Pixel notes - var graphicPixel = FlxG.bitmap.add(Paths.image('weeb/pixelUI/arrows-pixels', 'week6'), false, null); - if (graphicPixel == null) trace('ERROR: Could not load graphic: ' + Paths.image('weeb/pixelUI/arrows-pixels', 'week6')); - var frameCollectionPixel = FlxTileFrames.fromGraphic(graphicPixel, new FlxPoint(17, 17)); - for (i in 0...frameCollectionPixel.frames.length) - { - var frame:Null = frameCollectionPixel.frames[i]; - if (frame == null) continue; + @:access(funkin.play.notes.notestyle.NoteStyle) + @:nullSafety(Off) + function addNoteStyleAnimations(noteStyle:NoteStyle):Void + { + var prefix:String = noteStyle.id.toTitleCase(); + var suffix:String = noteStyle.id.toTitleCase(); - frame.name = 'pixel' + i; - noteFrameCollection.pushFrame(frame); - } + var leftData:AnimationData = noteStyle.fetchNoteAnimationData(NoteDirection.LEFT); + this.animation.addByPrefix('tapLeft$suffix', '$prefix${leftData.prefix}', leftData.frameRate, leftData.looped, leftData.flipX, leftData.flipY); + + var downData:AnimationData = noteStyle.fetchNoteAnimationData(NoteDirection.DOWN); + this.animation.addByPrefix('tapDown$suffix', '$prefix${downData.prefix}', downData.frameRate, downData.looped, downData.flipX, downData.flipY); + + var upData:AnimationData = noteStyle.fetchNoteAnimationData(NoteDirection.UP); + this.animation.addByPrefix('tapUp$suffix', '$prefix${upData.prefix}', upData.frameRate, upData.looped, upData.flipX, upData.flipY); + + var rightData:AnimationData = noteStyle.fetchNoteAnimationData(NoteDirection.RIGHT); + this.animation.addByPrefix('tapRight$suffix', '$prefix${rightData.prefix}', rightData.frameRate, rightData.looped, rightData.flipX, rightData.flipY); } @:nullSafety(Off) @@ -187,10 +185,20 @@ class ChartEditorNoteSprite extends FlxSprite function get_noteStyle():String { - // Fall back to Funkin' if it's not a valid note style. - return if (NOTE_STYLES.contains(this.parentState.currentSongNoteStyle)) this.parentState.currentSongNoteStyle else 'funkin'; + if (this.parentState.currentCustomNoteKindStyle != null) + { + return this.parentState.currentCustomNoteKindStyle; + } + + if (NOTE_STYLES.contains(this.parentState.currentSongNoteStyle)) + { + return this.parentState.currentSongNoteStyle; + } + + return 'funkin'; } + @:nullSafety(Off) public function playNoteAnimation():Void { if (this.noteData == null) return; @@ -213,8 +221,8 @@ class ChartEditorNoteSprite extends FlxSprite } this.updateHitbox(); - // TODO: Make this an attribute of the note skin. - this.antialiasing = (this.parentState.currentSongNoteStyle != 'Pixel'); + var bruhStyle:NoteStyle = fetchNoteStyle(this.noteStyle); + this.antialiasing = !bruhStyle._data?.assets?.note?.isPixel ?? true; } /** diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index d4fc69fc19..952513f0e2 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -4,6 +4,8 @@ import haxe.ui.components.DropDown; import haxe.ui.components.TextField; import haxe.ui.events.UIEvent; import funkin.ui.debug.charting.util.ChartEditorDropdowns; +import funkin.play.notes.notestyle.NoteStyle; +import funkin.play.notes.notekind.NoteKindManager; /** * The toolbox which allows modifying information like Note Kind. @@ -73,6 +75,9 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox var customKind:Null = event?.target?.text; chartEditorState.noteKindToPlace = customKind; + var noteStyle:Null = NoteKindManager.getNoteStyle(customKind); + chartEditorState.currentCustomNoteKindStyle = noteStyle?.id; + if (chartEditorState.currentEventSelection.length > 0) { // Edit the note data of any selected notes. From 9d7846a0d551a005897f1ea0ca099d19f4ac359f Mon Sep 17 00:00:00 2001 From: lemz Date: Sun, 2 Jun 2024 02:58:56 +0200 Subject: [PATCH 163/469] loop through all note style entries --- .../charting/components/ChartEditorNoteSprite.hx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx index c97aee1f82..6517b61c2d 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx @@ -70,20 +70,26 @@ class ChartEditorNoteSprite extends FlxSprite this.parentState = parent; + var entries:Array = NoteStyleRegistry.instance.listEntryIds(); + if (noteFrameCollection == null) { buildEmptyFrameCollection(); - addNoteStyleFrames(fetchNoteStyle('funkin')); - addNoteStyleFrames(fetchNoteStyle('pixel')); + for (entry in entries) + { + addNoteStyleFrames(fetchNoteStyle(entry)); + } } if (noteFrameCollection == null) throw 'ERROR: Could not initialize note sprite animations.'; this.frames = noteFrameCollection; - addNoteStyleAnimations(fetchNoteStyle('funkin')); - addNoteStyleAnimations(fetchNoteStyle('pixel')); + for (entry in entries) + { + addNoteStyleAnimations(fetchNoteStyle(entry)); + } } static var noteFrameCollection:Null = null; From ca2cbb44f520361b73c60378e0beaf0459e308c2 Mon Sep 17 00:00:00 2001 From: lemz Date: Sun, 2 Jun 2024 22:11:17 +0200 Subject: [PATCH 164/469] display custom note style in chart editor --- .../play/notes/notekind/NoteKindManager.hx | 10 ++++++ .../ui/debug/charting/ChartEditorState.hx | 10 ++++-- .../components/ChartEditorHoldNoteSprite.hx | 34 +++++++++++++++++++ .../components/ChartEditorNoteSprite.hx | 22 ++++++------ .../toolboxes/ChartEditorNoteDataToolbox.hx | 3 -- 5 files changed, 61 insertions(+), 18 deletions(-) diff --git a/source/funkin/play/notes/notekind/NoteKindManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx index 849034fc43..82b2c4d396 100644 --- a/source/funkin/play/notes/notekind/NoteKindManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -73,4 +73,14 @@ class NoteKindManager return NoteStyleRegistry.instance.fetchEntry(noteStyleId); } + + /** + * Retrieve the note style id from the given note kind + * @param noteKind note kind name + * @return Null + */ + public static function getNoteStyleId(noteKind:String):Null + { + return noteKinds.get(noteKind)?.noteStyleId; + } } diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 0117d8a515..bc0acc6d60 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -45,6 +45,7 @@ import funkin.input.TurboActionHandler; import funkin.input.TurboButtonHandler; import funkin.input.TurboKeyHandler; import funkin.modding.events.ScriptEvent; +import funkin.play.notes.notekind.NoteKindManager; import funkin.play.character.BaseCharacter.CharacterType; import funkin.play.character.CharacterData; import funkin.play.character.CharacterData.CharacterDataParser; @@ -1663,8 +1664,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState return currentSongMetadata.playData.characters.instrumental = value; } - var currentCustomNoteKindStyle:Null; - /** * HAXEUI COMPONENTS */ @@ -3586,6 +3585,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // The note sprite handles animation playback and positioning. noteSprite.noteData = noteData; + noteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind) ?? currentSongNoteStyle; noteSprite.overrideStepTime = null; noteSprite.overrideData = null; @@ -3606,6 +3606,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState holdNoteSprite.noteData = noteSprite.noteData; holdNoteSprite.noteDirection = noteSprite.noteData.getDirection(); + holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteSprite.noteData.kind) ?? currentSongNoteStyle; holdNoteSprite.setHeightDirectly(noteLengthPixels); @@ -3671,7 +3672,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState holdNoteSprite.noteData = noteData; holdNoteSprite.noteDirection = noteData.getDirection(); - + holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind) ?? currentSongNoteStyle; holdNoteSprite.setHeightDirectly(noteLengthPixels); holdNoteSprite.updateHoldNotePosition(renderedHoldNotes); @@ -4570,6 +4571,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState gridGhostHoldNote.visible = true; gridGhostHoldNote.noteData = currentPlaceNoteData; gridGhostHoldNote.noteDirection = currentPlaceNoteData.getDirection(); + gridGhostHoldNote.noteStyle = NoteKindManager.getNoteStyleId(currentPlaceNoteData.kind) ?? currentSongNoteStyle; gridGhostHoldNote.setHeightDirectly(dragLengthPixels, true); gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes); @@ -4893,6 +4895,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState { noteData.kind = noteKindToPlace; noteData.data = cursorColumn; + gridGhostNote.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind) ?? currentSongNoteStyle; gridGhostNote.playNoteAnimation(); } noteData.time = cursorSnappedMs; @@ -5281,6 +5284,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // Readd the new ghost hold note. ghostHold.noteData = targetNoteData.clone(); ghostHold.noteDirection = ghostHold.noteData.getDirection(); + ghostHold.noteStyle = NoteKindManager.getNoteStyleId(ghostHold.noteData.kind) ?? currentSongNoteStyle; ghostHold.visible = true; ghostHold.alpha = 0.6; ghostHold.setHeightDirectly(0); diff --git a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx index ded48abe39..ccf462c565 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx @@ -2,6 +2,7 @@ package funkin.ui.debug.charting.components; import funkin.play.notes.Strumline; import funkin.data.notestyle.NoteStyleRegistry; +import funkin.play.notes.notestyle.NoteStyle; import flixel.FlxObject; import flixel.FlxSprite; import flixel.graphics.frames.FlxFramesCollection; @@ -15,6 +16,7 @@ import flixel.math.FlxMath; * A sprite that can be used to display the trail of a hold note in a chart. * Designed to be used and reused efficiently. Has no gameplay functionality. */ +@:access(funkin.ui.debug.charting.ChartEditorState) @:nullSafety class ChartEditorHoldNoteSprite extends SustainTrail { @@ -23,6 +25,22 @@ class ChartEditorHoldNoteSprite extends SustainTrail */ public var parentState:ChartEditorState; + @:isVar + public var noteStyle(get, set):Null; + + function get_noteStyle():Null + { + return this.noteStyle ?? this.parentState.currentSongNoteStyle; + } + + @:nullSafety(Off) + function set_noteStyle(value:Null):Null + { + this.noteStyle = value; + this.updateHoldNoteGraphic(); + return value; + } + public function new(parent:ChartEditorState) { var noteStyle = NoteStyleRegistry.instance.fetchDefault(); @@ -41,6 +59,22 @@ class ChartEditorHoldNoteSprite extends SustainTrail setup(); } + @:nullSafety(Off) + function updateHoldNoteGraphic():Void + { + var bruhStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle); + this.setupHoldNoteGraphic(bruhStyle); + + zoom = 1.0; + zoom *= bruhStyle.fetchHoldNoteScale(); + zoom *= 0.7; + zoom *= ChartEditorState.GRID_SIZE / Strumline.STRUMLINE_SIZE; + + flipY = false; + + setup(); + } + public override function updateHitbox():Void { // Expand the clickable hitbox to the full column width, then nudge to the left to re-center it. diff --git a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx index 6517b61c2d..0095324019 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx @@ -40,7 +40,8 @@ class ChartEditorNoteSprite extends FlxSprite /** * The name of the note style currently in use. */ - public var noteStyle(get, never):String; + @:isVar + public var noteStyle(get, set):Null; public var overrideStepTime(default, set):Null = null; @@ -189,19 +190,16 @@ class ChartEditorNoteSprite extends FlxSprite } } - function get_noteStyle():String + function get_noteStyle():Null { - if (this.parentState.currentCustomNoteKindStyle != null) - { - return this.parentState.currentCustomNoteKindStyle; - } - - if (NOTE_STYLES.contains(this.parentState.currentSongNoteStyle)) - { - return this.parentState.currentSongNoteStyle; - } + return this.noteStyle ?? this.parentState.currentSongNoteStyle; + } - return 'funkin'; + function set_noteStyle(value:Null):Null + { + this.noteStyle = value; + this.playNoteAnimation(); + return value; } @:nullSafety(Off) diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index 952513f0e2..f1223eb9c3 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -75,9 +75,6 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox var customKind:Null = event?.target?.text; chartEditorState.noteKindToPlace = customKind; - var noteStyle:Null = NoteKindManager.getNoteStyle(customKind); - chartEditorState.currentCustomNoteKindStyle = noteStyle?.id; - if (chartEditorState.currentEventSelection.length > 0) { // Edit the note data of any selected notes. From 040fc85a6263408646e6cac60c1a25da2c99fcaf Mon Sep 17 00:00:00 2001 From: lemz Date: Sun, 2 Jun 2024 23:31:49 +0200 Subject: [PATCH 165/469] change note style on change --- .../components/ChartEditorHoldNoteSprite.hx | 2 +- .../toolboxes/ChartEditorNoteDataToolbox.hx | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx index ccf462c565..26322c6f9d 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx @@ -63,7 +63,7 @@ class ChartEditorHoldNoteSprite extends SustainTrail function updateHoldNoteGraphic():Void { var bruhStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle); - this.setupHoldNoteGraphic(bruhStyle); + setupHoldNoteGraphic(bruhStyle); zoom = 1.0; zoom *= bruhStyle.fetchHoldNoteScale(); diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index f1223eb9c3..d872eda38b 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -4,6 +4,8 @@ import haxe.ui.components.DropDown; import haxe.ui.components.TextField; import haxe.ui.events.UIEvent; import funkin.ui.debug.charting.util.ChartEditorDropdowns; +import funkin.ui.debug.charting.components.ChartEditorNoteSprite; +import funkin.ui.debug.charting.components.ChartEditorHoldNoteSprite; import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.notekind.NoteKindManager; @@ -59,8 +61,32 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox if (!_initializing && chartEditorState.currentNoteSelection.length > 0) { // Edit the note data of any selected notes. + var noteSprites:Array = chartEditorState.renderedNotes.members.copy(); + var holdNoteSprites:Array = chartEditorState.renderedHoldNotes.members.copy(); for (note in chartEditorState.currentNoteSelection) { + // update note sprites + for (noteSprite in noteSprites) + { + if (noteSprite.noteData == note) + { + noteSprite.noteStyle = NoteKindManager.getNoteStyleId(chartEditorState.noteKindToPlace) ?? chartEditorState.currentSongNoteStyle; + noteSprites.remove(noteSprite); + break; + } + } + + // update hold note sprites + for (holdNoteSprite in holdNoteSprites) + { + if (holdNoteSprite.noteData == note) + { + holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(chartEditorState.noteKindToPlace) ?? chartEditorState.currentSongNoteStyle; + holdNoteSprites.remove(holdNoteSprite); + break; + } + } + note.kind = chartEditorState.noteKindToPlace; } chartEditorState.saveDataDirty = true; From bb975491712445a272f5e8817e34dacbd62a0d03 Mon Sep 17 00:00:00 2001 From: lemz Date: Mon, 3 Jun 2024 00:03:47 +0200 Subject: [PATCH 166/469] fixed a bit --- .../ui/debug/charting/ChartEditorState.hx | 11 ++++++----- .../components/ChartEditorHoldNoteSprite.hx | 2 ++ .../toolboxes/ChartEditorNoteDataToolbox.hx | 18 +++++++----------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index bc0acc6d60..c867ddfd2a 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -3606,10 +3606,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState holdNoteSprite.noteData = noteSprite.noteData; holdNoteSprite.noteDirection = noteSprite.noteData.getDirection(); - holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteSprite.noteData.kind) ?? currentSongNoteStyle; holdNoteSprite.setHeightDirectly(noteLengthPixels); + holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteSprite.noteData.kind) ?? currentSongNoteStyle; + holdNoteSprite.updateHoldNotePosition(renderedHoldNotes); trace(holdNoteSprite.x + ', ' + holdNoteSprite.y + ', ' + holdNoteSprite.width + ', ' + holdNoteSprite.height); @@ -3672,9 +3673,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState holdNoteSprite.noteData = noteData; holdNoteSprite.noteDirection = noteData.getDirection(); - holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind) ?? currentSongNoteStyle; holdNoteSprite.setHeightDirectly(noteLengthPixels); + holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind) ?? currentSongNoteStyle; + holdNoteSprite.updateHoldNotePosition(renderedHoldNotes); displayedHoldNoteData.push(noteData); @@ -4571,9 +4573,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState gridGhostHoldNote.visible = true; gridGhostHoldNote.noteData = currentPlaceNoteData; gridGhostHoldNote.noteDirection = currentPlaceNoteData.getDirection(); - gridGhostHoldNote.noteStyle = NoteKindManager.getNoteStyleId(currentPlaceNoteData.kind) ?? currentSongNoteStyle; gridGhostHoldNote.setHeightDirectly(dragLengthPixels, true); - + gridGhostHoldNote.noteStyle = NoteKindManager.getNoteStyleId(currentPlaceNoteData.kind) ?? currentSongNoteStyle; gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes); } else @@ -5284,10 +5285,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // Readd the new ghost hold note. ghostHold.noteData = targetNoteData.clone(); ghostHold.noteDirection = ghostHold.noteData.getDirection(); - ghostHold.noteStyle = NoteKindManager.getNoteStyleId(ghostHold.noteData.kind) ?? currentSongNoteStyle; ghostHold.visible = true; ghostHold.alpha = 0.6; ghostHold.setHeightDirectly(0); + ghostHold.noteStyle = NoteKindManager.getNoteStyleId(ghostHold.noteData.kind) ?? currentSongNoteStyle; ghostHold.updateHoldNotePosition(renderedHoldNotes); } diff --git a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx index 26322c6f9d..e8ca991b69 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx @@ -73,6 +73,8 @@ class ChartEditorHoldNoteSprite extends SustainTrail flipY = false; setup(); + + triggerRedraw(); } public override function updateHitbox():Void diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index d872eda38b..531bce255a 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -60,34 +60,30 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox if (!_initializing && chartEditorState.currentNoteSelection.length > 0) { - // Edit the note data of any selected notes. - var noteSprites:Array = chartEditorState.renderedNotes.members.copy(); - var holdNoteSprites:Array = chartEditorState.renderedHoldNotes.members.copy(); for (note in chartEditorState.currentNoteSelection) { + // Edit the note data of any selected notes. + note.kind = chartEditorState.noteKindToPlace; + // update note sprites - for (noteSprite in noteSprites) + for (noteSprite in chartEditorState.renderedNotes.members) { if (noteSprite.noteData == note) { - noteSprite.noteStyle = NoteKindManager.getNoteStyleId(chartEditorState.noteKindToPlace) ?? chartEditorState.currentSongNoteStyle; - noteSprites.remove(noteSprite); + noteSprite.noteStyle = NoteKindManager.getNoteStyleId(note.kind) ?? chartEditorState.currentSongNoteStyle; break; } } // update hold note sprites - for (holdNoteSprite in holdNoteSprites) + for (holdNoteSprite in chartEditorState.renderedHoldNotes.members) { if (holdNoteSprite.noteData == note) { - holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(chartEditorState.noteKindToPlace) ?? chartEditorState.currentSongNoteStyle; - holdNoteSprites.remove(holdNoteSprite); + holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(note.kind) ?? chartEditorState.currentSongNoteStyle; break; } } - - note.kind = chartEditorState.noteKindToPlace; } chartEditorState.saveDataDirty = true; chartEditorState.noteDisplayDirty = true; From 49d302be956edb19d470ccfdee3792d3fcdae324 Mon Sep 17 00:00:00 2001 From: lemz Date: Wed, 5 Jun 2024 19:47:34 +0200 Subject: [PATCH 167/469] make code a bit simpler --- .../components/ChartEditorHoldNoteSprite.hx | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx index e8ca991b69..e474ee93d5 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx @@ -48,15 +48,6 @@ class ChartEditorHoldNoteSprite extends SustainTrail super(0, 100, noteStyle); this.parentState = parent; - - zoom = 1.0; - zoom *= noteStyle.fetchHoldNoteScale(); - zoom *= 0.7; - zoom *= ChartEditorState.GRID_SIZE / Strumline.STRUMLINE_SIZE; - - flipY = false; - - setup(); } @:nullSafety(Off) @@ -64,17 +55,38 @@ class ChartEditorHoldNoteSprite extends SustainTrail { var bruhStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle); setupHoldNoteGraphic(bruhStyle); + } + + override function setupHoldNoteGraphic(noteStyle:NoteStyle):Void + { + loadGraphic(noteStyle.getHoldNoteAssetPath()); + + antialiasing = true; + + this.isPixel = noteStyle.isHoldNotePixel(); + if (isPixel) + { + endOffset = bottomClip = 1; + antialiasing = false; + } zoom = 1.0; - zoom *= bruhStyle.fetchHoldNoteScale(); + zoom *= noteStyle.fetchHoldNoteScale(); zoom *= 0.7; zoom *= ChartEditorState.GRID_SIZE / Strumline.STRUMLINE_SIZE; + graphicWidth = graphic.width / 8 * zoom; // amount of notes * 2 + graphicHeight = sustainLength * 0.45; // sustainHeight + flipY = false; - setup(); + alpha = 1.0; - triggerRedraw(); + updateColorTransform(); + + updateClipping(); + + setup(); } public override function updateHitbox():Void From c4855c0ca8e6e77d62b32b705c40ab0a0e106c11 Mon Sep 17 00:00:00 2001 From: lemz Date: Tue, 11 Jun 2024 20:52:08 +0200 Subject: [PATCH 168/469] fix hold note --- source/funkin/play/notes/SustainTrail.hx | 5 +++++ .../debug/charting/components/ChartEditorHoldNoteSprite.hx | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/source/funkin/play/notes/SustainTrail.hx b/source/funkin/play/notes/SustainTrail.hx index 570c05190b..90b36b0099 100644 --- a/source/funkin/play/notes/SustainTrail.hx +++ b/source/funkin/play/notes/SustainTrail.hx @@ -129,6 +129,11 @@ class SustainTrail extends FlxSprite endOffset = bottomClip = 1; antialiasing = false; } + else + { + endOffset = 0.5; + bottomClip = 0.9; + } zoom = 1.0; zoom *= noteStyle.fetchHoldNoteScale(); diff --git a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx index e474ee93d5..b8d6ee22e7 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx @@ -69,6 +69,11 @@ class ChartEditorHoldNoteSprite extends SustainTrail endOffset = bottomClip = 1; antialiasing = false; } + else + { + endOffset = 0.5; + bottomClip = 0.9; + } zoom = 1.0; zoom *= noteStyle.fetchHoldNoteScale(); From 928de7b8eb8861878b2e78a3502a8ee64b2784d9 Mon Sep 17 00:00:00 2001 From: lemz Date: Tue, 11 Jun 2024 23:45:08 +0200 Subject: [PATCH 169/469] check for pixel style if necessary --- source/funkin/play/notes/Strumline.hx | 4 ++-- source/funkin/play/notes/notekind/NoteKind.hx | 4 ++-- .../play/notes/notekind/NoteKindManager.hx | 21 +++++++++++++++---- .../ui/debug/charting/ChartEditorState.hx | 17 +++++++++------ 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index ebc48a8c74..86b7a3ee19 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -709,7 +709,7 @@ class Strumline extends FlxSpriteGroup if (noteSprite != null) { - var noteKindStyle:NoteStyle = NoteKindManager.getNoteStyle(note.kind) ?? this.noteStyle; + var noteKindStyle:NoteStyle = NoteKindManager.getNoteStyle(note.kind, this.noteStyle.isHoldNotePixel()) ?? this.noteStyle; noteSprite.setupNoteGraphic(noteKindStyle); noteSprite.direction = note.getDirection(); @@ -731,7 +731,7 @@ class Strumline extends FlxSpriteGroup if (holdNoteSprite != null) { - var noteKindStyle:NoteStyle = NoteKindManager.getNoteStyle(note.kind) ?? this.noteStyle; + var noteKindStyle:NoteStyle = NoteKindManager.getNoteStyle(note.kind, this.noteStyle.isHoldNotePixel()) ?? this.noteStyle; holdNoteSprite.setupHoldNoteGraphic(noteKindStyle); holdNoteSprite.parentStrumline = this; diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index 53393623af..6d7bad77fb 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -21,9 +21,9 @@ class NoteKind implements INoteScriptedClass /** * Custom note style */ - public var noteStyleId:String; + public var noteStyleId:Null; - public function new(noteKind:String, description:String = "", noteStyleId:String = "") + public function new(noteKind:String, description:String = "", ?noteStyleId:String) { this.noteKind = noteKind; this.description = description; diff --git a/source/funkin/play/notes/notekind/NoteKindManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx index 82b2c4d396..110e1859b9 100644 --- a/source/funkin/play/notes/notekind/NoteKindManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -65,11 +65,17 @@ class NoteKindManager /** * Retrieve the note style from the given note kind * @param noteKind note kind name + * @param isPixel whether to use pixel style * @return NoteStyle */ - public static function getNoteStyle(noteKind:String):Null + public static function getNoteStyle(noteKind:String, isPixel:Bool = false):Null { - var noteStyleId:String = noteKinds.get(noteKind)?.noteStyleId ?? ""; + var noteStyleId:Null = getNoteStyleId(noteKind, isPixel); + + if (noteStyleId == null) + { + return null; + } return NoteStyleRegistry.instance.fetchEntry(noteStyleId); } @@ -77,10 +83,17 @@ class NoteKindManager /** * Retrieve the note style id from the given note kind * @param noteKind note kind name + * @param isPixel whether to use pixel style * @return Null */ - public static function getNoteStyleId(noteKind:String):Null + public static function getNoteStyleId(noteKind:String, isPixel:Bool = false):Null { - return noteKinds.get(noteKind)?.noteStyleId; + var noteStyleId:Null = noteKinds.get(noteKind)?.noteStyleId; + if (isPixel && noteStyleId != null) + { + noteStyleId = NoteStyleRegistry.instance.hasEntry('$noteStyleId-pixel') ? '$noteStyleId-pixel' : noteStyleId; + } + + return noteStyleId; } } diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index c867ddfd2a..2a07be52d3 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -3585,7 +3585,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // The note sprite handles animation playback and positioning. noteSprite.noteData = noteData; - noteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind) ?? currentSongNoteStyle; + noteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind, isPixelStyle()) ?? currentSongNoteStyle; noteSprite.overrideStepTime = null; noteSprite.overrideData = null; @@ -3609,7 +3609,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState holdNoteSprite.setHeightDirectly(noteLengthPixels); - holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteSprite.noteData.kind) ?? currentSongNoteStyle; + holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteSprite.noteData.kind, isPixelStyle()) ?? currentSongNoteStyle; holdNoteSprite.updateHoldNotePosition(renderedHoldNotes); @@ -3675,7 +3675,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState holdNoteSprite.noteDirection = noteData.getDirection(); holdNoteSprite.setHeightDirectly(noteLengthPixels); - holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind) ?? currentSongNoteStyle; + holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind, isPixelStyle()) ?? currentSongNoteStyle; holdNoteSprite.updateHoldNotePosition(renderedHoldNotes); @@ -4574,7 +4574,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState gridGhostHoldNote.noteData = currentPlaceNoteData; gridGhostHoldNote.noteDirection = currentPlaceNoteData.getDirection(); gridGhostHoldNote.setHeightDirectly(dragLengthPixels, true); - gridGhostHoldNote.noteStyle = NoteKindManager.getNoteStyleId(currentPlaceNoteData.kind) ?? currentSongNoteStyle; + gridGhostHoldNote.noteStyle = NoteKindManager.getNoteStyleId(currentPlaceNoteData.kind, isPixelStyle()) ?? currentSongNoteStyle; gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes); } else @@ -4896,7 +4896,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState { noteData.kind = noteKindToPlace; noteData.data = cursorColumn; - gridGhostNote.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind) ?? currentSongNoteStyle; + gridGhostNote.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind, isPixelStyle()) ?? currentSongNoteStyle; gridGhostNote.playNoteAnimation(); } noteData.time = cursorSnappedMs; @@ -5288,7 +5288,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState ghostHold.visible = true; ghostHold.alpha = 0.6; ghostHold.setHeightDirectly(0); - ghostHold.noteStyle = NoteKindManager.getNoteStyleId(ghostHold.noteData.kind) ?? currentSongNoteStyle; + ghostHold.noteStyle = NoteKindManager.getNoteStyleId(ghostHold.noteData.kind, isPixelStyle()) ?? currentSongNoteStyle; ghostHold.updateHoldNotePosition(renderedHoldNotes); } @@ -6413,6 +6413,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState return note != null && currentNoteSelection.indexOf(note) != -1; } + function isPixelStyle():Bool + { + return currentSongNoteStyle == 'pixel'; + } + override function destroy():Void { super.destroy(); From 7a0c7ade357a7e6151295b182650dd807e52390f Mon Sep 17 00:00:00 2001 From: lemz Date: Tue, 11 Jun 2024 23:48:05 +0200 Subject: [PATCH 170/469] Update ChartEditorDropdowns.hx --- source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx b/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx index f20b756506..65ec2a0c3b 100644 --- a/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx +++ b/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx @@ -126,7 +126,10 @@ class ChartEditorDropdowns { dropDown.dataSource.clear(); - var noteStyleIds:Array = NoteStyleRegistry.instance.listEntryIds(); + // hardcoding this because i dont want note kind styles to be shown as well + // there is probably a better solution + // var noteStyleIds:Array = NoteStyleRegistry.instance.listEntryIds(); + var noteStyleIds:Array = ['funkin', 'pixel']; var returnValue:DropDownEntry = {id: "funkin", text: "Funkin'"}; From fbcc73dceebdcfcba2b14922c6b7c38adae0b086 Mon Sep 17 00:00:00 2001 From: lemz Date: Mon, 17 Jun 2024 17:21:52 +0200 Subject: [PATCH 171/469] unhardcode notestyledropdown --- .../ui/debug/charting/util/ChartEditorDropdowns.hx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx b/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx index 65ec2a0c3b..6a426c3911 100644 --- a/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx +++ b/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx @@ -126,10 +126,7 @@ class ChartEditorDropdowns { dropDown.dataSource.clear(); - // hardcoding this because i dont want note kind styles to be shown as well - // there is probably a better solution - // var noteStyleIds:Array = NoteStyleRegistry.instance.listEntryIds(); - var noteStyleIds:Array = ['funkin', 'pixel']; + var noteStyleIds:Array = NoteStyleRegistry.instance.listEntryIds(); var returnValue:DropDownEntry = {id: "funkin", text: "Funkin'"}; @@ -138,6 +135,14 @@ class ChartEditorDropdowns var noteStyle:Null = NoteStyleRegistry.instance.fetchEntry(noteStyleId); if (noteStyle == null) continue; + // check if the note style has all necessary assets (strums, notes, holdNotes) + if (noteStyle._data?.assets?.noteStrumline == null + || noteStyle._data?.assets?.note == null + || noteStyle._data?.assets?.holdNote == null) + { + continue; + } + var value = {id: noteStyleId, text: noteStyle.getName()}; if (startingStyleId == noteStyleId) returnValue = value; From acd8912d162d71b3dd11d97e112e64da06815ec9 Mon Sep 17 00:00:00 2001 From: lemz Date: Fri, 21 Jun 2024 23:54:29 +0200 Subject: [PATCH 172/469] Add Custom Params For NoteKind still need to implement the chart editor stuff --- source/funkin/play/notes/notekind/NoteKind.hx | 70 ++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index 6d7bad77fb..6d7ddcd1bb 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -23,11 +23,17 @@ class NoteKind implements INoteScriptedClass */ public var noteStyleId:Null; - public function new(noteKind:String, description:String = "", ?noteStyleId:String) + /** + * Custom parameters for the chart editor + */ + public var params:Array; + + public function new(noteKind:String, description:String = "", ?noteStyleId:String, ?params:Array) { this.noteKind = noteKind; this.description = description; this.noteStyleId = noteStyleId; + this.params = params ?? []; } public function toString():String @@ -35,6 +41,25 @@ class NoteKind implements INoteScriptedClass return noteKind; } + /** + * Retrieve the param with the given name + * If there exists no param with the given name then `null` is returned + * @param name Name of the param + * @return Null + */ + public function getParam(name:String):Null + { + for (param in params) + { + if (param.name == name) + { + return param; + } + } + + return null; + } + /** * Retrieve all notes of this kind * @return Array @@ -61,3 +86,46 @@ class NoteKind implements INoteScriptedClass public function onNoteMiss(event:NoteScriptEvent):Void {} } + +/** + * Abstract for setting the type of the `NoteKindParam` + * This was supposed to be an enum but polymod kept being annoying + */ +abstract NoteKindParamType(String) +{ + public static var STRING:String = "String"; + + public static var INT:String = "Int"; + + public static var RANGED_INT:String = "RangedInt"; + + public static var FLOAT:String = "Float"; + + public static var RANGED_FLOAT:String = "RangedFloat"; +} + +typedef NoteKindParamData = +{ + /** + * Only used for `RangedInt` and `RangedFloat` + */ + var min:Null; + + /** + * Only used for `RangedInt` and `RangedFloat` + */ + var max:Null; + + var value:Dynamic; +} + +/** + * Typedef for creating custom parameters in the chart editor + */ +typedef NoteKindParam = +{ + var name:String; + var description:String; + var type:NoteKindParamType; + var data:NoteKindParamData; +} From c8d019da2fdcc22627dbfb78c587a5374273f6b6 Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 22 Jun 2024 00:20:58 +0200 Subject: [PATCH 173/469] Update NoteKind.hx --- source/funkin/play/notes/notekind/NoteKind.hx | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index 6d7ddcd1bb..3aa02088be 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -2,6 +2,7 @@ package funkin.play.notes.notekind; import funkin.modding.IScriptedClass.INoteScriptedClass; import funkin.modding.events.ScriptEvent; +import flixel.math.FlxMath; /** * Class for note scripts @@ -42,24 +43,49 @@ class NoteKind implements INoteScriptedClass } /** - * Retrieve the param with the given name + * Retrieve the value of the param with the given name * If there exists no param with the given name then `null` is returned * @param name Name of the param - * @return Null + * @return Null */ - public function getParam(name:String):Null + public function getParam(name:String):Null { for (param in params) { if (param.name == name) { - return param; + return param.data.value; } } return null; } + /** + * Set the value of the param with the given name + * @param name Name of the param + * @param value New value + */ + public function setParam(name:String, value:Dynamic):Void + { + for (param in params) + { + if (param.name == name) + { + if (param.type == NoteKindParamType.RANGED_INT || param.type == NoteKindParamType.RANGED_FLOAT) + { + param.data.value = FlxMath.bound(value, param.data.min, param.data.max); + } + else + { + param.data.value = value; + } + + break; + } + } + } + /** * Retrieve all notes of this kind * @return Array @@ -91,7 +117,7 @@ class NoteKind implements INoteScriptedClass * Abstract for setting the type of the `NoteKindParam` * This was supposed to be an enum but polymod kept being annoying */ -abstract NoteKindParamType(String) +abstract NoteKindParamType(String) to String { public static var STRING:String = "String"; @@ -108,11 +134,13 @@ typedef NoteKindParamData = { /** * Only used for `RangedInt` and `RangedFloat` + * If `min` is null, there is no minimum */ var min:Null; /** * Only used for `RangedInt` and `RangedFloat` + * If `max` is null, there is no maximum */ var max:Null; From 94fe4a06b90089fb8187c177c39cb255c90609fb Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 22 Jun 2024 10:44:12 +0200 Subject: [PATCH 174/469] Update NoteKind.hx --- source/funkin/play/notes/notekind/NoteKind.hx | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index 3aa02088be..a0f7599490 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -72,13 +72,12 @@ class NoteKind implements INoteScriptedClass { if (param.name == name) { - if (param.type == NoteKindParamType.RANGED_INT || param.type == NoteKindParamType.RANGED_FLOAT) + switch (param.type) { - param.data.value = FlxMath.bound(value, param.data.min, param.data.max); - } - else - { - param.data.value = value; + case NoteKindParamType.INT | NoteKindParamType.FLOAT: + param.data.value = FlxMath.bound(value, param.data.min, param.data.max); + default: + param.data.value = value; } break; @@ -123,23 +122,17 @@ abstract NoteKindParamType(String) to String public static var INT:String = "Int"; - public static var RANGED_INT:String = "RangedInt"; - public static var FLOAT:String = "Float"; - - public static var RANGED_FLOAT:String = "RangedFloat"; } typedef NoteKindParamData = { /** - * Only used for `RangedInt` and `RangedFloat` * If `min` is null, there is no minimum */ var min:Null; /** - * Only used for `RangedInt` and `RangedFloat` * If `max` is null, there is no maximum */ var max:Null; From 9a563ec46b56e4daacd70f301c1d107347e6738f Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 22 Jun 2024 10:52:26 +0200 Subject: [PATCH 175/469] switch seemingly doesnt work --- source/funkin/play/notes/notekind/NoteKind.hx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index a0f7599490..f2a44dc8ad 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -72,12 +72,13 @@ class NoteKind implements INoteScriptedClass { if (param.name == name) { - switch (param.type) + if (param.type == NoteKindParamType.INT || param.type == NoteKindParamType.FLOAT) { - case NoteKindParamType.INT | NoteKindParamType.FLOAT: - param.data.value = FlxMath.bound(value, param.data.min, param.data.max); - default: - param.data.value = value; + param.data.value = FlxMath.bound(value, param.data.min, param.data.max); + } + else + { + param.data.value = value; } break; @@ -118,11 +119,11 @@ class NoteKind implements INoteScriptedClass */ abstract NoteKindParamType(String) to String { - public static var STRING:String = "String"; + public static final STRING:String = 'String'; - public static var INT:String = "Int"; + public static final INT:String = 'Int'; - public static var FLOAT:String = "Float"; + public static final FLOAT:String = 'Float'; } typedef NoteKindParamData = From d9b9854d9106d20d2ea7ec75468fcc315b281e98 Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 22 Jun 2024 14:17:07 +0200 Subject: [PATCH 176/469] add params to toolbox not completely finished --- .../play/notes/notekind/NoteKindManager.hx | 10 +++ .../toolboxes/ChartEditorNoteDataToolbox.hx | 73 ++++++++++++++++++- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/source/funkin/play/notes/notekind/NoteKindManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx index 110e1859b9..3e2174a669 100644 --- a/source/funkin/play/notes/notekind/NoteKindManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -96,4 +96,14 @@ class NoteKindManager return noteStyleId; } + + /** + * Retrive custom params of the given note kind + * @param noteKind Name of the note kind + * @return Array + */ + public static function getParams(noteKind:String):Array + { + return noteKinds.get(noteKind)?.params ?? []; + } } diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index 531bce255a..472372a6e9 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -2,11 +2,12 @@ package funkin.ui.debug.charting.toolboxes; import haxe.ui.components.DropDown; import haxe.ui.components.TextField; +import haxe.ui.components.Label; +import haxe.ui.components.NumberStepper; +import haxe.ui.containers.Grid; +import haxe.ui.core.Component; import haxe.ui.events.UIEvent; import funkin.ui.debug.charting.util.ChartEditorDropdowns; -import funkin.ui.debug.charting.components.ChartEditorNoteSprite; -import funkin.ui.debug.charting.components.ChartEditorHoldNoteSprite; -import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.notekind.NoteKindManager; /** @@ -16,8 +17,12 @@ import funkin.play.notes.notekind.NoteKindManager; @:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/toolboxes/note-data.xml")) class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox { + static final DIALOG_HEIGHT:Int = 100; + + var toolboxNotesGrid:Grid; var toolboxNotesNoteKind:DropDown; var toolboxNotesCustomKind:TextField; + var toolboxNotesParams:Array = []; var _initializing:Bool = true; @@ -49,6 +54,7 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox if (noteKind == '~CUSTOM~') { showCustom(); + clearNoteKindParams(); toolboxNotesCustomKind.value = chartEditorState.noteKindToPlace; } else @@ -56,6 +62,25 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox hideCustom(); chartEditorState.noteKindToPlace = noteKind; toolboxNotesCustomKind.value = chartEditorState.noteKindToPlace; + + clearNoteKindParams(); + for (param in NoteKindManager.getParams(noteKind)) + { + var paramLabel:Label = new Label(); + paramLabel.value = param.description; + paramLabel.verticalAlign = "center"; + paramLabel.horizontalAlign = "right"; + + var paramStepper:NumberStepper = new NumberStepper(); + paramStepper.min = param.data.min; + paramStepper.max = param.data.max; + paramStepper.value = param.data.value; + paramStepper.precision = 1; + paramStepper.step = 0.1; + paramStepper.percentWidth = 100; + + addNoteKindParam(paramLabel, paramStepper); + } } if (!_initializing && chartEditorState.currentNoteSelection.length > 0) @@ -110,6 +135,9 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox } }; toolboxNotesCustomKind.value = chartEditorState.noteKindToPlace; + + // just to be safe + clearNoteKindParams(); } public override function refresh():Void @@ -132,8 +160,47 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox toolboxNotesCustomKind.hidden = true; } + function addNoteKindParam(label:Label, component:Component):Void + { + toolboxNotesParams.push({label: label, component: component}); + toolboxNotesGrid.addComponent(label); + toolboxNotesGrid.addComponent(component); + + this.height = Math.max(DIALOG_HEIGHT, DIALOG_HEIGHT - 30 + toolboxNotesParams.length * 30); + } + + override function update(elapsed:Float):Void + { + super.update(elapsed); + + // toolboxNotesGrid.height + 45 + // this is what i found out is the calculation by printing this.height and grid.height + var heightToSet:Int = Std.int(Math.max(DIALOG_HEIGHT, toolboxNotesGrid.height + 45)); + if (this.height != heightToSet) + { + this.height = heightToSet; + } + } + + function clearNoteKindParams():Void + { + for (param in toolboxNotesParams) + { + toolboxNotesGrid.removeComponent(param.component); + toolboxNotesGrid.removeComponent(param.label); + } + toolboxNotesParams = []; + this.height = DIALOG_HEIGHT; + } + public static function build(chartEditorState:ChartEditorState):ChartEditorNoteDataToolbox { return new ChartEditorNoteDataToolbox(chartEditorState); } } + +typedef ToolboxNoteKindParam = +{ + var label:Label; + var component:Component; +} From 93475ae8aa94ff5d12872dd5ebe4b1e207d3d85e Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 22 Jun 2024 14:31:07 +0200 Subject: [PATCH 177/469] minimized check --- .../charting/toolboxes/ChartEditorNoteDataToolbox.hx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index 472372a6e9..c557eef1f6 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -135,9 +135,6 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox } }; toolboxNotesCustomKind.value = chartEditorState.noteKindToPlace; - - // just to be safe - clearNoteKindParams(); } public override function refresh():Void @@ -173,6 +170,12 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox { super.update(elapsed); + // current dialog is minimized, dont change the height + if (this.minimized) + { + return; + } + // toolboxNotesGrid.height + 45 // this is what i found out is the calculation by printing this.height and grid.height var heightToSet:Int = Std.int(Math.max(DIALOG_HEIGHT, toolboxNotesGrid.height + 45)); From 437cc68ba7af70f4060efc739dfc6d3ff21bbeed Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 22 Jun 2024 14:49:04 +0200 Subject: [PATCH 178/469] adapt to minimize problem --- .../toolboxes/ChartEditorNoteDataToolbox.hx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index c557eef1f6..278fc1fd6f 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -17,8 +17,18 @@ import funkin.play.notes.notekind.NoteKindManager; @:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/toolboxes/note-data.xml")) class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox { + // 100 is the height used in note-data.xml static final DIALOG_HEIGHT:Int = 100; + // toolboxNotesGrid.height + 45 + // this is what i found out by printing this.height and grid.height + // and then seeing that this.height is 100 and grid.height is 55 + static final HEIGHT_OFFSET:Int = 45; + + // minimizing creates a gray bar the bottom, which would obscure the components, + // which is why we use an extra offset of 20 + static final MINIMIZE_FIX:Int = 20; + var toolboxNotesGrid:Grid; var toolboxNotesNoteKind:DropDown; var toolboxNotesCustomKind:TextField; @@ -176,9 +186,7 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox return; } - // toolboxNotesGrid.height + 45 - // this is what i found out is the calculation by printing this.height and grid.height - var heightToSet:Int = Std.int(Math.max(DIALOG_HEIGHT, toolboxNotesGrid.height + 45)); + var heightToSet:Int = Std.int(Math.max(DIALOG_HEIGHT, toolboxNotesGrid.height + HEIGHT_OFFSET)) + MINIMIZE_FIX; if (this.height != heightToSet) { this.height = heightToSet; From 0fe726cefa926b2e4de6963acc0284090e2fed99 Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 22 Jun 2024 15:38:04 +0200 Subject: [PATCH 179/469] fix min, max --- source/funkin/play/notes/notekind/NoteKind.hx | 58 +++---------------- .../toolboxes/ChartEditorNoteDataToolbox.hx | 14 ++++- 2 files changed, 18 insertions(+), 54 deletions(-) diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index f2a44dc8ad..7efa93de65 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -42,50 +42,6 @@ class NoteKind implements INoteScriptedClass return noteKind; } - /** - * Retrieve the value of the param with the given name - * If there exists no param with the given name then `null` is returned - * @param name Name of the param - * @return Null - */ - public function getParam(name:String):Null - { - for (param in params) - { - if (param.name == name) - { - return param.data.value; - } - } - - return null; - } - - /** - * Set the value of the param with the given name - * @param name Name of the param - * @param value New value - */ - public function setParam(name:String, value:Dynamic):Void - { - for (param in params) - { - if (param.name == name) - { - if (param.type == NoteKindParamType.INT || param.type == NoteKindParamType.FLOAT) - { - param.data.value = FlxMath.bound(value, param.data.min, param.data.max); - } - else - { - param.data.value = value; - } - - break; - } - } - } - /** * Retrieve all notes of this kind * @return Array @@ -131,14 +87,14 @@ typedef NoteKindParamData = /** * If `min` is null, there is no minimum */ - var min:Null; + ?min:Null, /** * If `max` is null, there is no maximum */ - var max:Null; + ?max:Null, - var value:Dynamic; + defaultValue:Dynamic } /** @@ -146,8 +102,8 @@ typedef NoteKindParamData = */ typedef NoteKindParam = { - var name:String; - var description:String; - var type:NoteKindParamType; - var data:NoteKindParamData; + name:String, + description:String, + type:NoteKindParamType, + data:NoteKindParamData } diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index 278fc1fd6f..751af5dff6 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -82,13 +82,21 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox paramLabel.horizontalAlign = "right"; var paramStepper:NumberStepper = new NumberStepper(); - paramStepper.min = param.data.min; - paramStepper.max = param.data.max; - paramStepper.value = param.data.value; + paramStepper.value = param.data.defaultValue; paramStepper.precision = 1; paramStepper.step = 0.1; paramStepper.percentWidth = 100; + // this check should be unnecessary but for some reason even when min or max is null it will set it to 0 + if (param.data.min != null) + { + paramStepper.min = param.data.min; + } + if (param.data.max != null) + { + paramStepper.max = param.data.max; + } + addNoteKindParam(paramLabel, paramStepper); } } From 764cdee63de762395f070916369a99bc6d81c80d Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 22 Jun 2024 15:48:07 +0200 Subject: [PATCH 180/469] add more options --- source/funkin/play/notes/notekind/NoteKind.hx | 10 ++++++++++ .../charting/toolboxes/ChartEditorNoteDataToolbox.hx | 10 +++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index 7efa93de65..a06670503e 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -94,6 +94,16 @@ typedef NoteKindParamData = */ ?max:Null, + /** + * If `step` is null, it will use 1.0 + */ + ?step:Null, + + /** + * If `precision` is null, there will be 0 decimal places + */ + ?precision:Null, + defaultValue:Dynamic } diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index 751af5dff6..26e495dcce 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -83,11 +83,11 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox var paramStepper:NumberStepper = new NumberStepper(); paramStepper.value = param.data.defaultValue; - paramStepper.precision = 1; - paramStepper.step = 0.1; paramStepper.percentWidth = 100; + paramStepper.step = param.data.step ?? 1; - // this check should be unnecessary but for some reason even when min or max is null it will set it to 0 + // this check should be unnecessary but for some reason + // even when these are null it will set it to 0 if (param.data.min != null) { paramStepper.min = param.data.min; @@ -96,6 +96,10 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox { paramStepper.max = param.data.max; } + if (param.data.precision != null) + { + paramStepper.precision = param.data.precision; + } addNoteKindParam(paramLabel, paramStepper); } From c41d846df5dfd5331dad37e71aa4d2b18595b3a9 Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 22 Jun 2024 20:49:30 +0200 Subject: [PATCH 181/469] store params in chart chart editor still doesnt fully work --- source/funkin/data/song/SongData.hx | 41 +++++++++++++++++-- source/funkin/play/notes/NoteSprite.hx | 34 +++++++++++++++ .../play/notes/notekind/NoteKindManager.hx | 5 ++- .../ui/debug/charting/ChartEditorState.hx | 19 ++++++--- .../toolboxes/ChartEditorNoteDataToolbox.hx | 24 ++++++++++- 5 files changed, 111 insertions(+), 12 deletions(-) diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx index 769af8f08a..6bbeb44353 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -951,12 +951,18 @@ class SongNoteDataRaw implements ICloneable return this.kind = value; } - public function new(time:Float, data:Int, length:Float = 0, kind:String = '') + @:alias("p") + @:default([]) + @:optional + public var params:Array; + + public function new(time:Float, data:Int, length:Float = 0, kind:String = '', ?params:Array) { this.time = time; this.data = data; this.length = length; this.kind = kind; + this.params = params ?? []; } /** @@ -1053,7 +1059,7 @@ class SongNoteDataRaw implements ICloneable public function clone():SongNoteDataRaw { - return new SongNoteDataRaw(this.time, this.data, this.length, this.kind); + return new SongNoteDataRaw(this.time, this.data, this.length, this.kind, this.params); } public function toString():String @@ -1069,9 +1075,9 @@ class SongNoteDataRaw implements ICloneable @:forward abstract SongNoteData(SongNoteDataRaw) from SongNoteDataRaw to SongNoteDataRaw { - public function new(time:Float, data:Int, length:Float = 0, kind:String = '') + public function new(time:Float, data:Int, length:Float = 0, kind:String = '', ?params:Array) { - this = new SongNoteDataRaw(time, data, length, kind); + this = new SongNoteDataRaw(time, data, length, kind, params); } public static function buildDirectionName(data:Int, strumlineSize:Int = 4):String @@ -1183,3 +1189,30 @@ abstract SongNoteData(SongNoteDataRaw) from SongNoteDataRaw to SongNoteDataRaw + (this.kind != '' ? ' [kind: ${this.kind}])' : ')'); } } + +class NoteParamData implements ICloneable +{ + @:alias("n") + public var name:String; + + @:alias("v") + @:jcustomparse(funkin.data.DataParse.dynamicValue) + @:jcustomwrite(funkin.data.DataWrite.dynamicValue) + public var value:Dynamic; + + public function new(name:String, value:Dynamic) + { + this.name = name; + this.value = value; + } + + public function clone():NoteParamData + { + return new NoteParamData(this.name, this.value); + } + + public function toString():String + { + return 'NoteParamData(${this.name}, ${this.value})'; + } +} diff --git a/source/funkin/play/notes/NoteSprite.hx b/source/funkin/play/notes/NoteSprite.hx index 17a5e57fcf..d8d4714968 100644 --- a/source/funkin/play/notes/NoteSprite.hx +++ b/source/funkin/play/notes/NoteSprite.hx @@ -1,6 +1,7 @@ package funkin.play.notes; import funkin.data.song.SongData.SongNoteData; +import funkin.data.song.SongData.NoteParamData; import funkin.play.notes.notestyle.NoteStyle; import flixel.graphics.frames.FlxAtlasFrames; import flixel.FlxSprite; @@ -65,6 +66,22 @@ class NoteSprite extends FunkinSprite return this.noteData.kind = value; } + /** + * An array of custom parameters for this note + */ + public var params(get, set):Array; + + function get_params():Array + { + return this.noteData?.params ?? []; + } + + function set_params(value:Array):Array + { + if (this.noteData == null) return value; + return this.noteData.params = value; + } + /** * The data of the note (i.e. the direction.) */ @@ -154,6 +171,23 @@ class NoteSprite extends FunkinSprite this.shader = hsvShader; } + /** + * Retrieve the value of the param with the given name + * @param name Name of the param + * @return Null + */ + public function getParam(name:String):Null + { + for (param in params) + { + if (param.name == name) + { + return param.value; + } + } + return null; + } + #if FLX_DEBUG /** * Call this to override how debug bounding boxes are drawn for this sprite. diff --git a/source/funkin/play/notes/notekind/NoteKindManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx index 3e2174a669..30eede9789 100644 --- a/source/funkin/play/notes/notekind/NoteKindManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -5,6 +5,7 @@ import funkin.modding.events.ScriptEvent; import funkin.ui.debug.charting.util.ChartEditorDropdowns; import funkin.data.notestyle.NoteStyleRegistry; import funkin.play.notes.notestyle.NoteStyle; +import funkin.play.notes.notekind.NoteKind.NoteKindParam; class NoteKindManager { @@ -100,9 +101,9 @@ class NoteKindManager /** * Retrive custom params of the given note kind * @param noteKind Name of the note kind - * @return Array + * @return Array */ - public static function getParams(noteKind:String):Array + public static function getParams(noteKind:String):Array { return noteKinds.get(noteKind)?.params ?? []; } diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 2a07be52d3..22de29849e 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -35,6 +35,7 @@ import funkin.data.song.SongData.SongEventData; import funkin.data.song.SongData.SongMetadata; import funkin.data.song.SongData.SongNoteData; import funkin.data.song.SongData.SongOffsets; +import funkin.data.song.SongData.NoteParamData; import funkin.data.song.SongDataUtils; import funkin.data.song.SongRegistry; import funkin.data.stage.StageData; @@ -539,6 +540,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ var noteKindToPlace:Null = null; + /** + * The note params to use for notes being placed in the chart. Defaults to `[]`. + */ + var noteParamsToPlace:Array = []; + /** * The event type to use for events being placed in the chart. Defaults to `''`. */ @@ -2437,7 +2443,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState gridGhostNote = new ChartEditorNoteSprite(this); gridGhostNote.alpha = 0.6; - gridGhostNote.noteData = new SongNoteData(0, 0, 0, ""); + gridGhostNote.noteData = new SongNoteData(0, 0, 0, "", []); gridGhostNote.visible = false; add(gridGhostNote); gridGhostNote.zIndex = 11; @@ -4731,7 +4737,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState else { // Create a note and place it in the chart. - var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, noteKindToPlace); + var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, noteKindToPlace, noteParamsToPlace.clone()); performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL)); @@ -4890,11 +4896,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState if (gridGhostNote == null) throw "ERROR: Tried to handle cursor, but gridGhostNote is null! Check ChartEditorState.buildGrid()"; - var noteData:SongNoteData = gridGhostNote.noteData != null ? gridGhostNote.noteData : new SongNoteData(cursorMs, cursorColumn, 0, noteKindToPlace); + var noteData:SongNoteData = gridGhostNote.noteData != null ? gridGhostNote.noteData : new SongNoteData(cursorMs, cursorColumn, 0, noteKindToPlace, + noteParamsToPlace.clone()); - if (cursorColumn != noteData.data || noteKindToPlace != noteData.kind) + if (cursorColumn != noteData.data || noteKindToPlace != noteData.kind || noteParamsToPlace != noteData.params) { noteData.kind = noteKindToPlace; + noteData.params = noteParamsToPlace; noteData.data = cursorColumn; gridGhostNote.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind, isPixelStyle()) ?? currentSongNoteStyle; gridGhostNote.playNoteAnimation(); @@ -5202,7 +5210,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState if (notesAtPos.length == 0 && !removeNoteInstead) { trace('Placing note. ${column}'); - var newNoteData:SongNoteData = new SongNoteData(playheadPosSnappedMs, column, 0, noteKindToPlace); + var newNoteData:SongNoteData = new SongNoteData(playheadPosSnappedMs, column, 0, noteKindToPlace, noteParamsToPlace.clone()); performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL)); currentLiveInputPlaceNoteData[column] = newNoteData; } @@ -5655,6 +5663,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState FlxG.watch.addQuick('musicTime', audioInstTrack?.time ?? 0.0); FlxG.watch.addQuick('noteKindToPlace', noteKindToPlace); + FlxG.watch.addQuick('noteParamsToPlace', noteParamsToPlace); FlxG.watch.addQuick('eventKindToPlace', eventKindToPlace); FlxG.watch.addQuick('scrollPosInPixels', scrollPositionInPixels); diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index 26e495dcce..027ffdf816 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -9,6 +9,8 @@ import haxe.ui.core.Component; import haxe.ui.events.UIEvent; import funkin.ui.debug.charting.util.ChartEditorDropdowns; import funkin.play.notes.notekind.NoteKindManager; +import funkin.play.notes.notekind.NoteKind.NoteKindParam; +import funkin.data.song.SongData.NoteParamData; /** * The toolbox which allows modifying information like Note Kind. @@ -60,6 +62,13 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox trace('ChartEditorToolboxHandler.buildToolboxNoteDataLayout() - Note kind changed: $noteKind'); + var noteKindParams:Array = NoteKindManager.getParams(noteKind); + var noteParamData:Array = []; + for (noteKindParam in noteKindParams) + { + noteParamData.push(new NoteParamData(noteKindParam.name, noteKindParam.data.defaultValue)); + } + // Edit the note data to place. if (noteKind == '~CUSTOM~') { @@ -71,10 +80,11 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox { hideCustom(); chartEditorState.noteKindToPlace = noteKind; + chartEditorState.noteParamsToPlace = noteParamData; toolboxNotesCustomKind.value = chartEditorState.noteKindToPlace; clearNoteKindParams(); - for (param in NoteKindManager.getParams(noteKind)) + for (param in noteKindParams) { var paramLabel:Label = new Label(); paramLabel.value = param.description; @@ -107,10 +117,22 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox if (!_initializing && chartEditorState.currentNoteSelection.length > 0) { + for (i in 0...toolboxNotesParams.length) + { + var toolboxComponent:Component = toolboxNotesParams[i].component; + toolboxComponent.onChange = function(event:UIEvent) { + for (note in chartEditorState.currentNoteSelection) + { + note.params[i].value = toolboxComponent.value; + } + } + } + for (note in chartEditorState.currentNoteSelection) { // Edit the note data of any selected notes. note.kind = chartEditorState.noteKindToPlace; + note.params = noteParamData.clone(); // update note sprites for (noteSprite in chartEditorState.renderedNotes.members) From 44d978531727eba19e694ac0bed8e0f1f9c76306 Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 22 Jun 2024 22:36:39 +0200 Subject: [PATCH 182/469] editing them works now still need to implement String and do some testing --- source/funkin/data/song/SongData.hx | 18 +- .../play/notes/notekind/NoteKindManager.hx | 7 +- .../ui/debug/charting/ChartEditorState.hx | 17 +- .../toolboxes/ChartEditorNoteDataToolbox.hx | 157 +++++++++++------- 4 files changed, 128 insertions(+), 71 deletions(-) diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx index 6bbeb44353..7bf3f8f19e 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -1057,9 +1057,19 @@ class SongNoteDataRaw implements ICloneable _stepLength = null; } + public function cloneParams():Array + { + var params:Array = []; + for (param in this.params) + { + params.push(param.clone()); + } + return params; + } + public function clone():SongNoteDataRaw { - return new SongNoteDataRaw(this.time, this.data, this.length, this.kind, this.params); + return new SongNoteDataRaw(this.time, this.data, this.length, this.kind, cloneParams()); } public function toString():String @@ -1121,7 +1131,7 @@ abstract SongNoteData(SongNoteDataRaw) from SongNoteDataRaw to SongNoteDataRaw if (other.kind == '' || this.kind == null) return false; } - return this.time == other.time && this.data == other.data && this.length == other.length; + return this.time == other.time && this.data == other.data && this.length == other.length && this.params == other.params; } @:op(A != B) @@ -1140,7 +1150,7 @@ abstract SongNoteData(SongNoteDataRaw) from SongNoteDataRaw to SongNoteDataRaw if (other.kind == '') return true; } - return this.time != other.time || this.data != other.data || this.length != other.length; + return this.time != other.time || this.data != other.data || this.length != other.length || this.params != other.params; } @:op(A > B) @@ -1177,7 +1187,7 @@ abstract SongNoteData(SongNoteDataRaw) from SongNoteDataRaw to SongNoteDataRaw public function clone():SongNoteData { - return new SongNoteData(this.time, this.data, this.length, this.kind); + return new SongNoteData(this.time, this.data, this.length, this.kind, this.params); } /** diff --git a/source/funkin/play/notes/notekind/NoteKindManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx index 30eede9789..8de3cdccac 100644 --- a/source/funkin/play/notes/notekind/NoteKindManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -103,8 +103,13 @@ class NoteKindManager * @param noteKind Name of the note kind * @return Array */ - public static function getParams(noteKind:String):Array + public static function getParams(noteKind:Null):Array { + if (noteKind == null) + { + return []; + } + return noteKinds.get(noteKind)?.params ?? []; } } diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 22de29849e..f7abfba895 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -4737,7 +4737,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState else { // Create a note and place it in the chart. - var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, noteKindToPlace, noteParamsToPlace.clone()); + var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, noteKindToPlace, + ChartEditorState.cloneNoteParams(noteParamsToPlace)); performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL)); @@ -4897,7 +4898,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState if (gridGhostNote == null) throw "ERROR: Tried to handle cursor, but gridGhostNote is null! Check ChartEditorState.buildGrid()"; var noteData:SongNoteData = gridGhostNote.noteData != null ? gridGhostNote.noteData : new SongNoteData(cursorMs, cursorColumn, 0, noteKindToPlace, - noteParamsToPlace.clone()); + ChartEditorState.cloneNoteParams(noteParamsToPlace)); if (cursorColumn != noteData.data || noteKindToPlace != noteData.kind || noteParamsToPlace != noteData.params) { @@ -5210,7 +5211,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState if (notesAtPos.length == 0 && !removeNoteInstead) { trace('Placing note. ${column}'); - var newNoteData:SongNoteData = new SongNoteData(playheadPosSnappedMs, column, 0, noteKindToPlace, noteParamsToPlace.clone()); + var newNoteData:SongNoteData = new SongNoteData(playheadPosSnappedMs, column, 0, noteKindToPlace, ChartEditorState.cloneNoteParams(noteParamsToPlace)); performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL)); currentLiveInputPlaceNoteData[column] = newNoteData; } @@ -6532,6 +6533,16 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState } return input; } + + public static function cloneNoteParams(paramsToClone:Array):Array + { + var params:Array = []; + for (param in paramsToClone) + { + params.push(param.clone()); + } + return params; + } } /** diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index 027ffdf816..a81cac5c27 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -62,77 +62,30 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox trace('ChartEditorToolboxHandler.buildToolboxNoteDataLayout() - Note kind changed: $noteKind'); - var noteKindParams:Array = NoteKindManager.getParams(noteKind); - var noteParamData:Array = []; - for (noteKindParam in noteKindParams) - { - noteParamData.push(new NoteParamData(noteKindParam.name, noteKindParam.data.defaultValue)); - } - // Edit the note data to place. if (noteKind == '~CUSTOM~') { showCustom(); - clearNoteKindParams(); toolboxNotesCustomKind.value = chartEditorState.noteKindToPlace; } else { hideCustom(); chartEditorState.noteKindToPlace = noteKind; - chartEditorState.noteParamsToPlace = noteParamData; toolboxNotesCustomKind.value = chartEditorState.noteKindToPlace; - - clearNoteKindParams(); - for (param in noteKindParams) - { - var paramLabel:Label = new Label(); - paramLabel.value = param.description; - paramLabel.verticalAlign = "center"; - paramLabel.horizontalAlign = "right"; - - var paramStepper:NumberStepper = new NumberStepper(); - paramStepper.value = param.data.defaultValue; - paramStepper.percentWidth = 100; - paramStepper.step = param.data.step ?? 1; - - // this check should be unnecessary but for some reason - // even when these are null it will set it to 0 - if (param.data.min != null) - { - paramStepper.min = param.data.min; - } - if (param.data.max != null) - { - paramStepper.max = param.data.max; - } - if (param.data.precision != null) - { - paramStepper.precision = param.data.precision; - } - - addNoteKindParam(paramLabel, paramStepper); - } } + createNoteKindParams(noteKind); + if (!_initializing && chartEditorState.currentNoteSelection.length > 0) { - for (i in 0...toolboxNotesParams.length) - { - var toolboxComponent:Component = toolboxNotesParams[i].component; - toolboxComponent.onChange = function(event:UIEvent) { - for (note in chartEditorState.currentNoteSelection) - { - note.params[i].value = toolboxComponent.value; - } - } - } - for (note in chartEditorState.currentNoteSelection) { // Edit the note data of any selected notes. note.kind = chartEditorState.noteKindToPlace; - note.params = noteParamData.clone(); + trace(note.params); + note.params = ChartEditorState.cloneNoteParams(chartEditorState.noteParamsToPlace); + trace(note.params); // update note sprites for (noteSprite in chartEditorState.renderedNotes.members) @@ -187,6 +140,8 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox toolboxNotesNoteKind.value = ChartEditorDropdowns.lookupNoteKind(chartEditorState.noteKindToPlace); toolboxNotesCustomKind.value = chartEditorState.noteKindToPlace; + + createNoteKindParams(chartEditorState.noteKindToPlace); } function showCustom():Void @@ -201,6 +156,82 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox toolboxNotesCustomKind.hidden = true; } + function createNoteKindParams(noteKind:Null):Void + { + clearNoteKindParams(); + + var setParamsToPlace:Bool = false; + if (!_initializing) + { + for (note in chartEditorState.currentNoteSelection) + { + if (note.kind == chartEditorState.noteKindToPlace) + { + chartEditorState.noteParamsToPlace = ChartEditorState.cloneNoteParams(note.params); + setParamsToPlace = true; + break; + } + } + } + + var noteKindParams:Array = NoteKindManager.getParams(noteKind); + + for (i in 0...noteKindParams.length) + { + var param:NoteKindParam = noteKindParams[i]; + + var paramLabel:Label = new Label(); + paramLabel.value = param.description; + paramLabel.verticalAlign = "center"; + paramLabel.horizontalAlign = "right"; + + var paramStepper:NumberStepper = new NumberStepper(); + paramStepper.value = (setParamsToPlace ? chartEditorState.noteParamsToPlace[i].value : param.data.defaultValue); + paramStepper.percentWidth = 100; + paramStepper.step = param.data.step ?? 1; + + // this check should be unnecessary but for some reason + // even when these are null it will set it to 0 + if (param.data.min != null) + { + paramStepper.min = param.data.min; + } + if (param.data.max != null) + { + paramStepper.max = param.data.max; + } + if (param.data.precision != null) + { + paramStepper.precision = param.data.precision; + } + + paramStepper.onChange = function(event:UIEvent) { + chartEditorState.noteParamsToPlace[i].value = paramStepper.value; + + for (note in chartEditorState.currentNoteSelection) + { + if (note.params[i].name == param.name) + { + note.params[i].value = paramStepper.value; + trace(note.params[i]); + } + } + } + + addNoteKindParam(paramLabel, paramStepper); + } + + if (!setParamsToPlace) + { + var noteParamData:Array = []; + for (param in noteKindParams) + { + noteParamData.push(new NoteParamData(param.name, param.data.defaultValue)); + } + chartEditorState.noteParamsToPlace = noteParamData; + } + } + function addNoteKindParam(label:Label, component:Component):Void { toolboxNotesParams.push({label: label, component: component}); @@ -210,6 +241,17 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox this.height = Math.max(DIALOG_HEIGHT, DIALOG_HEIGHT - 30 + toolboxNotesParams.length * 30); } + function clearNoteKindParams():Void + { + for (param in toolboxNotesParams) + { + toolboxNotesGrid.removeComponent(param.component); + toolboxNotesGrid.removeComponent(param.label); + } + toolboxNotesParams = []; + this.height = DIALOG_HEIGHT; + } + override function update(elapsed:Float):Void { super.update(elapsed); @@ -227,17 +269,6 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox } } - function clearNoteKindParams():Void - { - for (param in toolboxNotesParams) - { - toolboxNotesGrid.removeComponent(param.component); - toolboxNotesGrid.removeComponent(param.label); - } - toolboxNotesParams = []; - this.height = DIALOG_HEIGHT; - } - public static function build(chartEditorState:ChartEditorState):ChartEditorNoteDataToolbox { return new ChartEditorNoteDataToolbox(chartEditorState); From 492af8add4e575d7e648f67911d2fafb006c9219 Mon Sep 17 00:00:00 2001 From: lemz Date: Sun, 23 Jun 2024 15:25:53 +0200 Subject: [PATCH 183/469] String works now --- source/funkin/play/notes/notekind/NoteKind.hx | 4 +- .../toolboxes/ChartEditorNoteDataToolbox.hx | 68 ++++++++++++------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index a06670503e..89c175e542 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -104,7 +104,7 @@ typedef NoteKindParamData = */ ?precision:Null, - defaultValue:Dynamic + ?defaultValue:Dynamic } /** @@ -115,5 +115,5 @@ typedef NoteKindParam = name:String, description:String, type:NoteKindParamType, - data:NoteKindParamData + ?data:NoteKindParamData } diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index a81cac5c27..264e62c5a1 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -10,6 +10,7 @@ import haxe.ui.events.UIEvent; import funkin.ui.debug.charting.util.ChartEditorDropdowns; import funkin.play.notes.notekind.NoteKindManager; import funkin.play.notes.notekind.NoteKind.NoteKindParam; +import funkin.play.notes.notekind.NoteKind.NoteKindParamType; import funkin.data.song.SongData.NoteParamData; /** @@ -83,9 +84,7 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox { // Edit the note data of any selected notes. note.kind = chartEditorState.noteKindToPlace; - trace(note.params); note.params = ChartEditorState.cloneNoteParams(chartEditorState.noteParamsToPlace); - trace(note.params); // update note sprites for (noteSprite in chartEditorState.renderedNotes.members) @@ -185,48 +184,71 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox paramLabel.verticalAlign = "center"; paramLabel.horizontalAlign = "right"; - var paramStepper:NumberStepper = new NumberStepper(); - paramStepper.value = (setParamsToPlace ? chartEditorState.noteParamsToPlace[i].value : param.data.defaultValue); - paramStepper.percentWidth = 100; - paramStepper.step = param.data.step ?? 1; + var paramComponent:Component = null; - // this check should be unnecessary but for some reason - // even when these are null it will set it to 0 - if (param.data.min != null) + final paramType:String = param.type; + switch (paramType) { - paramStepper.min = param.data.min; - } - if (param.data.max != null) - { - paramStepper.max = param.data.max; + case NoteKindParamType.INT | NoteKindParamType.FLOAT: + var paramStepper:NumberStepper = new NumberStepper(); + paramStepper.value = (setParamsToPlace ? chartEditorState.noteParamsToPlace[i].value : param.data?.defaultValue) ?? 0.0; + paramStepper.percentWidth = 100; + paramStepper.step = param.data?.step ?? 1; + + // this check should be unnecessary but for some reason + // even when these are null it will set it to 0 + if (param.data?.min != null) + { + paramStepper.min = param.data.min; + } + if (param.data?.max != null) + { + paramStepper.max = param.data.max; + } + if (param.data?.precision != null) + { + paramStepper.precision = param.data.precision; + } + paramComponent = paramStepper; + + case NoteKindParamType.STRING: + var paramTextField:TextField = new TextField(); + paramTextField.value = (setParamsToPlace ? chartEditorState.noteParamsToPlace[i].value : param.data?.defaultValue) ?? ''; + paramTextField.percentWidth = 100; + paramComponent = paramTextField; } - if (param.data.precision != null) + + if (paramComponent == null) { - paramStepper.precision = param.data.precision; + continue; } - paramStepper.onChange = function(event:UIEvent) { - chartEditorState.noteParamsToPlace[i].value = paramStepper.value; + paramComponent.onChange = function(event:UIEvent) { + chartEditorState.noteParamsToPlace[i].value = paramComponent.value; for (note in chartEditorState.currentNoteSelection) { + if (note.params.length != noteKindParams.length) + { + break; + } + if (note.params[i].name == param.name) { - note.params[i].value = paramStepper.value; - trace(note.params[i]); + note.params[i].value = paramComponent.value; } } } - addNoteKindParam(paramLabel, paramStepper); + addNoteKindParam(paramLabel, paramComponent); } if (!setParamsToPlace) { var noteParamData:Array = []; - for (param in noteKindParams) + for (i in 0...noteKindParams.length) { - noteParamData.push(new NoteParamData(param.name, param.data.defaultValue)); + noteParamData.push(new NoteParamData(noteKindParams[i].name, toolboxNotesParams[i].component.value)); } chartEditorState.noteParamsToPlace = noteParamData; } From f9bccb057a129443148e6031c907045c2ff8fb31 Mon Sep 17 00:00:00 2001 From: lemz Date: Mon, 24 Jun 2024 21:45:58 +0200 Subject: [PATCH 184/469] use suffixes instead of pixel boolean --- source/funkin/play/notes/Strumline.hx | 4 ++-- .../play/notes/notekind/NoteKindManager.hx | 21 ++++++++++++------- .../ui/debug/charting/ChartEditorState.hx | 17 ++++++--------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index 86b7a3ee19..5e76afa519 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -709,7 +709,7 @@ class Strumline extends FlxSpriteGroup if (noteSprite != null) { - var noteKindStyle:NoteStyle = NoteKindManager.getNoteStyle(note.kind, this.noteStyle.isHoldNotePixel()) ?? this.noteStyle; + var noteKindStyle:NoteStyle = NoteKindManager.getNoteStyle(note.kind, this.noteStyle.id) ?? this.noteStyle; noteSprite.setupNoteGraphic(noteKindStyle); noteSprite.direction = note.getDirection(); @@ -731,7 +731,7 @@ class Strumline extends FlxSpriteGroup if (holdNoteSprite != null) { - var noteKindStyle:NoteStyle = NoteKindManager.getNoteStyle(note.kind, this.noteStyle.isHoldNotePixel()) ?? this.noteStyle; + var noteKindStyle:NoteStyle = NoteKindManager.getNoteStyle(note.kind, this.noteStyle.id) ?? this.noteStyle; holdNoteSprite.setupHoldNoteGraphic(noteKindStyle); holdNoteSprite.parentStrumline = this; diff --git a/source/funkin/play/notes/notekind/NoteKindManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx index 8de3cdccac..d97eefcf87 100644 --- a/source/funkin/play/notes/notekind/NoteKindManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -66,12 +66,12 @@ class NoteKindManager /** * Retrieve the note style from the given note kind * @param noteKind note kind name - * @param isPixel whether to use pixel style + * @param suffix Used for song note styles * @return NoteStyle */ - public static function getNoteStyle(noteKind:String, isPixel:Bool = false):Null + public static function getNoteStyle(noteKind:String, ?suffix:String):Null { - var noteStyleId:Null = getNoteStyleId(noteKind, isPixel); + var noteStyleId:Null = getNoteStyleId(noteKind, suffix); if (noteStyleId == null) { @@ -83,16 +83,21 @@ class NoteKindManager /** * Retrieve the note style id from the given note kind - * @param noteKind note kind name - * @param isPixel whether to use pixel style + * @param noteKind Note kind name + * @param suffix Used for song note styles * @return Null */ - public static function getNoteStyleId(noteKind:String, isPixel:Bool = false):Null + public static function getNoteStyleId(noteKind:String, ?suffix:String):Null { + if (suffix == null) + { + suffix = ''; + } + var noteStyleId:Null = noteKinds.get(noteKind)?.noteStyleId; - if (isPixel && noteStyleId != null) + if (noteStyleId != null) { - noteStyleId = NoteStyleRegistry.instance.hasEntry('$noteStyleId-pixel') ? '$noteStyleId-pixel' : noteStyleId; + noteStyleId = NoteStyleRegistry.instance.hasEntry('$noteStyleId-$suffix') ? '$noteStyleId-$suffix' : noteStyleId; } return noteStyleId; diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index f7abfba895..d3ddb1bca0 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -3591,7 +3591,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // The note sprite handles animation playback and positioning. noteSprite.noteData = noteData; - noteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind, isPixelStyle()) ?? currentSongNoteStyle; + noteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind, currentSongNoteStyle) ?? currentSongNoteStyle; noteSprite.overrideStepTime = null; noteSprite.overrideData = null; @@ -3615,7 +3615,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState holdNoteSprite.setHeightDirectly(noteLengthPixels); - holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteSprite.noteData.kind, isPixelStyle()) ?? currentSongNoteStyle; + holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteSprite.noteData.kind, currentSongNoteStyle) ?? currentSongNoteStyle; holdNoteSprite.updateHoldNotePosition(renderedHoldNotes); @@ -3681,7 +3681,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState holdNoteSprite.noteDirection = noteData.getDirection(); holdNoteSprite.setHeightDirectly(noteLengthPixels); - holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind, isPixelStyle()) ?? currentSongNoteStyle; + holdNoteSprite.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind, currentSongNoteStyle) ?? currentSongNoteStyle; holdNoteSprite.updateHoldNotePosition(renderedHoldNotes); @@ -4580,7 +4580,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState gridGhostHoldNote.noteData = currentPlaceNoteData; gridGhostHoldNote.noteDirection = currentPlaceNoteData.getDirection(); gridGhostHoldNote.setHeightDirectly(dragLengthPixels, true); - gridGhostHoldNote.noteStyle = NoteKindManager.getNoteStyleId(currentPlaceNoteData.kind, isPixelStyle()) ?? currentSongNoteStyle; + gridGhostHoldNote.noteStyle = NoteKindManager.getNoteStyleId(currentPlaceNoteData.kind, currentSongNoteStyle) ?? currentSongNoteStyle; gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes); } else @@ -4905,7 +4905,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState noteData.kind = noteKindToPlace; noteData.params = noteParamsToPlace; noteData.data = cursorColumn; - gridGhostNote.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind, isPixelStyle()) ?? currentSongNoteStyle; + gridGhostNote.noteStyle = NoteKindManager.getNoteStyleId(noteData.kind, currentSongNoteStyle) ?? currentSongNoteStyle; gridGhostNote.playNoteAnimation(); } noteData.time = cursorSnappedMs; @@ -5297,7 +5297,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState ghostHold.visible = true; ghostHold.alpha = 0.6; ghostHold.setHeightDirectly(0); - ghostHold.noteStyle = NoteKindManager.getNoteStyleId(ghostHold.noteData.kind, isPixelStyle()) ?? currentSongNoteStyle; + ghostHold.noteStyle = NoteKindManager.getNoteStyleId(ghostHold.noteData.kind, currentSongNoteStyle) ?? currentSongNoteStyle; ghostHold.updateHoldNotePosition(renderedHoldNotes); } @@ -6423,11 +6423,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState return note != null && currentNoteSelection.indexOf(note) != -1; } - function isPixelStyle():Bool - { - return currentSongNoteStyle == 'pixel'; - } - override function destroy():Void { super.destroy(); From 4746c1da0eed756e2af8630d572ad9770a5185f1 Mon Sep 17 00:00:00 2001 From: lemz Date: Mon, 24 Jun 2024 21:48:09 +0200 Subject: [PATCH 185/469] checking empty suffix is stupid --- source/funkin/play/notes/notekind/NoteKindManager.hx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/funkin/play/notes/notekind/NoteKindManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx index d97eefcf87..3040c0a96b 100644 --- a/source/funkin/play/notes/notekind/NoteKindManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -89,13 +89,13 @@ class NoteKindManager */ public static function getNoteStyleId(noteKind:String, ?suffix:String):Null { - if (suffix == null) + if (suffix == '') { - suffix = ''; + suffix = null; } var noteStyleId:Null = noteKinds.get(noteKind)?.noteStyleId; - if (noteStyleId != null) + if (noteStyleId != null && suffix != null) { noteStyleId = NoteStyleRegistry.instance.hasEntry('$noteStyleId-$suffix') ? '$noteStyleId-$suffix' : noteStyleId; } From 1fe44fa3686b33f91b99efdb00070e4c59fdee45 Mon Sep 17 00:00:00 2001 From: lemz Date: Sat, 29 Jun 2024 15:33:41 +0200 Subject: [PATCH 186/469] add from --- source/funkin/play/notes/notekind/NoteKind.hx | 2 +- .../ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/source/funkin/play/notes/notekind/NoteKind.hx b/source/funkin/play/notes/notekind/NoteKind.hx index 89c175e542..c1c6e815aa 100644 --- a/source/funkin/play/notes/notekind/NoteKind.hx +++ b/source/funkin/play/notes/notekind/NoteKind.hx @@ -73,7 +73,7 @@ class NoteKind implements INoteScriptedClass * Abstract for setting the type of the `NoteKindParam` * This was supposed to be an enum but polymod kept being annoying */ -abstract NoteKindParamType(String) to String +abstract NoteKindParamType(String) from String to String { public static final STRING:String = 'String'; diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index 264e62c5a1..ea46cf72aa 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -186,8 +186,7 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox var paramComponent:Component = null; - final paramType:String = param.type; - switch (paramType) + switch (param.type) { case NoteKindParamType.INT | NoteKindParamType.FLOAT: var paramStepper:NumberStepper = new NumberStepper(); From f76309c91ec2cd0ca4bad6163b47fefa2e57228c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 12 Jul 2024 04:13:20 -0400 Subject: [PATCH 187/469] Update rendering for custom note styles --- source/funkin/data/notestyle/NoteStyleData.hx | 8 ++++++++ source/funkin/play/notes/NoteSprite.hx | 16 +++++++++------- source/funkin/play/notes/Strumline.hx | 1 + source/funkin/play/notes/notestyle/NoteStyle.hx | 16 ++++++++++++++-- .../charting/components/ChartEditorNoteSprite.hx | 10 ++++++++-- .../toolboxes/ChartEditorNoteDataToolbox.hx | 2 +- .../debug/charting/util/ChartEditorDropdowns.hx | 6 +++--- 7 files changed, 44 insertions(+), 15 deletions(-) diff --git a/source/funkin/data/notestyle/NoteStyleData.hx b/source/funkin/data/notestyle/NoteStyleData.hx index 04fda67ca6..fcdb3b4f9e 100644 --- a/source/funkin/data/notestyle/NoteStyleData.hx +++ b/source/funkin/data/notestyle/NoteStyleData.hx @@ -109,6 +109,14 @@ typedef NoteStyleAssetData = @:optional var isPixel:Bool; + /** + * If true, animations will be played on the graphic. + * @default `false` to save performance. + */ + @:default(false) + @:optional + var animated:Bool; + /** * The structure of this data depends on the asset. */ diff --git a/source/funkin/play/notes/NoteSprite.hx b/source/funkin/play/notes/NoteSprite.hx index d8d4714968..e8cacaa4d6 100644 --- a/source/funkin/play/notes/NoteSprite.hx +++ b/source/funkin/play/notes/NoteSprite.hx @@ -91,7 +91,7 @@ class NoteSprite extends FunkinSprite { if (frames == null) return value; - animation.play(DIRECTION_COLORS[value] + 'Scroll'); + playNoteAnimation(value); this.direction = value; return this.direction; @@ -152,9 +152,6 @@ class NoteSprite extends FunkinSprite this.hsvShader = new HSVShader(); setupNoteGraphic(noteStyle); - - // Disables the update() function for performance. - this.active = false; } /** @@ -165,10 +162,10 @@ class NoteSprite extends FunkinSprite { noteStyle.buildNoteSprite(this); - setGraphicSize(Strumline.STRUMLINE_SIZE); - updateHitbox(); - this.shader = hsvShader; + + // `false` disables the update() function for performance. + this.active = noteStyle.isNoteAnimated(); } /** @@ -211,6 +208,11 @@ class NoteSprite extends FunkinSprite } #end + function playNoteAnimation(value:Int):Void + { + animation.play(DIRECTION_COLORS[value] + 'Scroll'); + } + public function desaturate():Void { this.hsvShader.saturation = 0.2; diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index 5e76afa519..1e5782ad2f 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -717,6 +717,7 @@ class Strumline extends FlxSpriteGroup noteSprite.x = this.x; noteSprite.x += getXPos(DIRECTIONS[note.getDirection() % KEY_COUNT]); + noteSprite.x -= (noteSprite.width - Strumline.STRUMLINE_SIZE) / 2; // Center it noteSprite.x -= NUDGE; // noteSprite.x += INITIAL_OFFSET; noteSprite.y = -9999; diff --git a/source/funkin/play/notes/notestyle/NoteStyle.hx b/source/funkin/play/notes/notestyle/NoteStyle.hx index d0cc09f6a8..3993cce523 100644 --- a/source/funkin/play/notes/notestyle/NoteStyle.hx +++ b/source/funkin/play/notes/notestyle/NoteStyle.hx @@ -89,12 +89,14 @@ class NoteStyle implements IRegistryEntry target.frames = atlas; - target.scale.x = _data.assets.note.scale; - target.scale.y = _data.assets.note.scale; target.antialiasing = !_data.assets.note.isPixel; // Apply the animations. buildNoteAnimations(target); + + // Set the scale. + target.setGraphicSize(Strumline.STRUMLINE_SIZE * getNoteScale()); + target.updateHitbox(); } var noteFrames:FlxAtlasFrames = null; @@ -156,6 +158,16 @@ class NoteStyle implements IRegistryEntry target.animation.addByPrefix('redScroll', rightData.prefix, rightData.frameRate, rightData.looped, rightData.flipX, rightData.flipY); } + public function isNoteAnimated():Bool + { + return _data.assets.note.animated; + } + + public function getNoteScale():Float + { + return _data.assets.note.scale; + } + function fetchNoteAnimationData(dir:NoteDirection):AnimationData { var result:Null = switch (dir) diff --git a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx index 0095324019..5fd0c74aa7 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx @@ -107,6 +107,12 @@ class ChartEditorNoteSprite extends FlxSprite var prefix:String = noteStyle.id.toTitleCase(); var frameCollection:FlxAtlasFrames = Paths.getSparrowAtlas(noteStyle.getNoteAssetPath(), noteStyle.getNoteAssetLibrary()); + if (frameCollection == null) + { + trace('Could not retrieve frame collection for ${noteStyle}: ${Paths.image(noteStyle.getNoteAssetPath(), noteStyle.getNoteAssetLibrary())}'); + FlxG.log.error('Could not retrieve frame collection for ${noteStyle}: ${Paths.image(noteStyle.getNoteAssetPath(), noteStyle.getNoteAssetLibrary())}'); + return; + } for (frame in frameCollection.frames) { // cloning the frame because else @@ -221,9 +227,9 @@ class ChartEditorNoteSprite extends FlxSprite switch (baseAnimationName) { case 'tap': - this.setGraphicSize(0, ChartEditorState.GRID_SIZE); + this.setGraphicSize(ChartEditorState.GRID_SIZE, 0); + this.updateHitbox(); } - this.updateHitbox(); var bruhStyle:NoteStyle = fetchNoteStyle(this.noteStyle); this.antialiasing = !bruhStyle._data?.assets?.note?.isPixel ?? true; diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index ea46cf72aa..12f7f7d631 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -283,7 +283,7 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox return; } - var heightToSet:Int = Std.int(Math.max(DIALOG_HEIGHT, toolboxNotesGrid.height + HEIGHT_OFFSET)) + MINIMIZE_FIX; + var heightToSet:Int = Std.int(Math.max(DIALOG_HEIGHT, (toolboxNotesGrid?.height ?? 50) + HEIGHT_OFFSET)) + MINIMIZE_FIX; if (this.height != heightToSet) { this.height = heightToSet; diff --git a/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx b/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx index 6a426c3911..21938b0057 100644 --- a/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx +++ b/source/funkin/ui/debug/charting/util/ChartEditorDropdowns.hx @@ -195,11 +195,11 @@ class ChartEditorDropdowns { dropDown.dataSource.clear(); - var returnValue:DropDownEntry = lookupNoteKind('~CUSTOM'); + var returnValue:DropDownEntry = lookupNoteKind(''); for (noteKindId in NOTE_KINDS.keys()) { - var noteKind:String = NOTE_KINDS.get(noteKindId) ?? 'Default'; + var noteKind:String = NOTE_KINDS.get(noteKindId) ?? 'Unknown'; var value:DropDownEntry = {id: noteKindId, text: noteKind}; if (startingKindId == noteKindId) returnValue = value; @@ -216,7 +216,7 @@ class ChartEditorDropdowns { if (noteKindId == null) return lookupNoteKind(''); if (!NOTE_KINDS.exists(noteKindId)) return {id: '~CUSTOM~', text: 'Custom'}; - return {id: noteKindId ?? '', text: NOTE_KINDS.get(noteKindId) ?? 'Default'}; + return {id: noteKindId ?? '', text: NOTE_KINDS.get(noteKindId) ?? 'Unknown'}; } /** From ace762413e42a211a92ad37544ec22f1c2f3da8d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 12 Jul 2024 16:39:42 -0400 Subject: [PATCH 188/469] Fix an issue where display would break on invalid note styles. --- assets | 2 +- source/funkin/ui/debug/charting/ChartEditorState.hx | 4 +++- .../charting/components/ChartEditorHoldNoteSprite.hx | 3 ++- .../charting/components/ChartEditorNoteSprite.hx | 12 ++++++++++-- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/assets b/assets index 005c96f85f..4af95a506f 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 005c96f85f4304865acb196e7cc4d6d83f9d76d8 +Subproject commit 4af95a506fc62cd683422dfb9c599877b26c27db diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index d3ddb1bca0..6f5979c906 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -1408,7 +1408,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState function get_currentSongNoteStyle():String { - if (currentSongMetadata.playData.noteStyle == null) + if (currentSongMetadata.playData.noteStyle == null + || currentSongMetadata.playData.noteStyle == '' + || currentSongMetadata.playData.noteStyle == 'item') { // Initialize to the default value if not set. currentSongMetadata.playData.noteStyle = Constants.DEFAULT_NOTE_STYLE; diff --git a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx index b8d6ee22e7..1e631f2cf4 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx @@ -53,7 +53,8 @@ class ChartEditorHoldNoteSprite extends SustainTrail @:nullSafety(Off) function updateHoldNoteGraphic():Void { - var bruhStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle); + var bruhStyle:Null = NoteStyleRegistry.instance.fetchEntry(noteStyle); + if (bruhStyle == null) bruhStyle = NoteStyleRegistry.instance.fetchDefault(); setupHoldNoteGraphic(bruhStyle); } diff --git a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx index 5fd0c74aa7..c8f40da623 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorNoteSprite.hx @@ -97,7 +97,9 @@ class ChartEditorNoteSprite extends FlxSprite function fetchNoteStyle(noteStyleId:String):NoteStyle { - return NoteStyleRegistry.instance.fetchEntry(noteStyleId) ?? NoteStyleRegistry.instance.fetchDefault(); + var result = NoteStyleRegistry.instance.fetchEntry(noteStyleId); + if (result != null) return result; + return NoteStyleRegistry.instance.fetchDefault(); } @:access(funkin.play.notes.notestyle.NoteStyle) @@ -198,7 +200,12 @@ class ChartEditorNoteSprite extends FlxSprite function get_noteStyle():Null { - return this.noteStyle ?? this.parentState.currentSongNoteStyle; + if (this.noteStyle == null) + { + var result = this.parentState.currentSongNoteStyle; + return result; + } + return this.noteStyle; } function set_noteStyle(value:Null):Null @@ -218,6 +225,7 @@ class ChartEditorNoteSprite extends FlxSprite // Play the appropriate animation for the type, direction, and skin. var dirName:String = overrideData != null ? SongNoteData.buildDirectionName(overrideData) : this.noteData.getDirectionName(); + var noteStyleSuffix:String = this.noteStyle?.toTitleCase() ?? Constants.DEFAULT_NOTE_STYLE.toTitleCase(); var animationName:String = '${baseAnimationName}${dirName}${this.noteStyle.toTitleCase()}'; this.animation.play(animationName); From d4cbe74939425c5a86f0ad752e2620e560380e27 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 12 Jul 2024 21:40:46 -0400 Subject: [PATCH 189/469] Smaller fixes tied to note kinds --- source/funkin/data/song/importer/FNFLegacyImporter.hx | 2 ++ source/funkin/modding/PolymodHandler.hx | 2 ++ source/funkin/play/PlayState.hx | 6 +++--- source/funkin/play/character/BaseCharacter.hx | 6 ++++++ source/funkin/play/notes/notekind/NoteKindManager.hx | 1 + source/funkin/play/stage/Bopper.hx | 4 ++-- 6 files changed, 16 insertions(+), 5 deletions(-) diff --git a/source/funkin/data/song/importer/FNFLegacyImporter.hx b/source/funkin/data/song/importer/FNFLegacyImporter.hx index acbb993425..96a1051cc4 100644 --- a/source/funkin/data/song/importer/FNFLegacyImporter.hx +++ b/source/funkin/data/song/importer/FNFLegacyImporter.hx @@ -199,6 +199,8 @@ class FNFLegacyImporter { // Handle the dumb logic for mustHitSection. var noteData = note.data; + if (noteData < 0) continue; // Exclude Psych event notes. + if (noteData > (STRUMLINE_SIZE * 2)) noteData = noteData % (2 * STRUMLINE_SIZE); // Handle other engine event notes. // Flip notes if mustHitSection is FALSE (not true lol). if (!mustHitSection) diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index c352aa6062..59c8707f7c 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -7,6 +7,7 @@ import funkin.data.dialogue.speaker.SpeakerRegistry; import funkin.data.event.SongEventRegistry; import funkin.data.story.level.LevelRegistry; import funkin.data.notestyle.NoteStyleRegistry; +import funkin.play.notes.notekind.NoteKindManager; import funkin.data.song.SongRegistry; import funkin.data.freeplay.player.PlayerRegistry; import funkin.data.stage.StageRegistry; @@ -383,6 +384,7 @@ class PolymodHandler StageRegistry.instance.loadEntries(); CharacterDataParser.loadCharacterCache(); // TODO: Migrate characters to BaseRegistry. + NoteKindManager.loadScripts(); ModuleHandler.loadModuleCache(); } } diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index da343f43f6..10546cdbd1 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1166,6 +1166,9 @@ class PlayState extends MusicBeatSubState // super.dispatchEvent(event) dispatches event to module scripts. super.dispatchEvent(event); + // Dispatch event to note kind scripts + NoteKindManager.callEvent(event); + // Dispatch event to stage script. ScriptEventDispatcher.callEvent(currentStage, event); @@ -1177,9 +1180,6 @@ class PlayState extends MusicBeatSubState // Dispatch event to conversation script. ScriptEventDispatcher.callEvent(currentConversation, event); - - // Dispatch event to note kind scripts - NoteKindManager.callEvent(event); } /** diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index 0dab2101a6..4328811648 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -521,6 +521,9 @@ class BaseCharacter extends Bopper { super.onNoteHit(event); + // If another script cancelled the event, don't do anything. + if (event.eventCanceled) return; + if (event.note.noteData.getMustHitNote() && characterType == BF) { // If the note is from the same strumline, play the sing animation. @@ -553,6 +556,9 @@ class BaseCharacter extends Bopper { super.onNoteMiss(event); + // If another script cancelled the event, don't do anything. + if (event.eventCanceled) return; + if (event.note.noteData.getMustHitNote() && characterType == BF) { // If the note is from the same strumline, play the sing animation. diff --git a/source/funkin/play/notes/notekind/NoteKindManager.hx b/source/funkin/play/notes/notekind/NoteKindManager.hx index 3040c0a96b..e17e103d19 100644 --- a/source/funkin/play/notes/notekind/NoteKindManager.hx +++ b/source/funkin/play/notes/notekind/NoteKindManager.hx @@ -5,6 +5,7 @@ import funkin.modding.events.ScriptEvent; import funkin.ui.debug.charting.util.ChartEditorDropdowns; import funkin.data.notestyle.NoteStyleRegistry; import funkin.play.notes.notestyle.NoteStyle; +import funkin.play.notes.notekind.ScriptedNoteKind; import funkin.play.notes.notekind.NoteKind.NoteKindParam; class NoteKindManager diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index 87151de21b..fa35b4e15e 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -45,8 +45,8 @@ class Bopper extends StageProp implements IPlayStateScriptedClass public var idleSuffix(default, set):String = ''; /** - * If this bopper is rendered with pixel art, - * disable anti-aliasing and render at 6x scale. + * If this bopper is rendered with pixel art, disable anti-aliasing. + * @default `false` */ public var isPixel(default, set):Bool = false; From 6116ec3639b3720c3b2453be6a76adaa6998e447 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 12 Jul 2024 22:31:04 -0400 Subject: [PATCH 190/469] Some more fixes for offsets handling and stuffs --- source/funkin/play/PauseSubState.hx | 2 +- source/funkin/play/song/Song.hx | 3 ++- source/funkin/play/stage/Bopper.hx | 18 +++--------------- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index d0c759b16a..6f8908eea9 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -306,7 +306,7 @@ class PauseSubState extends MusicBeatSubState metadataDifficulty.setFormat(Paths.font('vcr.ttf'), 32, FlxColor.WHITE, FlxTextAlign.RIGHT); if (PlayState.instance?.currentDifficulty != null) { - metadataDifficulty.text += PlayState.instance.currentDifficulty.toTitleCase(); + metadataDifficulty.text += PlayState.instance.currentDifficulty.replace('-', ' ').toTitleCase(); } metadataDifficulty.scrollFactor.set(0, 0); metadata.add(metadataDifficulty); diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 91d35d8fa2..4e9f70920c 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -277,7 +277,8 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry Date: Sat, 13 Jul 2024 22:45:58 +0300 Subject: [PATCH 191/469] custom popups and countdowns yay!!! --- assets | 2 +- source/funkin/play/Countdown.hx | 152 ++++++++++---------- source/funkin/play/PlayState.hx | 6 +- source/funkin/play/components/PopUpStuff.hx | 58 ++++++-- 4 files changed, 123 insertions(+), 95 deletions(-) diff --git a/assets b/assets index 2e1594ee4c..514a987ee5 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 2e1594ee4c04c7148628bae471bdd061c9deb6b7 +Subproject commit 514a987ee57827b097ed035a1c2fdd1377a53b17 diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 10636afdf9..7686e5b75e 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -10,6 +10,7 @@ import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEvent.CountdownScriptEvent; import flixel.util.FlxTimer; import funkin.audio.FunkinSound; +import openfl.utils.Assets; class Countdown { @@ -18,6 +19,22 @@ class Countdown */ public static var countdownStep(default, null):CountdownStep = BEFORE; + /** + * Which alternate countdown sound effect to use. + * You can set this via script. + * For example, in Week 6 it is `-pixel`. + */ + public static var soundSuffix:String = ''; + + /** + * Which alternate graphic on countdown to use. + * You can set this via script. + * For example, in Week 6 it is `-pixel`. + */ + public static var graphicSuffix:String = ''; + + + /** * The currently running countdown. This will be null if there is no countdown running. */ @@ -29,7 +46,7 @@ class Countdown * This will automatically stop and restart the countdown if it is already running. * @returns `false` if the countdown was cancelled by a script. */ - public static function performCountdown(isPixelStyle:Bool):Bool + public static function performCountdown():Bool { countdownStep = BEFORE; var cancelled:Bool = propagateCountdownEvent(countdownStep); @@ -64,10 +81,10 @@ class Countdown // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); // Countdown graphic. - showCountdownGraphic(countdownStep, isPixelStyle); + showCountdownGraphic(countdownStep, graphicSuffix.toLowerCase().contains('pixel')); // Countdown sound. - playCountdownSound(countdownStep, isPixelStyle); + playCountdownSound(countdownStep); // Event handling bullshit. var cancelled:Bool = propagateCountdownEvent(countdownStep); @@ -175,53 +192,31 @@ class Countdown } } + /** + * Reset the countdown configuration to the default. + */ + public static function reset() + { + soundSuffix = ''; + graphicSuffix = ''; + } + /** * Retrieves the graphic to use for this step of the countdown. - * TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles? - * - * This is public so modules can do lol funny shit. */ - public static function showCountdownGraphic(index:CountdownStep, isPixelStyle:Bool):Void + public static function showCountdownGraphic(index:CountdownStep, isGraphicPixel:Bool):Void { var spritePath:String = null; - - if (isPixelStyle) - { - switch (index) - { - case TWO: - spritePath = 'weeb/pixelUI/ready-pixel'; - case ONE: - spritePath = 'weeb/pixelUI/set-pixel'; - case GO: - spritePath = 'weeb/pixelUI/date-pixel'; - default: - // null - } - } - else - { - switch (index) - { - case TWO: - spritePath = 'ready'; - case ONE: - spritePath = 'set'; - case GO: - spritePath = 'go'; - default: - // null - } - } + spritePath = resolveGraphicPath(graphicSuffix, index); if (spritePath == null) return; var countdownSprite:FunkinSprite = FunkinSprite.create(spritePath); countdownSprite.scrollFactor.set(0, 0); - if (isPixelStyle) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE)); + if (isGraphicPixel) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE)); - countdownSprite.antialiasing = !isPixelStyle; + countdownSprite.antialiasing = !isGraphicPixel; countdownSprite.updateHitbox(); countdownSprite.screenCenter(); @@ -238,52 +233,55 @@ class Countdown PlayState.instance.add(countdownSprite); } - /** - * Retrieves the sound file to use for this step of the countdown. - * TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles? - * - * This is public so modules can do lol funny shit. - */ - public static function playCountdownSound(index:CountdownStep, isPixelStyle:Bool):Void + static function resolveGraphicPath(suffix:String, index:CountdownStep):Null { - var soundPath:String = null; - - if (isPixelStyle) + var basePath:String = 'ui/countdown/'; + var indexString:String = null; + switch (index) { - switch (index) - { - case THREE: - soundPath = 'intro3-pixel'; - case TWO: - soundPath = 'intro2-pixel'; - case ONE: - soundPath = 'intro1-pixel'; - case GO: - soundPath = 'introGo-pixel'; - default: - // null - } + case TWO: + indexString = 'ready'; + case ONE: + indexString = 'set'; + case GO: + indexString = 'go'; + default: + // null } - else + basePath += indexString; + var spritePath:String = basePath + suffix; + while (!Assets.exists(Paths.image(spritePath)) && suffix.length > 0) { - switch (index) - { - case THREE: - soundPath = 'intro3'; - case TWO: - soundPath = 'intro2'; - case ONE: - soundPath = 'intro1'; - case GO: - soundPath = 'introGo'; - default: - // null - } + suffix = suffix.split('-').slice(0, -1).join('-'); + spritePath = basePath + suffix; } + if (!Assets.exists(Paths.image(spritePath))) return null; + trace('Resolved sprite path: ' + Paths.image(spritePath)); + return spritePath; + } - if (soundPath == null) return; + /** + * Retrieves the sound file to use for this step of the countdown. + */ + public static function playCountdownSound(index:CountdownStep):Void + { + FunkinSound.playOnce(resolveSoundPath(soundSuffix, index), Constants.COUNTDOWN_VOLUME); + } + + static function resolveSoundPath(suffix:String, step:CountdownStep):Null + { + var basePath:String = 'gameplay/countdown/intro'; + if (step != CountdownStep.BEFORE || step != CountdownStep.AFTER) basePath += step; - FunkinSound.playOnce(Paths.sound(soundPath), Constants.COUNTDOWN_VOLUME); + var soundPath:String = Paths.sound(basePath + suffix); + while (!Assets.exists(soundPath) && suffix.length > 0) + { + suffix = suffix.split('-').slice(0, -1).join('-'); + soundPath = Paths.sound(basePath + suffix); + } + if (!Assets.exists(soundPath)) return null; + trace('Resolved sound path: ' + soundPath); + return soundPath; } public static function decrement(step:CountdownStep):CountdownStep diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index f55cef3888..bf401ece2b 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -899,7 +899,7 @@ class PlayState extends MusicBeatSubState health = Constants.HEALTH_STARTING; songScore = 0; Highscore.tallies.combo = 0; - Countdown.performCountdown(currentStageId.startsWith('school')); + Countdown.performCountdown(); needsReset = false; } @@ -1920,7 +1920,7 @@ class PlayState extends MusicBeatSubState public function startCountdown():Void { // If Countdown.performCountdown returns false, then the countdown was canceled by a script. - var result:Bool = Countdown.performCountdown(currentStageId.startsWith('school')); + var result:Bool = Countdown.performCountdown(); if (!result) return; isInCutscene = false; @@ -3061,6 +3061,8 @@ class PlayState extends MusicBeatSubState GameOverSubState.reset(); PauseSubState.reset(); + Countdown.reset(); + PopUpStuff.reset(); // Clear the static reference to this state. instance = null; diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index b7e206e977..ddf24d24bc 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -7,25 +7,52 @@ import flixel.util.FlxDirection; import funkin.graphics.FunkinSprite; import funkin.play.PlayState; import funkin.util.TimerUtil; +import openfl.utils.Assets; class PopUpStuff extends FlxTypedGroup { public var offsets:Array = [0, 0]; + /** + * Which alternate graphic on popup to use. + * You can set this via script. + * For example, in Week 6 it is `-pixel`. + */ + public static var graphicSuffix:String = ''; + override public function new() { super(); } + static function resolveGraphicPath(suffix:String, index:String):Null + { + var folder:String; + if (suffix != '') + folder = suffix.substring(0, suffix.indexOf("-")) + suffix.substring(suffix.indexOf("-") + 1); + else + folder = 'normal'; + var basePath:String = 'gameplay/popup/$folder/$index'; + var spritePath:String = basePath + suffix; + trace(spritePath); + while (!Assets.exists(Paths.image(spritePath)) && suffix.length > 0) + { + suffix = suffix.split('-').slice(0, -1).join('-'); + spritePath = basePath + suffix; + } + if (!Assets.exists(Paths.image(spritePath))) return null; + return spritePath; + } + public function displayRating(daRating:String) { var perfStart:Float = TimerUtil.start(); if (daRating == null) daRating = "good"; - var ratingPath:String = daRating; + var ratingPath:String = resolveGraphicPath(graphicSuffix, daRating); - if (PlayState.instance.currentStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel"; + //if (PlayState.instance.currentStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel"; var rating:FunkinSprite = FunkinSprite.create(0, 0, ratingPath); rating.scrollFactor.set(0.2, 0.2); @@ -40,7 +67,7 @@ class PopUpStuff extends FlxTypedGroup add(rating); - if (PlayState.instance.currentStageId.startsWith('school')) + if (graphicSuffix.toLowerCase().contains('pixel')) { rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.7)); rating.antialiasing = false; @@ -73,15 +100,8 @@ class PopUpStuff extends FlxTypedGroup if (combo == null) combo = 0; - var pixelShitPart1:String = ""; - var pixelShitPart2:String = ''; - - if (PlayState.instance.currentStageId.startsWith('school')) - { - pixelShitPart1 = 'weeb/pixelUI/'; - pixelShitPart2 = '-pixel'; - } - var comboSpr:FunkinSprite = FunkinSprite.create(pixelShitPart1 + 'combo' + pixelShitPart2); + var comboPath:String = resolveGraphicPath(graphicSuffix, Std.string(combo)); + var comboSpr:FunkinSprite = FunkinSprite.create(comboPath); comboSpr.y = (FlxG.camera.height * 0.44) + offsets[1]; comboSpr.x = (FlxG.width * 0.507) + offsets[0]; // comboSpr.x -= FlxG.camera.scroll.x * 0.2; @@ -92,7 +112,7 @@ class PopUpStuff extends FlxTypedGroup // add(comboSpr); - if (PlayState.instance.currentStageId.startsWith('school')) + if (graphicSuffix.toLowerCase().contains('pixel')) { comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 0.7)); comboSpr.antialiasing = false; @@ -129,9 +149,9 @@ class PopUpStuff extends FlxTypedGroup var daLoop:Int = 1; for (i in seperatedScore) { - var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, pixelShitPart1 + 'num' + Std.int(i) + pixelShitPart2); + var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, resolveGraphicPath(graphicSuffix, 'num' + Std.int(i))); - if (PlayState.instance.currentStageId.startsWith('school')) + if (graphicSuffix.toLowerCase().contains('pixel')) { numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 0.7)); numScore.antialiasing = false; @@ -166,4 +186,12 @@ class PopUpStuff extends FlxTypedGroup return combo; } + + /** + * Reset the popup configuration to the default. + */ + public static function reset() + { + graphicSuffix = ''; + } } From 2547fbb8f4033f815d3d8d5cef9eb9ddc02c2832 Mon Sep 17 00:00:00 2001 From: anysad Date: Sat, 13 Jul 2024 23:08:16 +0300 Subject: [PATCH 192/469] remove trace --- assets | 2 +- source/funkin/play/components/PopUpStuff.hx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/assets b/assets index 514a987ee5..e414ee618b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 514a987ee57827b097ed035a1c2fdd1377a53b17 +Subproject commit e414ee618b4fa577dc3c3df4e156488e91c6348d diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index ddf24d24bc..0a4d6b0193 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -34,7 +34,6 @@ class PopUpStuff extends FlxTypedGroup folder = 'normal'; var basePath:String = 'gameplay/popup/$folder/$index'; var spritePath:String = basePath + suffix; - trace(spritePath); while (!Assets.exists(Paths.image(spritePath)) && suffix.length > 0) { suffix = suffix.split('-').slice(0, -1).join('-'); From 9d3c043f8ab462973a1e760336581dee89326e42 Mon Sep 17 00:00:00 2001 From: anysad Date: Sat, 13 Jul 2024 23:29:26 +0300 Subject: [PATCH 193/469] cache textures at their new locations! --- source/funkin/ui/transition/LoadingState.hx | 33 ++++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index bc26ad97a1..d0b8cfbe88 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -291,17 +291,28 @@ class LoadingState extends MusicBeatSubState FunkinSprite.preparePurgeCache(); FunkinSprite.cacheTexture(Paths.image('healthBar')); FunkinSprite.cacheTexture(Paths.image('menuDesat')); - FunkinSprite.cacheTexture(Paths.image('combo')); - FunkinSprite.cacheTexture(Paths.image('num0')); - FunkinSprite.cacheTexture(Paths.image('num1')); - FunkinSprite.cacheTexture(Paths.image('num2')); - FunkinSprite.cacheTexture(Paths.image('num3')); - FunkinSprite.cacheTexture(Paths.image('num4')); - FunkinSprite.cacheTexture(Paths.image('num5')); - FunkinSprite.cacheTexture(Paths.image('num6')); - FunkinSprite.cacheTexture(Paths.image('num7')); - FunkinSprite.cacheTexture(Paths.image('num8')); - FunkinSprite.cacheTexture(Paths.image('num9')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/combo')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num0')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num1')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num2')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num3')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num4')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num5')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num6')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num7')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num8')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num9')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/combo-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num0-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num1-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num2-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num3-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num4-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num5-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num6-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num7-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num8-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num9-pixel')); FunkinSprite.cacheTexture(Paths.image('notes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared')); From aed100df476df55d38e820cc86f643ed62117284 Mon Sep 17 00:00:00 2001 From: anysad Date: Sat, 13 Jul 2024 23:39:56 +0300 Subject: [PATCH 194/469] how the hell did I miss these? --- source/funkin/ui/transition/LoadingState.hx | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index d0b8cfbe88..a571126fea 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -317,13 +317,20 @@ class LoadingState extends MusicBeatSubState FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared')); FunkinSprite.cacheTexture(Paths.image('NOTE_hold_assets')); - FunkinSprite.cacheTexture(Paths.image('ready', 'shared')); - FunkinSprite.cacheTexture(Paths.image('set', 'shared')); - FunkinSprite.cacheTexture(Paths.image('go', 'shared')); - FunkinSprite.cacheTexture(Paths.image('sick', 'shared')); - FunkinSprite.cacheTexture(Paths.image('good', 'shared')); - FunkinSprite.cacheTexture(Paths.image('bad', 'shared')); - FunkinSprite.cacheTexture(Paths.image('shit', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/ready', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/set', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/go', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/ready-pixel', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/set-pixel', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/go-pixel', 'shared')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/sick')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/good')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/bad')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/shit')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/sick-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/good-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/bad-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/shit-pixel')); FunkinSprite.cacheTexture(Paths.image('miss', 'shared')); // TODO: remove this // List all image assets in the level's library. From 416d60000f369620a4a1c5b744fd3f0a1c25ea95 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 15 Jul 2024 06:30:10 -0400 Subject: [PATCH 195/469] Implement Nene Christmas, and fix Nene offsets on other stages. --- assets | 2 +- source/funkin/play/character/BaseCharacter.hx | 16 +++++++--- source/funkin/ui/freeplay/FreeplayState.hx | 30 +++++++++++++------ source/funkin/ui/freeplay/SongMenuItem.hx | 3 +- source/funkin/ui/mainmenu/MainMenuState.hx | 5 +++- 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/assets b/assets index 005c96f85f..d5b2d05df6 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 005c96f85f4304865acb196e7cc4d6d83f9d76d8 +Subproject commit d5b2d05df6d197f90a1b93f5b50835209c21adf7 diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index 0dab2101a6..4778e0c1c6 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -382,12 +382,19 @@ class BaseCharacter extends Bopper // and Darnell (this keeps the flame on his lighter flickering). // Works for idle, singLEFT/RIGHT/UP/DOWN, alt singing animations, and anything else really. - if (!getCurrentAnimation().endsWith(Constants.ANIMATION_HOLD_SUFFIX) - && hasAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX) - && isAnimationFinished()) + if (isAnimationFinished() + && !getCurrentAnimation().endsWith(Constants.ANIMATION_HOLD_SUFFIX) + && hasAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX)) { playAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX); } + else + { + if (isAnimationFinished()) + { + trace('Not playing hold (${getCurrentAnimation()}) (${isAnimationFinished()}, ${getCurrentAnimation().endsWith(Constants.ANIMATION_HOLD_SUFFIX)}, ${hasAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX)})'); + } + } // Handle character note hold time. if (isSinging()) @@ -427,7 +434,7 @@ class BaseCharacter extends Bopper else { // Play the idle animation. - trace('${characterId}: attempting dance'); + // trace('${characterId}: attempting dance'); dance(true); } } @@ -638,6 +645,7 @@ class BaseCharacter extends Bopper var anim:String = 'sing${dir.nameUpper}${miss ? 'miss' : ''}${suffix != '' ? '-${suffix}' : ''}'; // restart even if already playing, because the character might sing the same note twice. + trace('Playing ${anim}...'); playAnimation(anim, true); } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index dc42bd6510..416e79df6b 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -178,9 +178,22 @@ class FreeplayState extends MusicBeatSubState var stickerSubState:Null = null; - public static var rememberedDifficulty:Null = Constants.DEFAULT_DIFFICULTY; + /** + * The difficulty we were on when this menu was last accessed. + */ + public static var rememberedDifficulty:String = Constants.DEFAULT_DIFFICULTY; + + /** + * The song we were on when this menu was last accessed. + * NOTE: `null` if the last song was `Random`. + */ public static var rememberedSongId:Null = 'tutorial'; + /** + * The character we were on when this menu was last accessed. + */ + public static var rememberedCharacterId:String = Constants.DEFAULT_CHARACTER; + var funnyCam:FunkinCamera; var rankCamera:FunkinCamera; var rankBg:FunkinSprite; @@ -210,14 +223,16 @@ class FreeplayState extends MusicBeatSubState public function new(?params:FreeplayStateParams, ?stickers:StickerSubState) { - currentCharacterId = params?.character ?? Constants.DEFAULT_CHARACTER; + currentCharacterId = params?.character ?? rememberedCharacterId; var fetchPlayableCharacter = function():PlayableCharacter { - var result = PlayerRegistry.instance.fetchEntry(params?.character ?? Constants.DEFAULT_CHARACTER); + var result = PlayerRegistry.instance.fetchEntry(params?.character ?? rememberedCharacterId); if (result == null) throw 'No valid playable character with id ${params?.character}'; return result; }; currentCharacter = fetchPlayableCharacter(); + rememberedCharacterId = currentCharacter?.id ?? Constants.DEFAULT_CHARACTER; + fromResultsParams = params?.fromResults; if (fromResultsParams?.playRankAnim == true) @@ -744,10 +759,7 @@ class FreeplayState extends MusicBeatSubState var tempSongs:Array> = songs; // Remember just the difficulty because it's important for song sorting. - if (rememberedDifficulty != null) - { - currentDifficulty = rememberedDifficulty; - } + currentDifficulty = rememberedDifficulty; if (filterStuff != null) tempSongs = sortSongs(tempSongs, filterStuff); @@ -1216,7 +1228,7 @@ class FreeplayState extends MusicBeatSubState FlxG.switchState(FreeplayState.build( { { - character: currentCharacterId == "pico" ? "bf" : "pico", + character: currentCharacterId == "pico" ? Constants.DEFAULT_CHARACTER : "pico", } })); } @@ -1889,7 +1901,7 @@ class FreeplayState extends MusicBeatSubState intendedCompletion = 0.0; diffIdsCurrent = diffIdsTotal; rememberedSongId = null; - rememberedDifficulty = null; + rememberedDifficulty = Constants.DEFAULT_DIFFICULTY; albumRoll.albumId = null; } diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx index 2eec83223e..b4409d377b 100644 --- a/source/funkin/ui/freeplay/SongMenuItem.hx +++ b/source/funkin/ui/freeplay/SongMenuItem.hx @@ -162,7 +162,7 @@ class SongMenuItem extends FlxSpriteGroup sparkle = new FlxSprite(ranking.x, ranking.y); sparkle.frames = Paths.getSparrowAtlas('freeplay/sparkle'); - sparkle.animation.addByPrefix('sparkle', 'sparkle', 24, false); + sparkle.animation.addByPrefix('sparkle', 'sparkle Export0', 24, false); sparkle.animation.play('sparkle', true); sparkle.scale.set(0.8, 0.8); sparkle.blend = BlendMode.ADD; @@ -523,7 +523,6 @@ class SongMenuItem extends FlxSpriteGroup checkWeek(songData?.songId); } - var frameInTicker:Float = 0; var frameInTypeBeat:Int = 0; diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index d219bfbcfa..3f532d33c3 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -119,7 +119,10 @@ class MainMenuState extends MusicBeatState openSubState(new FreeplayState( { - character: FlxG.keys.pressed.SHIFT ? 'pico' : 'bf', + #if debug + // If SHIFT is held, toggle the selected character, else use the remembered character + character: (FlxG.keys.pressed.SHIFT) ? (FreeplayState.rememberedCharacterId == Constants.DEFAULT_CHARACTER ? 'pico' : 'bf') : null, + #end })); }); From 35ba5663a1ff9b9d95bf2c10e843156c17be4801 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 15 Jul 2024 23:10:08 -0400 Subject: [PATCH 196/469] uncapped framerate on web --- source/Main.hx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source/Main.hx b/source/Main.hx index add5bbc67b..0e5ed0c31d 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -66,6 +66,18 @@ class Main extends Sprite function init(?event:Event):Void { + #if web + untyped js.Syntax.code(" + window.requestAnimationFrame = function(callback, element) { + var currTime = new Date().getTime(); + var timeToCall = 0; + var id = window.setTimeout(function() { callback(currTime + timeToCall); }, + timeToCall); + lastTime = currTime + timeToCall; + return id; + }"); + #end + if (hasEventListener(Event.ADDED_TO_STAGE)) { removeEventListener(Event.ADDED_TO_STAGE, init); From f8232911cb114e867448f10c134dc63b202b1e48 Mon Sep 17 00:00:00 2001 From: 7oltan <87986834+7oltan@users.noreply.github.com> Date: Tue, 16 Jul 2024 12:52:51 +0300 Subject: [PATCH 197/469] FIX CHARACTER PATHS ISSUE --- source/funkin/play/character/AnimateAtlasCharacter.hx | 2 +- source/funkin/play/character/MultiSparrowCharacter.hx | 4 ++-- source/funkin/play/character/PackerCharacter.hx | 2 +- source/funkin/play/character/SparrowCharacter.hx | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/funkin/play/character/AnimateAtlasCharacter.hx b/source/funkin/play/character/AnimateAtlasCharacter.hx index ed58b92b55..5b126db6c6 100644 --- a/source/funkin/play/character/AnimateAtlasCharacter.hx +++ b/source/funkin/play/character/AnimateAtlasCharacter.hx @@ -131,7 +131,7 @@ class AnimateAtlasCharacter extends BaseCharacter { trace('[ATLASCHAR] Loading sprite atlas for ${characterId}.'); - var sprite:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas(_data.assetPath, 'shared')); + var sprite:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas(_data.assetPath)); sprite.onAnimationFinish.removeAll(); sprite.onAnimationFinish.add(this.onAnimationFinished); diff --git a/source/funkin/play/character/MultiSparrowCharacter.hx b/source/funkin/play/character/MultiSparrowCharacter.hx index 48c5afb58d..48a0ec6326 100644 --- a/source/funkin/play/character/MultiSparrowCharacter.hx +++ b/source/funkin/play/character/MultiSparrowCharacter.hx @@ -60,7 +60,7 @@ class MultiSparrowCharacter extends BaseCharacter } } - var texture:FlxAtlasFrames = Paths.getSparrowAtlas(_data.assetPath, 'shared'); + var texture:FlxAtlasFrames = Paths.getSparrowAtlas(_data.assetPath); if (texture == null) { @@ -74,7 +74,7 @@ class MultiSparrowCharacter extends BaseCharacter for (asset in assetList) { - var subTexture:FlxAtlasFrames = Paths.getSparrowAtlas(asset, 'shared'); + var subTexture:FlxAtlasFrames = Paths.getSparrowAtlas(asset); // If we don't do this, the unused textures will be removed as soon as they're loaded. if (subTexture == null) diff --git a/source/funkin/play/character/PackerCharacter.hx b/source/funkin/play/character/PackerCharacter.hx index 2bfac800ac..b7dda424f2 100644 --- a/source/funkin/play/character/PackerCharacter.hx +++ b/source/funkin/play/character/PackerCharacter.hx @@ -30,7 +30,7 @@ class PackerCharacter extends BaseCharacter { trace('[PACKERCHAR] Loading spritesheet ${_data.assetPath} for ${characterId}'); - var tex:FlxFramesCollection = Paths.getPackerAtlas(_data.assetPath, 'shared'); + var tex:FlxFramesCollection = Paths.getPackerAtlas(_data.assetPath); if (tex == null) { trace('Could not load Packer sprite: ${_data.assetPath}'); diff --git a/source/funkin/play/character/SparrowCharacter.hx b/source/funkin/play/character/SparrowCharacter.hx index a36aed84da..90a695ec3a 100644 --- a/source/funkin/play/character/SparrowCharacter.hx +++ b/source/funkin/play/character/SparrowCharacter.hx @@ -33,7 +33,7 @@ class SparrowCharacter extends BaseCharacter { trace('[SPARROWCHAR] Loading spritesheet ${_data.assetPath} for ${characterId}'); - var tex:FlxFramesCollection = Paths.getSparrowAtlas(_data.assetPath, 'shared'); + var tex:FlxFramesCollection = Paths.getSparrowAtlas(_data.assetPath); if (tex == null) { trace('Could not load Sparrow sprite: ${_data.assetPath}'); From f8a0627fd270745ba102e8c6a4b0a223e8cf9d3e Mon Sep 17 00:00:00 2001 From: anysad Date: Tue, 16 Jul 2024 20:53:12 +0300 Subject: [PATCH 198/469] goodbye scripts, hello notestyles! --- assets | 2 +- source/funkin/play/Countdown.hx | 101 +++++++++++--------- source/funkin/play/components/PopUpStuff.hx | 75 +++++++-------- source/funkin/ui/transition/LoadingState.hx | 73 +++++++------- source/funkin/util/Constants.hx | 5 + 5 files changed, 132 insertions(+), 124 deletions(-) diff --git a/assets b/assets index e414ee618b..ca4d97554f 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit e414ee618b4fa577dc3c3df4e156488e91c6348d +Subproject commit ca4d97554f9e3853fda25733f21a5fa108c0b902 diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 7686e5b75e..b97451fa1b 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -11,6 +11,8 @@ import funkin.modding.events.ScriptEvent.CountdownScriptEvent; import flixel.util.FlxTimer; import funkin.audio.FunkinSound; import openfl.utils.Assets; +import funkin.data.notestyle.NoteStyleRegistry; +import funkin.play.notes.notestyle.NoteStyle; class Countdown { @@ -20,20 +22,13 @@ class Countdown public static var countdownStep(default, null):CountdownStep = BEFORE; /** - * Which alternate countdown sound effect to use. - * You can set this via script. - * For example, in Week 6 it is `-pixel`. + * Which alternate graphic/sound on countdown to use. + * This is set via the current notestyle. + * For example, in Week 6 it is `pixel`. */ - public static var soundSuffix:String = ''; - - /** - * Which alternate graphic on countdown to use. - * You can set this via script. - * For example, in Week 6 it is `-pixel`. - */ - public static var graphicSuffix:String = ''; - + static var noteStyle:NoteStyle; + static var isPixel:Bool = false; /** * The currently running countdown. This will be null if there is no countdown running. @@ -64,6 +59,11 @@ class Countdown // @:privateAccess // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); + var fetchedNoteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(PlayState.instance.currentChart.noteStyle); + if (fetchedNoteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); + else noteStyle = fetchedNoteStyle; + if (noteStyle._data.assets.note.isPixel) isPixel = true; + // The timer function gets called based on the beat of the song. countdownTimer = new FlxTimer(); @@ -81,10 +81,10 @@ class Countdown // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); // Countdown graphic. - showCountdownGraphic(countdownStep, graphicSuffix.toLowerCase().contains('pixel')); + showCountdownGraphic(countdownStep, noteStyle, isPixel); // Countdown sound. - playCountdownSound(countdownStep); + playCountdownSound(countdownStep, noteStyle); // Event handling bullshit. var cancelled:Bool = propagateCountdownEvent(countdownStep); @@ -197,17 +197,31 @@ class Countdown */ public static function reset() { - soundSuffix = ''; - graphicSuffix = ''; + noteStyle = NoteStyleRegistry.instance.fetchDefault(); + isPixel = false; } /** * Retrieves the graphic to use for this step of the countdown. */ - public static function showCountdownGraphic(index:CountdownStep, isGraphicPixel:Bool):Void + public static function showCountdownGraphic(index:CountdownStep, noteStyle:NoteStyle, isGraphicPixel:Bool):Void { + var indexString:String = null; + switch (index) + { + case TWO: + indexString = 'ready'; + case ONE: + indexString = 'set'; + case GO: + indexString = 'go'; + default: + // null + } + if (indexString == null) return; + var spritePath:String = null; - spritePath = resolveGraphicPath(graphicSuffix, index); + spritePath = resolveGraphicPath(noteStyle, indexString); if (spritePath == null) return; @@ -215,12 +229,15 @@ class Countdown countdownSprite.scrollFactor.set(0, 0); if (isGraphicPixel) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE)); + else countdownSprite.setGraphicSize(Std.int(countdownSprite.width * 0.7)); countdownSprite.antialiasing = !isGraphicPixel; countdownSprite.updateHitbox(); countdownSprite.screenCenter(); + countdownSprite.cameras = [PlayState.instance.camHUD]; + // Fade sprite in, then out, then destroy it. FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.instance.beatLengthMs / 1000, { @@ -233,29 +250,18 @@ class Countdown PlayState.instance.add(countdownSprite); } - static function resolveGraphicPath(suffix:String, index:CountdownStep):Null + static function resolveGraphicPath(noteStyle:NoteStyle, index:String):Null { var basePath:String = 'ui/countdown/'; - var indexString:String = null; - switch (index) - { - case TWO: - indexString = 'ready'; - case ONE: - indexString = 'set'; - case GO: - indexString = 'go'; - default: - // null - } - basePath += indexString; - var spritePath:String = basePath + suffix; - while (!Assets.exists(Paths.image(spritePath)) && suffix.length > 0) + + var spritePath:String = basePath + noteStyle.id + '/$index'; + // If nothing is found, revert it to default notestyle skin + if (!Assets.exists(Paths.image(spritePath))) { - suffix = suffix.split('-').slice(0, -1).join('-'); - spritePath = basePath + suffix; + if (!isPixel) spritePath = basePath + Constants.DEFAULT_NOTE_STYLE + '/$index'; + else spritePath = basePath + Constants.DEFAULT_PIXEL_NOTE_STYLE + '/$index'; } - if (!Assets.exists(Paths.image(spritePath))) return null; + trace('Resolved sprite path: ' + Paths.image(spritePath)); return spritePath; } @@ -263,23 +269,24 @@ class Countdown /** * Retrieves the sound file to use for this step of the countdown. */ - public static function playCountdownSound(index:CountdownStep):Void + public static function playCountdownSound(step:CountdownStep, noteStyle:NoteStyle):Void { - FunkinSound.playOnce(resolveSoundPath(soundSuffix, index), Constants.COUNTDOWN_VOLUME); + return FunkinSound.playOnce(Paths.sound(resolveSoundPath(noteStyle, step)), Constants.COUNTDOWN_VOLUME); } - static function resolveSoundPath(suffix:String, step:CountdownStep):Null + static function resolveSoundPath(noteStyle:NoteStyle, step:CountdownStep):Null { - var basePath:String = 'gameplay/countdown/intro'; - if (step != CountdownStep.BEFORE || step != CountdownStep.AFTER) basePath += step; + if (step == CountdownStep.BEFORE || step == CountdownStep.AFTER) return null; + var basePath:String = 'gameplay/countdown/'; - var soundPath:String = Paths.sound(basePath + suffix); - while (!Assets.exists(soundPath) && suffix.length > 0) + var soundPath:String = basePath + noteStyle.id + '/intro$step'; + // If nothing is found, revert it to default notestyle sound + if (!Assets.exists(Paths.sound(soundPath))) { - suffix = suffix.split('-').slice(0, -1).join('-'); - soundPath = Paths.sound(basePath + suffix); + if (!isPixel) soundPath = basePath + Constants.DEFAULT_NOTE_STYLE + '/intro$step'; + else soundPath = basePath + Constants.DEFAULT_PIXEL_NOTE_STYLE + '/intro$step'; } - if (!Assets.exists(soundPath)) return null; + trace('Resolved sound path: ' + soundPath); return soundPath; } diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index 0a4d6b0193..eb59a99224 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -8,6 +8,8 @@ import funkin.graphics.FunkinSprite; import funkin.play.PlayState; import funkin.util.TimerUtil; import openfl.utils.Assets; +import funkin.data.notestyle.NoteStyleRegistry; +import funkin.play.notes.notestyle.NoteStyle; class PopUpStuff extends FlxTypedGroup { @@ -15,31 +17,35 @@ class PopUpStuff extends FlxTypedGroup /** * Which alternate graphic on popup to use. - * You can set this via script. - * For example, in Week 6 it is `-pixel`. + * This is set via the current notestyle. + * For example, in Week 6 it is `pixel`. */ - public static var graphicSuffix:String = ''; + static var noteStyle:NoteStyle; + + static var isPixel:Bool = false; override public function new() { super(); + + var fetchedNoteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(PlayState.instance.currentChart.noteStyle); + if (fetchedNoteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); + else noteStyle = fetchedNoteStyle; + if (noteStyle._data.assets.note.isPixel) isPixel = true; } - static function resolveGraphicPath(suffix:String, index:String):Null + static function resolveGraphicPath(noteStyle:NoteStyle, index:String):Null { - var folder:String; - if (suffix != '') - folder = suffix.substring(0, suffix.indexOf("-")) + suffix.substring(suffix.indexOf("-") + 1); - else - folder = 'normal'; - var basePath:String = 'gameplay/popup/$folder/$index'; - var spritePath:String = basePath + suffix; - while (!Assets.exists(Paths.image(spritePath)) && suffix.length > 0) + var basePath:String = 'ui/popup/'; + + var spritePath:String = basePath + noteStyle.id + '/$index'; + // If nothing is found, revert it to default notestyle skin + if (!Assets.exists(Paths.image(spritePath))) { - suffix = suffix.split('-').slice(0, -1).join('-'); - spritePath = basePath + suffix; + if (!isPixel) spritePath = basePath + Constants.DEFAULT_NOTE_STYLE + '/$index'; + else spritePath = basePath + Constants.DEFAULT_PIXEL_NOTE_STYLE + '/$index'; } - if (!Assets.exists(Paths.image(spritePath))) return null; + return spritePath; } @@ -49,7 +55,7 @@ class PopUpStuff extends FlxTypedGroup if (daRating == null) daRating = "good"; - var ratingPath:String = resolveGraphicPath(graphicSuffix, daRating); + var ratingPath:String = resolveGraphicPath(noteStyle, daRating); //if (PlayState.instance.currentStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel"; @@ -66,7 +72,7 @@ class PopUpStuff extends FlxTypedGroup add(rating); - if (graphicSuffix.toLowerCase().contains('pixel')) + if (isPixel) { rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.7)); rating.antialiasing = false; @@ -99,7 +105,7 @@ class PopUpStuff extends FlxTypedGroup if (combo == null) combo = 0; - var comboPath:String = resolveGraphicPath(graphicSuffix, Std.string(combo)); + var comboPath:String = resolveGraphicPath(noteStyle, 'combo'); var comboSpr:FunkinSprite = FunkinSprite.create(comboPath); comboSpr.y = (FlxG.camera.height * 0.44) + offsets[1]; comboSpr.x = (FlxG.width * 0.507) + offsets[0]; @@ -111,16 +117,10 @@ class PopUpStuff extends FlxTypedGroup // add(comboSpr); - if (graphicSuffix.toLowerCase().contains('pixel')) - { - comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 0.7)); - comboSpr.antialiasing = false; - } - else - { - comboSpr.setGraphicSize(Std.int(comboSpr.width * 0.7)); - comboSpr.antialiasing = true; - } + if (isPixel) comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 0.7)); + else comboSpr.setGraphicSize(Std.int(comboSpr.width * 0.7)); + + comboSpr.antialiasing = !isPixel; comboSpr.updateHitbox(); FlxTween.tween(comboSpr, {alpha: 0}, 0.2, @@ -148,18 +148,12 @@ class PopUpStuff extends FlxTypedGroup var daLoop:Int = 1; for (i in seperatedScore) { - var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, resolveGraphicPath(graphicSuffix, 'num' + Std.int(i))); + var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, resolveGraphicPath(noteStyle, 'num' + Std.int(i))); - if (graphicSuffix.toLowerCase().contains('pixel')) - { - numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 0.7)); - numScore.antialiasing = false; - } - else - { - numScore.setGraphicSize(Std.int(numScore.width * 0.45)); - numScore.antialiasing = true; - } + if (isPixel) numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 0.7)); + else numScore.setGraphicSize(Std.int(numScore.width * 0.45)); + + numScore.antialiasing = !isPixel; numScore.updateHitbox(); numScore.x = comboSpr.x - (36 * daLoop) - 65; //- 90; @@ -191,6 +185,7 @@ class PopUpStuff extends FlxTypedGroup */ public static function reset() { - graphicSuffix = ''; + noteStyle = NoteStyleRegistry.instance.fetchDefault(); + isPixel = false; } } diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index a571126fea..e50032d65f 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -291,46 +291,47 @@ class LoadingState extends MusicBeatSubState FunkinSprite.preparePurgeCache(); FunkinSprite.cacheTexture(Paths.image('healthBar')); FunkinSprite.cacheTexture(Paths.image('menuDesat')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/combo')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num0')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num1')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num2')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num3')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num4')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num5')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num6')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num7')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num8')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num9')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/combo-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num0-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num1-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num2-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num3-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num4-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num5-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num6-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num7-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num8-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num9-pixel')); + // Lord have mercy on me and this caching -anysad + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/combo')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num0')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num1')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num2')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num3')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num4')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num5')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num6')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num7')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num8')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num9')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/combo')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num0')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num1')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num2')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num3')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num4')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num5')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num6')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num7')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num8')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num9')); FunkinSprite.cacheTexture(Paths.image('notes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared')); FunkinSprite.cacheTexture(Paths.image('NOTE_hold_assets')); - FunkinSprite.cacheTexture(Paths.image('ui/countdown/ready', 'shared')); - FunkinSprite.cacheTexture(Paths.image('ui/countdown/set', 'shared')); - FunkinSprite.cacheTexture(Paths.image('ui/countdown/go', 'shared')); - FunkinSprite.cacheTexture(Paths.image('ui/countdown/ready-pixel', 'shared')); - FunkinSprite.cacheTexture(Paths.image('ui/countdown/set-pixel', 'shared')); - FunkinSprite.cacheTexture(Paths.image('ui/countdown/go-pixel', 'shared')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/sick')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/good')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/bad')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/shit')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/sick-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/good-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/bad-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/shit-pixel')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/ready', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/set', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/go', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/ready', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/set', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/go', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/sick')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/good')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/bad')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/shit')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/sick')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/good')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/bad')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/shit')); FunkinSprite.cacheTexture(Paths.image('miss', 'shared')); // TODO: remove this // List all image assets in the level's library. diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index 1e0978839a..79b0e05c56 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -258,6 +258,11 @@ class Constants */ public static final DEFAULT_NOTE_STYLE:String = 'funkin'; + /** + * The default pixel note style for songs. + */ + public static final DEFAULT_PIXEL_NOTE_STYLE:String = 'pixel'; + /** * The default album for songs in Freeplay. */ From 5e1d25e791a14f6b301730043607e14b53f36e8d Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:17:53 +0300 Subject: [PATCH 199/469] [BUGFIX] Prevented infinite recursion on freeplay when a song doesn't have the `normal` difficulty Any attempt to create a song without the `normal` difficulty will result in a silent crash when opening freeplay. After some debugging, I discovered that the issue is caused by infinite recursion, which gets triggered at the start of `FreeplaySongData`'s `updateValues`. --- source/funkin/ui/freeplay/FreeplayState.hx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 0caaf45915..9873f3be64 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -2077,7 +2077,11 @@ class FreeplaySongData function updateValues(variations:Array):Void { this.songDifficulties = song.listDifficulties(null, variations, false, false); - if (!this.songDifficulties.contains(currentDifficulty)) currentDifficulty = Constants.DEFAULT_DIFFICULTY; + if (!this.songDifficulties.contains(currentDifficulty) && currentDifficulty != Constants.DEFAULT_DIFFICULTY) + { + currentDifficulty = Constants.DEFAULT_DIFFICULTY; + return; + } var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, null, variations); if (songDifficulty == null) return; From ee449819959ed51b5719f7ebd39493118c307eee Mon Sep 17 00:00:00 2001 From: anysad Date: Tue, 16 Jul 2024 22:02:05 +0300 Subject: [PATCH 200/469] you know what, make countdown sprites a little bigger --- source/funkin/play/Countdown.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index b97451fa1b..ccd4783479 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -228,8 +228,8 @@ class Countdown var countdownSprite:FunkinSprite = FunkinSprite.create(spritePath); countdownSprite.scrollFactor.set(0, 0); - if (isGraphicPixel) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE)); - else countdownSprite.setGraphicSize(Std.int(countdownSprite.width * 0.7)); + if (isGraphicPixel) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE * 1.1)); + else countdownSprite.setGraphicSize(Std.int(countdownSprite.width * 0.8)); countdownSprite.antialiasing = !isGraphicPixel; From a93cd05aeb6c30c241c6c8f8211abb953bb65532 Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:36:11 +0300 Subject: [PATCH 201/469] Added a comment --- source/funkin/ui/freeplay/FreeplayState.hx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 9873f3be64..51fda06773 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -2080,6 +2080,8 @@ class FreeplaySongData if (!this.songDifficulties.contains(currentDifficulty) && currentDifficulty != Constants.DEFAULT_DIFFICULTY) { currentDifficulty = Constants.DEFAULT_DIFFICULTY; + // This method gets called again by the setter-method, + // so there's no need to continue. return; } From 691da783fc41fc648b760833d4439c4368ef29a3 Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:20:48 +0300 Subject: [PATCH 202/469] Updated to extend #2712 --- source/funkin/ui/freeplay/FreeplayState.hx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 51fda06773..a22652586e 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -2077,11 +2077,11 @@ class FreeplaySongData function updateValues(variations:Array):Void { this.songDifficulties = song.listDifficulties(null, variations, false, false); - if (!this.songDifficulties.contains(currentDifficulty) && currentDifficulty != Constants.DEFAULT_DIFFICULTY) + if (!this.songDifficulties.contains(currentDifficulty)) { currentDifficulty = Constants.DEFAULT_DIFFICULTY; - // This method gets called again by the setter-method, - // so there's no need to continue. + // This method gets called again by the setter-method + // or the difficulty didn't change, so there's no need to continue. return; } From 4d81aa0fe696a1878d3678209e20c985e1631425 Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Wed, 17 Jul 2024 18:00:07 +0300 Subject: [PATCH 203/469] [BUGFIX] Ensure the variation used for the next song is valid. When playing in story mode, `PlayState` assumes all the songs use the same variation, which is usually true, but not guaranteed. This PR makes `PlayState` use the `getFirstValidVariation` method to find the valid variation instead of using the `currentVariation` variable, which might not be valid. --- source/funkin/play/PlayState.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index f55cef3888..9b1cc2ed97 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2963,7 +2963,7 @@ class PlayState extends MusicBeatSubState { targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty, - targetVariation: currentVariation, + targetVariation: targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty), cameraFollowPoint: cameraFollowPoint.getPosition(), }); }); @@ -2975,7 +2975,7 @@ class PlayState extends MusicBeatSubState { targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty, - targetVariation: currentVariation, + targetVariation: targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty), cameraFollowPoint: cameraFollowPoint.getPosition(), }); } From ff1ab1eb4245092829701e322c1b4dbe27f80590 Mon Sep 17 00:00:00 2001 From: anysad Date: Wed, 17 Jul 2024 23:19:18 +0300 Subject: [PATCH 204/469] welcome fallback note styles! --- source/funkin/play/Countdown.hx | 53 ++++++++++++++----- source/funkin/play/components/PopUpStuff.hx | 21 +++++++- .../funkin/play/notes/notestyle/NoteStyle.hx | 2 +- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index ccd4783479..25a896317b 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -28,6 +28,8 @@ class Countdown */ static var noteStyle:NoteStyle; + static var fallbackNoteStyle:Null; + static var isPixel:Bool = false; /** @@ -59,10 +61,7 @@ class Countdown // @:privateAccess // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); - var fetchedNoteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(PlayState.instance.currentChart.noteStyle); - if (fetchedNoteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); - else noteStyle = fetchedNoteStyle; - if (noteStyle._data.assets.note.isPixel) isPixel = true; + fetchNoteStyle(); // The timer function gets called based on the beat of the song. countdownTimer = new FlxTimer(); @@ -81,7 +80,7 @@ class Countdown // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); // Countdown graphic. - showCountdownGraphic(countdownStep, noteStyle, isPixel); + showCountdownGraphic(countdownStep, noteStyle); // Countdown sound. playCountdownSound(countdownStep, noteStyle); @@ -201,10 +200,19 @@ class Countdown isPixel = false; } + static function fetchNoteStyle():Void + { + var fetchedNoteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(PlayState.instance.currentChart.noteStyle); + if (fetchedNoteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); + else noteStyle = fetchedNoteStyle; + fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); + isPixel = false; + } + /** * Retrieves the graphic to use for this step of the countdown. */ - public static function showCountdownGraphic(index:CountdownStep, noteStyle:NoteStyle, isGraphicPixel:Bool):Void + public static function showCountdownGraphic(index:CountdownStep, noteStyle:NoteStyle):Void { var indexString:String = null; switch (index) @@ -228,16 +236,16 @@ class Countdown var countdownSprite:FunkinSprite = FunkinSprite.create(spritePath); countdownSprite.scrollFactor.set(0, 0); - if (isGraphicPixel) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE * 1.1)); + if (isPixel) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE * 1.1)); else countdownSprite.setGraphicSize(Std.int(countdownSprite.width * 0.8)); - countdownSprite.antialiasing = !isGraphicPixel; + countdownSprite.antialiasing = !isPixel; + + countdownSprite.cameras = [PlayState.instance.camHUD]; countdownSprite.updateHitbox(); countdownSprite.screenCenter(); - countdownSprite.cameras = [PlayState.instance.camHUD]; - // Fade sprite in, then out, then destroy it. FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.instance.beatLengthMs / 1000, { @@ -252,10 +260,18 @@ class Countdown static function resolveGraphicPath(noteStyle:NoteStyle, index:String):Null { + fetchNoteStyle(); var basePath:String = 'ui/countdown/'; - var spritePath:String = basePath + noteStyle.id + '/$index'; - // If nothing is found, revert it to default notestyle skin + + while (!Assets.exists(Paths.image(spritePath)) && fallbackNoteStyle != null) { + noteStyle = fallbackNoteStyle; + fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); + spritePath = basePath + noteStyle.id + '/$index'; + } + if (noteStyle.isHoldNotePixel()) isPixel = true; + + // If ABSOLUTELY nothing is found, revert it to default notestyle skin if (!Assets.exists(Paths.image(spritePath))) { if (!isPixel) spritePath = basePath + Constants.DEFAULT_NOTE_STYLE + '/$index'; @@ -277,10 +293,19 @@ class Countdown static function resolveSoundPath(noteStyle:NoteStyle, step:CountdownStep):Null { if (step == CountdownStep.BEFORE || step == CountdownStep.AFTER) return null; + fetchNoteStyle(); var basePath:String = 'gameplay/countdown/'; - var soundPath:String = basePath + noteStyle.id + '/intro$step'; - // If nothing is found, revert it to default notestyle sound + + while (!Assets.exists(Paths.sound(soundPath)) && fallbackNoteStyle != null) + { + noteStyle = fallbackNoteStyle; + fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); + soundPath = basePath + noteStyle.id + '/intro$step'; + } + if (noteStyle.isHoldNotePixel()) isPixel = true; + + // If ABSOLUTELY nothing is found, revert it to default notestyle sound if (!Assets.exists(Paths.sound(soundPath))) { if (!isPixel) soundPath = basePath + Constants.DEFAULT_NOTE_STYLE + '/intro$step'; diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index eb59a99224..6c111c0dbf 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -22,23 +22,40 @@ class PopUpStuff extends FlxTypedGroup */ static var noteStyle:NoteStyle; + static var fallbackNoteStyle:Null; + static var isPixel:Bool = false; override public function new() { super(); + fetchNoteStyle(); + } + + static function fetchNoteStyle():Void + { var fetchedNoteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(PlayState.instance.currentChart.noteStyle); if (fetchedNoteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); else noteStyle = fetchedNoteStyle; - if (noteStyle._data.assets.note.isPixel) isPixel = true; + fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); + isPixel = false; } static function resolveGraphicPath(noteStyle:NoteStyle, index:String):Null { + fetchNoteStyle(); var basePath:String = 'ui/popup/'; - var spritePath:String = basePath + noteStyle.id + '/$index'; + + while (!Assets.exists(Paths.image(spritePath)) && fallbackNoteStyle != null) + { + noteStyle = fallbackNoteStyle; + fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); + spritePath = basePath + noteStyle.id + '/$index'; + } + if (noteStyle.isHoldNotePixel()) isPixel = true; + // If nothing is found, revert it to default notestyle skin if (!Assets.exists(Paths.image(spritePath))) { diff --git a/source/funkin/play/notes/notestyle/NoteStyle.hx b/source/funkin/play/notes/notestyle/NoteStyle.hx index d0cc09f6a8..edfcff2ee1 100644 --- a/source/funkin/play/notes/notestyle/NoteStyle.hx +++ b/source/funkin/play/notes/notestyle/NoteStyle.hx @@ -72,7 +72,7 @@ class NoteStyle implements IRegistryEntry * Get the note style ID of the parent note style. * @return The string ID, or `null` if there is no parent. */ - function getFallbackID():Null + public function getFallbackID():Null { return _data.fallback; } From 73fc855f8e6eb8efb14e7ab7d35ff269e76287f2 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 18 Jul 2024 05:23:31 +0100 Subject: [PATCH 205/469] new stage names --- source/funkin/data/stage/StageRegistry.hx | 4 ++-- source/funkin/ui/debug/charting/ChartEditorState.hx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/funkin/data/stage/StageRegistry.hx b/source/funkin/data/stage/StageRegistry.hx index a033712960..fbb6f188e3 100644 --- a/source/funkin/data/stage/StageRegistry.hx +++ b/source/funkin/data/stage/StageRegistry.hx @@ -93,8 +93,8 @@ class StageRegistry extends BaseRegistry public function listBaseGameStageIds():Array { return [ - "mainStage", "spookyMansion", "phillyTrain", "limoRide", "mallXmas", "mallEvil", "school", "schoolEvil", "tankmanBattlefield", "phillyStreets", - "phillyBlazin", + "mainStage", "spookyMansion", "phillyTrain", "phillyTrainErect", "limoRide", "mallXmas", "mallEvil", "school", "schoolEvil", "tankmanBattlefield", + "phillyStreets", "phillyBlazin", ]; } diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index f72cca77f6..a4e0de61eb 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -5703,9 +5703,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState { case 'mainStage': PlayStatePlaylist.campaignId = 'week1'; - case 'spookyMansion': + case 'spookyMansion' | 'spookyMansionErect': PlayStatePlaylist.campaignId = 'week2'; - case 'phillyTrain': + case 'phillyTrain' | 'phillyTrainErect': PlayStatePlaylist.campaignId = 'week3'; case 'limoRide': PlayStatePlaylist.campaignId = 'week4'; From ad93706b1df16f8191e839b6920da4e5e316c071 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 18 Jul 2024 05:23:37 +0100 Subject: [PATCH 206/469] adjust color shader --- .../graphics/shaders/AdjustColorShader.hx | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 source/funkin/graphics/shaders/AdjustColorShader.hx diff --git a/source/funkin/graphics/shaders/AdjustColorShader.hx b/source/funkin/graphics/shaders/AdjustColorShader.hx new file mode 100644 index 0000000000..2b0970eeb2 --- /dev/null +++ b/source/funkin/graphics/shaders/AdjustColorShader.hx @@ -0,0 +1,55 @@ +package funkin.graphics.shaders; + +import flixel.addons.display.FlxRuntimeShader; +import funkin.Paths; +import openfl.utils.Assets; + +class AdjustColorShader extends FlxRuntimeShader +{ + public var hue(default, set):Float; + public var saturation(default, set):Float; + public var brightness(default, set):Float; + public var contrast(default, set):Float; + + public function new() + { + super(Assets.getText(Paths.frag('adjustColor'))); + // FlxG.debugger.addTrackerProfile(new TrackerProfile(HSVShader, ['hue', 'saturation', 'brightness', 'contrast'])); + hue = 0; + saturation = 0; + brightness = 0; + contrast = 0; + } + + function set_hue(value:Float):Float + { + this.setFloat('hue', value); + this.hue = value; + + return this.hue; + } + + function set_saturation(value:Float):Float + { + this.setFloat('saturation', value); + this.saturation = value; + + return this.saturation; + } + + function set_brightness(value:Float):Float + { + this.setFloat('brightness', value); + this.brightness = value; + + return this.brightness; + } + + function set_contrast(value:Float):Float + { + this.setFloat('contrast', value); + this.contrast = value; + + return this.contrast; + } +} From 4bcfbc957f82726973b4aa1428b285d1132a19e7 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 18 Jul 2024 05:24:15 +0100 Subject: [PATCH 207/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 005c96f85f..68bf145d57 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 005c96f85f4304865acb196e7cc4d6d83f9d76d8 +Subproject commit 68bf145d5786b2c3e4539a46727da67bef1fd039 From a0ab216617b16d87ed363a590979b4cdc8879012 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 18 Jul 2024 06:04:19 +0100 Subject: [PATCH 208/469] merged rain shader stuff from 'feature/week2-erect-bg-rain' --- .../shaders/RuntimePostEffectShader.hx | 31 +++++++++++++++++++ .../graphics/shaders/RuntimeRainShader.hx | 8 +++++ 2 files changed, 39 insertions(+) diff --git a/source/funkin/graphics/shaders/RuntimePostEffectShader.hx b/source/funkin/graphics/shaders/RuntimePostEffectShader.hx index 9f49da0759..d39f57efe4 100644 --- a/source/funkin/graphics/shaders/RuntimePostEffectShader.hx +++ b/source/funkin/graphics/shaders/RuntimePostEffectShader.hx @@ -2,6 +2,7 @@ package funkin.graphics.shaders; import flixel.FlxCamera; import flixel.FlxG; +import flixel.graphics.frames.FlxFrame; import flixel.addons.display.FlxRuntimeShader; import lime.graphics.opengl.GLProgram; import lime.utils.Log; @@ -32,6 +33,9 @@ class RuntimePostEffectShader extends FlxRuntimeShader // equals (camera.viewLeft, camera.viewTop, camera.viewRight, camera.viewBottom) uniform vec4 uCameraBounds; + // equals (frame.left, frame.top, frame.right, frame.bottom) + uniform vec4 uFrameBounds; + // screen coord -> world coord conversion // returns world coord in px vec2 screenToWorld(vec2 screenCoord) { @@ -56,6 +60,25 @@ class RuntimePostEffectShader extends FlxRuntimeShader return (worldCoord - offset) / scale; } + // screen coord -> frame coord conversion + // returns normalized frame coord + vec2 screenToFrame(vec2 screenCoord) { + float left = uFrameBounds.x; + float top = uFrameBounds.y; + float right = uFrameBounds.z; + float bottom = uFrameBounds.w; + float width = right - left; + float height = bottom - top; + + float clampedX = clamp(screenCoord.x, left, right); + float clampedY = clamp(screenCoord.y, top, bottom); + + return vec2( + (clampedX - left) / (width), + (clampedY - top) / (height) + ); + } + // internally used to get the maximum `openfl_TextureCoordv` vec2 bitmapCoordScale() { return openfl_TextureCoordv / screenCoord; @@ -80,6 +103,8 @@ class RuntimePostEffectShader extends FlxRuntimeShader { super(fragmentSource, null, glVersion); uScreenResolution.value = [FlxG.width, FlxG.height]; + uCameraBounds.value = [0, 0, FlxG.width, FlxG.height]; + uFrameBounds.value = [0, 0, FlxG.width, FlxG.height]; } // basically `updateViewInfo(FlxG.width, FlxG.height, FlxG.camera)` is good @@ -89,6 +114,12 @@ class RuntimePostEffectShader extends FlxRuntimeShader uCameraBounds.value = [camera.viewLeft, camera.viewTop, camera.viewRight, camera.viewBottom]; } + public function updateFrameInfo(frame:FlxFrame) + { + // NOTE: uv.width is actually the right pos and uv.height is the bottom pos + uFrameBounds.value = [frame.uv.x, frame.uv.y, frame.uv.width, frame.uv.height]; + } + override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram { try diff --git a/source/funkin/graphics/shaders/RuntimeRainShader.hx b/source/funkin/graphics/shaders/RuntimeRainShader.hx index 239276bbef..68a2031792 100644 --- a/source/funkin/graphics/shaders/RuntimeRainShader.hx +++ b/source/funkin/graphics/shaders/RuntimeRainShader.hx @@ -32,6 +32,14 @@ class RuntimeRainShader extends RuntimePostEffectShader return time = value; } + public var spriteMode(default, set):Bool = false; + + function set_spriteMode(value:Bool):Bool + { + this.setBool('uSpriteMode', value); + return spriteMode = value; + } + // The scale of the rain depends on the world coordinate system, so higher resolution makes // the raindrops smaller. This parameter can be used to adjust the total scale of the scene. // The size of the raindrops is proportional to the value of this parameter. From 563905ebd42810550904a3c4c894bc24afbc4f30 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 18 Jul 2024 06:04:32 +0100 Subject: [PATCH 209/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 68bf145d57..cfd67caa68 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 68bf145d5786b2c3e4539a46727da67bef1fd039 +Subproject commit cfd67caa688465b4a282837434832c107b661b04 From ba96b111960e2945c14eef0112368a30152d1e7c Mon Sep 17 00:00:00 2001 From: anysad Date: Thu, 18 Jul 2024 17:56:54 +0300 Subject: [PATCH 210/469] hopefully final polish? --- source/funkin/play/Countdown.hx | 26 ++++++++++----------- source/funkin/play/components/PopUpStuff.hx | 10 ++++---- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 25a896317b..1a9605597f 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -61,8 +61,6 @@ class Countdown // @:privateAccess // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); - fetchNoteStyle(); - // The timer function gets called based on the beat of the song. countdownTimer = new FlxTimer(); @@ -80,10 +78,10 @@ class Countdown // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); // Countdown graphic. - showCountdownGraphic(countdownStep, noteStyle); + showCountdownGraphic(countdownStep); // Countdown sound. - playCountdownSound(countdownStep, noteStyle); + playCountdownSound(countdownStep); // Event handling bullshit. var cancelled:Bool = propagateCountdownEvent(countdownStep); @@ -212,7 +210,7 @@ class Countdown /** * Retrieves the graphic to use for this step of the countdown. */ - public static function showCountdownGraphic(index:CountdownStep, noteStyle:NoteStyle):Void + public static function showCountdownGraphic(index:CountdownStep):Void { var indexString:String = null; switch (index) @@ -229,25 +227,24 @@ class Countdown if (indexString == null) return; var spritePath:String = null; - spritePath = resolveGraphicPath(noteStyle, indexString); + spritePath = resolveGraphicPath(indexString); if (spritePath == null) return; var countdownSprite:FunkinSprite = FunkinSprite.create(spritePath); countdownSprite.scrollFactor.set(0, 0); - if (isPixel) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE * 1.1)); - else countdownSprite.setGraphicSize(Std.int(countdownSprite.width * 0.8)); + if (isPixel) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE)); + else countdownSprite.setGraphicSize(Std.int(countdownSprite.width * 0.7)); countdownSprite.antialiasing = !isPixel; countdownSprite.cameras = [PlayState.instance.camHUD]; countdownSprite.updateHitbox(); - countdownSprite.screenCenter(); // Fade sprite in, then out, then destroy it. - FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.instance.beatLengthMs / 1000, + FlxTween.tween(countdownSprite, {alpha: 0}, Conductor.instance.beatLengthMs / 1000, { ease: FlxEase.cubeInOut, onComplete: function(twn:FlxTween) { @@ -256,9 +253,10 @@ class Countdown }); PlayState.instance.add(countdownSprite); + countdownSprite.screenCenter(); } - static function resolveGraphicPath(noteStyle:NoteStyle, index:String):Null + static function resolveGraphicPath(index:String):Null { fetchNoteStyle(); var basePath:String = 'ui/countdown/'; @@ -285,12 +283,12 @@ class Countdown /** * Retrieves the sound file to use for this step of the countdown. */ - public static function playCountdownSound(step:CountdownStep, noteStyle:NoteStyle):Void + public static function playCountdownSound(step:CountdownStep):Void { - return FunkinSound.playOnce(Paths.sound(resolveSoundPath(noteStyle, step)), Constants.COUNTDOWN_VOLUME); + return FunkinSound.playOnce(Paths.sound(resolveSoundPath(step)), Constants.COUNTDOWN_VOLUME); } - static function resolveSoundPath(noteStyle:NoteStyle, step:CountdownStep):Null + static function resolveSoundPath(step:CountdownStep):Null { if (step == CountdownStep.BEFORE || step == CountdownStep.AFTER) return null; fetchNoteStyle(); diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index 6c111c0dbf..4025358785 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -29,8 +29,6 @@ class PopUpStuff extends FlxTypedGroup override public function new() { super(); - - fetchNoteStyle(); } static function fetchNoteStyle():Void @@ -42,7 +40,7 @@ class PopUpStuff extends FlxTypedGroup isPixel = false; } - static function resolveGraphicPath(noteStyle:NoteStyle, index:String):Null + static function resolveGraphicPath(index:String):Null { fetchNoteStyle(); var basePath:String = 'ui/popup/'; @@ -72,7 +70,7 @@ class PopUpStuff extends FlxTypedGroup if (daRating == null) daRating = "good"; - var ratingPath:String = resolveGraphicPath(noteStyle, daRating); + var ratingPath:String = resolveGraphicPath(daRating); //if (PlayState.instance.currentStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel"; @@ -122,7 +120,7 @@ class PopUpStuff extends FlxTypedGroup if (combo == null) combo = 0; - var comboPath:String = resolveGraphicPath(noteStyle, 'combo'); + var comboPath:String = resolveGraphicPath('combo'); var comboSpr:FunkinSprite = FunkinSprite.create(comboPath); comboSpr.y = (FlxG.camera.height * 0.44) + offsets[1]; comboSpr.x = (FlxG.width * 0.507) + offsets[0]; @@ -165,7 +163,7 @@ class PopUpStuff extends FlxTypedGroup var daLoop:Int = 1; for (i in seperatedScore) { - var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, resolveGraphicPath(noteStyle, 'num' + Std.int(i))); + var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, resolveGraphicPath('num' + Std.int(i))); if (isPixel) numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 0.7)); else numScore.setGraphicSize(Std.int(numScore.width * 0.45)); From 558ec535320cf87c1ad4e31f3f611758c93a74a8 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 18 Jul 2024 23:27:12 -0400 Subject: [PATCH 211/469] Switch songs with no difficulties from an error to a warning. --- source/funkin/play/song/Song.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 91d35d8fa2..147923add3 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -277,7 +277,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry Date: Thu, 18 Jul 2024 23:27:24 -0400 Subject: [PATCH 212/469] Blacklist haxe.Unserializer in scripts. --- source/funkin/modding/PolymodHandler.hx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index c352aa6062..c5dfcdca30 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -251,6 +251,10 @@ class PolymodHandler // Lib.load() can load malicious DLLs Polymod.blacklistImport('cpp.Lib'); + // `Unserializer` + // Unserializerr.DEFAULT_RESOLVER.resolveClass() can access blacklisted packages + Polymod.blacklistImport('Unserializer'); + // `polymod.*` // You can probably unblacklist a module for (cls in ClassMacro.listClassesInPackage('polymod')) From 754787553593fe42a2479db09db728192673603d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 18 Jul 2024 23:27:41 -0400 Subject: [PATCH 213/469] Allow hiding HUD on launcher builds. --- source/funkin/play/PlayState.hx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 8d7d82aabe..a39f10d972 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -578,7 +578,6 @@ class PlayState extends MusicBeatSubState // TODO: Refactor or document var generatedMusic:Bool = false; - var perfectMode:Bool = false; static final BACKGROUND_COLOR:FlxColor = FlxColor.BLACK; @@ -2610,12 +2609,6 @@ class PlayState extends MusicBeatSubState */ function debugKeyShit():Void { - #if !debug - perfectMode = false; - #else - if (FlxG.keys.justPressed.H) camHUD.visible = !camHUD.visible; - #end - #if CHART_EDITOR_SUPPORTED // Open the stage editor overlaying the current state. if (controls.DEBUG_STAGE) @@ -2647,6 +2640,9 @@ class PlayState extends MusicBeatSubState #end #if (debug || FORCE_DEBUG_VERSION) + // H: Hide the HUD. + if (FlxG.keys.justPressed.H) camHUD.visible = !camHUD.visible; + // 1: End the song immediately. if (FlxG.keys.justPressed.ONE) endSong(true); From 0110936a3e2cd00e192c4200d2cf6a5589c3e41d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 20 Jul 2024 02:19:34 -0400 Subject: [PATCH 214/469] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index d5b2d05df6..c7589a95af 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit d5b2d05df6d197f90a1b93f5b50835209c21adf7 +Subproject commit c7589a95af2709d240e1b1a2994e68a04565b00a From 1515719a0f1c6f38de0bbea8aeff24f612ad6270 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 22 Jul 2024 18:38:22 -0400 Subject: [PATCH 215/469] Implemented instrumental select (you need to beat the song as Pico first)! --- assets | 2 +- .../funkin/play/event/ZoomCameraSongEvent.hx | 2 +- source/funkin/play/song/Song.hx | 22 +++ .../toolboxes/ChartEditorEventDataToolbox.hx | 4 +- .../funkin/ui/freeplay/CapsuleOptionsMenu.hx | 176 ++++++++++++++++++ source/funkin/ui/freeplay/FreeplayState.hx | 136 ++++++++++---- source/funkin/ui/mainmenu/MainMenuState.hx | 8 +- 7 files changed, 305 insertions(+), 45 deletions(-) create mode 100644 source/funkin/ui/freeplay/CapsuleOptionsMenu.hx diff --git a/assets b/assets index c7589a95af..8af9bd2cf7 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit c7589a95af2709d240e1b1a2994e68a04565b00a +Subproject commit 8af9bd2cf7122a5ad4dd2ca3939db75b11a5b239 diff --git a/source/funkin/play/event/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx index 748abda198..ee2eea8ad5 100644 --- a/source/funkin/play/event/ZoomCameraSongEvent.hx +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -114,7 +114,7 @@ class ZoomCameraSongEvent extends SongEvent name: 'zoom', title: 'Zoom Level', defaultValue: 1.0, - step: 0.1, + step: 0.05, type: SongEventFieldType.FLOAT, units: 'x' }, diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 91d35d8fa2..333ab1ad43 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -514,6 +514,28 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry + { + var targetDifficulty:Null = getDifficulty(difficultyId, variationId); + if (targetDifficulty == null) return []; + + return targetDifficulty?.characters?.altInstrumentals ?? []; + } + + public function getBaseInstrumentalId(difficultyId:String, variationId:String):String + { + var targetDifficulty:Null = getDifficulty(difficultyId, variationId); + if (targetDifficulty == null) return ''; + + return targetDifficulty?.characters?.instrumental ?? ''; + } + /** * Purge the cached chart data for each difficulty of this song. */ diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx index 8f021840ac..6534153633 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx @@ -190,8 +190,8 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox var numberStepper:NumberStepper = new NumberStepper(); numberStepper.id = field.name; numberStepper.step = field.step ?? 1.0; - numberStepper.min = field.min ?? 0.0; - numberStepper.max = field.max ?? 10.0; + if (field.min != null) numberStepper.min = field.min; + if (field.max != null) numberStepper.max = field.max; if (field.defaultValue != null) numberStepper.value = field.defaultValue; input = numberStepper; case FLOAT: diff --git a/source/funkin/ui/freeplay/CapsuleOptionsMenu.hx b/source/funkin/ui/freeplay/CapsuleOptionsMenu.hx new file mode 100644 index 0000000000..cb0fa7b289 --- /dev/null +++ b/source/funkin/ui/freeplay/CapsuleOptionsMenu.hx @@ -0,0 +1,176 @@ +package funkin.ui.freeplay; + +import funkin.graphics.shaders.PureColor; +import funkin.input.Controls; +import flixel.group.FlxSpriteGroup; +import funkin.graphics.FunkinSprite; +import flixel.util.FlxColor; +import flixel.util.FlxTimer; +import flixel.text.FlxText; +import flixel.text.FlxText.FlxTextAlign; + +@:nullSafety +class CapsuleOptionsMenu extends FlxSpriteGroup +{ + var capsuleMenuBG:FunkinSprite; + var parent:FreeplayState; + + var queueDestroy:Bool = false; + + var instrumentalIds:Array = ['']; + var currentInstrumentalIndex:Int = 0; + + var currentInstrumental:FlxText; + + public function new(parent:FreeplayState, x:Float = 0, y:Float = 0, instIds:Array):Void + { + super(x, y); + + this.parent = parent; + this.instrumentalIds = instIds; + + capsuleMenuBG = FunkinSprite.createSparrow(0, 0, 'freeplay/instBox/instBox'); + + capsuleMenuBG.animation.addByPrefix('open', 'open0', 24, false); + capsuleMenuBG.animation.addByPrefix('idle', 'idle0', 24, true); + capsuleMenuBG.animation.addByPrefix('open', 'open0', 24, false); + + currentInstrumental = new FlxText(0, 36, capsuleMenuBG.width, ''); + currentInstrumental.setFormat('VCR OSD Mono', 40, FlxTextAlign.CENTER, true); + + final PAD = 4; + var leftArrow = new InstrumentalSelector(parent, PAD, 30, false, parent.getControls()); + var rightArrow = new InstrumentalSelector(parent, capsuleMenuBG.width - leftArrow.width - PAD, 30, true, parent.getControls()); + + var label:FlxText = new FlxText(0, 5, capsuleMenuBG.width, 'INSTRUMENTAL'); + label.setFormat('VCR OSD Mono', 24, FlxTextAlign.CENTER, true); + + add(capsuleMenuBG); + add(leftArrow); + add(rightArrow); + add(label); + add(currentInstrumental); + + capsuleMenuBG.animation.finishCallback = function(_) { + capsuleMenuBG.animation.play('idle', true); + }; + capsuleMenuBG.animation.play('open', true); + } + + public override function update(elapsed:Float):Void + { + super.update(elapsed); + + if (queueDestroy) + { + destroy(); + return; + } + @:privateAccess + if (parent.controls.BACK) + { + close(); + return; + } + + var changedInst = false; + if (parent.getControls().UI_LEFT_P) + { + currentInstrumentalIndex = (currentInstrumentalIndex + 1) % instrumentalIds.length; + changedInst = true; + } + if (parent.getControls().UI_RIGHT_P) + { + currentInstrumentalIndex = (currentInstrumentalIndex - 1 + instrumentalIds.length) % instrumentalIds.length; + changedInst = true; + } + if (!changedInst && currentInstrumental.text == '') changedInst = true; + + if (changedInst) + { + currentInstrumental.text = instrumentalIds[currentInstrumentalIndex].toTitleCase() ?? ''; + if (currentInstrumental.text == '') currentInstrumental.text = 'Default'; + } + + if (parent.getControls().ACCEPT) + { + onConfirm(instrumentalIds[currentInstrumentalIndex] ?? ''); + } + } + + public function close():Void + { + // Play in reverse. + capsuleMenuBG.animation.play('open', true, true); + capsuleMenuBG.animation.finishCallback = function(_) { + parent.cleanupCapsuleOptionsMenu(); + queueDestroy = true; + }; + } + + /** + * Override this with `capsuleOptionsMenu.onConfirm = myFunction;` + */ + public dynamic function onConfirm(targetInstId:String):Void + { + throw 'onConfirm not implemented!'; + } +} + +/** + * The difficulty selector arrows to the left and right of the difficulty. + */ +class InstrumentalSelector extends FunkinSprite +{ + var controls:Controls; + var whiteShader:PureColor; + + var parent:FreeplayState; + + var baseScale:Float = 0.6; + + public function new(parent:FreeplayState, x:Float, y:Float, flipped:Bool, controls:Controls) + { + super(x, y); + + this.parent = parent; + + this.controls = controls; + + frames = Paths.getSparrowAtlas('freeplay/freeplaySelector'); + animation.addByPrefix('shine', 'arrow pointer loop', 24); + animation.play('shine'); + + whiteShader = new PureColor(FlxColor.WHITE); + + shader = whiteShader; + + flipX = flipped; + + scale.x = scale.y = 1 * baseScale; + updateHitbox(); + } + + override function update(elapsed:Float):Void + { + if (flipX && controls.UI_RIGHT_P) moveShitDown(); + if (!flipX && controls.UI_LEFT_P) moveShitDown(); + + super.update(elapsed); + } + + function moveShitDown():Void + { + offset.y -= 5; + + whiteShader.colorSet = true; + + scale.x = scale.y = 0.5 * baseScale; + + new FlxTimer().start(2 / 24, function(tmr) { + scale.x = scale.y = 1 * baseScale; + whiteShader.colorSet = false; + updateHitbox(); + }); + } +} diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 416e79df6b..1399d48b22 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -645,8 +645,8 @@ class FreeplayState extends MusicBeatSubState speed: 0.3 }); - var diffSelLeft:DifficultySelector = new DifficultySelector(20, grpDifficulties.y - 10, false, controls); - var diffSelRight:DifficultySelector = new DifficultySelector(325, grpDifficulties.y - 10, true, controls); + var diffSelLeft:DifficultySelector = new DifficultySelector(this, 20, grpDifficulties.y - 10, false, controls); + var diffSelRight:DifficultySelector = new DifficultySelector(this, 325, grpDifficulties.y - 10, true, controls); diffSelLeft.visible = false; diffSelRight.visible = false; add(diffSelLeft); @@ -822,7 +822,7 @@ class FreeplayState extends MusicBeatSubState funnyMenu.init(FlxG.width, 0, tempSong); funnyMenu.onConfirm = function() { - capsuleOnConfirmDefault(funnyMenu); + capsuleOnOpenDefault(funnyMenu); }; funnyMenu.y = funnyMenu.intendedY(i + 1) + 10; funnyMenu.targetPos.x = funnyMenu.x; @@ -1203,7 +1203,7 @@ class FreeplayState extends MusicBeatSubState /** * If true, disable interaction with the interface. */ - var busy:Bool = false; + public var busy:Bool = false; var originalPos:FlxPoint = new FlxPoint(); @@ -1223,16 +1223,6 @@ class FreeplayState extends MusicBeatSubState }); } - if (FlxG.keys.justPressed.P) - { - FlxG.switchState(FreeplayState.build( - { - { - character: currentCharacterId == "pico" ? Constants.DEFAULT_CHARACTER : "pico", - } - })); - } - // if (FlxG.keys.justPressed.H) // { // rankDisplayNew(fromResultsParams); @@ -1496,7 +1486,7 @@ class FreeplayState extends MusicBeatSubState generateSongList(currentFilter, true); } - if (controls.BACK) + if (controls.BACK && !busy) { busy = true; FlxTween.globalManager.clear(); @@ -1749,7 +1739,86 @@ class FreeplayState extends MusicBeatSubState capsuleOnConfirmDefault(targetSong); } - function capsuleOnConfirmDefault(cap:SongMenuItem):Void + /** + * Called when hitting ENTER to open the instrumental list. + */ + function capsuleOnOpenDefault(cap:SongMenuItem):Void + { + var targetSongId:String = cap?.songData?.songId ?? 'unknown'; + var targetSongNullable:Null = SongRegistry.instance.fetchEntry(targetSongId); + if (targetSongNullable == null) + { + FlxG.log.warn('WARN: could not find song with id (${targetSongId})'); + return; + } + var targetSong:Song = targetSongNullable; + var targetDifficultyId:String = currentDifficulty; + var targetVariation:Null = targetSong.getFirstValidVariation(targetDifficultyId, currentCharacter); + var targetLevelId:Null = cap?.songData?.levelId; + PlayStatePlaylist.campaignId = targetLevelId ?? null; + + var targetDifficulty:Null = targetSong.getDifficulty(targetDifficultyId, targetVariation); + if (targetDifficulty == null) + { + FlxG.log.warn('WARN: could not find difficulty with id (${targetDifficultyId})'); + return; + } + + trace('target difficulty: ${targetDifficultyId}'); + trace('target variation: ${targetDifficulty?.variation ?? Constants.DEFAULT_VARIATION}'); + + var baseInstrumentalId:String = targetSong.getBaseInstrumentalId(targetDifficultyId, targetDifficulty?.variation ?? Constants.DEFAULT_VARIATION) ?? ''; + var altInstrumentalIds:Array = targetSong.listAltInstrumentalIds(targetDifficultyId, + targetDifficulty?.variation ?? Constants.DEFAULT_VARIATION) ?? []; + + if (altInstrumentalIds.length > 0) + { + var instrumentalIds = [baseInstrumentalId].concat(altInstrumentalIds); + openInstrumentalList(cap, instrumentalIds); + } + else + { + trace('NO ALTS'); + capsuleOnConfirmDefault(cap); + } + } + + public function getControls():Controls + { + return controls; + } + + function openInstrumentalList(cap:SongMenuItem, instrumentalIds:Array):Void + { + busy = true; + + capsuleOptionsMenu = new CapsuleOptionsMenu(this, cap.x + 175, cap.y + 115, instrumentalIds); + capsuleOptionsMenu.cameras = [funnyCam]; + capsuleOptionsMenu.zIndex = 10000; + add(capsuleOptionsMenu); + + capsuleOptionsMenu.onConfirm = function(targetInstId:String) { + capsuleOnConfirmDefault(cap, targetInstId); + }; + } + + var capsuleOptionsMenu:Null = null; + + public function cleanupCapsuleOptionsMenu():Void + { + this.busy = false; + + if (capsuleOptionsMenu != null) + { + remove(capsuleOptionsMenu); + capsuleOptionsMenu = null; + } + } + + /** + * Called when hitting ENTER to play the song. + */ + function capsuleOnConfirmDefault(cap:SongMenuItem, ?targetInstId:String):Void { busy = true; letterSort.inputEnabled = false; @@ -1776,18 +1845,11 @@ class FreeplayState extends MusicBeatSubState return; } - var baseInstrumentalId:String = targetDifficulty?.characters?.instrumental ?? ''; - var altInstrumentalIds:Array = targetDifficulty?.characters?.altInstrumentals ?? []; - - var targetInstId:String = baseInstrumentalId; + var baseInstrumentalId:String = targetSong?.getBaseInstrumentalId(targetDifficultyId, targetDifficulty.variation ?? Constants.DEFAULT_VARIATION) ?? ''; + var altInstrumentalIds:Array = targetSong?.listAltInstrumentalIds(targetDifficultyId, + targetDifficulty.variation ?? Constants.DEFAULT_VARIATION) ?? []; - // TODO: Make this a UI element. - #if (debug || FORCE_DEBUG_VERSION) - if (altInstrumentalIds.length > 0 && FlxG.keys.pressed.CONTROL) - { - targetInstId = altInstrumentalIds[0]; - } - #end + if (targetInstId == null) targetInstId = baseInstrumentalId; // Visual and audio effects. FunkinSound.playOnce(Paths.sound('confirmMenu')); @@ -1942,10 +2004,12 @@ class FreeplayState extends MusicBeatSubState if (previewSongId == null) return; var previewSong:Null = SongRegistry.instance.fetchEntry(previewSongId); - var songDifficulty = previewSong?.getDifficulty(currentDifficulty, - previewSong?.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST); - var baseInstrumentalId:String = songDifficulty?.characters?.instrumental ?? ''; - var altInstrumentalIds:Array = songDifficulty?.characters?.altInstrumentals ?? []; + var currentVariation = previewSong?.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST; + var songDifficulty = previewSong?.getDifficulty(currentDifficulty, currentVariation); + + var baseInstrumentalId:String = previewSong?.getBaseInstrumentalId(currentDifficulty, songDifficulty?.variation ?? Constants.DEFAULT_VARIATION) ?? ''; + var altInstrumentalIds:Array = previewSong?.listAltInstrumentalIds(currentDifficulty, + songDifficulty?.variation ?? Constants.DEFAULT_VARIATION) ?? []; var instSuffix:String = baseInstrumentalId; @@ -2008,10 +2072,14 @@ class DifficultySelector extends FlxSprite var controls:Controls; var whiteShader:PureColor; - public function new(x:Float, y:Float, flipped:Bool, controls:Controls) + var parent:FreeplayState; + + public function new(parent:FreeplayState, x:Float, y:Float, flipped:Bool, controls:Controls) { super(x, y); + this.parent = parent; + this.controls = controls; frames = Paths.getSparrowAtlas('freeplay/freeplaySelector'); @@ -2027,8 +2095,8 @@ class DifficultySelector extends FlxSprite override function update(elapsed:Float):Void { - if (flipX && controls.UI_RIGHT_P) moveShitDown(); - if (!flipX && controls.UI_LEFT_P) moveShitDown(); + if (flipX && controls.UI_RIGHT_P && !parent.busy) moveShitDown(); + if (!flipX && controls.UI_LEFT_P && !parent.busy) moveShitDown(); super.update(elapsed); } diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 3f532d33c3..1438b75c7b 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -117,13 +117,7 @@ class MainMenuState extends MusicBeatState FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - openSubState(new FreeplayState( - { - #if debug - // If SHIFT is held, toggle the selected character, else use the remembered character - character: (FlxG.keys.pressed.SHIFT) ? (FreeplayState.rememberedCharacterId == Constants.DEFAULT_CHARACTER ? 'pico' : 'bf') : null, - #end - })); + openSubState(new FreeplayState()); }); #if CAN_OPEN_LINKS From a071e90746d823694c87cd573b962c12f5a91bef Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 22 Jul 2024 20:52:03 -0400 Subject: [PATCH 216/469] Added alt vocal offsets feature, disabled alt instrumentals on Pico mix (for now?) --- assets | 2 +- source/funkin/data/song/CHANGELOG.md | 4 ++++ source/funkin/data/song/SongData.hx | 28 +++++++++++++++++++------ source/funkin/data/song/SongRegistry.hx | 2 +- source/funkin/play/PlayState.hx | 8 +++---- source/funkin/play/song/Song.hx | 6 +++--- 6 files changed, 35 insertions(+), 15 deletions(-) diff --git a/assets b/assets index 8af9bd2cf7..d6e024e373 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8af9bd2cf7122a5ad4dd2ca3939db75b11a5b239 +Subproject commit d6e024e373ee78c53852a8e37de1956041856ad5 diff --git a/source/funkin/data/song/CHANGELOG.md b/source/funkin/data/song/CHANGELOG.md index 4f1c66adec..2e42fe7bd8 100644 --- a/source/funkin/data/song/CHANGELOG.md +++ b/source/funkin/data/song/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.2.4] +### Added +- Added `offsets.altVocals` field to apply vocal offsets when alternate instrumentals are used. + ## [2.2.3] ### Added - Added `charter` field to denote authorship of a chart. diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx index 769af8f08a..c7206c2938 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -257,13 +257,21 @@ class SongOffsets implements ICloneable public var altInstrumentals:Map; /** - * The offset, in milliseconds, to apply to the song's vocals, relative to the chart. + * The offset, in milliseconds, to apply to the song's vocals, relative to the song's base instrumental. * These are applied ON TOP OF the instrumental offset. */ @:optional @:default([]) public var vocals:Map; + /** + * The offset, in milliseconds, to apply to the songs vocals, relative to each alternate instrumental. + * This is useful for the circumstance where, for example, an alt instrumental has a few seconds of lead in before the song starts. + */ + @:optional + @:default([]) + public var altVocals:Map>; + public function new(instrumental:Float = 0.0, ?altInstrumentals:Map, ?vocals:Map) { this.instrumental = instrumental; @@ -293,11 +301,19 @@ class SongOffsets implements ICloneable return value; } - public function getVocalOffset(charId:String):Float + public function getVocalOffset(charId:String, ?instrumental:String):Float { - if (!this.vocals.exists(charId)) return 0.0; - - return this.vocals.get(charId); + if (instrumental == null) + { + if (!this.vocals.exists(charId)) return 0.0; + return this.vocals.get(charId); + } + else + { + if (!this.altVocals.exists(instrumental)) return 0.0; + if (!this.altVocals.get(instrumental).exists(charId)) return 0.0; + return this.altVocals.get(instrumental).get(charId); + } } public function setVocalOffset(charId:String, value:Float):Float @@ -320,7 +336,7 @@ class SongOffsets implements ICloneable */ public function toString():String { - return 'SongOffsets(${this.instrumental}ms, ${this.altInstrumentals}, ${this.vocals})'; + return 'SongOffsets(${this.instrumental}ms, ${this.altInstrumentals}, ${this.vocals}, ${this.altVocals})'; } } diff --git a/source/funkin/data/song/SongRegistry.hx b/source/funkin/data/song/SongRegistry.hx index a3305c4ecc..e7cab246c6 100644 --- a/source/funkin/data/song/SongRegistry.hx +++ b/source/funkin/data/song/SongRegistry.hx @@ -20,7 +20,7 @@ class SongRegistry extends BaseRegistry * Handle breaking changes by incrementing this value * and adding migration to the `migrateStageData()` function. */ - public static final SONG_METADATA_VERSION:thx.semver.Version = "2.2.3"; + public static final SONG_METADATA_VERSION:thx.semver.Version = "2.2.4"; public static final SONG_METADATA_VERSION_RULE:thx.semver.VersionRule = "2.2.x"; diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 8d7d82aabe..80c0e16cc8 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -666,7 +666,7 @@ class PlayState extends MusicBeatSubState // Prepare the current song's instrumental and vocals to be played. if (!overrideMusic && currentChart != null) { - currentChart.cacheInst(); + currentChart.cacheInst(currentInstrumental); currentChart.cacheVocals(); } @@ -675,7 +675,7 @@ class PlayState extends MusicBeatSubState if (currentChart.offsets != null) { - Conductor.instance.instrumentalOffset = currentChart.offsets.getInstrumentalOffset(); + Conductor.instance.instrumentalOffset = currentChart.offsets.getInstrumentalOffset(currentInstrumental); } Conductor.instance.mapTimeChanges(currentChart.timeChanges); @@ -863,7 +863,7 @@ class PlayState extends MusicBeatSubState { // Stop the vocals if they already exist. if (vocals != null) vocals.stop(); - vocals = currentChart.buildVocals(); + vocals = currentChart.buildVocals(currentInstrumental); if (vocals.members.length == 0) { @@ -1852,7 +1852,7 @@ class PlayState extends MusicBeatSubState { // Stop the vocals if they already exist. if (vocals != null) vocals.stop(); - vocals = currentChart.buildVocals(); + vocals = currentChart.buildVocals(currentInstrumental); if (vocals.members.length == 0) { diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 333ab1ad43..156cfda059 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -813,7 +813,7 @@ class SongDifficulty * @param charId The player ID. * @return The generated vocal group. */ - public function buildVocals():VoicesGroup + public function buildVocals(?instId:String = ''):VoicesGroup { var result:VoicesGroup = new VoicesGroup(); @@ -839,8 +839,8 @@ class SongDifficulty } } - result.playerVoicesOffset = offsets.getVocalOffset(characters.player); - result.opponentVoicesOffset = offsets.getVocalOffset(characters.opponent); + result.playerVoicesOffset = offsets.getVocalOffset(characters.player, instId); + result.opponentVoicesOffset = offsets.getVocalOffset(characters.opponent, instId); return result; } From 04c88baff0ffa05ca23b6344ab090eff0c1ff388 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 22 Jul 2024 20:53:34 -0400 Subject: [PATCH 217/469] CI fix? --- .github/labeler.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index e8e4908652..e8250b4e77 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,7 +1,6 @@ # Add Documentation tag to PR's changing markdown files, or anyhting in the docs folder Documentation: - changed-files: - - any-glob-to-any-file: - any-glob-to-any-file: - docs/* - '**/*.md' From 9b8961d4b5c8e150a2c77d39d53566bed5fe6ea7 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 22 Jul 2024 22:20:51 -0400 Subject: [PATCH 218/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 4af95a506f..aa1231e8cf 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 4af95a506fc62cd683422dfb9c599877b26c27db +Subproject commit aa1231e8cf2990bb902eac3b37815c010fa9919a From c8caeb42ded0be5292680db20c5cb99b2a37bf46 Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Tue, 23 Jul 2024 11:34:26 +0300 Subject: [PATCH 219/469] A fix requested by Eric --- source/funkin/play/PlayState.hx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 9b1cc2ed97..20b8d784bd 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2959,11 +2959,16 @@ class PlayState extends MusicBeatSubState FunkinSound.playOnce(Paths.sound('Lights_Shut_off'), function() { // no camFollow so it centers on horror tree var targetSong:Song = SongRegistry.instance.fetchEntry(targetSongId); + var targetVariation:String = currentVariation; + if (!targetSong.hasDifficulty(PlayStatePlaylist.campaignDifficulty, currentVariation)) + { + targetVariation = targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty) ?? Constants.DEFAULT_VARIATION; + } LoadingState.loadPlayState( { targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty, - targetVariation: targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty), + targetVariation: targetVariation, cameraFollowPoint: cameraFollowPoint.getPosition(), }); }); @@ -2971,11 +2976,16 @@ class PlayState extends MusicBeatSubState else { var targetSong:Song = SongRegistry.instance.fetchEntry(targetSongId); + var targetVariation:String = currentVariation; + if (!targetSong.hasDifficulty(PlayStatePlaylist.campaignDifficulty, currentVariation)) + { + targetVariation = targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty) ?? Constants.DEFAULT_VARIATION; + } LoadingState.loadPlayState( { targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty, - targetVariation: targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty), + targetVariation: targetVariation, cameraFollowPoint: cameraFollowPoint.getPosition(), }); } From 8778ab1d0ec46556338e60fe0eb84c6d26064d7f Mon Sep 17 00:00:00 2001 From: Burgerballs <107233412+Burgerballs@users.noreply.github.com> Date: Tue, 23 Jul 2024 18:09:39 +0100 Subject: [PATCH 220/469] Update PlayState.hx --- source/funkin/play/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index f55cef3888..f82fd791ef 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2033,7 +2033,7 @@ class PlayState extends MusicBeatSubState vocals.pause(); - FlxG.sound.music.play(FlxG.sound.music.time); + FlxG.sound.music.play(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset); vocals.time = FlxG.sound.music.time; vocals.play(false, FlxG.sound.music.time); From 964f6878c3fb8d13f4faf6723d299997edceaccf Mon Sep 17 00:00:00 2001 From: Burgerballs <107233412+Burgerballs@users.noreply.github.com> Date: Tue, 23 Jul 2024 19:02:09 +0100 Subject: [PATCH 221/469] Update PlayState.hx --- source/funkin/play/PlayState.hx | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index f82fd791ef..07e4f0b1df 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1404,17 +1404,6 @@ class PlayState extends MusicBeatSubState if (isGamePaused) return false; - if (!startingSong - && FlxG.sound.music != null - && (Math.abs(FlxG.sound.music.time - (Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)) > 200 - || Math.abs(vocals.checkSyncError(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)) > 200)) - { - trace("VOCALS NEED RESYNC"); - if (vocals != null) trace(vocals.checkSyncError(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)); - trace(FlxG.sound.music.time - (Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)); - resyncVocals(); - } - if (iconP1 != null) iconP1.onStepHit(Std.int(Conductor.instance.currentStep)); if (iconP2 != null) iconP2.onStepHit(Std.int(Conductor.instance.currentStep)); @@ -1436,6 +1425,17 @@ class PlayState extends MusicBeatSubState // activeNotes.sort(SortUtil.byStrumtime, FlxSort.DESCENDING); } + if (!startingSong + && FlxG.sound.music != null + && (Math.abs(FlxG.sound.music.time - (Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)) > 100 + || Math.abs(vocals.checkSyncError(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)) > 100)) + { + trace("VOCALS NEED RESYNC"); + if (vocals != null) trace(vocals.checkSyncError(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)); + trace(FlxG.sound.music.time - (Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)); + resyncVocals(); + } + // Only bop camera if zoom level is below 135% if (Preferences.zoomCamera && FlxG.camera.zoom < (1.35 * FlxCamera.defaultZoom) @@ -2030,13 +2030,15 @@ class PlayState extends MusicBeatSubState // Skip this if the music is paused (GameOver, Pause menu, start-of-song offset, etc.) if (!FlxG.sound.music.playing) return; - + var timeToPlayAt:Float = Conductor.instance.songPosition - Conductor.instance.instrumentalOffset; + FlxG.sound.music.pause(); vocals.pause(); - FlxG.sound.music.play(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset); + FlxG.sound.music.time = timeToPlayAt; + FlxG.sound.music.play(false, timeToPlayAt); - vocals.time = FlxG.sound.music.time; - vocals.play(false, FlxG.sound.music.time); + vocals.time = timeToPlayAt; + vocals.play(false, timeToPlayAt); } /** From 6e301bf648e6df451289a4b1a6ef8453766e7a41 Mon Sep 17 00:00:00 2001 From: Burgerballs <107233412+Burgerballs@users.noreply.github.com> Date: Tue, 23 Jul 2024 18:09:39 +0100 Subject: [PATCH 222/469] Update PlayState.hx --- source/funkin/play/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 8d7d82aabe..8730820916 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2043,7 +2043,7 @@ class PlayState extends MusicBeatSubState vocals.pause(); - FlxG.sound.music.play(FlxG.sound.music.time); + FlxG.sound.music.play(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset); vocals.time = FlxG.sound.music.time; vocals.play(false, FlxG.sound.music.time); From 1831daac382894aa121180dc8e704d35ce55755c Mon Sep 17 00:00:00 2001 From: Burgerballs <107233412+Burgerballs@users.noreply.github.com> Date: Tue, 23 Jul 2024 19:02:09 +0100 Subject: [PATCH 223/469] Update PlayState.hx --- source/funkin/play/PlayState.hx | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 8730820916..18ec0f002c 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1417,17 +1417,6 @@ class PlayState extends MusicBeatSubState if (isGamePaused) return false; - if (!startingSong - && FlxG.sound.music != null - && (Math.abs(FlxG.sound.music.time - (Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)) > 200 - || Math.abs(vocals.checkSyncError(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)) > 200)) - { - trace("VOCALS NEED RESYNC"); - if (vocals != null) trace(vocals.checkSyncError(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)); - trace(FlxG.sound.music.time - (Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)); - resyncVocals(); - } - if (iconP1 != null) iconP1.onStepHit(Std.int(Conductor.instance.currentStep)); if (iconP2 != null) iconP2.onStepHit(Std.int(Conductor.instance.currentStep)); @@ -1449,6 +1438,17 @@ class PlayState extends MusicBeatSubState // activeNotes.sort(SortUtil.byStrumtime, FlxSort.DESCENDING); } + if (!startingSong + && FlxG.sound.music != null + && (Math.abs(FlxG.sound.music.time - (Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)) > 100 + || Math.abs(vocals.checkSyncError(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)) > 100)) + { + trace("VOCALS NEED RESYNC"); + if (vocals != null) trace(vocals.checkSyncError(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)); + trace(FlxG.sound.music.time - (Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)); + resyncVocals(); + } + // Only bop camera if zoom level is below 135% if (Preferences.zoomCamera && FlxG.camera.zoom < (1.35 * FlxCamera.defaultZoom) @@ -2040,13 +2040,15 @@ class PlayState extends MusicBeatSubState // Skip this if the music is paused (GameOver, Pause menu, start-of-song offset, etc.) if (!FlxG.sound.music.playing) return; - + var timeToPlayAt:Float = Conductor.instance.songPosition - Conductor.instance.instrumentalOffset; + FlxG.sound.music.pause(); vocals.pause(); - FlxG.sound.music.play(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset); + FlxG.sound.music.time = timeToPlayAt; + FlxG.sound.music.play(false, timeToPlayAt); - vocals.time = FlxG.sound.music.time; - vocals.play(false, FlxG.sound.music.time); + vocals.time = timeToPlayAt; + vocals.play(false, timeToPlayAt); } /** From aaab24850df5b3882b2282d5b3c8f6d8174853be Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 24 Jun 2024 22:21:53 -0400 Subject: [PATCH 224/469] flixel haxelib updates --- hmm.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hmm.json b/hmm.json index 8eaf242122..07216a3518 100644 --- a/hmm.json +++ b/hmm.json @@ -11,14 +11,14 @@ "name": "flixel", "type": "git", "dir": null, - "ref": "a7d8e3bad89a0a3506a4714121f73d8e34522c49", + "ref": "10c2a203c43a78ff1ff26b8368fd736576829d8d", "url": "https://github.com/FunkinCrew/flixel" }, { "name": "flixel-addons", "type": "git", "dir": null, - "ref": "a523c3b56622f0640933944171efed46929e360e", + "ref": "9c6fb47968e894eb36bf10e94725cd7640c49281", "url": "https://github.com/FunkinCrew/flixel-addons" }, { @@ -30,7 +30,7 @@ "name": "flixel-ui", "type": "git", "dir": null, - "ref": "719b4f10d94186ed55f6fef1b6618d32abec8c15", + "ref": "d0afed7293c71ffdb1184751317fc709b44c9056", "url": "https://github.com/HaxeFlixel/flixel-ui" }, { From a15695c3dc8b3c38d8049cb39db4bbf27f2f1d9a Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 19 Jul 2024 19:13:58 -0400 Subject: [PATCH 225/469] compile dev version of hxcpp --- .github/workflows/build-game.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-game.yml b/.github/workflows/build-game.yml index 07802557ce..ba8167607a 100644 --- a/.github/workflows/build-game.yml +++ b/.github/workflows/build-game.yml @@ -45,7 +45,11 @@ jobs: uses: ./.github/actions/setup-haxe with: gh-token: ${{ steps.app_token.outputs.token }} - + - name: Setup HXCPP dev commit + run: | + cd .haxelib/hxcpp/git/tools/hxcpp + haxe compile.hxml + cd ../../../../.. - name: Build game if: ${{ matrix.target == 'windows' }} run: | From 4dfb46955239664439fbb3c64d8bd40a41b1169c Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 22 Jul 2024 10:12:41 -0400 Subject: [PATCH 226/469] update hxcpp haxelib --- hmm.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hmm.json b/hmm.json index 8eaf242122..387d63f5e4 100644 --- a/hmm.json +++ b/hmm.json @@ -99,8 +99,10 @@ }, { "name": "hxcpp", - "type": "haxelib", - "version": "4.3.2" + "type": "git", + "dir": null, + "url": "https://github.com/HaxeFoundation/hxcpp", + "ref": "01cfee282a9a783e10c5a7774a3baaf547e6b0a7" }, { "name": "hxcpp-debug-server", From 3db0cf3e0ab4817f211cd3d8a74c616b15be1239 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 26 Jul 2024 00:22:36 -0400 Subject: [PATCH 227/469] Update Polymod to support applying JSON patches in the _merge folder of mods. --- hmm.json | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index 375fdd0ac9..fabd8b51dc 100644 --- a/hmm.json +++ b/hmm.json @@ -123,6 +123,20 @@ "ref": "a8c26f18463c98da32f744c214fe02273e1823fa", "url": "https://github.com/FunkinCrew/json2object" }, + { + "name": "jsonpatch", + "type": "git", + "dir": null, + "ref": "f9b83215acd586dc28754b4ae7f69d4c06c3b4d3", + "url": "https://github.com/EliteMasterEric/jsonpatch" + }, + { + "name": "jsonpath", + "type": "git", + "dir": null, + "ref": "7a24193717b36393458c15c0435bb7c4470ecdda", + "url": "https://github.com/EliteMasterEric/jsonpath" + }, { "name": "lime", "type": "git", @@ -169,7 +183,7 @@ "name": "polymod", "type": "git", "dir": null, - "ref": "bfbe30d81601b3543d80dce580108ad6b7e182c7", + "ref": "98945c6c7f5ecde01a32c4623d3515bf012a023a", "url": "https://github.com/larsiusprime/polymod" }, { From f3624f7e76cdd8dcba0762a39ebe1be28389ecef Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sun, 28 Jul 2024 01:42:09 -0400 Subject: [PATCH 228/469] Fixes for scripted song events, define vocal tracks per variation, display suffixed difficulties properly. --- source/funkin/data/event/SongEventRegistry.hx | 4 +- source/funkin/data/song/CHANGELOG.md | 7 + source/funkin/data/song/SongData.hx | 59 ++++--- source/funkin/modding/PolymodHandler.hx | 2 + source/funkin/play/PlayState.hx | 8 +- source/funkin/play/song/Song.hx | 158 ++++++++++++------ source/funkin/ui/freeplay/FreeplayState.hx | 30 +++- source/funkin/ui/freeplay/SongMenuItem.hx | 3 +- 8 files changed, 183 insertions(+), 88 deletions(-) diff --git a/source/funkin/data/event/SongEventRegistry.hx b/source/funkin/data/event/SongEventRegistry.hx index 9b01635574..5ee2d39fa6 100644 --- a/source/funkin/data/event/SongEventRegistry.hx +++ b/source/funkin/data/event/SongEventRegistry.hx @@ -46,7 +46,7 @@ class SongEventRegistry if (event != null) { - trace(' Loaded built-in song event: (${event.id})'); + trace(' Loaded built-in song event: ${event.id}'); eventCache.set(event.id, event); } else @@ -59,9 +59,9 @@ class SongEventRegistry static function registerScriptedEvents() { var scriptedEventClassNames:Array = ScriptedSongEvent.listScriptClasses(); + trace('Instantiating ${scriptedEventClassNames.length} scripted song events...'); if (scriptedEventClassNames == null || scriptedEventClassNames.length == 0) return; - trace('Instantiating ${scriptedEventClassNames.length} scripted song events...'); for (eventCls in scriptedEventClassNames) { var event:SongEvent = ScriptedSongEvent.init(eventCls, "UKNOWN"); diff --git a/source/funkin/data/song/CHANGELOG.md b/source/funkin/data/song/CHANGELOG.md index 4f1c66adec..ca36a1d6dd 100644 --- a/source/funkin/data/song/CHANGELOG.md +++ b/source/funkin/data/song/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.2.4] +### Added +- Added `playData.characters.opponentVocals` to specify which vocal track(s) to play for the opponent. + - If the value isn't present, it will use the `playData.characters.opponent`, but if it is present, it will be used (even if it's empty, in which case no vocals will be used for the opponent) +- Added `playData.characters.playerVocals` to specify which vocal track(s) to play for the player. + - If the value isn't present, it will use the `playData.characters.player`, but if it is present, it will be used (even if it's empty, in which case no vocals will be used for the player) + ## [2.2.3] ### Added - Added `charter` field to denote authorship of a chart. diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx index 7bf3f8f19e..f487eb54dc 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -529,12 +529,26 @@ class SongCharacterData implements ICloneable @:default([]) public var altInstrumentals:Array = []; - public function new(player:String = '', girlfriend:String = '', opponent:String = '', instrumental:String = '') + @:optional + public var opponentVocals:Null> = null; + + @:optional + public var playerVocals:Null> = null; + + public function new(player:String = '', girlfriend:String = '', opponent:String = '', instrumental:String = '', ?altInstrumentals:Array, + ?opponentVocals:Array, ?playerVocals:Array) { this.player = player; this.girlfriend = girlfriend; this.opponent = opponent; this.instrumental = instrumental; + + this.altInstrumentals = altInstrumentals; + this.opponentVocals = opponentVocals; + this.playerVocals = playerVocals; + + if (opponentVocals == null) this.opponentVocals = [opponent]; + if (playerVocals == null) this.playerVocals = [player]; } public function clone():SongCharacterData @@ -722,18 +736,6 @@ class SongEventDataRaw implements ICloneable { return new SongEventDataRaw(this.time, this.eventKind, this.value); } -} - -/** - * Wrap SongEventData in an abstract so we can overload operators. - */ -@:forward(time, eventKind, value, activated, getStepTime, clone) -abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataRaw -{ - public function new(time:Float, eventKind:String, value:Dynamic = null) - { - this = new SongEventDataRaw(time, eventKind, value); - } public function valueAsStruct(?defaultKey:String = "key"):Dynamic { @@ -757,27 +759,27 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR } } - public inline function getHandler():Null + public function getHandler():Null { return SongEventRegistry.getEvent(this.eventKind); } - public inline function getSchema():Null + public function getSchema():Null { return SongEventRegistry.getEventSchema(this.eventKind); } - public inline function getDynamic(key:String):Null + public function getDynamic(key:String):Null { return this.value == null ? null : Reflect.field(this.value, key); } - public inline function getBool(key:String):Null + public function getBool(key:String):Null { return this.value == null ? null : cast Reflect.field(this.value, key); } - public inline function getInt(key:String):Null + public function getInt(key:String):Null { if (this.value == null) return null; var result = Reflect.field(this.value, key); @@ -787,7 +789,7 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR return cast result; } - public inline function getFloat(key:String):Null + public function getFloat(key:String):Null { if (this.value == null) return null; var result = Reflect.field(this.value, key); @@ -797,17 +799,17 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR return cast result; } - public inline function getString(key:String):String + public function getString(key:String):String { return this.value == null ? null : cast Reflect.field(this.value, key); } - public inline function getArray(key:String):Array + public function getArray(key:String):Array { return this.value == null ? null : cast Reflect.field(this.value, key); } - public inline function getBoolArray(key:String):Array + public function getBoolArray(key:String):Array { return this.value == null ? null : cast Reflect.field(this.value, key); } @@ -839,6 +841,19 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR return result; } +} + +/** + * Wrap SongEventData in an abstract so we can overload operators. + */ +@:forward(time, eventKind, value, activated, getStepTime, clone, getHandler, getSchema, getDynamic, getBool, getInt, getFloat, getString, getArray, + getBoolArray, buildTooltip, valueAsStruct) +abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataRaw +{ + public function new(time:Float, eventKind:String, value:Dynamic = null) + { + this = new SongEventDataRaw(time, eventKind, value); + } public function clone():SongEventData { diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index 9a9ef9e660..5767199baa 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -234,6 +234,8 @@ class PolymodHandler // NOTE: Scripted classes are automatically aliased to their parent class. Polymod.addImportAlias('flixel.math.FlxPoint', flixel.math.FlxPoint.FlxBasePoint); + Polymod.addImportAlias('funkin.data.event.SongEventSchema', funkin.data.event.SongEventSchema.SongEventSchemaRaw); + // Add blacklisting for prohibited classes and packages. // `Sys` diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 32b6e7b621..871c784df9 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -580,6 +580,8 @@ class PlayState extends MusicBeatSubState // TODO: Refactor or document var generatedMusic:Bool = false; + var skipEndingTransition:Bool = false; + static final BACKGROUND_COLOR:FlxColor = FlxColor.BLACK; /** @@ -1926,7 +1928,9 @@ class PlayState extends MusicBeatSubState return; } - FlxG.sound.music.onComplete = endSong.bind(false); + FlxG.sound.music.onComplete = function() { + endSong(skipEndingTransition); + }; // A negative instrumental offset means the song skips the first few milliseconds of the track. // This just gets added into the startTimestamp behavior so we don't need to do anything extra. FlxG.sound.music.play(true, startTimestamp - Conductor.instance.instrumentalOffset); @@ -1965,7 +1969,7 @@ class PlayState extends MusicBeatSubState if (vocals == null) return; // Skip this if the music is paused (GameOver, Pause menu, start-of-song offset, etc.) - if (!FlxG.sound.music.playing) return; + if (!(FlxG?.sound?.music?.playing ?? false)) return; vocals.pause(); diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 147923add3..2e7e13f513 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -494,6 +494,24 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry, ?showLocked:Bool, ?showHidden:Bool):Array + { + var result = []; + + for (variation in variationIds) + { + var difficulties = listDifficulties(variation, null, showLocked, showHidden); + for (difficulty in difficulties) + { + var suffixedDifficulty = (variation != Constants.DEFAULT_VARIATION + && variation != 'erect') ? '$difficulty-${variation}' : difficulty; + result.push(suffixedDifficulty); + } + } + + return result; + } + public function hasDifficulty(diffId:String, ?variationId:String, ?variationIds:Array):Bool { if (variationIds == null) variationIds = []; @@ -706,10 +724,11 @@ class SongDifficulty * Cache the vocals for a given character. * @param id The character we are about to play. */ - public inline function cacheVocals():Void + public function cacheVocals():Void { for (voice in buildVoiceList()) { + trace('Caching vocal track: $voice'); FlxG.sound.cache(voice); } } @@ -721,6 +740,20 @@ class SongDifficulty * @param id The character we are about to play. */ public function buildVoiceList():Array + { + var result:Array = []; + result = result.concat(buildPlayerVoiceList()); + result = result.concat(buildOpponentVoiceList()); + if (result.length == 0) + { + var suffix:String = (variation != null && variation != '' && variation != 'default') ? '-$variation' : ''; + // Try to use `Voices.ogg` if no other voices are found. + if (Assets.exists(Paths.voices(this.song.id, ''))) result.push(Paths.voices(this.song.id, '$suffix')); + } + return result; + } + + public function buildPlayerVoiceList():Array { var suffix:String = (variation != null && variation != '' && variation != 'default') ? '-$variation' : ''; @@ -728,62 +761,88 @@ class SongDifficulty // For example, if `Voices-bf-car-erect.ogg` does not exist, check for `Voices-bf-erect.ogg`. // Then, check for `Voices-bf-car.ogg`, then `Voices-bf.ogg`. - var playerId:String = characters.player; - var voicePlayer:String = Paths.voices(this.song.id, '-$playerId$suffix'); - while (voicePlayer != null && !Assets.exists(voicePlayer)) - { - // Remove the last suffix. - // For example, bf-car becomes bf. - playerId = playerId.split('-').slice(0, -1).join('-'); - // Try again. - voicePlayer = playerId == '' ? null : Paths.voices(this.song.id, '-${playerId}$suffix'); - } - if (voicePlayer == null) + if (characters.playerVocals == null) { - // Try again without $suffix. - playerId = characters.player; - voicePlayer = Paths.voices(this.song.id, '-${playerId}'); - while (voicePlayer != null && !Assets.exists(voicePlayer)) + var playerId:String = characters.player; + var playerVoice:String = Paths.voices(this.song.id, '-${playerId}$suffix'); + + while (playerVoice != null && !Assets.exists(playerVoice)) { // Remove the last suffix. + // For example, bf-car becomes bf. playerId = playerId.split('-').slice(0, -1).join('-'); // Try again. - voicePlayer = playerId == '' ? null : Paths.voices(this.song.id, '-${playerId}$suffix'); + playerVoice = playerId == '' ? null : Paths.voices(this.song.id, '-${playerId}$suffix'); + } + if (playerVoice == null) + { + // Try again without $suffix. + playerId = characters.player; + playerVoice = Paths.voices(this.song.id, '-${playerId}'); + while (playerVoice != null && !Assets.exists(playerVoice)) + { + // Remove the last suffix. + playerId = playerId.split('-').slice(0, -1).join('-'); + // Try again. + playerVoice = playerId == '' ? null : Paths.voices(this.song.id, '-${playerId}$suffix'); + } } - } - var opponentId:String = characters.opponent; - var voiceOpponent:String = Paths.voices(this.song.id, '-${opponentId}$suffix'); - while (voiceOpponent != null && !Assets.exists(voiceOpponent)) + return playerVoice != null ? [playerVoice] : []; + } + else { - // Remove the last suffix. - opponentId = opponentId.split('-').slice(0, -1).join('-'); - // Try again. - voiceOpponent = opponentId == '' ? null : Paths.voices(this.song.id, '-${opponentId}$suffix'); + // The metadata explicitly defines the list of voices. + var playerIds:Array = characters?.playerVocals ?? [characters.player]; + var playerVoices:Array = playerIds.map((id) -> Paths.voices(this.song.id, '-$id$suffix')); + + return playerVoices; } - if (voiceOpponent == null) + } + + public function buildOpponentVoiceList():Array + { + var suffix:String = (variation != null && variation != '' && variation != 'default') ? '-$variation' : ''; + + // Automatically resolve voices by removing suffixes. + // For example, if `Voices-bf-car-erect.ogg` does not exist, check for `Voices-bf-erect.ogg`. + // Then, check for `Voices-bf-car.ogg`, then `Voices-bf.ogg`. + + if (characters.opponentVocals == null) { - // Try again without $suffix. - opponentId = characters.opponent; - voiceOpponent = Paths.voices(this.song.id, '-${opponentId}'); - while (voiceOpponent != null && !Assets.exists(voiceOpponent)) + var opponentId:String = characters.opponent; + var opponentVoice:String = Paths.voices(this.song.id, '-${opponentId}$suffix'); + while (opponentVoice != null && !Assets.exists(opponentVoice)) { // Remove the last suffix. opponentId = opponentId.split('-').slice(0, -1).join('-'); // Try again. - voiceOpponent = opponentId == '' ? null : Paths.voices(this.song.id, '-${opponentId}$suffix'); + opponentVoice = opponentId == '' ? null : Paths.voices(this.song.id, '-${opponentId}$suffix'); + } + if (opponentVoice == null) + { + // Try again without $suffix. + opponentId = characters.opponent; + opponentVoice = Paths.voices(this.song.id, '-${opponentId}'); + while (opponentVoice != null && !Assets.exists(opponentVoice)) + { + // Remove the last suffix. + opponentId = opponentId.split('-').slice(0, -1).join('-'); + // Try again. + opponentVoice = opponentId == '' ? null : Paths.voices(this.song.id, '-${opponentId}$suffix'); + } } - } - var result:Array = []; - if (voicePlayer != null) result.push(voicePlayer); - if (voiceOpponent != null) result.push(voiceOpponent); - if (voicePlayer == null && voiceOpponent == null) + return opponentVoice != null ? [opponentVoice] : []; + } + else { - // Try to use `Voices.ogg` if no other voices are found. - if (Assets.exists(Paths.voices(this.song.id, ''))) result.push(Paths.voices(this.song.id, '$suffix')); + // The metadata explicitly defines the list of voices. + var opponentIds:Array = characters?.opponentVocals ?? [characters.opponent]; + var opponentVoices:Array = opponentIds.map((id) -> Paths.voices(this.song.id, '-$id$suffix')); + + return opponentVoices; } - return result; } /** @@ -795,26 +854,19 @@ class SongDifficulty { var result:VoicesGroup = new VoicesGroup(); - var voiceList:Array = buildVoiceList(); + var playerVoiceList:Array = this.buildPlayerVoiceList(); + var opponentVoiceList:Array = this.buildOpponentVoiceList(); - if (voiceList.length == 0) + // Add player vocals. + for (playerVoice in playerVoiceList) { - trace('Could not find any voices for song ${this.song.id}'); - return result; + result.addPlayerVoice(FunkinSound.load(playerVoice)); } - // Add player vocals. - if (voiceList[0] != null) result.addPlayerVoice(FunkinSound.load(voiceList[0])); // Add opponent vocals. - if (voiceList[1] != null) result.addOpponentVoice(FunkinSound.load(voiceList[1])); - - // Add additional vocals. - if (voiceList.length > 2) + for (opponentVoice in opponentVoiceList) { - for (i in 2...voiceList.length) - { - result.add(FunkinSound.load(Assets.getSound(voiceList[i]))); - } + result.addOpponentVoice(FunkinSound.load(opponentVoice)); } result.playerVoicesOffset = offsets.getVocalOffset(characters.player); diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index dc42bd6510..2341f04a6e 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -339,7 +339,7 @@ class FreeplayState extends MusicBeatSubState // Only display songs which actually have available difficulties for the current character. var displayedVariations = song.getVariationsByCharacter(currentCharacter); trace('Displayed Variations (${songId}): $displayedVariations'); - var availableDifficultiesForSong:Array = song.listDifficulties(displayedVariations, false); + var availableDifficultiesForSong:Array = song.listSuffixedDifficulties(displayedVariations, false, false); trace('Available Difficulties: $availableDifficultiesForSong'); if (availableDifficultiesForSong.length == 0) continue; @@ -1120,7 +1120,7 @@ class FreeplayState extends MusicBeatSubState // NOW we can interact with the menu busy = false; - grpCapsules.members[curSelected].sparkle.alpha = 0.7; + capsule.sparkle.alpha = 0.7; playCurSongPreview(capsule); }, null); @@ -1674,6 +1674,9 @@ class FreeplayState extends MusicBeatSubState songCapsule.init(null, null, null); } } + + // Reset the song preview in case we changed variations (normal->erect etc) + playCurSongPreview(); } // Set the album graphic and play the animation if relevant. @@ -1912,8 +1915,10 @@ class FreeplayState extends MusicBeatSubState } } - public function playCurSongPreview(daSongCapsule:SongMenuItem):Void + public function playCurSongPreview(?daSongCapsule:SongMenuItem):Void { + if (daSongCapsule == null) daSongCapsule = grpCapsules.members[curSelected]; + if (curSelected == 0) { FunkinSound.playMusic('freeplayRandom', @@ -2145,7 +2150,7 @@ class FreeplaySongData function updateValues(variations:Array):Void { - this.songDifficulties = song.listDifficulties(null, variations, false, false); + this.songDifficulties = song.listSuffixedDifficulties(variations, false, false); if (!this.songDifficulties.contains(currentDifficulty)) currentDifficulty = Constants.DEFAULT_DIFFICULTY; var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, null, variations); @@ -2207,15 +2212,26 @@ class DifficultySprite extends FlxSprite difficultyId = diffId; - if (Assets.exists(Paths.file('images/freeplay/freeplay${diffId}.xml'))) + var assetDiffId:String = diffId; + while (!Assets.exists(Paths.image('freeplay/freeplay${assetDiffId}'))) + { + // Remove the last suffix of the difficulty id until we find an asset or there are no more suffixes. + var assetDiffIdParts:Array = assetDiffId.split('-'); + assetDiffIdParts.pop(); + if (assetDiffIdParts.length == 0) break; + assetDiffId = assetDiffIdParts.join('-'); + } + + // Check for an XML to use an animation instead of an image. + if (Assets.exists(Paths.file('images/freeplay/freeplay${assetDiffId}.xml'))) { - this.frames = Paths.getSparrowAtlas('freeplay/freeplay${diffId}'); + this.frames = Paths.getSparrowAtlas('freeplay/freeplay${assetDiffId}'); this.animation.addByPrefix('idle', 'idle0', 24, true); if (Preferences.flashingLights) this.animation.play('idle'); } else { - this.loadGraphic(Paths.image('freeplay/freeplay' + diffId)); + this.loadGraphic(Paths.image('freeplay/freeplay' + assetDiffId)); } } } diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx index 2eec83223e..b4409d377b 100644 --- a/source/funkin/ui/freeplay/SongMenuItem.hx +++ b/source/funkin/ui/freeplay/SongMenuItem.hx @@ -162,7 +162,7 @@ class SongMenuItem extends FlxSpriteGroup sparkle = new FlxSprite(ranking.x, ranking.y); sparkle.frames = Paths.getSparrowAtlas('freeplay/sparkle'); - sparkle.animation.addByPrefix('sparkle', 'sparkle', 24, false); + sparkle.animation.addByPrefix('sparkle', 'sparkle Export0', 24, false); sparkle.animation.play('sparkle', true); sparkle.scale.set(0.8, 0.8); sparkle.blend = BlendMode.ADD; @@ -523,7 +523,6 @@ class SongMenuItem extends FlxSpriteGroup checkWeek(songData?.songId); } - var frameInTicker:Float = 0; var frameInTypeBeat:Int = 0; From 0e7fad090baae898cb7d95aca6a43d0604fc74db Mon Sep 17 00:00:00 2001 From: anysad Date: Sat, 13 Jul 2024 22:45:58 +0300 Subject: [PATCH 229/469] custom popups and countdowns yay!!! --- assets | 2 +- source/funkin/play/Countdown.hx | 156 ++++++++++---------- source/funkin/play/PlayState.hx | 6 +- source/funkin/play/components/PopUpStuff.hx | 59 ++++++-- 4 files changed, 124 insertions(+), 99 deletions(-) diff --git a/assets b/assets index aa1231e8cf..3a9e051084 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit aa1231e8cf2990bb902eac3b37815c010fa9919a +Subproject commit 3a9e0510841533f96228609dcd50f2515443bb90 diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 55c2a8992a..542cb067cb 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -11,6 +11,7 @@ import funkin.modding.events.ScriptEvent.CountdownScriptEvent; import flixel.util.FlxTimer; import funkin.util.EaseUtil; import funkin.audio.FunkinSound; +import openfl.utils.Assets; class Countdown { @@ -19,6 +20,20 @@ class Countdown */ public static var countdownStep(default, null):CountdownStep = BEFORE; + /** + * Which alternate countdown sound effect to use. + * You can set this via script. + * For example, in Week 6 it is `-pixel`. + */ + public static var soundSuffix:String = ''; + + /** + * Which alternate graphic on countdown to use. + * You can set this via script. + * For example, in Week 6 it is `-pixel`. + */ + public static var graphicSuffix:String = ''; + /** * The currently running countdown. This will be null if there is no countdown running. */ @@ -30,7 +45,7 @@ class Countdown * This will automatically stop and restart the countdown if it is already running. * @returns `false` if the countdown was cancelled by a script. */ - public static function performCountdown(isPixelStyle:Bool):Bool + public static function performCountdown():Bool { countdownStep = BEFORE; var cancelled:Bool = propagateCountdownEvent(countdownStep); @@ -65,10 +80,10 @@ class Countdown // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); // Countdown graphic. - showCountdownGraphic(countdownStep, isPixelStyle); + showCountdownGraphic(countdownStep, graphicSuffix.toLowerCase().contains('pixel')); // Countdown sound. - playCountdownSound(countdownStep, isPixelStyle); + playCountdownSound(countdownStep); // Event handling bullshit. var cancelled:Bool = propagateCountdownEvent(countdownStep); @@ -176,56 +191,34 @@ class Countdown } } + /** + * Reset the countdown configuration to the default. + */ + public static function reset() + { + soundSuffix = ''; + graphicSuffix = ''; + } + /** * Retrieves the graphic to use for this step of the countdown. - * TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles? - * - * This is public so modules can do lol funny shit. */ - public static function showCountdownGraphic(index:CountdownStep, isPixelStyle:Bool):Void + public static function showCountdownGraphic(index:CountdownStep, isGraphicPixel:Bool):Void { var spritePath:String = null; - - var fadeEase = FlxEase.cubeInOut; - - if (isPixelStyle) - { - fadeEase = EaseUtil.stepped(8); - switch (index) - { - case TWO: - spritePath = 'weeb/pixelUI/ready-pixel'; - case ONE: - spritePath = 'weeb/pixelUI/set-pixel'; - case GO: - spritePath = 'weeb/pixelUI/date-pixel'; - default: - // null - } - } - else - { - switch (index) - { - case TWO: - spritePath = 'ready'; - case ONE: - spritePath = 'set'; - case GO: - spritePath = 'go'; - default: - // null - } - } + spritePath = resolveGraphicPath(graphicSuffix, index); if (spritePath == null) return; var countdownSprite:FunkinSprite = FunkinSprite.create(spritePath); countdownSprite.scrollFactor.set(0, 0); - if (isPixelStyle) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE)); + if (isGraphicPixel) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE)); + + var fadeEase = FlxEase.cubeInOut; + if (isGraphicPixel) fadeEase = EaseUtil.stepped(8); - countdownSprite.antialiasing = !isPixelStyle; + countdownSprite.antialiasing = !isGraphicPixel; countdownSprite.updateHitbox(); countdownSprite.screenCenter(); @@ -247,52 +240,55 @@ class Countdown PlayState.instance.add(countdownSprite); } - /** - * Retrieves the sound file to use for this step of the countdown. - * TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles? - * - * This is public so modules can do lol funny shit. - */ - public static function playCountdownSound(index:CountdownStep, isPixelStyle:Bool):Void + static function resolveGraphicPath(suffix:String, index:CountdownStep):Null { - var soundPath:String = null; - - if (isPixelStyle) + var basePath:String = 'ui/countdown/'; + var indexString:String = null; + switch (index) { - switch (index) - { - case THREE: - soundPath = 'intro3-pixel'; - case TWO: - soundPath = 'intro2-pixel'; - case ONE: - soundPath = 'intro1-pixel'; - case GO: - soundPath = 'introGo-pixel'; - default: - // null - } + case TWO: + indexString = 'ready'; + case ONE: + indexString = 'set'; + case GO: + indexString = 'go'; + default: + // null } - else + basePath += indexString; + var spritePath:String = basePath + suffix; + while (!Assets.exists(Paths.image(spritePath)) && suffix.length > 0) { - switch (index) - { - case THREE: - soundPath = 'intro3'; - case TWO: - soundPath = 'intro2'; - case ONE: - soundPath = 'intro1'; - case GO: - soundPath = 'introGo'; - default: - // null - } + suffix = suffix.split('-').slice(0, -1).join('-'); + spritePath = basePath + suffix; } + if (!Assets.exists(Paths.image(spritePath))) return null; + trace('Resolved sprite path: ' + Paths.image(spritePath)); + return spritePath; + } + + /** + * Retrieves the sound file to use for this step of the countdown. + */ + public static function playCountdownSound(index:CountdownStep):Void + { + FunkinSound.playOnce(resolveSoundPath(soundSuffix, index), Constants.COUNTDOWN_VOLUME); + } - if (soundPath == null) return; + static function resolveSoundPath(suffix:String, step:CountdownStep):Null + { + var basePath:String = 'gameplay/countdown/intro'; + if (step != CountdownStep.BEFORE || step != CountdownStep.AFTER) basePath += step; - FunkinSound.playOnce(Paths.sound(soundPath), Constants.COUNTDOWN_VOLUME); + var soundPath:String = Paths.sound(basePath + suffix); + while (!Assets.exists(soundPath) && suffix.length > 0) + { + suffix = suffix.split('-').slice(0, -1).join('-'); + soundPath = Paths.sound(basePath + suffix); + } + if (!Assets.exists(soundPath)) return null; + trace('Resolved sound path: ' + soundPath); + return soundPath; } public static function decrement(step:CountdownStep):CountdownStep diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 32b6e7b621..384c28056a 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -900,7 +900,7 @@ class PlayState extends MusicBeatSubState health = Constants.HEALTH_STARTING; songScore = 0; Highscore.tallies.combo = 0; - Countdown.performCountdown(currentStageId.startsWith('school')); + Countdown.performCountdown(); needsReset = false; } @@ -1857,7 +1857,7 @@ class PlayState extends MusicBeatSubState public function startCountdown():Void { // If Countdown.performCountdown returns false, then the countdown was canceled by a script. - var result:Bool = Countdown.performCountdown(currentStageId.startsWith('school')); + var result:Bool = Countdown.performCountdown(); if (!result) return; isInCutscene = false; @@ -3004,6 +3004,8 @@ class PlayState extends MusicBeatSubState GameOverSubState.reset(); PauseSubState.reset(); + Countdown.reset(); + PopUpStuff.reset(); // Clear the static reference to this state. instance = null; diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index 1bdfd98a83..4ae425cb6e 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -8,25 +8,51 @@ import funkin.graphics.FunkinSprite; import funkin.play.PlayState; import funkin.util.TimerUtil; import funkin.util.EaseUtil; +import openfl.utils.Assets; class PopUpStuff extends FlxTypedGroup { public var offsets:Array = [0, 0]; + /** + * Which alternate graphic on popup to use. + * You can set this via script. + * For example, in Week 6 it is `-pixel`. + */ + public static var graphicSuffix:String = ''; + override public function new() { super(); } - public function displayRating(daRating:String):Void + static function resolveGraphicPath(suffix:String, index:String):Null + { + var folder:String; + if (suffix != '') folder = suffix.substring(0, suffix.indexOf("-")) + suffix.substring(suffix.indexOf("-") + 1); + else + folder = 'normal'; + var basePath:String = 'gameplay/popup/$folder/$index'; + var spritePath:String = basePath + suffix; + trace(spritePath); + while (!Assets.exists(Paths.image(spritePath)) && suffix.length > 0) + { + suffix = suffix.split('-').slice(0, -1).join('-'); + spritePath = basePath + suffix; + } + if (!Assets.exists(Paths.image(spritePath))) return null; + return spritePath; + } + + public function displayRating(daRating:String) { var perfStart:Float = TimerUtil.start(); if (daRating == null) daRating = "good"; - var ratingPath:String = daRating; + var ratingPath:String = resolveGraphicPath(graphicSuffix, daRating); - if (PlayState.instance.currentStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel"; + // if (PlayState.instance.currentStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel"; var rating:FunkinSprite = FunkinSprite.create(0, 0, ratingPath); rating.scrollFactor.set(0.2, 0.2); @@ -43,7 +69,7 @@ class PopUpStuff extends FlxTypedGroup var fadeEase = null; - if (PlayState.instance.currentStageId.startsWith('school')) + if (graphicSuffix.toLowerCase().contains('pixel')) { rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.7)); rating.antialiasing = false; @@ -80,15 +106,8 @@ class PopUpStuff extends FlxTypedGroup if (combo == null) combo = 0; - var pixelShitPart1:String = ""; - var pixelShitPart2:String = ''; - - if (PlayState.instance.currentStageId.startsWith('school')) - { - pixelShitPart1 = 'weeb/pixelUI/'; - pixelShitPart2 = '-pixel'; - } - var comboSpr:FunkinSprite = FunkinSprite.create(pixelShitPart1 + 'combo' + pixelShitPart2); + var comboPath:String = resolveGraphicPath(graphicSuffix, Std.string(combo)); + var comboSpr:FunkinSprite = FunkinSprite.create(comboPath); comboSpr.y = (FlxG.camera.height * 0.44) + offsets[1]; comboSpr.x = (FlxG.width * 0.507) + offsets[0]; // comboSpr.x -= FlxG.camera.scroll.x * 0.2; @@ -101,7 +120,7 @@ class PopUpStuff extends FlxTypedGroup var fadeEase = null; - if (PlayState.instance.currentStageId.startsWith('school')) + if (graphicSuffix.toLowerCase().contains('pixel')) { comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 1)); comboSpr.antialiasing = false; @@ -142,9 +161,9 @@ class PopUpStuff extends FlxTypedGroup var daLoop:Int = 1; for (i in seperatedScore) { - var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, pixelShitPart1 + 'num' + Std.int(i) + pixelShitPart2); + var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, resolveGraphicPath(graphicSuffix, 'num' + Std.int(i))); - if (PlayState.instance.currentStageId.startsWith('school')) + if (graphicSuffix.toLowerCase().contains('pixel')) { numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 1)); numScore.antialiasing = false; @@ -182,4 +201,12 @@ class PopUpStuff extends FlxTypedGroup return combo; } + + /** + * Reset the popup configuration to the default. + */ + public static function reset() + { + graphicSuffix = ''; + } } From bf1d4031af0c60287cbfd700a4597a2dc8fb9829 Mon Sep 17 00:00:00 2001 From: anysad Date: Sat, 13 Jul 2024 23:08:16 +0300 Subject: [PATCH 230/469] remove trace --- source/funkin/play/components/PopUpStuff.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index 4ae425cb6e..971ed10646 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -34,7 +34,6 @@ class PopUpStuff extends FlxTypedGroup folder = 'normal'; var basePath:String = 'gameplay/popup/$folder/$index'; var spritePath:String = basePath + suffix; - trace(spritePath); while (!Assets.exists(Paths.image(spritePath)) && suffix.length > 0) { suffix = suffix.split('-').slice(0, -1).join('-'); From dc9dbe84fa68fd988eeca6666f3d199c92a3f945 Mon Sep 17 00:00:00 2001 From: anysad Date: Sat, 13 Jul 2024 23:29:26 +0300 Subject: [PATCH 231/469] cache textures at their new locations! --- source/funkin/ui/transition/LoadingState.hx | 33 ++++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index 0f2ce10766..b4987ff274 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -291,17 +291,28 @@ class LoadingState extends MusicBeatSubState FunkinSprite.preparePurgeCache(); FunkinSprite.cacheTexture(Paths.image('healthBar')); FunkinSprite.cacheTexture(Paths.image('menuDesat')); - FunkinSprite.cacheTexture(Paths.image('combo')); - FunkinSprite.cacheTexture(Paths.image('num0')); - FunkinSprite.cacheTexture(Paths.image('num1')); - FunkinSprite.cacheTexture(Paths.image('num2')); - FunkinSprite.cacheTexture(Paths.image('num3')); - FunkinSprite.cacheTexture(Paths.image('num4')); - FunkinSprite.cacheTexture(Paths.image('num5')); - FunkinSprite.cacheTexture(Paths.image('num6')); - FunkinSprite.cacheTexture(Paths.image('num7')); - FunkinSprite.cacheTexture(Paths.image('num8')); - FunkinSprite.cacheTexture(Paths.image('num9')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/combo')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num0')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num1')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num2')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num3')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num4')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num5')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num6')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num7')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num8')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num9')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/combo-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num0-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num1-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num2-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num3-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num4-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num5-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num6-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num7-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num8-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num9-pixel')); FunkinSprite.cacheTexture(Paths.image('notes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared')); From d5a53855d35458fbf4ceb1ab7ad09436d650f9c6 Mon Sep 17 00:00:00 2001 From: anysad Date: Sat, 13 Jul 2024 23:39:56 +0300 Subject: [PATCH 232/469] how the hell did I miss these? --- source/funkin/ui/transition/LoadingState.hx | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index b4987ff274..3e9dec96b4 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -317,13 +317,20 @@ class LoadingState extends MusicBeatSubState FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared')); FunkinSprite.cacheTexture(Paths.image('NOTE_hold_assets')); - FunkinSprite.cacheTexture(Paths.image('ready', 'shared')); - FunkinSprite.cacheTexture(Paths.image('set', 'shared')); - FunkinSprite.cacheTexture(Paths.image('go', 'shared')); - FunkinSprite.cacheTexture(Paths.image('sick', 'shared')); - FunkinSprite.cacheTexture(Paths.image('good', 'shared')); - FunkinSprite.cacheTexture(Paths.image('bad', 'shared')); - FunkinSprite.cacheTexture(Paths.image('shit', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/ready', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/set', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/go', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/ready-pixel', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/set-pixel', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/go-pixel', 'shared')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/sick')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/good')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/bad')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/shit')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/sick-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/good-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/bad-pixel')); + FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/shit-pixel')); FunkinSprite.cacheTexture(Paths.image('miss', 'shared')); // TODO: remove this // List all image assets in the level's library. From d3209e57b7f177df969a5610b16f4bd57163ffda Mon Sep 17 00:00:00 2001 From: anysad Date: Tue, 16 Jul 2024 20:53:12 +0300 Subject: [PATCH 233/469] goodbye scripts, hello notestyles! --- source/funkin/play/Countdown.hx | 98 +++++++++++++-------- source/funkin/play/components/PopUpStuff.hx | 48 ++++++---- source/funkin/ui/transition/LoadingState.hx | 73 +++++++-------- source/funkin/util/Constants.hx | 5 ++ 4 files changed, 131 insertions(+), 93 deletions(-) diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 542cb067cb..139b6cacd5 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -12,6 +12,8 @@ import flixel.util.FlxTimer; import funkin.util.EaseUtil; import funkin.audio.FunkinSound; import openfl.utils.Assets; +import funkin.data.notestyle.NoteStyleRegistry; +import funkin.play.notes.notestyle.NoteStyle; class Countdown { @@ -21,9 +23,9 @@ class Countdown public static var countdownStep(default, null):CountdownStep = BEFORE; /** - * Which alternate countdown sound effect to use. - * You can set this via script. - * For example, in Week 6 it is `-pixel`. + * Which alternate graphic/sound on countdown to use. + * This is set via the current notestyle. + * For example, in Week 6 it is `pixel`. */ public static var soundSuffix:String = ''; @@ -34,6 +36,10 @@ class Countdown */ public static var graphicSuffix:String = ''; + static var noteStyle:NoteStyle; + + static var isPixel:Bool = false; + /** * The currently running countdown. This will be null if there is no countdown running. */ @@ -63,6 +69,12 @@ class Countdown // @:privateAccess // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); + var fetchedNoteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(PlayState.instance.currentChart.noteStyle); + if (fetchedNoteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); + else + noteStyle = fetchedNoteStyle; + if (noteStyle._data.assets.note.isPixel) isPixel = true; + // The timer function gets called based on the beat of the song. countdownTimer = new FlxTimer(); @@ -80,10 +92,10 @@ class Countdown // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); // Countdown graphic. - showCountdownGraphic(countdownStep, graphicSuffix.toLowerCase().contains('pixel')); + showCountdownGraphic(countdownStep, noteStyle, isPixel); // Countdown sound. - playCountdownSound(countdownStep); + playCountdownSound(countdownStep, noteStyle); // Event handling bullshit. var cancelled:Bool = propagateCountdownEvent(countdownStep); @@ -196,17 +208,31 @@ class Countdown */ public static function reset() { - soundSuffix = ''; - graphicSuffix = ''; + noteStyle = NoteStyleRegistry.instance.fetchDefault(); + isPixel = false; } /** * Retrieves the graphic to use for this step of the countdown. */ - public static function showCountdownGraphic(index:CountdownStep, isGraphicPixel:Bool):Void + public static function showCountdownGraphic(index:CountdownStep, noteStyle:NoteStyle, isGraphicPixel:Bool):Void { + var indexString:String = null; + switch (index) + { + case TWO: + indexString = 'ready'; + case ONE: + indexString = 'set'; + case GO: + indexString = 'go'; + default: + // null + } + if (indexString == null) return; + var spritePath:String = null; - spritePath = resolveGraphicPath(graphicSuffix, index); + spritePath = resolveGraphicPath(noteStyle, indexString); if (spritePath == null) return; @@ -214,6 +240,8 @@ class Countdown countdownSprite.scrollFactor.set(0, 0); if (isGraphicPixel) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE)); + else + countdownSprite.setGraphicSize(Std.int(countdownSprite.width * 0.7)); var fadeEase = FlxEase.cubeInOut; if (isGraphicPixel) fadeEase = EaseUtil.stepped(8); @@ -223,6 +251,8 @@ class Countdown countdownSprite.updateHitbox(); countdownSprite.screenCenter(); + countdownSprite.cameras = [PlayState.instance.camHUD]; + // Fade sprite in, then out, then destroy it. FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100}, Conductor.instance.beatLengthMs / 1000, { @@ -240,29 +270,19 @@ class Countdown PlayState.instance.add(countdownSprite); } - static function resolveGraphicPath(suffix:String, index:CountdownStep):Null + static function resolveGraphicPath(noteStyle:NoteStyle, index:String):Null { var basePath:String = 'ui/countdown/'; - var indexString:String = null; - switch (index) - { - case TWO: - indexString = 'ready'; - case ONE: - indexString = 'set'; - case GO: - indexString = 'go'; - default: - // null - } - basePath += indexString; - var spritePath:String = basePath + suffix; - while (!Assets.exists(Paths.image(spritePath)) && suffix.length > 0) + + var spritePath:String = basePath + noteStyle.id + '/$index'; + // If nothing is found, revert it to default notestyle skin + if (!Assets.exists(Paths.image(spritePath))) { - suffix = suffix.split('-').slice(0, -1).join('-'); - spritePath = basePath + suffix; + if (!isPixel) spritePath = basePath + Constants.DEFAULT_NOTE_STYLE + '/$index'; + else + spritePath = basePath + Constants.DEFAULT_PIXEL_NOTE_STYLE + '/$index'; } - if (!Assets.exists(Paths.image(spritePath))) return null; + trace('Resolved sprite path: ' + Paths.image(spritePath)); return spritePath; } @@ -270,23 +290,25 @@ class Countdown /** * Retrieves the sound file to use for this step of the countdown. */ - public static function playCountdownSound(index:CountdownStep):Void + public static function playCountdownSound(step:CountdownStep, noteStyle:NoteStyle):Void { - FunkinSound.playOnce(resolveSoundPath(soundSuffix, index), Constants.COUNTDOWN_VOLUME); + return FunkinSound.playOnce(Paths.sound(resolveSoundPath(noteStyle, step)), Constants.COUNTDOWN_VOLUME); } - static function resolveSoundPath(suffix:String, step:CountdownStep):Null + static function resolveSoundPath(noteStyle:NoteStyle, step:CountdownStep):Null { - var basePath:String = 'gameplay/countdown/intro'; - if (step != CountdownStep.BEFORE || step != CountdownStep.AFTER) basePath += step; + if (step == CountdownStep.BEFORE || step == CountdownStep.AFTER) return null; + var basePath:String = 'gameplay/countdown/'; - var soundPath:String = Paths.sound(basePath + suffix); - while (!Assets.exists(soundPath) && suffix.length > 0) + var soundPath:String = basePath + noteStyle.id + '/intro$step'; + // If nothing is found, revert it to default notestyle sound + if (!Assets.exists(Paths.sound(soundPath))) { - suffix = suffix.split('-').slice(0, -1).join('-'); - soundPath = Paths.sound(basePath + suffix); + if (!isPixel) soundPath = basePath + Constants.DEFAULT_NOTE_STYLE + '/intro$step'; + else + soundPath = basePath + Constants.DEFAULT_PIXEL_NOTE_STYLE + '/intro$step'; } - if (!Assets.exists(soundPath)) return null; + trace('Resolved sound path: ' + soundPath); return soundPath; } diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index 971ed10646..2bd2bf4dba 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -9,6 +9,8 @@ import funkin.play.PlayState; import funkin.util.TimerUtil; import funkin.util.EaseUtil; import openfl.utils.Assets; +import funkin.data.notestyle.NoteStyleRegistry; +import funkin.play.notes.notestyle.NoteStyle; class PopUpStuff extends FlxTypedGroup { @@ -16,30 +18,37 @@ class PopUpStuff extends FlxTypedGroup /** * Which alternate graphic on popup to use. - * You can set this via script. - * For example, in Week 6 it is `-pixel`. + * This is set via the current notestyle. + * For example, in Week 6 it is `pixel`. */ - public static var graphicSuffix:String = ''; + static var noteStyle:NoteStyle; + + static var isPixel:Bool = false; override public function new() { super(); + + var fetchedNoteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(PlayState.instance.currentChart.noteStyle); + if (fetchedNoteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); + else + noteStyle = fetchedNoteStyle; + if (noteStyle._data.assets.note.isPixel) isPixel = true; } - static function resolveGraphicPath(suffix:String, index:String):Null + static function resolveGraphicPath(noteStyle:NoteStyle, index:String):Null { - var folder:String; - if (suffix != '') folder = suffix.substring(0, suffix.indexOf("-")) + suffix.substring(suffix.indexOf("-") + 1); - else - folder = 'normal'; - var basePath:String = 'gameplay/popup/$folder/$index'; - var spritePath:String = basePath + suffix; - while (!Assets.exists(Paths.image(spritePath)) && suffix.length > 0) + var basePath:String = 'ui/popup/'; + + var spritePath:String = basePath + noteStyle.id + '/$index'; + // If nothing is found, revert it to default notestyle skin + if (!Assets.exists(Paths.image(spritePath))) { - suffix = suffix.split('-').slice(0, -1).join('-'); - spritePath = basePath + suffix; + if (!isPixel) spritePath = basePath + Constants.DEFAULT_NOTE_STYLE + '/$index'; + else + spritePath = basePath + Constants.DEFAULT_PIXEL_NOTE_STYLE + '/$index'; } - if (!Assets.exists(Paths.image(spritePath))) return null; + return spritePath; } @@ -49,7 +58,7 @@ class PopUpStuff extends FlxTypedGroup if (daRating == null) daRating = "good"; - var ratingPath:String = resolveGraphicPath(graphicSuffix, daRating); + var ratingPath:String = resolveGraphicPath(noteStyle, daRating); // if (PlayState.instance.currentStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel"; @@ -68,7 +77,7 @@ class PopUpStuff extends FlxTypedGroup var fadeEase = null; - if (graphicSuffix.toLowerCase().contains('pixel')) + if (isPixel) { rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.7)); rating.antialiasing = false; @@ -105,7 +114,7 @@ class PopUpStuff extends FlxTypedGroup if (combo == null) combo = 0; - var comboPath:String = resolveGraphicPath(graphicSuffix, Std.string(combo)); + var comboPath:String = resolveGraphicPath(noteStyle, 'combo'); var comboSpr:FunkinSprite = FunkinSprite.create(comboPath); comboSpr.y = (FlxG.camera.height * 0.44) + offsets[1]; comboSpr.x = (FlxG.width * 0.507) + offsets[0]; @@ -160,7 +169,7 @@ class PopUpStuff extends FlxTypedGroup var daLoop:Int = 1; for (i in seperatedScore) { - var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, resolveGraphicPath(graphicSuffix, 'num' + Std.int(i))); + var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, resolveGraphicPath(noteStyle, 'num' + Std.int(i))); if (graphicSuffix.toLowerCase().contains('pixel')) { @@ -206,6 +215,7 @@ class PopUpStuff extends FlxTypedGroup */ public static function reset() { - graphicSuffix = ''; + noteStyle = NoteStyleRegistry.instance.fetchDefault(); + isPixel = false; } } diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index 3e9dec96b4..d48ca1c0b8 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -291,46 +291,47 @@ class LoadingState extends MusicBeatSubState FunkinSprite.preparePurgeCache(); FunkinSprite.cacheTexture(Paths.image('healthBar')); FunkinSprite.cacheTexture(Paths.image('menuDesat')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/combo')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num0')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num1')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num2')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num3')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num4')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num5')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num6')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num7')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num8')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/num9')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/combo-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num0-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num1-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num2-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num3-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num4-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num5-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num6-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num7-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num8-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/num9-pixel')); + // Lord have mercy on me and this caching -anysad + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/combo')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num0')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num1')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num2')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num3')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num4')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num5')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num6')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num7')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num8')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num9')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/combo')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num0')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num1')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num2')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num3')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num4')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num5')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num6')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num7')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num8')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num9')); FunkinSprite.cacheTexture(Paths.image('notes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared')); FunkinSprite.cacheTexture(Paths.image('NOTE_hold_assets')); - FunkinSprite.cacheTexture(Paths.image('ui/countdown/ready', 'shared')); - FunkinSprite.cacheTexture(Paths.image('ui/countdown/set', 'shared')); - FunkinSprite.cacheTexture(Paths.image('ui/countdown/go', 'shared')); - FunkinSprite.cacheTexture(Paths.image('ui/countdown/ready-pixel', 'shared')); - FunkinSprite.cacheTexture(Paths.image('ui/countdown/set-pixel', 'shared')); - FunkinSprite.cacheTexture(Paths.image('ui/countdown/go-pixel', 'shared')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/sick')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/good')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/bad')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/normal/shit')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/sick-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/good-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/bad-pixel')); - FunkinSprite.cacheTexture(Paths.image('gameplay/popup/pixel/shit-pixel')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/ready', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/set', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/go', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/ready', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/set', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/go', 'shared')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/sick')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/good')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/bad')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/shit')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/sick')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/good')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/bad')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/shit')); FunkinSprite.cacheTexture(Paths.image('miss', 'shared')); // TODO: remove this // List all image assets in the level's library. diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index 2d4fef1f44..85cd1a27b6 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -258,6 +258,11 @@ class Constants */ public static final DEFAULT_NOTE_STYLE:String = 'funkin'; + /** + * The default pixel note style for songs. + */ + public static final DEFAULT_PIXEL_NOTE_STYLE:String = 'pixel'; + /** * The default album for songs in Freeplay. */ From 82a6ca591687c01ff82a6605c74195da3237ac6d Mon Sep 17 00:00:00 2001 From: anysad Date: Wed, 17 Jul 2024 23:19:18 +0300 Subject: [PATCH 234/469] welcome fallback note styles! --- source/funkin/play/Countdown.hx | 54 ++++++++++++++----- source/funkin/play/components/PopUpStuff.hx | 21 +++++++- .../funkin/play/notes/notestyle/NoteStyle.hx | 2 +- 3 files changed, 60 insertions(+), 17 deletions(-) diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 139b6cacd5..a8bf1b500a 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -38,6 +38,8 @@ class Countdown static var noteStyle:NoteStyle; + static var fallbackNoteStyle:Null; + static var isPixel:Bool = false; /** @@ -69,11 +71,7 @@ class Countdown // @:privateAccess // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); - var fetchedNoteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(PlayState.instance.currentChart.noteStyle); - if (fetchedNoteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); - else - noteStyle = fetchedNoteStyle; - if (noteStyle._data.assets.note.isPixel) isPixel = true; + fetchNoteStyle(); // The timer function gets called based on the beat of the song. countdownTimer = new FlxTimer(); @@ -92,7 +90,7 @@ class Countdown // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); // Countdown graphic. - showCountdownGraphic(countdownStep, noteStyle, isPixel); + showCountdownGraphic(countdownStep, noteStyle); // Countdown sound. playCountdownSound(countdownStep, noteStyle); @@ -212,10 +210,20 @@ class Countdown isPixel = false; } + static function fetchNoteStyle():Void + { + var fetchedNoteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(PlayState.instance.currentChart.noteStyle); + if (fetchedNoteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); + else + noteStyle = fetchedNoteStyle; + fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); + isPixel = false; + } + /** * Retrieves the graphic to use for this step of the countdown. */ - public static function showCountdownGraphic(index:CountdownStep, noteStyle:NoteStyle, isGraphicPixel:Bool):Void + public static function showCountdownGraphic(index:CountdownStep, noteStyle:NoteStyle):Void { var indexString:String = null; switch (index) @@ -246,13 +254,13 @@ class Countdown var fadeEase = FlxEase.cubeInOut; if (isGraphicPixel) fadeEase = EaseUtil.stepped(8); - countdownSprite.antialiasing = !isGraphicPixel; + countdownSprite.antialiasing = !isPixel; + + countdownSprite.cameras = [PlayState.instance.camHUD]; countdownSprite.updateHitbox(); countdownSprite.screenCenter(); - countdownSprite.cameras = [PlayState.instance.camHUD]; - // Fade sprite in, then out, then destroy it. FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100}, Conductor.instance.beatLengthMs / 1000, { @@ -272,10 +280,19 @@ class Countdown static function resolveGraphicPath(noteStyle:NoteStyle, index:String):Null { + fetchNoteStyle(); var basePath:String = 'ui/countdown/'; - var spritePath:String = basePath + noteStyle.id + '/$index'; - // If nothing is found, revert it to default notestyle skin + + while (!Assets.exists(Paths.image(spritePath)) && fallbackNoteStyle != null) + { + noteStyle = fallbackNoteStyle; + fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); + spritePath = basePath + noteStyle.id + '/$index'; + } + if (noteStyle.isHoldNotePixel()) isPixel = true; + + // If ABSOLUTELY nothing is found, revert it to default notestyle skin if (!Assets.exists(Paths.image(spritePath))) { if (!isPixel) spritePath = basePath + Constants.DEFAULT_NOTE_STYLE + '/$index'; @@ -298,10 +315,19 @@ class Countdown static function resolveSoundPath(noteStyle:NoteStyle, step:CountdownStep):Null { if (step == CountdownStep.BEFORE || step == CountdownStep.AFTER) return null; + fetchNoteStyle(); var basePath:String = 'gameplay/countdown/'; - var soundPath:String = basePath + noteStyle.id + '/intro$step'; - // If nothing is found, revert it to default notestyle sound + + while (!Assets.exists(Paths.sound(soundPath)) && fallbackNoteStyle != null) + { + noteStyle = fallbackNoteStyle; + fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); + soundPath = basePath + noteStyle.id + '/intro$step'; + } + if (noteStyle.isHoldNotePixel()) isPixel = true; + + // If ABSOLUTELY nothing is found, revert it to default notestyle sound if (!Assets.exists(Paths.sound(soundPath))) { if (!isPixel) soundPath = basePath + Constants.DEFAULT_NOTE_STYLE + '/intro$step'; diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index 2bd2bf4dba..97e1d291d7 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -23,24 +23,41 @@ class PopUpStuff extends FlxTypedGroup */ static var noteStyle:NoteStyle; + static var fallbackNoteStyle:Null; + static var isPixel:Bool = false; override public function new() { super(); + fetchNoteStyle(); + } + + static function fetchNoteStyle():Void + { var fetchedNoteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(PlayState.instance.currentChart.noteStyle); if (fetchedNoteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); else noteStyle = fetchedNoteStyle; - if (noteStyle._data.assets.note.isPixel) isPixel = true; + fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); + isPixel = false; } static function resolveGraphicPath(noteStyle:NoteStyle, index:String):Null { + fetchNoteStyle(); var basePath:String = 'ui/popup/'; - var spritePath:String = basePath + noteStyle.id + '/$index'; + + while (!Assets.exists(Paths.image(spritePath)) && fallbackNoteStyle != null) + { + noteStyle = fallbackNoteStyle; + fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); + spritePath = basePath + noteStyle.id + '/$index'; + } + if (noteStyle.isHoldNotePixel()) isPixel = true; + // If nothing is found, revert it to default notestyle skin if (!Assets.exists(Paths.image(spritePath))) { diff --git a/source/funkin/play/notes/notestyle/NoteStyle.hx b/source/funkin/play/notes/notestyle/NoteStyle.hx index 3993cce523..5a877aa5be 100644 --- a/source/funkin/play/notes/notestyle/NoteStyle.hx +++ b/source/funkin/play/notes/notestyle/NoteStyle.hx @@ -72,7 +72,7 @@ class NoteStyle implements IRegistryEntry * Get the note style ID of the parent note style. * @return The string ID, or `null` if there is no parent. */ - function getFallbackID():Null + public function getFallbackID():Null { return _data.fallback; } From d07564cdc234a39daaa4aebc8ec9b69d4bf2eaf0 Mon Sep 17 00:00:00 2001 From: anysad Date: Thu, 18 Jul 2024 17:56:54 +0300 Subject: [PATCH 235/469] hopefully final polish? --- source/funkin/play/Countdown.hx | 29 ++++++++------------- source/funkin/play/components/PopUpStuff.hx | 10 +++---- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index a8bf1b500a..47d6faa9c1 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -71,8 +71,6 @@ class Countdown // @:privateAccess // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); - fetchNoteStyle(); - // The timer function gets called based on the beat of the song. countdownTimer = new FlxTimer(); @@ -90,10 +88,10 @@ class Countdown // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0)); // Countdown graphic. - showCountdownGraphic(countdownStep, noteStyle); + showCountdownGraphic(countdownStep); // Countdown sound. - playCountdownSound(countdownStep, noteStyle); + playCountdownSound(countdownStep); // Event handling bullshit. var cancelled:Bool = propagateCountdownEvent(countdownStep); @@ -223,7 +221,7 @@ class Countdown /** * Retrieves the graphic to use for this step of the countdown. */ - public static function showCountdownGraphic(index:CountdownStep, noteStyle:NoteStyle):Void + public static function showCountdownGraphic(index:CountdownStep):Void { var indexString:String = null; switch (index) @@ -240,7 +238,7 @@ class Countdown if (indexString == null) return; var spritePath:String = null; - spritePath = resolveGraphicPath(noteStyle, indexString); + spritePath = resolveGraphicPath(indexString); if (spritePath == null) return; @@ -259,26 +257,21 @@ class Countdown countdownSprite.cameras = [PlayState.instance.camHUD]; countdownSprite.updateHitbox(); - countdownSprite.screenCenter(); // Fade sprite in, then out, then destroy it. - FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100}, Conductor.instance.beatLengthMs / 1000, + FlxTween.tween(countdownSprite, {alpha: 0}, Conductor.instance.beatLengthMs / 1000, { - ease: FlxEase.cubeInOut, + ease: fadeEase, onComplete: function(twn:FlxTween) { countdownSprite.destroy(); } }); - FlxTween.tween(countdownSprite, {alpha: 0}, Conductor.instance.beatLengthMs / 1000, - { - ease: fadeEase - }); - PlayState.instance.add(countdownSprite); + countdownSprite.screenCenter(); } - static function resolveGraphicPath(noteStyle:NoteStyle, index:String):Null + static function resolveGraphicPath(index:String):Null { fetchNoteStyle(); var basePath:String = 'ui/countdown/'; @@ -307,12 +300,12 @@ class Countdown /** * Retrieves the sound file to use for this step of the countdown. */ - public static function playCountdownSound(step:CountdownStep, noteStyle:NoteStyle):Void + public static function playCountdownSound(step:CountdownStep):Void { - return FunkinSound.playOnce(Paths.sound(resolveSoundPath(noteStyle, step)), Constants.COUNTDOWN_VOLUME); + return FunkinSound.playOnce(Paths.sound(resolveSoundPath(step)), Constants.COUNTDOWN_VOLUME); } - static function resolveSoundPath(noteStyle:NoteStyle, step:CountdownStep):Null + static function resolveSoundPath(step:CountdownStep):Null { if (step == CountdownStep.BEFORE || step == CountdownStep.AFTER) return null; fetchNoteStyle(); diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index 97e1d291d7..b9479ec8bd 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -30,8 +30,6 @@ class PopUpStuff extends FlxTypedGroup override public function new() { super(); - - fetchNoteStyle(); } static function fetchNoteStyle():Void @@ -44,7 +42,7 @@ class PopUpStuff extends FlxTypedGroup isPixel = false; } - static function resolveGraphicPath(noteStyle:NoteStyle, index:String):Null + static function resolveGraphicPath(index:String):Null { fetchNoteStyle(); var basePath:String = 'ui/popup/'; @@ -75,7 +73,7 @@ class PopUpStuff extends FlxTypedGroup if (daRating == null) daRating = "good"; - var ratingPath:String = resolveGraphicPath(noteStyle, daRating); + var ratingPath:String = resolveGraphicPath(daRating); // if (PlayState.instance.currentStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel"; @@ -131,7 +129,7 @@ class PopUpStuff extends FlxTypedGroup if (combo == null) combo = 0; - var comboPath:String = resolveGraphicPath(noteStyle, 'combo'); + var comboPath:String = resolveGraphicPath('combo'); var comboSpr:FunkinSprite = FunkinSprite.create(comboPath); comboSpr.y = (FlxG.camera.height * 0.44) + offsets[1]; comboSpr.x = (FlxG.width * 0.507) + offsets[0]; @@ -186,7 +184,7 @@ class PopUpStuff extends FlxTypedGroup var daLoop:Int = 1; for (i in seperatedScore) { - var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, resolveGraphicPath(noteStyle, 'num' + Std.int(i))); + var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, resolveGraphicPath('num' + Std.int(i))); if (graphicSuffix.toLowerCase().contains('pixel')) { From 41bd0246ac31bf9b1195b4c317e1f3ea36ae94bb Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sun, 28 Jul 2024 17:10:32 -0400 Subject: [PATCH 236/469] Custom coutndown, judgements, and combo count now use the note style file instead of hardcoded paths. --- assets | 2 +- source/funkin/data/BaseRegistry.hx | 2 +- source/funkin/data/notestyle/CHANGELOG.md | 31 + source/funkin/data/notestyle/NoteStyleData.hx | 88 ++- .../data/notestyle/NoteStyleRegistry.hx | 4 +- source/funkin/play/Countdown.hx | 119 +-- source/funkin/play/PlayState.hx | 23 +- source/funkin/play/components/PopUpStuff.hx | 182 +---- source/funkin/play/notes/StrumlineNote.hx | 7 + .../funkin/play/notes/notestyle/NoteStyle.hx | 712 ++++++++++++++++-- .../components/ChartEditorHoldNoteSprite.hx | 4 +- 11 files changed, 838 insertions(+), 336 deletions(-) create mode 100644 source/funkin/data/notestyle/CHANGELOG.md diff --git a/assets b/assets index 3a9e051084..c4bd528188 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 3a9e0510841533f96228609dcd50f2515443bb90 +Subproject commit c4bd5281880ac2a1e26016c1219824d2f4247536 diff --git a/source/funkin/data/BaseRegistry.hx b/source/funkin/data/BaseRegistry.hx index 118516bec3..83413ad00a 100644 --- a/source/funkin/data/BaseRegistry.hx +++ b/source/funkin/data/BaseRegistry.hx @@ -263,7 +263,7 @@ abstract class BaseRegistry & Constructible + public function parseEntryDataWithMigration(id:String, version:Null):Null { if (version == null) { diff --git a/source/funkin/data/notestyle/CHANGELOG.md b/source/funkin/data/notestyle/CHANGELOG.md new file mode 100644 index 0000000000..d85c11cad7 --- /dev/null +++ b/source/funkin/data/notestyle/CHANGELOG.md @@ -0,0 +1,31 @@ +# Note Style Data Schema Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.1.0] +### Added +- Added several new `assets`: + - `countdownThree` + - `countdownTwo` + - `countdownOne` + - `countdownGo` + - `judgementSick` + - `judgementGood` + - `judgementBad` + - `judgementShit` + - `comboNumber0` + - `comboNumber1` + - `comboNumber2` + - `comboNumber3` + - `comboNumber4` + - `comboNumber5` + - `comboNumber6` + - `comboNumber7` + - `comboNumber8` + - `comboNumber9` + +## [1.0.0] +Initial version. diff --git a/source/funkin/data/notestyle/NoteStyleData.hx b/source/funkin/data/notestyle/NoteStyleData.hx index fcdb3b4f9e..39162b896e 100644 --- a/source/funkin/data/notestyle/NoteStyleData.hx +++ b/source/funkin/data/notestyle/NoteStyleData.hx @@ -74,6 +74,84 @@ typedef NoteStyleAssetsData = */ @:optional var holdNoteCover:NoteStyleAssetData; + + /** + * The THREE sound (and an optional pre-READY graphic). + */ + @:optional + var countdownThree:NoteStyleAssetData; + + /** + * The TWO sound and READY graphic. + */ + @:optional + var countdownTwo:NoteStyleAssetData; + + /** + * The ONE sound and SET graphic. + */ + @:optional + var countdownOne:NoteStyleAssetData; + + /** + * The GO sound and GO! graphic. + */ + @:optional + var countdownGo:NoteStyleAssetData; + + /** + * The SICK! judgement. + */ + @:optional + var judgementSick:NoteStyleAssetData; + + /** + * The GOOD! judgement. + */ + @:optional + var judgementGood:NoteStyleAssetData; + + /** + * The BAD! judgement. + */ + @:optional + var judgementBad:NoteStyleAssetData; + + /** + * The SHIT! judgement. + */ + @:optional + var judgementShit:NoteStyleAssetData; + + @:optional + var comboNumber0:NoteStyleAssetData; + + @:optional + var comboNumber1:NoteStyleAssetData; + + @:optional + var comboNumber2:NoteStyleAssetData; + + @:optional + var comboNumber3:NoteStyleAssetData; + + @:optional + var comboNumber4:NoteStyleAssetData; + + @:optional + var comboNumber5:NoteStyleAssetData; + + @:optional + var comboNumber6:NoteStyleAssetData; + + @:optional + var comboNumber7:NoteStyleAssetData; + + @:optional + var comboNumber8:NoteStyleAssetData; + + @:optional + var comboNumber9:NoteStyleAssetData; } /** @@ -120,7 +198,8 @@ typedef NoteStyleAssetData = /** * The structure of this data depends on the asset. */ - var data:T; + @:optional + var data:Null; } typedef NoteStyleData_Note = @@ -131,7 +210,14 @@ typedef NoteStyleData_Note = var right:UnnamedAnimationData; } +typedef NoteStyleData_Countdown = +{ + var audioPath:String; +} + typedef NoteStyleData_HoldNote = {} +typedef NoteStyleData_Judgement = {} +typedef NoteStyleData_ComboNum = {} /** * Data on animations for each direction of the strumline. diff --git a/source/funkin/data/notestyle/NoteStyleRegistry.hx b/source/funkin/data/notestyle/NoteStyleRegistry.hx index 5e9fa9a3dd..36d1b92001 100644 --- a/source/funkin/data/notestyle/NoteStyleRegistry.hx +++ b/source/funkin/data/notestyle/NoteStyleRegistry.hx @@ -11,9 +11,9 @@ class NoteStyleRegistry extends BaseRegistry * Handle breaking changes by incrementing this value * and adding migration to the `migrateNoteStyleData()` function. */ - public static final NOTE_STYLE_DATA_VERSION:thx.semver.Version = "1.0.0"; + public static final NOTE_STYLE_DATA_VERSION:thx.semver.Version = "1.1.0"; - public static final NOTE_STYLE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; + public static final NOTE_STYLE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.1.x"; public static var instance(get, never):NoteStyleRegistry; static var _instance:Null = null; diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 47d6faa9c1..643883a43f 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -40,8 +40,6 @@ class Countdown static var fallbackNoteStyle:Null; - static var isPixel:Bool = false; - /** * The currently running countdown. This will be null if there is no countdown running. */ @@ -204,18 +202,22 @@ class Countdown */ public static function reset() { - noteStyle = NoteStyleRegistry.instance.fetchDefault(); - isPixel = false; + noteStyle = null; } - static function fetchNoteStyle():Void + /** + * Retrieve the note style data (if we haven't already) + * @param noteStyleId The id of the note style to fetch. Defaults to the one used by the current PlayState. + * @param force Fetch the note style from the registry even if we've already fetched it. + */ + static function fetchNoteStyle(?noteStyleId:String, force:Bool = false):Void { - var fetchedNoteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(PlayState.instance.currentChart.noteStyle); - if (fetchedNoteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); - else - noteStyle = fetchedNoteStyle; - fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); - isPixel = false; + if (noteStyle != null && !force) return; + + if (noteStyleId == null) noteStyleId = PlayState.instance?.currentChart?.noteStyle; + + noteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyleId); + if (noteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); } /** @@ -223,40 +225,13 @@ class Countdown */ public static function showCountdownGraphic(index:CountdownStep):Void { - var indexString:String = null; - switch (index) - { - case TWO: - indexString = 'ready'; - case ONE: - indexString = 'set'; - case GO: - indexString = 'go'; - default: - // null - } - if (indexString == null) return; - - var spritePath:String = null; - spritePath = resolveGraphicPath(indexString); - - if (spritePath == null) return; - - var countdownSprite:FunkinSprite = FunkinSprite.create(spritePath); - countdownSprite.scrollFactor.set(0, 0); + fetchNoteStyle(); - if (isGraphicPixel) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE)); - else - countdownSprite.setGraphicSize(Std.int(countdownSprite.width * 0.7)); + var countdownSprite = noteStyle.buildCountdownSprite(index); + if (countdownSprite == null) return; var fadeEase = FlxEase.cubeInOut; - if (isGraphicPixel) fadeEase = EaseUtil.stepped(8); - - countdownSprite.antialiasing = !isPixel; - - countdownSprite.cameras = [PlayState.instance.camHUD]; - - countdownSprite.updateHitbox(); + if (noteStyle.isCountdownSpritePixel(index)) fadeEase = EaseUtil.stepped(8); // Fade sprite in, then out, then destroy it. FlxTween.tween(countdownSprite, {alpha: 0}, Conductor.instance.beatLengthMs / 1000, @@ -267,69 +242,25 @@ class Countdown } }); + countdownSprite.cameras = [PlayState.instance.camHUD]; PlayState.instance.add(countdownSprite); countdownSprite.screenCenter(); - } - - static function resolveGraphicPath(index:String):Null - { - fetchNoteStyle(); - var basePath:String = 'ui/countdown/'; - var spritePath:String = basePath + noteStyle.id + '/$index'; - - while (!Assets.exists(Paths.image(spritePath)) && fallbackNoteStyle != null) - { - noteStyle = fallbackNoteStyle; - fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); - spritePath = basePath + noteStyle.id + '/$index'; - } - if (noteStyle.isHoldNotePixel()) isPixel = true; - - // If ABSOLUTELY nothing is found, revert it to default notestyle skin - if (!Assets.exists(Paths.image(spritePath))) - { - if (!isPixel) spritePath = basePath + Constants.DEFAULT_NOTE_STYLE + '/$index'; - else - spritePath = basePath + Constants.DEFAULT_PIXEL_NOTE_STYLE + '/$index'; - } - trace('Resolved sprite path: ' + Paths.image(spritePath)); - return spritePath; + var offsets = noteStyle.getCountdownSpriteOffsets(index); + countdownSprite.x += offsets[0]; + countdownSprite.y += offsets[1]; } /** * Retrieves the sound file to use for this step of the countdown. */ - public static function playCountdownSound(step:CountdownStep):Void + public static function playCountdownSound(step:CountdownStep):FunkinSound { - return FunkinSound.playOnce(Paths.sound(resolveSoundPath(step)), Constants.COUNTDOWN_VOLUME); - } - - static function resolveSoundPath(step:CountdownStep):Null - { - if (step == CountdownStep.BEFORE || step == CountdownStep.AFTER) return null; fetchNoteStyle(); - var basePath:String = 'gameplay/countdown/'; - var soundPath:String = basePath + noteStyle.id + '/intro$step'; - - while (!Assets.exists(Paths.sound(soundPath)) && fallbackNoteStyle != null) - { - noteStyle = fallbackNoteStyle; - fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); - soundPath = basePath + noteStyle.id + '/intro$step'; - } - if (noteStyle.isHoldNotePixel()) isPixel = true; - - // If ABSOLUTELY nothing is found, revert it to default notestyle sound - if (!Assets.exists(Paths.sound(soundPath))) - { - if (!isPixel) soundPath = basePath + Constants.DEFAULT_NOTE_STYLE + '/intro$step'; - else - soundPath = basePath + Constants.DEFAULT_PIXEL_NOTE_STYLE + '/intro$step'; - } + var path = noteStyle.getCountdownSoundPath(step); + if (path == null) return null; - trace('Resolved sound path: ' + soundPath); - return soundPath; + return FunkinSound.playOnce(path, Constants.COUNTDOWN_VOLUME); } public static function decrement(step:CountdownStep):CountdownStep diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 384c28056a..3fd68efe90 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -694,12 +694,7 @@ class PlayState extends MusicBeatSubState initMinimalMode(); } initStrumlines(); - - // Initialize the judgements and combo meter. - comboPopUps = new PopUpStuff(); - comboPopUps.zIndex = 900; - add(comboPopUps); - comboPopUps.cameras = [camHUD]; + initPopups(); #if discord_rpc // Initialize Discord Rich Presence. @@ -1727,6 +1722,21 @@ class PlayState extends MusicBeatSubState opponentStrumline.fadeInArrows(); } + /** + * Configures the judgement and combo popups. + */ + function initPopups():Void + { + var noteStyleId:String = currentChart.noteStyle; + var noteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyleId); + if (noteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); + // Initialize the judgements and combo meter. + comboPopUps = new PopUpStuff(noteStyle); + comboPopUps.zIndex = 900; + add(comboPopUps); + comboPopUps.cameras = [camHUD]; + } + /** * Initializes the Discord Rich Presence. */ @@ -3005,7 +3015,6 @@ class PlayState extends MusicBeatSubState GameOverSubState.reset(); PauseSubState.reset(); Countdown.reset(); - PopUpStuff.reset(); // Clear the static reference to this state. instance = null; diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index b9479ec8bd..911c3578c0 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -12,103 +12,47 @@ import openfl.utils.Assets; import funkin.data.notestyle.NoteStyleRegistry; import funkin.play.notes.notestyle.NoteStyle; +@:nullSafety class PopUpStuff extends FlxTypedGroup { - public var offsets:Array = [0, 0]; - /** - * Which alternate graphic on popup to use. - * This is set via the current notestyle. - * For example, in Week 6 it is `pixel`. + * The current note style to use. This determines which graphics to display. + * For example, Week 6 uses the `pixel` note style, and mods can create their own. */ - static var noteStyle:NoteStyle; - - static var fallbackNoteStyle:Null; + var noteStyle:NoteStyle; - static var isPixel:Bool = false; - - override public function new() + override public function new(noteStyle:NoteStyle) { super(); - } - - static function fetchNoteStyle():Void - { - var fetchedNoteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(PlayState.instance.currentChart.noteStyle); - if (fetchedNoteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault(); - else - noteStyle = fetchedNoteStyle; - fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); - isPixel = false; - } - - static function resolveGraphicPath(index:String):Null - { - fetchNoteStyle(); - var basePath:String = 'ui/popup/'; - var spritePath:String = basePath + noteStyle.id + '/$index'; - - while (!Assets.exists(Paths.image(spritePath)) && fallbackNoteStyle != null) - { - noteStyle = fallbackNoteStyle; - fallbackNoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyle.getFallbackID()); - spritePath = basePath + noteStyle.id + '/$index'; - } - if (noteStyle.isHoldNotePixel()) isPixel = true; - - // If nothing is found, revert it to default notestyle skin - if (!Assets.exists(Paths.image(spritePath))) - { - if (!isPixel) spritePath = basePath + Constants.DEFAULT_NOTE_STYLE + '/$index'; - else - spritePath = basePath + Constants.DEFAULT_PIXEL_NOTE_STYLE + '/$index'; - } - return spritePath; + this.noteStyle = noteStyle; } - public function displayRating(daRating:String) + public function displayRating(daRating:Null) { - var perfStart:Float = TimerUtil.start(); - if (daRating == null) daRating = "good"; - var ratingPath:String = resolveGraphicPath(daRating); + var rating:Null = noteStyle.buildJudgementSprite(daRating); + if (rating == null) return; + + rating.zIndex = 1000; - // if (PlayState.instance.currentStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel"; + rating.x = (FlxG.width * 0.474); + rating.x -= rating.width / 2; + rating.y = (FlxG.camera.height * 0.45 - 60); + rating.y -= rating.height / 2; - var rating:FunkinSprite = FunkinSprite.create(0, 0, ratingPath); - rating.scrollFactor.set(0.2, 0.2); + var offsets = noteStyle.getJudgementSpriteOffsets(daRating); + rating.x += offsets[0]; + rating.y += offsets[1]; - rating.zIndex = 1000; - rating.x = (FlxG.width * 0.474) + offsets[0]; - // rating.x -= FlxG.camera.scroll.x * 0.2; - rating.y = (FlxG.camera.height * 0.45 - 60) + offsets[1]; rating.acceleration.y = 550; rating.velocity.y -= FlxG.random.int(140, 175); rating.velocity.x -= FlxG.random.int(0, 10); add(rating); - var fadeEase = null; - - if (isPixel) - { - rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.7)); - rating.antialiasing = false; - rating.pixelPerfectRender = true; - rating.pixelPerfectPosition = true; - fadeEase = EaseUtil.stepped(2); - } - else - { - rating.setGraphicSize(Std.int(rating.width * 0.65)); - rating.antialiasing = true; - } - rating.updateHitbox(); - - rating.x -= rating.width / 2; - rating.y -= rating.height / 2; + var fadeEase = noteStyle.isJudgementSpritePixel(daRating) ? EaseUtil.stepped(2) : null; FlxTween.tween(rating, {alpha: 0}, 0.2, { @@ -119,55 +63,10 @@ class PopUpStuff extends FlxTypedGroup startDelay: Conductor.instance.beatLengthMs * 0.001, ease: fadeEase }); - - trace('displayRating took: ${TimerUtil.seconds(perfStart)}'); } - public function displayCombo(?combo:Int = 0):Int + public function displayCombo(combo:Int = 0):Void { - var perfStart:Float = TimerUtil.start(); - - if (combo == null) combo = 0; - - var comboPath:String = resolveGraphicPath('combo'); - var comboSpr:FunkinSprite = FunkinSprite.create(comboPath); - comboSpr.y = (FlxG.camera.height * 0.44) + offsets[1]; - comboSpr.x = (FlxG.width * 0.507) + offsets[0]; - // comboSpr.x -= FlxG.camera.scroll.x * 0.2; - - comboSpr.acceleration.y = 600; - comboSpr.velocity.y -= 150; - comboSpr.velocity.x += FlxG.random.int(1, 10); - - // add(comboSpr); - - var fadeEase = null; - - if (graphicSuffix.toLowerCase().contains('pixel')) - { - comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 1)); - comboSpr.antialiasing = false; - comboSpr.pixelPerfectRender = true; - comboSpr.pixelPerfectPosition = true; - fadeEase = EaseUtil.stepped(2); - } - else - { - comboSpr.setGraphicSize(Std.int(comboSpr.width * 0.7)); - comboSpr.antialiasing = true; - } - comboSpr.updateHitbox(); - - FlxTween.tween(comboSpr, {alpha: 0}, 0.2, - { - onComplete: function(tween:FlxTween) { - remove(comboSpr, true); - comboSpr.destroy(); - }, - startDelay: Conductor.instance.beatLengthMs * 0.001, - ease: fadeEase - }); - var seperatedScore:Array = []; var tempCombo:Int = combo; @@ -182,31 +81,27 @@ class PopUpStuff extends FlxTypedGroup // seperatedScore.reverse(); var daLoop:Int = 1; - for (i in seperatedScore) + for (digit in seperatedScore) { - var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, resolveGraphicPath('num' + Std.int(i))); + var numScore:Null = noteStyle.buildComboNumSprite(digit); + if (numScore == null) continue; - if (graphicSuffix.toLowerCase().contains('pixel')) - { - numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 1)); - numScore.antialiasing = false; - numScore.pixelPerfectRender = true; - numScore.pixelPerfectPosition = true; - } - else - { - numScore.setGraphicSize(Std.int(numScore.width * 0.45)); - numScore.antialiasing = true; - } - numScore.updateHitbox(); + numScore.x = (FlxG.width * 0.507) - (36 * daLoop) - 65; + trace('numScore($daLoop) = ${numScore.x}'); + numScore.y = (FlxG.camera.height * 0.44); + + var offsets = noteStyle.getComboNumSpriteOffsets(digit); + numScore.x += offsets[0]; + numScore.y += offsets[1]; - numScore.x = comboSpr.x - (36 * daLoop) - 65; //- 90; numScore.acceleration.y = FlxG.random.int(250, 300); numScore.velocity.y -= FlxG.random.int(130, 150); numScore.velocity.x = FlxG.random.float(-5, 5); add(numScore); + var fadeEase = noteStyle.isComboNumSpritePixel(digit) ? EaseUtil.stepped(2) : null; + FlxTween.tween(numScore, {alpha: 0}, 0.2, { onComplete: function(tween:FlxTween) { @@ -219,18 +114,5 @@ class PopUpStuff extends FlxTypedGroup daLoop++; } - - trace('displayCombo took: ${TimerUtil.seconds(perfStart)}'); - - return combo; - } - - /** - * Reset the popup configuration to the default. - */ - public static function reset() - { - noteStyle = NoteStyleRegistry.instance.fetchDefault(); - isPixel = false; } } diff --git a/source/funkin/play/notes/StrumlineNote.hx b/source/funkin/play/notes/StrumlineNote.hx index 40d893255e..d8230aa282 100644 --- a/source/funkin/play/notes/StrumlineNote.hx +++ b/source/funkin/play/notes/StrumlineNote.hx @@ -75,6 +75,13 @@ class StrumlineNote extends FlxSprite function setup(noteStyle:NoteStyle):Void { + if (noteStyle == null) + { + // If you get an exception on this line, check the debug console. + // You probably have a parsing error in your note style's JSON file. + throw "FATAL ERROR: Attempted to initialize PlayState with an invalid NoteStyle."; + } + noteStyle.applyStrumlineFrames(this); noteStyle.applyStrumlineAnimations(this, this.direction); diff --git a/source/funkin/play/notes/notestyle/NoteStyle.hx b/source/funkin/play/notes/notestyle/NoteStyle.hx index 5a877aa5be..ee07703f1f 100644 --- a/source/funkin/play/notes/notestyle/NoteStyle.hx +++ b/source/funkin/play/notes/notestyle/NoteStyle.hx @@ -1,5 +1,6 @@ package funkin.play.notes.notestyle; +import funkin.play.Countdown; import flixel.graphics.frames.FlxAtlasFrames; import flixel.graphics.frames.FlxFramesCollection; import funkin.data.animation.AnimationData; @@ -16,6 +17,7 @@ using funkin.data.animation.AnimationData.AnimationDataUtil; * Holds the data for what assets to use for a note style, * and provides convenience methods for building sprites based on them. */ +@:nullSafety class NoteStyle implements IRegistryEntry { /** @@ -42,12 +44,8 @@ class NoteStyle implements IRegistryEntry this.id = id; _data = _fetchData(id); - if (_data == null) - { - throw 'Could not parse note style data for id: $id'; - } - - this.fallback = NoteStyleRegistry.instance.fetchEntry(getFallbackID()); + var fallbackID = _data.fallback; + if (fallbackID != null) this.fallback = NoteStyleRegistry.instance.fetchEntry(fallbackID); } /** @@ -80,7 +78,7 @@ class NoteStyle implements IRegistryEntry public function buildNoteSprite(target:NoteSprite):Void { // Apply the note sprite frames. - var atlas:FlxAtlasFrames = buildNoteFrames(false); + var atlas:Null = buildNoteFrames(false); if (atlas == null) { @@ -89,7 +87,7 @@ class NoteStyle implements IRegistryEntry target.frames = atlas; - target.antialiasing = !_data.assets.note.isPixel; + target.antialiasing = !(_data.assets?.note?.isPixel ?? false); // Apply the animations. buildNoteAnimations(target); @@ -99,21 +97,30 @@ class NoteStyle implements IRegistryEntry target.updateHitbox(); } - var noteFrames:FlxAtlasFrames = null; + var noteFrames:Null = null; - function buildNoteFrames(force:Bool = false):FlxAtlasFrames + function buildNoteFrames(force:Bool = false):Null { - if (!FunkinSprite.isTextureCached(Paths.image(getNoteAssetPath()))) + var noteAssetPath = getNoteAssetPath(); + if (noteAssetPath == null) return null; + + if (!FunkinSprite.isTextureCached(Paths.image(noteAssetPath))) { - FlxG.log.warn('Note texture is not cached: ${getNoteAssetPath()}'); + FlxG.log.warn('Note texture is not cached: ${noteAssetPath}'); } // Purge the note frames if the cached atlas is invalid. - if (noteFrames?.parent?.isDestroyed ?? false) noteFrames = null; + @:nullSafety(Off) + { + if (noteFrames?.parent?.isDestroyed ?? false) noteFrames = null; + } if (noteFrames != null && !force) return noteFrames; - noteFrames = Paths.getSparrowAtlas(getNoteAssetPath(), getNoteAssetLibrary()); + var noteAssetPath = getNoteAssetPath(); + if (noteAssetPath == null) return null; + + noteFrames = Paths.getSparrowAtlas(noteAssetPath, getNoteAssetLibrary()); if (noteFrames == null) { @@ -123,17 +130,18 @@ class NoteStyle implements IRegistryEntry return noteFrames; } - function getNoteAssetPath(raw:Bool = false):String + function getNoteAssetPath(raw:Bool = false):Null { if (raw) { var rawPath:Null = _data?.assets?.note?.assetPath; - if (rawPath == null) return fallback.getNoteAssetPath(true); + if (rawPath == null && fallback != null) return fallback.getNoteAssetPath(true); return rawPath; } // library:path - var parts = getNoteAssetPath(true).split(Constants.LIBRARY_SEPARATOR); + var parts = getNoteAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length == 0) return null; if (parts.length == 1) return getNoteAssetPath(true); return parts[1]; } @@ -141,57 +149,63 @@ class NoteStyle implements IRegistryEntry function getNoteAssetLibrary():Null { // library:path - var parts = getNoteAssetPath(true).split(Constants.LIBRARY_SEPARATOR); + var parts = getNoteAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length == 0) return null; if (parts.length == 1) return null; return parts[0]; } function buildNoteAnimations(target:NoteSprite):Void { - var leftData:AnimationData = fetchNoteAnimationData(LEFT); - target.animation.addByPrefix('purpleScroll', leftData.prefix, leftData.frameRate, leftData.looped, leftData.flipX, leftData.flipY); - var downData:AnimationData = fetchNoteAnimationData(DOWN); - target.animation.addByPrefix('blueScroll', downData.prefix, downData.frameRate, downData.looped, downData.flipX, downData.flipY); - var upData:AnimationData = fetchNoteAnimationData(UP); - target.animation.addByPrefix('greenScroll', upData.prefix, upData.frameRate, upData.looped, upData.flipX, upData.flipY); - var rightData:AnimationData = fetchNoteAnimationData(RIGHT); - target.animation.addByPrefix('redScroll', rightData.prefix, rightData.frameRate, rightData.looped, rightData.flipX, rightData.flipY); + var leftData:Null = fetchNoteAnimationData(LEFT); + if (leftData != null) target.animation.addByPrefix('purpleScroll', leftData.prefix ?? '', leftData.frameRate ?? 24, leftData.looped ?? false, + leftData.flipX, leftData.flipY); + var downData:Null = fetchNoteAnimationData(DOWN); + if (downData != null) target.animation.addByPrefix('blueScroll', downData.prefix ?? '', downData.frameRate ?? 24, downData.looped ?? false, + downData.flipX, downData.flipY); + var upData:Null = fetchNoteAnimationData(UP); + if (upData != null) target.animation.addByPrefix('greenScroll', upData.prefix ?? '', upData.frameRate ?? 24, upData.looped ?? false, upData.flipX, + upData.flipY); + var rightData:Null = fetchNoteAnimationData(RIGHT); + if (rightData != null) target.animation.addByPrefix('redScroll', rightData.prefix ?? '', rightData.frameRate ?? 24, rightData.looped ?? false, + rightData.flipX, rightData.flipY); } public function isNoteAnimated():Bool { - return _data.assets.note.animated; + return _data.assets?.note?.animated ?? false; } public function getNoteScale():Float { - return _data.assets.note.scale; + return _data.assets?.note?.scale ?? 1.0; } - function fetchNoteAnimationData(dir:NoteDirection):AnimationData + function fetchNoteAnimationData(dir:NoteDirection):Null { var result:Null = switch (dir) { - case LEFT: _data.assets.note.data.left.toNamed(); - case DOWN: _data.assets.note.data.down.toNamed(); - case UP: _data.assets.note.data.up.toNamed(); - case RIGHT: _data.assets.note.data.right.toNamed(); + case LEFT: _data.assets?.note?.data?.left?.toNamed(); + case DOWN: _data.assets?.note?.data?.down?.toNamed(); + case UP: _data.assets?.note?.data?.up?.toNamed(); + case RIGHT: _data.assets?.note?.data?.right?.toNamed(); }; - return (result == null) ? fallback.fetchNoteAnimationData(dir) : result; + return (result == null && fallback != null) ? fallback.fetchNoteAnimationData(dir) : result; } - public function getHoldNoteAssetPath(raw:Bool = false):String + public function getHoldNoteAssetPath(raw:Bool = false):Null { if (raw) { // TODO: figure out why ?. didn't work here var rawPath:Null = (_data?.assets?.holdNote == null) ? null : _data?.assets?.holdNote?.assetPath; - return (rawPath == null) ? fallback.getHoldNoteAssetPath(true) : rawPath; + return (rawPath == null && fallback != null) ? fallback.getHoldNoteAssetPath(true) : rawPath; } // library:path - var parts = getHoldNoteAssetPath(true).split(Constants.LIBRARY_SEPARATOR); + var parts = getHoldNoteAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length == 0) return null; if (parts.length == 1) return Paths.image(parts[0]); return Paths.image(parts[1], parts[0]); } @@ -199,15 +213,15 @@ class NoteStyle implements IRegistryEntry public function isHoldNotePixel():Bool { var data = _data?.assets?.holdNote; - if (data == null) return fallback.isHoldNotePixel(); - return data.isPixel; + if (data == null && fallback != null) return fallback.isHoldNotePixel(); + return data?.isPixel ?? false; } public function fetchHoldNoteScale():Float { var data = _data?.assets?.holdNote; - if (data == null) return fallback.fetchHoldNoteScale(); - return data.scale; + if (data == null && fallback != null) return fallback.fetchHoldNoteScale(); + return data?.scale ?? 1.0; } public function applyStrumlineFrames(target:StrumlineNote):Void @@ -215,7 +229,7 @@ class NoteStyle implements IRegistryEntry // TODO: Add support for multi-Sparrow. // Will be less annoying after this is merged: https://github.com/HaxeFlixel/flixel/pull/2772 - var atlas:FlxAtlasFrames = Paths.getSparrowAtlas(getStrumlineAssetPath(), getStrumlineAssetLibrary()); + var atlas:FlxAtlasFrames = Paths.getSparrowAtlas(getStrumlineAssetPath() ?? '', getStrumlineAssetLibrary()); if (atlas == null) { @@ -224,31 +238,30 @@ class NoteStyle implements IRegistryEntry target.frames = atlas; - target.scale.x = _data.assets.noteStrumline.scale; - target.scale.y = _data.assets.noteStrumline.scale; - target.antialiasing = !_data.assets.noteStrumline.isPixel; + target.scale.set(_data.assets.noteStrumline?.scale ?? 1.0); + target.antialiasing = !(_data.assets.noteStrumline?.isPixel ?? false); } - function getStrumlineAssetPath(raw:Bool = false):String + function getStrumlineAssetPath(raw:Bool = false):Null { if (raw) { var rawPath:Null = _data?.assets?.noteStrumline?.assetPath; - if (rawPath == null) return fallback.getStrumlineAssetPath(true); + if (rawPath == null && fallback != null) return fallback.getStrumlineAssetPath(true); return rawPath; } // library:path - var parts = getStrumlineAssetPath(true).split(Constants.LIBRARY_SEPARATOR); - if (parts.length == 1) return getStrumlineAssetPath(true); + var parts = getStrumlineAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length <= 1) return getStrumlineAssetPath(true); return parts[1]; } function getStrumlineAssetLibrary():Null { // library:path - var parts = getStrumlineAssetPath(true).split(Constants.LIBRARY_SEPARATOR); - if (parts.length == 1) return null; + var parts = getStrumlineAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length <= 1) return null; return parts[0]; } @@ -259,60 +272,592 @@ class NoteStyle implements IRegistryEntry function getStrumlineAnimationData(dir:NoteDirection):Array { - var result:Array = switch (dir) + var result:Array> = switch (dir) { case NoteDirection.LEFT: [ - _data.assets.noteStrumline.data.leftStatic.toNamed('static'), - _data.assets.noteStrumline.data.leftPress.toNamed('press'), - _data.assets.noteStrumline.data.leftConfirm.toNamed('confirm'), - _data.assets.noteStrumline.data.leftConfirmHold.toNamed('confirm-hold'), + _data.assets.noteStrumline?.data?.leftStatic?.toNamed('static'), + _data.assets.noteStrumline?.data?.leftPress?.toNamed('press'), + _data.assets.noteStrumline?.data?.leftConfirm?.toNamed('confirm'), + _data.assets.noteStrumline?.data?.leftConfirmHold?.toNamed('confirm-hold'), ]; case NoteDirection.DOWN: [ - _data.assets.noteStrumline.data.downStatic.toNamed('static'), - _data.assets.noteStrumline.data.downPress.toNamed('press'), - _data.assets.noteStrumline.data.downConfirm.toNamed('confirm'), - _data.assets.noteStrumline.data.downConfirmHold.toNamed('confirm-hold'), + _data.assets.noteStrumline?.data?.downStatic?.toNamed('static'), + _data.assets.noteStrumline?.data?.downPress?.toNamed('press'), + _data.assets.noteStrumline?.data?.downConfirm?.toNamed('confirm'), + _data.assets.noteStrumline?.data?.downConfirmHold?.toNamed('confirm-hold'), ]; case NoteDirection.UP: [ - _data.assets.noteStrumline.data.upStatic.toNamed('static'), - _data.assets.noteStrumline.data.upPress.toNamed('press'), - _data.assets.noteStrumline.data.upConfirm.toNamed('confirm'), - _data.assets.noteStrumline.data.upConfirmHold.toNamed('confirm-hold'), + _data.assets.noteStrumline?.data?.upStatic?.toNamed('static'), + _data.assets.noteStrumline?.data?.upPress?.toNamed('press'), + _data.assets.noteStrumline?.data?.upConfirm?.toNamed('confirm'), + _data.assets.noteStrumline?.data?.upConfirmHold?.toNamed('confirm-hold'), ]; case NoteDirection.RIGHT: [ - _data.assets.noteStrumline.data.rightStatic.toNamed('static'), - _data.assets.noteStrumline.data.rightPress.toNamed('press'), - _data.assets.noteStrumline.data.rightConfirm.toNamed('confirm'), - _data.assets.noteStrumline.data.rightConfirmHold.toNamed('confirm-hold'), + _data.assets.noteStrumline?.data?.rightStatic?.toNamed('static'), + _data.assets.noteStrumline?.data?.rightPress?.toNamed('press'), + _data.assets.noteStrumline?.data?.rightConfirm?.toNamed('confirm'), + _data.assets.noteStrumline?.data?.rightConfirmHold?.toNamed('confirm-hold'), ]; + default: []; }; - return result; + return thx.Arrays.filterNull(result); } - public function applyStrumlineOffsets(target:StrumlineNote) + public function applyStrumlineOffsets(target:StrumlineNote):Void { - target.x += _data.assets.noteStrumline.offsets[0]; - target.y += _data.assets.noteStrumline.offsets[1]; + var offsets = _data?.assets?.noteStrumline?.offsets ?? [0.0, 0.0]; + target.x += offsets[0]; + target.y += offsets[1]; } public function getStrumlineScale():Float { - return _data.assets.noteStrumline.scale; + return _data?.assets?.noteStrumline?.scale ?? 1.0; } public function isNoteSplashEnabled():Bool { var data = _data?.assets?.noteSplash?.data; - if (data == null) return fallback.isNoteSplashEnabled(); - return data.enabled; + if (data == null) return fallback?.isNoteSplashEnabled() ?? false; + return data.enabled ?? false; } public function isHoldNoteCoverEnabled():Bool { var data = _data?.assets?.holdNoteCover?.data; - if (data == null) return fallback.isHoldNoteCoverEnabled(); - return data.enabled; + if (data == null) return fallback?.isHoldNoteCoverEnabled() ?? false; + return data.enabled ?? false; + } + + /** + * Build a sprite for the given step of the countdown. + * @param step + * @return A `FunkinSprite`, or `null` if no graphic is available for this step. + */ + public function buildCountdownSprite(step:Countdown.CountdownStep):Null + { + var result = new FunkinSprite(); + + switch (step) + { + case THREE: + if (_data.assets.countdownThree == null) return fallback?.buildCountdownSprite(step); + var assetPath = buildCountdownSpritePath(step); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.countdownThree?.scale ?? 1.0; + result.scale.y = _data.assets.countdownThree?.scale ?? 1.0; + case TWO: + if (_data.assets.countdownTwo == null) return fallback?.buildCountdownSprite(step); + var assetPath = buildCountdownSpritePath(step); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.countdownTwo?.scale ?? 1.0; + result.scale.y = _data.assets.countdownTwo?.scale ?? 1.0; + case ONE: + if (_data.assets.countdownOne == null) return fallback?.buildCountdownSprite(step); + var assetPath = buildCountdownSpritePath(step); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.countdownOne?.scale ?? 1.0; + result.scale.y = _data.assets.countdownOne?.scale ?? 1.0; + case GO: + if (_data.assets.countdownGo == null) return fallback?.buildCountdownSprite(step); + var assetPath = buildCountdownSpritePath(step); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.countdownGo?.scale ?? 1.0; + result.scale.y = _data.assets.countdownGo?.scale ?? 1.0; + default: + // TODO: Do something here? + return null; + } + + result.scrollFactor.set(0, 0); + result.antialiasing = !isCountdownSpritePixel(step); + result.updateHitbox(); + + return result; + } + + function buildCountdownSpritePath(step:Countdown.CountdownStep):Null + { + var basePath:Null = null; + switch (step) + { + case THREE: + basePath = _data.assets.countdownThree?.assetPath; + case TWO: + basePath = _data.assets.countdownTwo?.assetPath; + case ONE: + basePath = _data.assets.countdownOne?.assetPath; + case GO: + basePath = _data.assets.countdownGo?.assetPath; + default: + basePath = null; + } + + if (basePath == null) return fallback?.buildCountdownSpritePath(step); + + var parts = basePath?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length < 1) return null; + if (parts.length == 1) return parts[0]; + + return parts[1]; + } + + function buildCountdownSpriteLibrary(step:Countdown.CountdownStep):Null + { + var basePath:Null = null; + switch (step) + { + case THREE: + basePath = _data.assets.countdownThree?.assetPath; + case TWO: + basePath = _data.assets.countdownTwo?.assetPath; + case ONE: + basePath = _data.assets.countdownOne?.assetPath; + case GO: + basePath = _data.assets.countdownGo?.assetPath; + default: + basePath = null; + } + + if (basePath == null) return fallback?.buildCountdownSpriteLibrary(step); + + var parts = basePath?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length <= 1) return null; + + return parts[0]; + } + + public function isCountdownSpritePixel(step:Countdown.CountdownStep):Bool + { + switch (step) + { + case THREE: + var result = _data.assets.countdownThree?.isPixel; + if (result == null && fallback != null) result = fallback.isCountdownSpritePixel(step); + return result ?? false; + case TWO: + var result = _data.assets.countdownTwo?.isPixel; + if (result == null && fallback != null) result = fallback.isCountdownSpritePixel(step); + return result ?? false; + case ONE: + var result = _data.assets.countdownOne?.isPixel; + if (result == null && fallback != null) result = fallback.isCountdownSpritePixel(step); + return result ?? false; + case GO: + var result = _data.assets.countdownGo?.isPixel; + if (result == null && fallback != null) result = fallback.isCountdownSpritePixel(step); + return result ?? false; + default: + return false; + } + } + + public function getCountdownSpriteOffsets(step:Countdown.CountdownStep):Array + { + switch (step) + { + case THREE: + var result = _data.assets.countdownThree?.offsets; + if (result == null && fallback != null) result = fallback.getCountdownSpriteOffsets(step); + return result ?? [0, 0]; + case TWO: + var result = _data.assets.countdownTwo?.offsets; + if (result == null && fallback != null) result = fallback.getCountdownSpriteOffsets(step); + return result ?? [0, 0]; + case ONE: + var result = _data.assets.countdownOne?.offsets; + if (result == null && fallback != null) result = fallback.getCountdownSpriteOffsets(step); + return result ?? [0, 0]; + case GO: + var result = _data.assets.countdownGo?.offsets; + if (result == null && fallback != null) result = fallback.getCountdownSpriteOffsets(step); + return result ?? [0, 0]; + default: + return [0, 0]; + } + } + + public function getCountdownSoundPath(step:Countdown.CountdownStep, raw:Bool = false):Null + { + if (raw) + { + // TODO: figure out why ?. didn't work here + var rawPath:Null = switch (step) + { + case Countdown.CountdownStep.THREE: + _data.assets.countdownThree?.data?.audioPath; + case Countdown.CountdownStep.TWO: + _data.assets.countdownTwo?.data?.audioPath; + case Countdown.CountdownStep.ONE: + _data.assets.countdownOne?.data?.audioPath; + case Countdown.CountdownStep.GO: + _data.assets.countdownGo?.data?.audioPath; + default: + null; + } + + return (rawPath == null && fallback != null) ? fallback.getCountdownSoundPath(step, true) : rawPath; + } + + // library:path + var parts = getCountdownSoundPath(step, true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length == 0) return null; + if (parts.length == 1) return Paths.image(parts[0]); + return Paths.sound(parts[1], parts[0]); + } + + public function buildJudgementSprite(rating:String):Null + { + var result = new FunkinSprite(); + + switch (rating) + { + case "sick": + if (_data.assets.judgementSick == null) return fallback?.buildJudgementSprite(rating); + var assetPath = buildJudgementSpritePath(rating); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.judgementSick?.scale ?? 1.0; + result.scale.y = _data.assets.judgementSick?.scale ?? 1.0; + case "good": + if (_data.assets.judgementGood == null) return fallback?.buildJudgementSprite(rating); + var assetPath = buildJudgementSpritePath(rating); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.judgementGood?.scale ?? 1.0; + result.scale.y = _data.assets.judgementGood?.scale ?? 1.0; + case "bad": + if (_data.assets.judgementBad == null) return fallback?.buildJudgementSprite(rating); + var assetPath = buildJudgementSpritePath(rating); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.judgementBad?.scale ?? 1.0; + result.scale.y = _data.assets.judgementBad?.scale ?? 1.0; + case "shit": + if (_data.assets.judgementShit == null) return fallback?.buildJudgementSprite(rating); + var assetPath = buildJudgementSpritePath(rating); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.judgementShit?.scale ?? 1.0; + result.scale.y = _data.assets.judgementShit?.scale ?? 1.0; + default: + return null; + } + + result.scrollFactor.set(0.2, 0.2); + var isPixel = isJudgementSpritePixel(rating); + result.antialiasing = !isPixel; + result.pixelPerfectRender = isPixel; + result.pixelPerfectPosition = isPixel; + result.updateHitbox(); + + return result; + } + + public function isJudgementSpritePixel(rating:String):Bool + { + switch (rating) + { + case "sick": + var result = _data.assets.judgementSick?.isPixel; + if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating); + return result ?? false; + case "good": + var result = _data.assets.judgementGood?.isPixel; + if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating); + return result ?? false; + case "bad": + var result = _data.assets.judgementBad?.isPixel; + if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating); + return result ?? false; + case "GO": + var result = _data.assets.judgementShit?.isPixel; + if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating); + return result ?? false; + default: + return false; + } + } + + function buildJudgementSpritePath(rating:String):Null + { + var basePath:Null = null; + switch (rating) + { + case "sick": + basePath = _data.assets.judgementSick?.assetPath; + case "good": + basePath = _data.assets.judgementGood?.assetPath; + case "bad": + basePath = _data.assets.judgementBad?.assetPath; + case "shit": + basePath = _data.assets.judgementShit?.assetPath; + default: + basePath = null; + } + + if (basePath == null) return fallback?.buildJudgementSpritePath(rating); + + var parts = basePath?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length < 1) return null; + if (parts.length == 1) return parts[0]; + + return parts[1]; + } + + public function getJudgementSpriteOffsets(rating:String):Array + { + switch (rating) + { + case "sick": + var result = _data.assets.judgementSick?.offsets; + if (result == null && fallback != null) result = fallback.getJudgementSpriteOffsets(rating); + return result ?? [0, 0]; + case "good": + var result = _data.assets.judgementGood?.offsets; + if (result == null && fallback != null) result = fallback.getJudgementSpriteOffsets(rating); + return result ?? [0, 0]; + case "bad": + var result = _data.assets.judgementBad?.offsets; + if (result == null && fallback != null) result = fallback.getJudgementSpriteOffsets(rating); + return result ?? [0, 0]; + case "shit": + var result = _data.assets.judgementShit?.offsets; + if (result == null && fallback != null) result = fallback.getJudgementSpriteOffsets(rating); + return result ?? [0, 0]; + default: + return [0, 0]; + } + } + + public function buildComboNumSprite(digit:Int):Null + { + var result = new FunkinSprite(); + + switch (digit) + { + case 0: + if (_data.assets.comboNumber0 == null) return fallback?.buildComboNumSprite(digit); + var assetPath = buildComboNumSpritePath(digit); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.comboNumber0?.scale ?? 1.0; + result.scale.y = _data.assets.comboNumber0?.scale ?? 1.0; + case 1: + if (_data.assets.comboNumber1 == null) return fallback?.buildComboNumSprite(digit); + var assetPath = buildComboNumSpritePath(digit); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.comboNumber1?.scale ?? 1.0; + result.scale.y = _data.assets.comboNumber1?.scale ?? 1.0; + case 2: + if (_data.assets.comboNumber2 == null) return fallback?.buildComboNumSprite(digit); + var assetPath = buildComboNumSpritePath(digit); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.comboNumber2?.scale ?? 1.0; + result.scale.y = _data.assets.comboNumber2?.scale ?? 1.0; + case 3: + if (_data.assets.comboNumber3 == null) return fallback?.buildComboNumSprite(digit); + var assetPath = buildComboNumSpritePath(digit); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.comboNumber3?.scale ?? 1.0; + result.scale.y = _data.assets.comboNumber3?.scale ?? 1.0; + case 4: + if (_data.assets.comboNumber4 == null) return fallback?.buildComboNumSprite(digit); + var assetPath = buildComboNumSpritePath(digit); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.comboNumber4?.scale ?? 1.0; + result.scale.y = _data.assets.comboNumber4?.scale ?? 1.0; + case 5: + if (_data.assets.comboNumber5 == null) return fallback?.buildComboNumSprite(digit); + var assetPath = buildComboNumSpritePath(digit); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.comboNumber5?.scale ?? 1.0; + result.scale.y = _data.assets.comboNumber5?.scale ?? 1.0; + case 6: + if (_data.assets.comboNumber6 == null) return fallback?.buildComboNumSprite(digit); + var assetPath = buildComboNumSpritePath(digit); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.comboNumber6?.scale ?? 1.0; + result.scale.y = _data.assets.comboNumber6?.scale ?? 1.0; + case 7: + if (_data.assets.comboNumber7 == null) return fallback?.buildComboNumSprite(digit); + var assetPath = buildComboNumSpritePath(digit); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.comboNumber7?.scale ?? 1.0; + result.scale.y = _data.assets.comboNumber7?.scale ?? 1.0; + case 8: + if (_data.assets.comboNumber8 == null) return fallback?.buildComboNumSprite(digit); + var assetPath = buildComboNumSpritePath(digit); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.comboNumber8?.scale ?? 1.0; + result.scale.y = _data.assets.comboNumber8?.scale ?? 1.0; + case 9: + if (_data.assets.comboNumber9 == null) return fallback?.buildComboNumSprite(digit); + var assetPath = buildComboNumSpritePath(digit); + if (assetPath == null) return null; + result.loadTexture(assetPath); + result.scale.x = _data.assets.comboNumber9?.scale ?? 1.0; + result.scale.y = _data.assets.comboNumber9?.scale ?? 1.0; + default: + return null; + } + + var isPixel = isComboNumSpritePixel(digit); + result.antialiasing = !isPixel; + result.pixelPerfectRender = isPixel; + result.pixelPerfectPosition = isPixel; + result.updateHitbox(); + + return result; + } + + public function isComboNumSpritePixel(digit:Int):Bool + { + switch (digit) + { + case 0: + var result = _data.assets.comboNumber0?.isPixel; + if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit); + return result ?? false; + case 1: + var result = _data.assets.comboNumber1?.isPixel; + if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit); + return result ?? false; + case 2: + var result = _data.assets.comboNumber2?.isPixel; + if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit); + return result ?? false; + case 3: + var result = _data.assets.comboNumber3?.isPixel; + if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit); + return result ?? false; + case 4: + var result = _data.assets.comboNumber4?.isPixel; + if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit); + return result ?? false; + case 5: + var result = _data.assets.comboNumber5?.isPixel; + if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit); + return result ?? false; + case 6: + var result = _data.assets.comboNumber6?.isPixel; + if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit); + return result ?? false; + case 7: + var result = _data.assets.comboNumber7?.isPixel; + if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit); + return result ?? false; + case 8: + var result = _data.assets.comboNumber8?.isPixel; + if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit); + return result ?? false; + case 9: + var result = _data.assets.comboNumber9?.isPixel; + if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit); + return result ?? false; + default: + return false; + } + } + + function buildComboNumSpritePath(digit:Int):Null + { + var basePath:Null = null; + switch (digit) + { + case 0: + basePath = _data.assets.comboNumber0?.assetPath; + case 1: + basePath = _data.assets.comboNumber1?.assetPath; + case 2: + basePath = _data.assets.comboNumber2?.assetPath; + case 3: + basePath = _data.assets.comboNumber3?.assetPath; + case 4: + basePath = _data.assets.comboNumber4?.assetPath; + case 5: + basePath = _data.assets.comboNumber5?.assetPath; + case 6: + basePath = _data.assets.comboNumber6?.assetPath; + case 7: + basePath = _data.assets.comboNumber7?.assetPath; + case 8: + basePath = _data.assets.comboNumber8?.assetPath; + case 9: + basePath = _data.assets.comboNumber9?.assetPath; + default: + basePath = null; + } + + if (basePath == null) return fallback?.buildComboNumSpritePath(digit); + + var parts = basePath?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length < 1) return null; + if (parts.length == 1) return parts[0]; + + return parts[1]; + } + + public function getComboNumSpriteOffsets(digit:Int):Array + { + switch (digit) + { + case 0: + var result = _data.assets.comboNumber0?.offsets; + if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit); + return result ?? [0, 0]; + case 1: + var result = _data.assets.comboNumber1?.offsets; + if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit); + return result ?? [0, 0]; + case 2: + var result = _data.assets.comboNumber2?.offsets; + if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit); + return result ?? [0, 0]; + case 3: + var result = _data.assets.comboNumber3?.offsets; + if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit); + return result ?? [0, 0]; + case 4: + var result = _data.assets.comboNumber4?.offsets; + if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit); + return result ?? [0, 0]; + case 5: + var result = _data.assets.comboNumber5?.offsets; + if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit); + return result ?? [0, 0]; + case 6: + var result = _data.assets.comboNumber6?.offsets; + if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit); + return result ?? [0, 0]; + case 7: + var result = _data.assets.comboNumber7?.offsets; + if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit); + return result ?? [0, 0]; + case 8: + var result = _data.assets.comboNumber8?.offsets; + if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit); + return result ?? [0, 0]; + case 9: + var result = _data.assets.comboNumber9?.offsets; + if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit); + return result ?? [0, 0]; + default: + return [0, 0]; + } } public function destroy():Void {} @@ -322,8 +867,17 @@ class NoteStyle implements IRegistryEntry return 'NoteStyle($id)'; } - static function _fetchData(id:String):Null + static function _fetchData(id:String):NoteStyleData { - return NoteStyleRegistry.instance.parseEntryDataWithMigration(id, NoteStyleRegistry.instance.fetchEntryVersion(id)); + var result = NoteStyleRegistry.instance.parseEntryDataWithMigration(id, NoteStyleRegistry.instance.fetchEntryVersion(id)); + + if (result == null) + { + throw 'Could not parse note style data for id: $id'; + } + else + { + return result; + } } } diff --git a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx index 1e631f2cf4..ff8446c499 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorHoldNoteSprite.hx @@ -60,7 +60,9 @@ class ChartEditorHoldNoteSprite extends SustainTrail override function setupHoldNoteGraphic(noteStyle:NoteStyle):Void { - loadGraphic(noteStyle.getHoldNoteAssetPath()); + var graphicPath = noteStyle.getHoldNoteAssetPath(); + if (graphicPath == null) return; + loadGraphic(graphicPath); antialiasing = true; From 195010c7edf31944e40ad790d44b2c987dd931cd Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 18 Jul 2024 17:45:48 -0400 Subject: [PATCH 237/469] hxcpp and lime hmm updates for arm64 mac --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index fabd8b51dc..60a35d4fe5 100644 --- a/hmm.json +++ b/hmm.json @@ -141,7 +141,7 @@ "name": "lime", "type": "git", "dir": null, - "ref": "872ff6db2f2d27c0243d4ff76802121ded550dd7", + "ref": "ede0005c3aedcbfea7b6247bfc4972ecf7f78a14", "url": "https://github.com/FunkinCrew/lime" }, { From 5e04628480b53b3d1c5b7386826ddb7a36e2ad93 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sun, 21 Jul 2024 19:02:58 -0400 Subject: [PATCH 238/469] lime shallow cairo --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index 60a35d4fe5..ba5274f0d1 100644 --- a/hmm.json +++ b/hmm.json @@ -141,7 +141,7 @@ "name": "lime", "type": "git", "dir": null, - "ref": "ede0005c3aedcbfea7b6247bfc4972ecf7f78a14", + "ref": "916da928134edc57db44f604036ae9bec828f340", "url": "https://github.com/FunkinCrew/lime" }, { From e4e4c66c6c06173b187109053769fd4ad4f08758 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 25 Jul 2024 08:33:01 -0400 Subject: [PATCH 239/469] Update Lime version --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index ba5274f0d1..5254fe5ddb 100644 --- a/hmm.json +++ b/hmm.json @@ -141,7 +141,7 @@ "name": "lime", "type": "git", "dir": null, - "ref": "916da928134edc57db44f604036ae9bec828f340", + "ref": "a393a54b6120bfa9a2f1db5a16fbf1137d41f257", "url": "https://github.com/FunkinCrew/lime" }, { From 68b7610225aa4fd382d53950d0ae889333d9e752 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 29 Jul 2024 17:26:49 -0400 Subject: [PATCH 240/469] Update OpenFL to latest (requires a few fixes) --- hmm.json | 14 ++++++++------ source/funkin/audio/visualize/ABotVis.hx | 2 +- source/funkin/audio/visualize/VisShit.hx | 2 +- source/funkin/audio/waveform/WaveformDataParser.hx | 2 +- .../toolboxes/ChartEditorNoteDataToolbox.hx | 4 ++-- source/funkin/ui/freeplay/FreeplayState.hx | 2 +- source/funkin/ui/story/LevelProp.hx | 2 +- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/hmm.json b/hmm.json index 5254fe5ddb..e9aa678eff 100644 --- a/hmm.json +++ b/hmm.json @@ -23,8 +23,10 @@ }, { "name": "flixel-text-input", - "type": "haxelib", - "version": "1.1.0" + "type": "git", + "dir": null, + "ref": "951a0103a17bfa55eed86703ce50b4fb0d7590bc", + "url": "https://github.com/FunkinCrew/flixel-text-input" }, { "name": "flixel-ui", @@ -75,14 +77,14 @@ "name": "haxeui-core", "type": "git", "dir": null, - "ref": "5dc4c933bdc029f6139a47962e3b8c754060f210", + "ref": "22f7c5a8ffca90d4677cffd6e570f53761709fbc", "url": "https://github.com/haxeui/haxeui-core" }, { "name": "haxeui-flixel", "type": "git", "dir": null, - "ref": "57c1604d6b5174839d7e0e012a4dd5dcbfc129da", + "ref": "28bb710d0ae5d94b5108787593052165be43b980", "url": "https://github.com/haxeui/haxeui-flixel" }, { @@ -141,7 +143,7 @@ "name": "lime", "type": "git", "dir": null, - "ref": "a393a54b6120bfa9a2f1db5a16fbf1137d41f257", + "ref": "f6153ffcb1ffcf733f91d531eac5fda4189e07f7", "url": "https://github.com/FunkinCrew/lime" }, { @@ -190,7 +192,7 @@ "name": "thx.core", "type": "git", "dir": null, - "ref": "6240b6e136f7490d9298edbe8c1891374bd7cdf2", + "ref": "76d87418fadd92eb8e1b61f004cff27d656e53dd", "url": "https://github.com/fponticelli/thx.core" }, { diff --git a/source/funkin/audio/visualize/ABotVis.hx b/source/funkin/audio/visualize/ABotVis.hx index cf43a8add8..a6ad0570eb 100644 --- a/source/funkin/audio/visualize/ABotVis.hx +++ b/source/funkin/audio/visualize/ABotVis.hx @@ -54,7 +54,7 @@ class ABotVis extends FlxTypedSpriteGroup public function initAnalyzer() { @:privateAccess - analyzer = new SpectralAnalyzer(snd._channel.__source, 7, 0.1, 40); + analyzer = new SpectralAnalyzer(snd._channel.__audioSource, 7, 0.1, 40); #if desktop // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 diff --git a/source/funkin/audio/visualize/VisShit.hx b/source/funkin/audio/visualize/VisShit.hx index ba235fe890..83b9496aca 100644 --- a/source/funkin/audio/visualize/VisShit.hx +++ b/source/funkin/audio/visualize/VisShit.hx @@ -117,7 +117,7 @@ class VisShit { // Math.pow3 @:privateAccess - var buf = snd._channel.__source.buffer; + var buf = snd._channel.__audioSource.buffer; // @:privateAccess audioData = cast buf.data; // jank and hacky lol! kinda busted on HTML5 also!! diff --git a/source/funkin/audio/waveform/WaveformDataParser.hx b/source/funkin/audio/waveform/WaveformDataParser.hx index 5aa54d744e..ca421581b7 100644 --- a/source/funkin/audio/waveform/WaveformDataParser.hx +++ b/source/funkin/audio/waveform/WaveformDataParser.hx @@ -16,7 +16,7 @@ class WaveformDataParser // Method 1. This only works if the sound has been played before. @:privateAccess - var soundBuffer:Null = sound?._channel?.__source?.buffer; + var soundBuffer:Null = sound?._channel?.__audioSource?.buffer; if (soundBuffer == null) { diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx index 12f7f7d631..100654a025 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorNoteDataToolbox.hx @@ -192,7 +192,7 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox var paramStepper:NumberStepper = new NumberStepper(); paramStepper.value = (setParamsToPlace ? chartEditorState.noteParamsToPlace[i].value : param.data?.defaultValue) ?? 0.0; paramStepper.percentWidth = 100; - paramStepper.step = param.data?.step ?? 1; + paramStepper.step = param.data?.step ?? 1.0; // this check should be unnecessary but for some reason // even when these are null it will set it to 0 @@ -283,7 +283,7 @@ class ChartEditorNoteDataToolbox extends ChartEditorBaseToolbox return; } - var heightToSet:Int = Std.int(Math.max(DIALOG_HEIGHT, (toolboxNotesGrid?.height ?? 50) + HEIGHT_OFFSET)) + MINIMIZE_FIX; + var heightToSet:Int = Std.int(Math.max(DIALOG_HEIGHT, (toolboxNotesGrid?.height ?? 50.0) + HEIGHT_OFFSET)) + MINIMIZE_FIX; if (this.height != heightToSet) { this.height = heightToSet; diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 2341f04a6e..690e3b910e 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1527,7 +1527,7 @@ class FreeplayState extends MusicBeatSubState var moveDataX = funnyMoveShit.x ?? spr.x; var moveDataY = funnyMoveShit.y ?? spr.y; var moveDataSpeed = funnyMoveShit.speed ?? 0.2; - var moveDataWait = funnyMoveShit.wait ?? 0; + var moveDataWait = funnyMoveShit.wait ?? 0.0; FlxTween.tween(spr, {x: moveDataX, y: moveDataY}, moveDataSpeed, {ease: FlxEase.expoIn}); diff --git a/source/funkin/ui/story/LevelProp.hx b/source/funkin/ui/story/LevelProp.hx index 0547404a1a..4e78415e36 100644 --- a/source/funkin/ui/story/LevelProp.hx +++ b/source/funkin/ui/story/LevelProp.hx @@ -16,7 +16,7 @@ class LevelProp extends Bopper this.propData = value; this.visible = this.propData != null; - danceEvery = this.propData?.danceEvery ?? 0; + danceEvery = this.propData?.danceEvery ?? 0.0; applyData(); } From bef2e9822244687b9bb85a57645ff699a8ed5df3 Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Wed, 17 Jul 2024 18:00:07 +0300 Subject: [PATCH 241/469] [BUGFIX] Ensure the variation used for the next song is valid. When playing in story mode, `PlayState` assumes all the songs use the same variation, which is usually true, but not guaranteed. This PR makes `PlayState` use the `getFirstValidVariation` method to find the valid variation instead of using the `currentVariation` variable, which might not be valid. --- source/funkin/play/PlayState.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 450f133008..5bb0decf54 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2912,7 +2912,7 @@ class PlayState extends MusicBeatSubState { targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty, - targetVariation: currentVariation, + targetVariation: targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty), cameraFollowPoint: cameraFollowPoint.getPosition(), }); }); @@ -2924,7 +2924,7 @@ class PlayState extends MusicBeatSubState { targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty, - targetVariation: currentVariation, + targetVariation: targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty), cameraFollowPoint: cameraFollowPoint.getPosition(), }); } From eb78b754f8464b9ac423191f36236c3a2d89841b Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Tue, 23 Jul 2024 11:34:26 +0300 Subject: [PATCH 242/469] A fix requested by Eric --- source/funkin/play/PlayState.hx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 5bb0decf54..da066dd6ad 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2908,11 +2908,16 @@ class PlayState extends MusicBeatSubState FunkinSound.playOnce(Paths.sound('Lights_Shut_off'), function() { // no camFollow so it centers on horror tree var targetSong:Song = SongRegistry.instance.fetchEntry(targetSongId); + var targetVariation:String = currentVariation; + if (!targetSong.hasDifficulty(PlayStatePlaylist.campaignDifficulty, currentVariation)) + { + targetVariation = targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty) ?? Constants.DEFAULT_VARIATION; + } LoadingState.loadPlayState( { targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty, - targetVariation: targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty), + targetVariation: targetVariation, cameraFollowPoint: cameraFollowPoint.getPosition(), }); }); @@ -2920,11 +2925,16 @@ class PlayState extends MusicBeatSubState else { var targetSong:Song = SongRegistry.instance.fetchEntry(targetSongId); + var targetVariation:String = currentVariation; + if (!targetSong.hasDifficulty(PlayStatePlaylist.campaignDifficulty, currentVariation)) + { + targetVariation = targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty) ?? Constants.DEFAULT_VARIATION; + } LoadingState.loadPlayState( { targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty, - targetVariation: targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty), + targetVariation: targetVariation, cameraFollowPoint: cameraFollowPoint.getPosition(), }); } From e2297af1cd019b704f7759a84f6542dce67a6289 Mon Sep 17 00:00:00 2001 From: 7oltan <87986834+7oltan@users.noreply.github.com> Date: Tue, 16 Jul 2024 12:52:51 +0300 Subject: [PATCH 243/469] FIX CHARACTER PATHS ISSUE --- source/funkin/play/character/AnimateAtlasCharacter.hx | 2 +- source/funkin/play/character/MultiSparrowCharacter.hx | 4 ++-- source/funkin/play/character/PackerCharacter.hx | 2 +- source/funkin/play/character/SparrowCharacter.hx | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/source/funkin/play/character/AnimateAtlasCharacter.hx b/source/funkin/play/character/AnimateAtlasCharacter.hx index ed58b92b55..5b126db6c6 100644 --- a/source/funkin/play/character/AnimateAtlasCharacter.hx +++ b/source/funkin/play/character/AnimateAtlasCharacter.hx @@ -131,7 +131,7 @@ class AnimateAtlasCharacter extends BaseCharacter { trace('[ATLASCHAR] Loading sprite atlas for ${characterId}.'); - var sprite:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas(_data.assetPath, 'shared')); + var sprite:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas(_data.assetPath)); sprite.onAnimationFinish.removeAll(); sprite.onAnimationFinish.add(this.onAnimationFinished); diff --git a/source/funkin/play/character/MultiSparrowCharacter.hx b/source/funkin/play/character/MultiSparrowCharacter.hx index 41c96fbfaa..e23afe18c5 100644 --- a/source/funkin/play/character/MultiSparrowCharacter.hx +++ b/source/funkin/play/character/MultiSparrowCharacter.hx @@ -62,7 +62,7 @@ class MultiSparrowCharacter extends BaseCharacter } } - var texture:FlxAtlasFrames = Paths.getSparrowAtlas(_data.assetPath, 'shared'); + var texture:FlxAtlasFrames = Paths.getSparrowAtlas(_data.assetPath); if (texture == null) { @@ -76,7 +76,7 @@ class MultiSparrowCharacter extends BaseCharacter for (asset in assetList) { - var subTexture:FlxAtlasFrames = Paths.getSparrowAtlas(asset, 'shared'); + var subTexture:FlxAtlasFrames = Paths.getSparrowAtlas(asset); // If we don't do this, the unused textures will be removed as soon as they're loaded. if (subTexture == null) diff --git a/source/funkin/play/character/PackerCharacter.hx b/source/funkin/play/character/PackerCharacter.hx index 22edbe339d..5d004606ca 100644 --- a/source/funkin/play/character/PackerCharacter.hx +++ b/source/funkin/play/character/PackerCharacter.hx @@ -30,7 +30,7 @@ class PackerCharacter extends BaseCharacter { trace('[PACKERCHAR] Loading spritesheet ${_data.assetPath} for ${characterId}'); - var tex:FlxFramesCollection = Paths.getPackerAtlas(_data.assetPath, 'shared'); + var tex:FlxFramesCollection = Paths.getPackerAtlas(_data.assetPath); if (tex == null) { trace('Could not load Packer sprite: ${_data.assetPath}'); diff --git a/source/funkin/play/character/SparrowCharacter.hx b/source/funkin/play/character/SparrowCharacter.hx index 81d98b138b..eacf799d8a 100644 --- a/source/funkin/play/character/SparrowCharacter.hx +++ b/source/funkin/play/character/SparrowCharacter.hx @@ -33,7 +33,7 @@ class SparrowCharacter extends BaseCharacter { trace('[SPARROWCHAR] Loading spritesheet ${_data.assetPath} for ${characterId}'); - var tex:FlxFramesCollection = Paths.getSparrowAtlas(_data.assetPath, 'shared'); + var tex:FlxFramesCollection = Paths.getSparrowAtlas(_data.assetPath); if (tex == null) { trace('Could not load Sparrow sprite: ${_data.assetPath}'); From 4f46a8e4d06207f2c12ade56cc54aeaa7136e84e Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:17:53 +0300 Subject: [PATCH 244/469] [BUGFIX] Prevented infinite recursion on freeplay when a song doesn't have the `normal` difficulty Any attempt to create a song without the `normal` difficulty will result in a silent crash when opening freeplay. After some debugging, I discovered that the issue is caused by infinite recursion, which gets triggered at the start of `FreeplaySongData`'s `updateValues`. --- source/funkin/ui/freeplay/FreeplayState.hx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 690e3b910e..a416dbf194 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -2150,8 +2150,11 @@ class FreeplaySongData function updateValues(variations:Array):Void { - this.songDifficulties = song.listSuffixedDifficulties(variations, false, false); - if (!this.songDifficulties.contains(currentDifficulty)) currentDifficulty = Constants.DEFAULT_DIFFICULTY; + this.songDifficulties = song.listDifficulties(null, variations, false, false); + if (!this.songDifficulties.contains(currentDifficulty) && currentDifficulty != Constants.DEFAULT_DIFFICULTY) + { + currentDifficulty = Constants.DEFAULT_DIFFICULTY; + } var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, null, variations); if (songDifficulty == null) return; From c8322c479ced92c8bd737ecf5d64b666958925f3 Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:36:11 +0300 Subject: [PATCH 245/469] Added a comment --- source/funkin/ui/freeplay/FreeplayState.hx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index a416dbf194..47f55f6860 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -2154,6 +2154,9 @@ class FreeplaySongData if (!this.songDifficulties.contains(currentDifficulty) && currentDifficulty != Constants.DEFAULT_DIFFICULTY) { currentDifficulty = Constants.DEFAULT_DIFFICULTY; + // This method gets called again by the setter-method, + // so there's no need to continue. + return; } var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, null, variations); From cb02ea7da5c662c6befac025510c412a329d939f Mon Sep 17 00:00:00 2001 From: AppleHair <95587502+AppleHair@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:20:48 +0300 Subject: [PATCH 246/469] Updated to extend #2712 --- source/funkin/ui/freeplay/FreeplayState.hx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 47f55f6860..dedf284ee3 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -2151,11 +2151,11 @@ class FreeplaySongData function updateValues(variations:Array):Void { this.songDifficulties = song.listDifficulties(null, variations, false, false); - if (!this.songDifficulties.contains(currentDifficulty) && currentDifficulty != Constants.DEFAULT_DIFFICULTY) + if (!this.songDifficulties.contains(currentDifficulty)) { currentDifficulty = Constants.DEFAULT_DIFFICULTY; - // This method gets called again by the setter-method, - // so there's no need to continue. + // This method gets called again by the setter-method + // or the difficulty didn't change, so there's no need to continue. return; } From c9c81fae51e5f2a23ee7fc1ac0cfd499e95b1b9a Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 29 Jul 2024 21:26:02 -0400 Subject: [PATCH 247/469] assets submod? --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 005c96f85f..aa1231e8cf 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 005c96f85f4304865acb196e7cc4d6d83f9d76d8 +Subproject commit aa1231e8cf2990bb902eac3b37815c010fa9919a From 3b7e65679357687bd6901f45c120de06970d46f8 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Wed, 31 Jul 2024 14:36:06 +0100 Subject: [PATCH 248/469] add stage names to registry + chart editor --- source/funkin/data/stage/StageRegistry.hx | 4 ++-- source/funkin/ui/debug/charting/ChartEditorState.hx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/funkin/data/stage/StageRegistry.hx b/source/funkin/data/stage/StageRegistry.hx index fbb6f188e3..1f05042479 100644 --- a/source/funkin/data/stage/StageRegistry.hx +++ b/source/funkin/data/stage/StageRegistry.hx @@ -93,8 +93,8 @@ class StageRegistry extends BaseRegistry public function listBaseGameStageIds():Array { return [ - "mainStage", "spookyMansion", "phillyTrain", "phillyTrainErect", "limoRide", "mallXmas", "mallEvil", "school", "schoolEvil", "tankmanBattlefield", - "phillyStreets", "phillyBlazin", + "mainStage", "spookyMansion", "phillyTrain", "phillyTrainErect", "limoRide", "limoRideErect", "mallXmas", "mallEvil", "school", "schoolEvil", + "tankmanBattlefield", "phillyStreets", "phillyBlazin", ]; } diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index a4e0de61eb..24d290abd7 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -5707,7 +5707,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState PlayStatePlaylist.campaignId = 'week2'; case 'phillyTrain' | 'phillyTrainErect': PlayStatePlaylist.campaignId = 'week3'; - case 'limoRide': + case 'limoRide' | 'limoRideErect': PlayStatePlaylist.campaignId = 'week4'; case 'mallXmas' | 'mallEvil': PlayStatePlaylist.campaignId = 'week5'; From 270748d1081a947c0c84ecd718e65ccb34ba6911 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Wed, 31 Jul 2024 14:36:24 +0100 Subject: [PATCH 249/469] fuck these decimals messing up my compiling --- source/funkin/ui/freeplay/FreeplayState.hx | 2 +- source/funkin/ui/story/LevelProp.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index dc42bd6510..fa895042f4 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1527,7 +1527,7 @@ class FreeplayState extends MusicBeatSubState var moveDataX = funnyMoveShit.x ?? spr.x; var moveDataY = funnyMoveShit.y ?? spr.y; var moveDataSpeed = funnyMoveShit.speed ?? 0.2; - var moveDataWait = funnyMoveShit.wait ?? 0; + var moveDataWait = funnyMoveShit.wait ?? 0.0; FlxTween.tween(spr, {x: moveDataX, y: moveDataY}, moveDataSpeed, {ease: FlxEase.expoIn}); diff --git a/source/funkin/ui/story/LevelProp.hx b/source/funkin/ui/story/LevelProp.hx index 0547404a1a..4e78415e36 100644 --- a/source/funkin/ui/story/LevelProp.hx +++ b/source/funkin/ui/story/LevelProp.hx @@ -16,7 +16,7 @@ class LevelProp extends Bopper this.propData = value; this.visible = this.propData != null; - danceEvery = this.propData?.danceEvery ?? 0; + danceEvery = this.propData?.danceEvery ?? 0.0; applyData(); } From 8200a08152ceea85cd242b990518007af5d4806f Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Wed, 31 Jul 2024 14:36:34 +0100 Subject: [PATCH 250/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index cfd67caa68..06067d187e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit cfd67caa688465b4a282837434832c107b661b04 +Subproject commit 06067d187e7699a8eec42ab07c53d195c589a690 From f4968b0ae5211e70f99d68f8de618553c8eb9a35 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 5 Aug 2024 10:47:59 -0400 Subject: [PATCH 251/469] openfl hmm commit to dev-funkin --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index e9aa678eff..c72b4240a9 100644 --- a/hmm.json +++ b/hmm.json @@ -178,7 +178,7 @@ "name": "openfl", "type": "git", "dir": null, - "ref": "228c1b5063911e2ad75cef6e3168ef0a4b9f9134", + "ref": "8306425c497766739510ab29e876059c96f77bd2", "url": "https://github.com/FunkinCrew/openfl" }, { From 62d0b33689f3cae21c32778e3428b93a7a50c60c Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 6 Aug 2024 13:44:02 -0400 Subject: [PATCH 252/469] Fix for switching to old icon on isPixel levels --- source/funkin/play/components/HealthIcon.hx | 54 ++++++++++++++------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/source/funkin/play/components/HealthIcon.hx b/source/funkin/play/components/HealthIcon.hx index 2442b0dc57..0e24d73fb2 100644 --- a/source/funkin/play/components/HealthIcon.hx +++ b/source/funkin/play/components/HealthIcon.hx @@ -150,13 +150,17 @@ class HealthIcon extends FunkinSprite { if (characterId == 'bf-old') { + isPixel = PlayState.instance.currentStage.getBoyfriend().isPixel; PlayState.instance.currentStage.getBoyfriend().initHealthIcon(false); } else { characterId = 'bf-old'; + isPixel = false; loadCharacter(characterId); } + + lerpIconSize(true); } /** @@ -200,31 +204,45 @@ class HealthIcon extends FunkinSprite if (bopEvery != 0) { - // Lerp the health icon back to its normal size, - // while maintaining aspect ratio. - if (this.width > this.height) - { - // Apply linear interpolation while accounting for frame rate. - var targetSize:Int = Std.int(MathUtil.coolLerp(this.width, HEALTH_ICON_SIZE * this.size.x, 0.15)); - - setGraphicSize(targetSize, 0); - } - else - { - var targetSize:Int = Std.int(MathUtil.coolLerp(this.height, HEALTH_ICON_SIZE * this.size.y, 0.15)); - - setGraphicSize(0, targetSize); - } + lerpIconSize(); // Lerp the health icon back to its normal angle. this.angle = MathUtil.coolLerp(this.angle, 0, 0.15); - - this.updateHitbox(); } this.updatePosition(); } + /** + * Does the calculation to lerp the icon size. Usually called every frame, but can be forced to the target size. + * Mainly forced when changing to old icon to not have a weird lerp related to changing from pixel icon to non-pixel old icon + * @param force Force the icon immedialtely to be the target size. Defaults to false. + */ + function lerpIconSize(force:Bool = false):Void + { + // Lerp the health icon back to its normal size, + // while maintaining aspect ratio. + if (this.width > this.height) + { + // Apply linear interpolation while accounting for frame rate. + var targetSize:Int = Std.int(MathUtil.coolLerp(this.width, HEALTH_ICON_SIZE * this.size.x, 0.15)); + + if (force) targetSize = Std.int(HEALTH_ICON_SIZE * this.size.x); + + setGraphicSize(targetSize, 0); + } + else + { + var targetSize:Int = Std.int(MathUtil.coolLerp(this.height, HEALTH_ICON_SIZE * this.size.y, 0.15)); + + if (force) targetSize = Std.int(HEALTH_ICON_SIZE * this.size.y); + + setGraphicSize(0, targetSize); + } + + this.updateHitbox(); + } + /** * Update the position (and status) of the health icon. */ @@ -412,6 +430,8 @@ class HealthIcon extends FunkinSprite isLegacyStyle = !isNewSpritesheet(charId); + trace(' Loading health icon for character: $charId (legacy: $isLegacyStyle)'); + if (!isLegacyStyle) { loadSparrow('icons/icon-$charId'); From ae648418c2e860dc33a9b61d4be611719209fb53 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 6 Aug 2024 14:03:25 -0400 Subject: [PATCH 253/469] assets submod senpai fix --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index aa1231e8cf..a659c2a58d 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit aa1231e8cf2990bb902eac3b37815c010fa9919a +Subproject commit a659c2a58d2d8b8036102e563a9c620a590da50a From c3d497a9d907b38cd11d1fa15f2d9a85bf82db96 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 7 Aug 2024 19:01:46 -0400 Subject: [PATCH 254/469] hxcpp bump --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index c72b4240a9..aad0be8cff 100644 --- a/hmm.json +++ b/hmm.json @@ -104,7 +104,7 @@ "type": "git", "dir": null, "url": "https://github.com/HaxeFoundation/hxcpp", - "ref": "01cfee282a9a783e10c5a7774a3baaf547e6b0a7" + "ref": "8dc8020f8465027de6c2aaaed90718bc693651ed" }, { "name": "hxcpp-debug-server", From 9ff3e0435ad7b51ef88ceabff8909b4f2845f18e Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 9 Aug 2024 23:02:46 -0400 Subject: [PATCH 255/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index a659c2a58d..62c4a82033 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit a659c2a58d2d8b8036102e563a9c620a590da50a +Subproject commit 62c4a8203362c1b434de0d376046ebccb96da254 From b47c695cb7acbe032dc717017332f0395dd92656 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 9 Aug 2024 23:09:23 -0400 Subject: [PATCH 256/469] perhaps use funkin versions of haxelib and hmm? --- .github/actions/setup-haxe/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml index 5a9f7b293a..701a023f97 100644 --- a/.github/actions/setup-haxe/action.yml +++ b/.github/actions/setup-haxe/action.yml @@ -60,8 +60,8 @@ runs: haxelib --debug --never deleterepo || true haxelib --debug --never newrepo echo "HAXEPATH=$(haxelib config)" >> "$GITHUB_ENV" - haxelib --debug --never git haxelib https://github.com/HaxeFoundation/haxelib.git master - haxelib --debug --global install hmm + haxelib --debug --never git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies + haxelib --debug --global git hmm https://github.com/FunkinCrew/hmm funkin-patches echo "TIMER_DEPS=$(date +%s)" >> "$GITHUB_ENV" - name: Restore cached dependencies From a0047a482fd7263ba1b015c4a015054f97b52d7b Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 9 Aug 2024 23:31:40 -0400 Subject: [PATCH 257/469] use --always when installing funkin-patches versions of haxelib and hmm --- .github/actions/setup-haxe/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml index 701a023f97..b22fc6f693 100644 --- a/.github/actions/setup-haxe/action.yml +++ b/.github/actions/setup-haxe/action.yml @@ -60,8 +60,8 @@ runs: haxelib --debug --never deleterepo || true haxelib --debug --never newrepo echo "HAXEPATH=$(haxelib config)" >> "$GITHUB_ENV" - haxelib --debug --never git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies - haxelib --debug --global git hmm https://github.com/FunkinCrew/hmm funkin-patches + haxelib --debug --always --global git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies + haxelib --debug --global --always git hmm https://github.com/FunkinCrew/hmm funkin-patches echo "TIMER_DEPS=$(date +%s)" >> "$GITHUB_ENV" - name: Restore cached dependencies From c209e0e192bd368685782bc0ddaac366db000767 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 14 Aug 2024 10:57:44 -0400 Subject: [PATCH 258/469] Ugh Erect and Cocoa Erect --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 62c4a82033..f4b0954f19 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 62c4a8203362c1b434de0d376046ebccb96da254 +Subproject commit f4b0954f1951563a09d5c74fc3a1a90959f1ee00 From 988e819e9bd2af12bbdd3df60983382afba78b6c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 14 Aug 2024 15:10:47 -0400 Subject: [PATCH 259/469] Add a bunch of remixes --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 62c4a82033..05f8c98761 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 62c4a8203362c1b434de0d376046ebccb96da254 +Subproject commit 05f8c987612ab7185ce6b266d9b34c0b391ba695 From 6679ec5519f35e2b3764ef308b745d8326216bb4 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 15 Aug 2024 15:14:41 -0400 Subject: [PATCH 260/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index f4b0954f19..e34113b545 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit f4b0954f1951563a09d5c74fc3a1a90959f1ee00 +Subproject commit e34113b545a15b9a62759db50acdf6e00547f3cf From 0270df6b236637c62374bbb0ada6932e10085e59 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 17 Aug 2024 14:58:42 -0400 Subject: [PATCH 261/469] assets submod --- art | 2 +- assets | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/art b/art index faeba700c5..55c1b56823 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit faeba700c5526bd4fd57ccc927d875c82b9d3553 +Subproject commit 55c1b56823d4d7a74397bab9aeab30f15126499c diff --git a/assets b/assets index d09b1e0f17..b1bc5a6477 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit d09b1e0f175dc19986a1ef6ef1185c060acd1b71 +Subproject commit b1bc5a647714ed806ba4e979e0f7b9259880445c From eb5bf449a4ba7a3efb82b093fdc90a6248a78224 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sat, 17 Aug 2024 15:06:10 -0400 Subject: [PATCH 262/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index b1bc5a6477..ac95dcfb5e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit b1bc5a647714ed806ba4e979e0f7b9259880445c +Subproject commit ac95dcfb5e702d76852badc21f19706518ceb6e2 From 9d85d423c6d9db326378e210cacbee336e189ae4 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Mon, 19 Aug 2024 04:29:22 +0100 Subject: [PATCH 263/469] add stage to registry and chart editor --- source/funkin/data/stage/StageRegistry.hx | 4 ++-- source/funkin/ui/debug/charting/ChartEditorState.hx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/funkin/data/stage/StageRegistry.hx b/source/funkin/data/stage/StageRegistry.hx index 1f05042479..7754c380e0 100644 --- a/source/funkin/data/stage/StageRegistry.hx +++ b/source/funkin/data/stage/StageRegistry.hx @@ -93,8 +93,8 @@ class StageRegistry extends BaseRegistry public function listBaseGameStageIds():Array { return [ - "mainStage", "spookyMansion", "phillyTrain", "phillyTrainErect", "limoRide", "limoRideErect", "mallXmas", "mallEvil", "school", "schoolEvil", - "tankmanBattlefield", "phillyStreets", "phillyBlazin", + "mainStage", "mainStageErect", "spookyMansion", "phillyTrain", "phillyTrainErect", "limoRide", "limoRideErect", "mallXmas", "mallEvil", "school", + "schoolEvil", "tankmanBattlefield", "phillyStreets", "phillyBlazin", ]; } diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 09a06661d3..ebfbe5eac5 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -5699,7 +5699,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // TODO: Rework asset system so we can remove this jank. switch (currentSongStage) { - case 'mainStage': + case 'mainStage' | 'mainStageErect': PlayStatePlaylist.campaignId = 'week1'; case 'spookyMansion' | 'spookyMansionErect': PlayStatePlaylist.campaignId = 'week2'; From 8a03cc7b2ac39ad9271e55536a5f6ec0734e4ddf Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Mon, 19 Aug 2024 04:29:28 +0100 Subject: [PATCH 264/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index ac95dcfb5e..45de6c66a0 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit ac95dcfb5e702d76852badc21f19706518ceb6e2 +Subproject commit 45de6c66a04f806d475eb95fa1a364d63466fdec From 4dad17d241df1e9405a68893736882cc2eb44eb9 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Mon, 19 Aug 2024 13:40:58 +0200 Subject: [PATCH 265/469] Prepare the FlxAnimate bizz --- source/funkin/ui/debug/anim/FlxAnimateTest.hx | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/source/funkin/ui/debug/anim/FlxAnimateTest.hx b/source/funkin/ui/debug/anim/FlxAnimateTest.hx index c83d2c370c..6a34850ee2 100644 --- a/source/funkin/ui/debug/anim/FlxAnimateTest.hx +++ b/source/funkin/ui/debug/anim/FlxAnimateTest.hx @@ -22,28 +22,14 @@ class FlxAnimateTest extends MusicBeatState { super.create(); - sprite = new FlxAtlasSprite(0, 0, 'shared:assets/shared/images/characters/tankman'); - add(sprite); + sprite = new FlxAtlasSprite(0, 0, 'assets/images/freeplay/freeplay-boyfriend'); // I suppose a specific atlas to test should go in here - sprite.playAnimation('idle'); + add(sprite); + sprite.anim.play("Boyfriend DJ"); } public override function update(elapsed:Float):Void { super.update(elapsed); - - if (FlxG.keys.justPressed.SPACE) sprite.playAnimation('idle'); - - if (FlxG.keys.justPressed.W) sprite.playAnimation('singUP'); - - if (FlxG.keys.justPressed.A) sprite.playAnimation('singLEFT'); - - if (FlxG.keys.justPressed.S) sprite.playAnimation('singDOWN'); - - if (FlxG.keys.justPressed.D) sprite.playAnimation('singRIGHT'); - - if (FlxG.keys.justPressed.J) sprite.playAnimation('hehPrettyGood'); - - if (FlxG.keys.justPressed.K) sprite.playAnimation('ugh'); } } From 8a2d8a9941090672b52b36d2e92f250e27b36e43 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Mon, 19 Aug 2024 13:41:29 +0200 Subject: [PATCH 266/469] Committing all the additional stuff (Lowering FPS For my PC lol) --- art | 2 +- assets | 2 +- source/Main.hx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/art b/art index 55c1b56823..faeba700c5 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit 55c1b56823d4d7a74397bab9aeab30f15126499c +Subproject commit faeba700c5526bd4fd57ccc927d875c82b9d3553 diff --git a/assets b/assets index ac95dcfb5e..d09b1e0f17 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit ac95dcfb5e702d76852badc21f19706518ceb6e2 +Subproject commit d09b1e0f175dc19986a1ef6ef1185c060acd1b71 diff --git a/source/Main.hx b/source/Main.hx index add5bbc67b..2f31a6d485 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -26,7 +26,7 @@ class Main extends Sprite var framerate:Int = 60; // How many frames per second the game should run at. #else // TODO: This should probably be in the options menu? - var framerate:Int = 144; // How many frames per second the game should run at. + var framerate:Int = 60; // How many frames per second the game should run at. #end var skipSplash:Bool = true; // Whether to skip the flixel splash screen that appears in release mode. var startFullscreen:Bool = false; // Whether to start the game in fullscreen on desktop targets From 82eea221d8ebc2234b6d60048543217ad32c85ea Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Mon, 19 Aug 2024 14:41:20 +0200 Subject: [PATCH 267/469] Can't seem to understand AtlasSprite --- source/funkin/ui/debug/anim/FlxAnimateTest.hx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/debug/anim/FlxAnimateTest.hx b/source/funkin/ui/debug/anim/FlxAnimateTest.hx index 6a34850ee2..2b76d39e01 100644 --- a/source/funkin/ui/debug/anim/FlxAnimateTest.hx +++ b/source/funkin/ui/debug/anim/FlxAnimateTest.hx @@ -23,9 +23,10 @@ class FlxAnimateTest extends MusicBeatState super.create(); sprite = new FlxAtlasSprite(0, 0, 'assets/images/freeplay/freeplay-boyfriend'); // I suppose a specific atlas to test should go in here - + // Doesn't input anything, How does AtlasSprite work??? -Cheems add(sprite); - sprite.anim.play("Boyfriend DJ"); + camera.follow(sprite); + sprite.playAnimation(null); } public override function update(elapsed:Float):Void From 0aa9c41c5b6177a10d8d7779fb475ef37a4a29d3 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 19 Aug 2024 18:59:08 -0400 Subject: [PATCH 268/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index ac95dcfb5e..7bc9a73d87 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit ac95dcfb5e702d76852badc21f19706518ceb6e2 +Subproject commit 7bc9a73d87cf288844a522ca825ae22cfa73d5fb From ec07a476c16dba410313b5316deba4fb23c139ca Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 19 Aug 2024 19:06:07 -0400 Subject: [PATCH 269/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 45de6c66a0..2eac236291 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 45de6c66a04f806d475eb95fa1a364d63466fdec +Subproject commit 2eac236291775208ce71287a5c27f8f15ae591c9 From a6f8cf0141ce0775914a25f6fe789412ea08a0bb Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Tue, 20 Aug 2024 01:20:13 +0200 Subject: [PATCH 270/469] Setting a simple mask test --- source/funkin/ui/debug/anim/FlxAnimateTest.hx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/source/funkin/ui/debug/anim/FlxAnimateTest.hx b/source/funkin/ui/debug/anim/FlxAnimateTest.hx index 2b76d39e01..44917e2a5c 100644 --- a/source/funkin/ui/debug/anim/FlxAnimateTest.hx +++ b/source/funkin/ui/debug/anim/FlxAnimateTest.hx @@ -22,15 +22,18 @@ class FlxAnimateTest extends MusicBeatState { super.create(); - sprite = new FlxAtlasSprite(0, 0, 'assets/images/freeplay/freeplay-boyfriend'); // I suppose a specific atlas to test should go in here - // Doesn't input anything, How does AtlasSprite work??? -Cheems + sprite = new FlxAtlasSprite(0, 0, 'assets/images/charSelect/maskTest'); add(sprite); - camera.follow(sprite); - sprite.playAnimation(null); + sprite.playAnimation(null, false, false, true); } public override function update(elapsed:Float):Void { super.update(elapsed); + + if (FlxG.keys.justPressed.SPACE) ((sprite.anim.isPlaying) ? sprite.anim.pause() : sprite.playAnimation(null, false, false, true)); + + if (FlxG.keys.anyJustPressed([A, LEFT])) sprite.anim.curFrame--; + if (FlxG.keys.anyJustPressed([D, RIGHT])) sprite.anim.curFrame++; } } From 94a08ff528f5b13d7cac0cc98c1dbe14feb4f58c Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Tue, 20 Aug 2024 01:28:03 +0200 Subject: [PATCH 271/469] asset stuff --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index d09b1e0f17..0e4392bcb0 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit d09b1e0f175dc19986a1ef6ef1185c060acd1b71 +Subproject commit 0e4392bcb0ed1dbb496764cff074635c20c91389 From f8b63c99684c4098881f6cda2fce3b0a3f4c4ee2 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 20 Aug 2024 14:19:04 -0400 Subject: [PATCH 272/469] update funkin.vis PR --- hmm.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hmm.json b/hmm.json index d677608827..4cd3e27c7d 100644 --- a/hmm.json +++ b/hmm.json @@ -58,8 +58,8 @@ "name": "funkin.vis", "type": "git", "dir": null, - "ref": "38261833590773cb1de34ac5d11e0825696fc340", - "url": "https://github.com/FunkinCrew/funkVis" + "ref": "22b1ce089dd924f15cdc4632397ef3504d464e90", + "url": "https://github.com/MidyGamy/funkVis/" }, { "name": "grig.audio", From 76edf2e4e9e6416ba097e724158fcfe78926d25a Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 20 Aug 2024 14:44:53 -0400 Subject: [PATCH 273/469] remove unused imports --- .../ui/debug/anim/DebugBoundingState.hx | 68 ------------------- 1 file changed, 68 deletions(-) diff --git a/source/funkin/ui/debug/anim/DebugBoundingState.hx b/source/funkin/ui/debug/anim/DebugBoundingState.hx index ed2f5870f3..7bb42c89e3 100644 --- a/source/funkin/ui/debug/anim/DebugBoundingState.hx +++ b/source/funkin/ui/debug/anim/DebugBoundingState.hx @@ -5,29 +5,22 @@ import flixel.addons.display.FlxGridOverlay; import flixel.FlxCamera; import flixel.FlxSprite; import flixel.FlxState; -import flixel.graphics.frames.FlxAtlasFrames; import flixel.graphics.frames.FlxFrame; import flixel.group.FlxGroup; import flixel.math.FlxPoint; import flixel.text.FlxText; import flixel.util.FlxColor; -import flixel.util.FlxSpriteUtil; -import flixel.util.FlxTimer; -import funkin.audio.FunkinSound; import funkin.input.Cursor; import funkin.play.character.BaseCharacter; import funkin.play.character.CharacterData; import funkin.play.character.CharacterData.CharacterDataParser; -import funkin.play.character.SparrowCharacter; import funkin.ui.mainmenu.MainMenuState; import funkin.util.MouseUtil; import funkin.util.SerializerUtil; import funkin.util.SortUtil; import haxe.ui.components.DropDown; import haxe.ui.containers.dialogs.CollapsibleDialog; -import haxe.ui.core.Component; import haxe.ui.core.Screen; -import haxe.ui.events.ItemEvent; import haxe.ui.events.UIEvent; import haxe.ui.RuntimeComponentBuilder; import lime.utils.Assets as LimeAssets; @@ -36,9 +29,6 @@ import openfl.events.Event; import openfl.events.IOErrorEvent; import openfl.geom.Rectangle; import openfl.net.FileReference; -import openfl.net.URLLoader; -import openfl.net.URLRequest; -import openfl.utils.ByteArray; using flixel.util.FlxSpriteUtil; @@ -164,64 +154,6 @@ class DebugBoundingState extends FlxState addInfo('Height', bf.height); spriteSheetView.add(swagOutlines); - - FlxG.stage.window.onDropFile.add(function(path:String) { - // WACKY ASS TESTING SHIT FOR WEB FILE LOADING?? - #if web - var swagList:FileList = cast path; - - var objShit = js.html.URL.createObjectURL(swagList.item(0)); - trace(objShit); - - var funnysound = new FunkinSound().loadStream('https://cdn.discordapp.com/attachments/767500676166451231/817821618251759666/Flutter.mp3', false, false, - null, function() { - trace('LOADED SHIT??'); - }); - - funnysound.volume = 1; - funnysound.play(); - - var urlShit = new URLLoader(new URLRequest(objShit)); - - new FlxTimer().start(3, function(tmr:FlxTimer) { - // music lol! - if (urlShit.dataFormat == BINARY) - { - // var daSwagBytes:ByteArray = urlShit.data; - - // FlxG.sound.playMusic(); - - // trace('is binary!!'); - } - trace(urlShit.dataFormat); - }); - - // remove(bf); - // FlxG.bitmap.removeByKey(Paths.image('characters/temp')); - // Assets.cache.clear(); - - // bf.loadGraphic(objShit); - // add(bf); - - // trace(swagList.item(0).name); - // var urlShit = js.html.URL.createObjectURL(path); - #end - - #if sys - trace("DROPPED FILE FROM: " + Std.string(path)); - var newPath = "./" + Paths.image('characters/temp'); - File.copy(path, newPath); - - var swag = Paths.image('characters/temp'); - - if (bf != null) remove(bf); - FlxG.bitmap.removeByKey(Paths.image('characters/temp')); - Assets.cache.clear(); - - bf.loadGraphic(Paths.image('characters/temp')); - add(bf); - #end - }); } function generateOutlines(frameShit:Array):Void From e1d8e758bb6ebe25a79f6f01417b9c5113457989 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 21 Aug 2024 16:31:10 -0400 Subject: [PATCH 274/469] completion server fixin? --- source/funkin/util/logging/CrashHandler.hx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/source/funkin/util/logging/CrashHandler.hx b/source/funkin/util/logging/CrashHandler.hx index 71d1ad3942..aa26220622 100644 --- a/source/funkin/util/logging/CrashHandler.hx +++ b/source/funkin/util/logging/CrashHandler.hx @@ -265,9 +265,10 @@ class CrashHandler static function renderMethod():String { - try + var outputStr:String = 'UNKNOWN'; + outputStr = try { - return switch (FlxG.renderMethod) + switch (FlxG.renderMethod) { case FlxRenderMethod.DRAW_TILES: 'DRAW_TILES'; case FlxRenderMethod.BLITTING: 'BLITTING'; @@ -276,7 +277,11 @@ class CrashHandler } catch (e) { - return 'ERROR ON QUERY RENDER METHOD: ${e}'; + 'ERROR ON QUERY RENDER METHOD: ${e}'; } + + if (outputStr == null) outputStr = 'UNKNOWN'; + + return outputStr; } } From 5756eff7cfd35b3eb0cbb234b31fc50705e0b779 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 21 Aug 2024 16:39:34 -0400 Subject: [PATCH 275/469] dont need null check, we just need to declare string type --- source/funkin/util/logging/CrashHandler.hx | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/funkin/util/logging/CrashHandler.hx b/source/funkin/util/logging/CrashHandler.hx index aa26220622..1b607ddfd6 100644 --- a/source/funkin/util/logging/CrashHandler.hx +++ b/source/funkin/util/logging/CrashHandler.hx @@ -280,8 +280,6 @@ class CrashHandler 'ERROR ON QUERY RENDER METHOD: ${e}'; } - if (outputStr == null) outputStr = 'UNKNOWN'; - return outputStr; } } From 7f0f0efe46b6d1b37d464227921e0f7477c1c344 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Thu, 22 Aug 2024 23:36:43 +0200 Subject: [PATCH 276/469] initial Commit --- assets | 2 +- .../graphics/adobeanimate/FlxAtlasSprite.hx | 3 +- source/funkin/ui/charSelect/CharSelectGF.hx | 71 +++++++++++++++- .../funkin/ui/charSelect/CharSelectPlayer.hx | 81 +++++++++++++++---- .../ui/charSelect/CharSelectSubState.hx | 52 +++++++++++- 5 files changed, 189 insertions(+), 20 deletions(-) diff --git a/assets b/assets index 0e4392bcb0..299f5ac20f 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0e4392bcb0ed1dbb496764cff074635c20c91389 +Subproject commit 299f5ac20fd9ee50fda5cadd6abb22b68d86fb87 diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index 049c6e2064..fdb53e30e5 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -182,6 +182,7 @@ class FlxAtlasSprite extends FlxAnimate trace('Playing animation $id'); this.anim.play(id, restart, false, startFrame); goToFrameLabel(id); + anim.curFrame += startFrame; this.currentAnimation = id; } @@ -301,7 +302,7 @@ class FlxAtlasSprite extends FlxAnimate public function getBasePosition():Null { - var stagePos = new FlxPoint(anim.stageInstance.matrix.tx, anim.stageInstance.matrix.ty); + // var stagePos = new FlxPoint(anim.stageInstance.matrix.tx, anim.stageInstance.matrix.ty); var instancePos = new FlxPoint(anim.curInstance.matrix.tx, anim.curInstance.matrix.ty); var firstElement = anim.curSymbol.timeline?.get(0)?.get(0)?.get(0); if (firstElement == null) return instancePos; diff --git a/source/funkin/ui/charSelect/CharSelectGF.hx b/source/funkin/ui/charSelect/CharSelectGF.hx index 6d8e3e657b..44a9a6088b 100644 --- a/source/funkin/ui/charSelect/CharSelectGF.hx +++ b/source/funkin/ui/charSelect/CharSelectGF.hx @@ -8,6 +8,7 @@ import funkin.util.FramesJSFLParser; import funkin.util.FramesJSFLParser.FramesJSFLInfo; import funkin.util.FramesJSFLParser.FramesJSFLFrame; import flixel.math.FlxMath; +import funkin.vis.dsp.SpectralAnalyzer; class CharSelectGF extends FlxAtlasSprite { @@ -20,14 +21,47 @@ class CharSelectGF extends FlxAtlasSprite var intendedYPos:Float = 0; var intendedAlpha:Float = 0; + var list:Array = []; + var char:String = "gf"; + + var analyzer:SpectralAnalyzer; public function new() { super(0, 0, Paths.animateAtlas("charSelect/gfChill")); anim.play(""); + list = anim.curSymbol.getFrameLabelNames(); + switchGF("bf"); } + var _addedCallback:String = ""; + + override public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false, loop:Bool = false, startFrame:Int = 0):Void + { + if (id == null) id = "idle"; + // var fr = anim.getFrameLabel("confirm"); + // fr.removeCallbacks(); + // fr.add(() -> trace("HEY")); + + if (id != _addedCallback) + { + var next = list[list.indexOf(_addedCallback) + 1]; + if (next != null) anim.getFrameLabel(next).removeCallbacks(); + + var index:Int = list.indexOf(id); + + _addedCallback = list[index]; + if (index != -1 && index + 1 < list.length) + { + var lb = anim.getFrameLabel(list[index + 1]); + @:privateAccess + lb.add(() -> playAnimation(list[index], true, false, false)); + } + } + super.playAnimation(id, restart, ignoreOther, loop, startFrame); + } + override public function update(elapsed:Float) { super.update(elapsed); @@ -59,6 +93,39 @@ class CharSelectGF extends FlxAtlasSprite } } + override public function draw() + { + if (analyzer != null) drawFFT(); + super.draw(); + } + + function drawFFT() + { + if (char == "nene") + { + var levels = analyzer.getLevels(); + var frame = anim.curSymbol.timeline.get("VIZ_bars").get(anim.curFrame); + var elements = frame.getList(); + var len:Int = cast Math.min(elements.length, 7); + + for (i in 0...len) + { + var animFrame:Int = Math.round(levels[i].value * 12); + + #if desktop + animFrame = Math.round(animFrame * FlxG.sound.volume); + #end + + animFrame = Math.floor(Math.min(12, animFrame)); + animFrame = Math.floor(Math.max(0, animFrame)); + + animFrame = Std.int(Math.abs(animFrame - 12)); // shitty dumbass flip, cuz dave got da shit backwards lol! + + elements[i].symbol.firstFrame = animFrame; + } + } + } + /** * @param animInfo Should not be confused with animInInfo! * This is merely a local var for the function! @@ -113,6 +180,7 @@ class CharSelectGF extends FlxAtlasSprite "gf"; } + char = str; switch str { default: @@ -123,7 +191,8 @@ class CharSelectGF extends FlxAtlasSprite animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + str + "AnimInfo/" + str + "Out.txt")); anim.play(""); - playAnimation("idle", true, false, true); + playAnimation("idle", true, false, false); + addFrameCallback(getNextFrameLabel("idle"), () -> playAnimation("idle", true, false, false)); updateHitbox(); } diff --git a/source/funkin/ui/charSelect/CharSelectPlayer.hx b/source/funkin/ui/charSelect/CharSelectPlayer.hx index 9052c60e90..8a5d575301 100644 --- a/source/funkin/ui/charSelect/CharSelectPlayer.hx +++ b/source/funkin/ui/charSelect/CharSelectPlayer.hx @@ -10,22 +10,76 @@ class CharSelectPlayer extends FlxAtlasSprite super(x, y, Paths.animateAtlas("charSelect/bfChill")); onAnimationComplete.add(function(animLabel:String) { - switch (animLabel) - { - case "slidein": - if (hasAnimation("slidein idle point")) playAnimation("slidein idle point", true, false, false); - else - playAnimation("idle", true, false, true); - case "slidein idle point": - playAnimation("idle", true, false, true); - case "select": - anim.pause(); - case "deselect": - playAnimation("deselect loop start", true, false, true); - } + if (hasAnimation("slidein idle point")) playAnimation("slidein idle point", true, false, false); + else + playAnimation("idle"); }); } + var _addedCall = false; + + override public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false, loop:Bool = false, startFrame:Int = 0):Void + { + if (id == null || id == "") id = "idle"; + switch (id) + { + case "idle", "slidein idle point": + if (!_addedCall) + { + var fr = anim.getFrameLabel("idle end"); + if (fr != null) fr.add(() -> { + playAnimation("idle", true, false, false); + }); + } + _addedCall = true; + + case "select": + if (_addedCall) + { + anim.getFrameLabel("idle end").removeCallbacks(); + _addedCall = false; + } + + var fr = anim.getFrameLabel("deselect"); + + fr.add(() -> { + anim.pause(); + anim.curFrame--; + }); + + _addedCall = true; + + case "deselect": + var og = anim.getFrameLabel("deselect"); + if (_addedCall) + { + og.removeCallbacks(); + _addedCall = false; + } + + var fr = anim.getFrameLabel("deselect loop end"); + + fr.removeCallbacks(); + fr.add(() -> playAnimation("deselect loop start", true, false, false)); + + _addedCall = true; + + case "slidein", "slideout": + if (_addedCall) + { + anim.getFrameLabel("deselect loop end").removeCallbacks(); + _addedCall = false; + } + default: + if (_addedCall) + { + anim.getFrameLabel("idle end").removeCallbacks(); + _addedCall = false; + } + } + super.playAnimation(id, restart, ignoreOther, loop, startFrame); + } + public function updatePosition(str:String) { switch (str) @@ -48,7 +102,6 @@ class CharSelectPlayer extends FlxAtlasSprite loadAtlas(Paths.animateAtlas("charSelect/" + str + "Chill")); } - anim.play(""); playAnimation("slidein", true, false, false); updateHitbox(); diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 8b1f050f54..2b577dd952 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -22,6 +22,8 @@ import flixel.util.FlxTimer; import flixel.tweens.FlxEase; import flixel.sound.FlxSound; import funkin.audio.FunkinSound; +import funkin.graphics.FunkinCamera; +import funkin.vis.dsp.SpectralAnalyzer; class CharSelectSubState extends MusicBeatSubState { @@ -63,6 +65,10 @@ class CharSelectSubState extends MusicBeatSubState var selectTimer:FlxTimer = new FlxTimer(); var selectSound:FunkinSound; + var charSelectCam:FunkinCamera; + + var introM:FunkinSound = null; + public function new() { super(); @@ -90,13 +96,21 @@ class CharSelectSubState extends MusicBeatSubState restartTrack: true }); var introMusic:String = Paths.music('stayFunky/stayFunky-intro'); - FunkinSound.load(introMusic, 1.0, false, true, true, () -> { + introM = FunkinSound.load(introMusic, 1.0, false, true, true, () -> { FunkinSound.playMusic('stayFunky', { startingVolume: 1, overrideExisting: true, restartTrack: true }); + @:privateAccess + gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1); + #if desktop + // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 + // So we want to manually change it! + @:privateAccess + gfChill.analyzer.fftN = 512; + #end }); var bg:FlxSprite = new FlxSprite(-153, -140); @@ -137,7 +151,7 @@ class CharSelectSubState extends MusicBeatSubState gfChill = new CharSelectGF(); gfChill.switchGF("bf"); add(gfChill); - + @:privateAccess playerChill = new CharSelectPlayer(0, 0); playerChill.switchChar("bf"); add(playerChill); @@ -349,6 +363,17 @@ class CharSelectSubState extends MusicBeatSubState override public function update(elapsed:Float):Void { super.update(elapsed); + @:privateAccess + if (introM != null && !introM.paused && gfChill.analyzer == null) + { + gfChill.analyzer = new SpectralAnalyzer(introM._channel.__audioSource, 7, 0.1); + #if desktop + // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 + // So we want to manually change it! + @:privateAccess + gfChill.analyzer.fftN = 512; + #end + } Conductor.instance.update(); @@ -449,6 +474,7 @@ class CharSelectSubState extends MusicBeatSubState FlxTween.tween(FlxG.sound.music, {pitch: 0.1}, 1.5, {ease: FlxEase.quadInOut}); playerChill.playAnimation("select"); + gfChill.playAnimation("confirm"); pressedSelect = true; selectTimer.start(1.5, (_) -> { pressedSelect = false; @@ -467,8 +493,20 @@ class CharSelectSubState extends MusicBeatSubState grpCursors.visible = true; FlxTween.globalManager.cancelTweensOf(FlxG.sound.music); - FlxTween.tween(FlxG.sound.music, {pitch: 1.0}, 1, {ease: FlxEase.quartInOut}); playerChill.playAnimation("deselect"); + gfChill.playAnimation("deselect"); + FlxTween.tween(FlxG.sound.music, {pitch: 1.0}, 1, + { + ease: FlxEase.quartInOut, + onComplete: (_) -> { + var fr = playerChill.anim.getFrameLabel("deselect loop end"); + if (fr != null) fr.removeCallbacks(); + @:privateAccess + playerChill._addedCall = false; + playerChill.playAnimation("idle"); + gfChill.playAnimation("idle"); + } + }); pressedSelect = false; selectTimer.cancel(); } @@ -572,6 +610,14 @@ class CharSelectSubState extends MusicBeatSubState memb.scale.set(2.6, 2.6); if (controls.ACCEPT) memb.animation.play("confirm"); + if (controls.BACK) + { + memb.animation.play("confirm", false, true); + member.animation.finishCallback = (_) -> { + member.animation.play("idle"); + member.animation.finishCallback = null; + }; + } } else { From 0d3fa41b29b4613ca9241c2081d16426ca1c850e Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 23 Aug 2024 13:35:50 +0100 Subject: [PATCH 277/469] blue fade shader --- source/funkin/graphics/shaders/BlueFade.hx | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 source/funkin/graphics/shaders/BlueFade.hx diff --git a/source/funkin/graphics/shaders/BlueFade.hx b/source/funkin/graphics/shaders/BlueFade.hx new file mode 100644 index 0000000000..f57bcfbf1c --- /dev/null +++ b/source/funkin/graphics/shaders/BlueFade.hx @@ -0,0 +1,51 @@ +package funkin.graphics.shaders; + +import flixel.system.FlxAssets.FlxShader; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; + +class BlueFade extends FlxShader +{ + public var fadeVal(default, set):Float; + + function set_fadeVal(val:Float):Float + { + fadeAmt.value = [val]; + fadeVal = val; + // trace(fadeVal); + + return val; + } + + public function fade(startAmt:Float = 0, targetAmt:Float = 1, duration:Float, _options:TweenOptions):Void + { + fadeVal = startAmt; + FlxTween.tween(this, {fadeVal: targetAmt}, duration, _options); + } + + @:glFragmentSource(' + #pragma header + + // Value from (0, 1) + uniform float fadeAmt; + + // fade the image to blue as it fades to black + + void main() + { + vec4 tex = flixel_texture2D(bitmap, openfl_TextureCoordv); + + vec4 finalColor = mix(vec4(vec4(0.0, 0.0, tex.b, tex.a) * fadeAmt), vec4(tex * fadeAmt), fadeAmt); + + // Output to screen + gl_FragColor = finalColor; + } + + ') + public function new() + { + super(); + + this.fadeVal = 1; + } +} From e37330f2888ee77982031bea0e19b3bd1e655943 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 23 Aug 2024 13:36:00 +0100 Subject: [PATCH 278/469] character select transition --- .../ui/charSelect/CharSelectSubState.hx | 120 +++++++++++++++--- 1 file changed, 104 insertions(+), 16 deletions(-) diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 8b1f050f54..34ffbaf580 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -22,6 +22,8 @@ import flixel.util.FlxTimer; import flixel.tweens.FlxEase; import flixel.sound.FlxSound; import funkin.audio.FunkinSound; +import funkin.graphics.shaders.BlueFade; +import openfl.filters.ShaderFilter; class CharSelectSubState extends MusicBeatSubState { @@ -53,9 +55,16 @@ class CharSelectSubState extends MusicBeatSubState var gfChill:CharSelectGF; var gfChillOut:CharSelectGF; + var barthing:FlxAtlasSprite; + var dipshitBacking:FlxSprite; + var chooseDipshit:FlxSprite; + var dipshitBlur:FlxSprite; + var transitionGradient:FlxSprite; + var curChar(default, set):String = "pico"; var nametag:Nametag; var camFollow:FlxObject; + var autoFollow:Bool = false; var availableChars:Map = new Map(); var pressedSelect:Bool = false; @@ -71,10 +80,17 @@ class CharSelectSubState extends MusicBeatSubState availableChars.set(3, "pico"); } + var fadeShader:BlueFade = new BlueFade(); + override public function create():Void { super.create(); + autoFollow = false; + + var fadeShaderFilter:ShaderFilter = new ShaderFilter(fadeShader); + FlxG.camera.setFilters([fadeShaderFilter]); + selectSound = new FunkinSound(); selectSound.loadEmbedded(Paths.sound('CS_select')); selectSound.pitch = 1; @@ -120,12 +136,15 @@ class CharSelectSubState extends MusicBeatSubState curtains.scrollFactor.set(1.4, 1.4); add(curtains); - var barthing:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/barThing")); + barthing = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/barThing")); barthing.anim.play(""); barthing.blend = BlendMode.MULTIPLY; barthing.scrollFactor.set(0, 0); add(barthing); + barthing.y += 80; + FlxTween.tween(barthing, {y: barthing.y - 80}, 1.3, {ease: FlxEase.expoOut}); + var charLight:FlxSprite = new FlxSprite(800, 250); charLight.loadGraphic(Paths.image('charSelect/charLight')); add(charLight); @@ -156,24 +175,33 @@ class CharSelectSubState extends MusicBeatSubState fgBlur.blend = openfl.display.BlendMode.MULTIPLY; add(fgBlur); - var dipshitBlur:FlxSprite = new FlxSprite(419, -65); + dipshitBlur = new FlxSprite(419, -65); dipshitBlur.frames = Paths.getSparrowAtlas("charSelect/dipshitBlur"); dipshitBlur.animation.addByPrefix('idle', "CHOOSE vertical", 24, true); dipshitBlur.blend = BlendMode.ADD; dipshitBlur.animation.play("idle"); add(dipshitBlur); - var dipshitBacking:FlxSprite = new FlxSprite(423, -17); + dipshitBacking = new FlxSprite(423, -17); dipshitBacking.frames = Paths.getSparrowAtlas("charSelect/dipshitBacking"); dipshitBacking.animation.addByPrefix('idle', "CHOOSE horizontal", 24, true); dipshitBacking.blend = BlendMode.ADD; dipshitBacking.animation.play("idle"); add(dipshitBacking); - var chooseDipshit:FlxSprite = new FlxSprite(426, -13); + dipshitBacking.y += 210; + FlxTween.tween(dipshitBacking, {y: dipshitBacking.y - 210}, 1.1, {ease: FlxEase.expoOut}); + + chooseDipshit = new FlxSprite(426, -13); chooseDipshit.loadGraphic(Paths.image('charSelect/chooseDipshit')); add(chooseDipshit); + chooseDipshit.y += 200; + FlxTween.tween(chooseDipshit, {y: chooseDipshit.y - 200}, 1, {ease: FlxEase.expoOut}); + + dipshitBlur.y += 220; + FlxTween.tween(dipshitBlur, {y: dipshitBlur.y - 220}, 1.2, {ease: FlxEase.expoOut}); + chooseDipshit.scrollFactor.set(); dipshitBacking.scrollFactor.set(); dipshitBlur.scrollFactor.set(); @@ -241,6 +269,12 @@ class CharSelectSubState extends MusicBeatSubState initLocks(); + for (index => member in grpIcons.members) + { + member.y += 300; + FlxTween.tween(member, {y: member.y - 300}, 1, {ease: FlxEase.expoOut}); + } + cursor.scrollFactor.set(); cursorBlue.scrollFactor.set(); cursorDarkBlue.scrollFactor.set(); @@ -258,7 +292,8 @@ class CharSelectSubState extends MusicBeatSubState add(camFollow); camFollow.screenCenter(); - FlxG.camera.follow(camFollow, LOCKON, 0.01); + // FlxG.camera.follow(camFollow, LOCKON, 0.01); + FlxG.camera.follow(camFollow, LOCKON); var temp:FlxSprite = new FlxSprite(); temp.loadGraphic(Paths.image('charSelect/placement')); @@ -266,6 +301,25 @@ class CharSelectSubState extends MusicBeatSubState temp.alpha = 0.0; Conductor.stepHit.add(spamOnStep); // FlxG.debugger.track(temp, "tempBG"); + + transitionGradient = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/transitionGradient')); + transitionGradient.scale.set(1280, 1); + transitionGradient.flipY = true; + transitionGradient.updateHitbox(); + FlxTween.tween(transitionGradient, {y: -720}, 1, {ease: FlxEase.expoOut}); + add(transitionGradient); + + camFollow.screenCenter(); + camFollow.y -= 150; + fadeShader.fade(0.0, 1.0, 0.8, {ease: FlxEase.quadOut}); + FlxTween.tween(camFollow, {y: camFollow.y + 150}, 1.5, + { + ease: FlxEase.expoOut, + onComplete: function(_) { + autoFollow = true; + FlxG.camera.follow(camFollow, LOCKON, 0.01); + } + }); } var grpIcons:FlxSpriteGroup; @@ -337,6 +391,35 @@ class CharSelectSubState extends MusicBeatSubState } } + function testLol():Void + { + autoFollow = false; + FlxTween.tween(barthing, {y: barthing.y + 80}, 0.8, {ease: FlxEase.backIn}); + FlxTween.tween(dipshitBacking, {y: dipshitBacking.y + 210}, 0.8, {ease: FlxEase.backIn}); + FlxTween.tween(chooseDipshit, {y: chooseDipshit.y + 200}, 0.8, {ease: FlxEase.backIn}); + FlxTween.tween(dipshitBlur, {y: dipshitBlur.y + 220}, 0.8, {ease: FlxEase.backIn}); + for (index => member in grpIcons.members) + { + // member.y += 300; + FlxTween.tween(member, {y: member.y + 300}, 0.8, {ease: FlxEase.backIn}); + } + FlxG.camera.follow(camFollow, LOCKON); + FlxTween.tween(transitionGradient, {y: -150}, 0.8, {ease: FlxEase.backIn}); + fadeShader.fade(1.0, 0, 0.8, {ease: FlxEase.quadIn}); + FlxTween.tween(camFollow, {y: camFollow.y - 150}, 0.8, + { + ease: FlxEase.backIn, + onComplete: function(_) { + FlxG.switchState(FreeplayState.build( + { + { + character: curChar + } + })); + } + }); + } + var holdTmrUp:Float = 0; var holdTmrDown:Float = 0; var holdTmrLeft:Float = 0; @@ -447,17 +530,19 @@ class CharSelectSubState extends MusicBeatSubState FlxG.sound.play(Paths.sound('CS_confirm')); - FlxTween.tween(FlxG.sound.music, {pitch: 0.1}, 1.5, {ease: FlxEase.quadInOut}); + FlxTween.tween(FlxG.sound.music, {pitch: 0.1}, 1, {ease: FlxEase.quadInOut}); + FlxTween.tween(FlxG.sound.music, {volume: 0.0}, 1.5, {ease: FlxEase.quadInOut}); playerChill.playAnimation("select"); pressedSelect = true; selectTimer.start(1.5, (_) -> { pressedSelect = false; - FlxG.switchState(FreeplayState.build( - { - { - character: curChar - } - })); + // FlxG.switchState(FreeplayState.build( + // { + // { + // character: curChar + // } + // })); + testLol(); }); } @@ -467,7 +552,7 @@ class CharSelectSubState extends MusicBeatSubState grpCursors.visible = true; FlxTween.globalManager.cancelTweensOf(FlxG.sound.music); - FlxTween.tween(FlxG.sound.music, {pitch: 1.0}, 1, {ease: FlxEase.quartInOut}); + FlxTween.tween(FlxG.sound.music, {pitch: 1.0, volume: 1.0}, 1, {ease: FlxEase.quartInOut}); playerChill.playAnimation("deselect"); pressedSelect = false; selectTimer.cancel(); @@ -491,9 +576,12 @@ class CharSelectSubState extends MusicBeatSubState updateLockAnims(); - camFollow.screenCenter(); - camFollow.x += cursorX * 10; - camFollow.y += cursorY * 10; + if (autoFollow == true) + { + camFollow.screenCenter(); + camFollow.x += cursorX * 10; + camFollow.y += cursorY * 10; + } cursorLocIntended.x = (cursorFactor * cursorX) + (FlxG.width / 2) - cursor.width / 2; cursorLocIntended.y = (cursorFactor * cursorY) + (FlxG.height / 2) - cursor.height / 2; From 55dd1857c96f7bbcb64b0738757c0a3dfdbebb62 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 23 Aug 2024 13:36:35 +0100 Subject: [PATCH 279/469] freeplay transition + wip card classes --- source/funkin/ui/freeplay/AlbumRoll.hx | 28 +- source/funkin/ui/freeplay/FreeplayState.hx | 385 ++++++++---------- .../ui/freeplay/backcards/BackingCard.hx | 217 ++++++++++ .../ui/freeplay/backcards/BoyfriendCard.hx | 168 ++++++++ .../funkin/ui/freeplay/backcards/PicoCard.hx | 74 ++++ 5 files changed, 650 insertions(+), 222 deletions(-) create mode 100644 source/funkin/ui/freeplay/backcards/BackingCard.hx create mode 100644 source/funkin/ui/freeplay/backcards/BoyfriendCard.hx create mode 100644 source/funkin/ui/freeplay/backcards/PicoCard.hx diff --git a/source/funkin/ui/freeplay/AlbumRoll.hx b/source/funkin/ui/freeplay/AlbumRoll.hx index 36dba0054a..beed7bacaa 100644 --- a/source/funkin/ui/freeplay/AlbumRoll.hx +++ b/source/funkin/ui/freeplay/AlbumRoll.hx @@ -41,6 +41,7 @@ class AlbumRoll extends FlxSpriteGroup var difficultyStars:DifficultyStars; var _exitMovers:Null; + var _exitMoversCharSel:Null; var albumData:Album; @@ -128,7 +129,7 @@ class AlbumRoll extends FlxSpriteGroup * Apply exit movers for the album roll. * @param exitMovers The exit movers to apply. */ - public function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData):Void + public function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData, ?exitMoversCharSel:FreeplayState.ExitMoverData):Void { if (exitMovers == null) { @@ -141,12 +142,30 @@ class AlbumRoll extends FlxSpriteGroup if (exitMovers == null) return; + if (exitMoversCharSel == null) + { + exitMoversCharSel = _exitMoversCharSel; + } + else + { + _exitMoversCharSel = exitMoversCharSel; + } + + if (exitMoversCharSel == null) return; + exitMovers.set([newAlbumArt, difficultyStars], { x: FlxG.width, speed: 0.4, wait: 0 }); + + exitMoversCharSel.set([newAlbumArt, difficultyStars], + { + y: -175, + speed: 0.8, + wait: 0.1 + }); } var titleTimer:Null = null; @@ -207,6 +226,13 @@ class AlbumRoll extends FlxSpriteGroup speed: 0.4, wait: 0 }); + + if (_exitMoversCharSel != null) _exitMoversCharSel.set([albumTitle], + { + y: -190, + speed: 0.8, + wait: 0.1 + }); } public function setDifficultyStars(?difficulty:Int):Void diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 63767d28e0..614e8cc869 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1,5 +1,8 @@ package funkin.ui.freeplay; +import funkin.ui.freeplay.backcards.BackingCard; +import funkin.ui.freeplay.backcards.BoyfriendCard; +import funkin.ui.freeplay.backcards.PicoCard; import flixel.addons.transition.FlxTransitionableState; import flixel.addons.ui.FlxInputText; import flixel.FlxCamera; @@ -30,7 +33,9 @@ import funkin.graphics.shaders.AngleMask; import funkin.graphics.shaders.GaussianBlurShader; import funkin.graphics.shaders.HSVShader; import funkin.graphics.shaders.PureColor; +import funkin.graphics.shaders.BlueFade; import funkin.graphics.shaders.StrokeShader; +import openfl.filters.ShaderFilter; import funkin.input.Controls; import funkin.play.PlayStatePlaylist; import funkin.play.scoring.Scoring; @@ -176,6 +181,8 @@ class FreeplayState extends MusicBeatSubState var letterSort:LetterSort; var exitMovers:ExitMoverData = new Map(); + var exitMoversCharSel:ExitMoverData = new Map(); + var stickerSubState:Null = null; public static var rememberedDifficulty:Null = Constants.DEFAULT_DIFFICULTY; @@ -186,23 +193,9 @@ class FreeplayState extends MusicBeatSubState var rankBg:FunkinSprite; var rankVignette:FlxSprite; - var backingTextYeah:FlxAtlasSprite; - var orangeBackShit:FunkinSprite; - var alsoOrangeLOL:FunkinSprite; - var pinkBack:FunkinSprite; - var confirmGlow:FlxSprite; - var confirmGlow2:FlxSprite; - var confirmTextGlow:FlxSprite; - - var moreWays:BGScrollingText; - var funnyScroll:BGScrollingText; - var txtNuts:BGScrollingText; - var funnyScroll2:BGScrollingText; - var moreWays2:BGScrollingText; - var funnyScroll3:BGScrollingText; + var backingCard:Null = null; - var bgDad:FlxSprite; - var cardGlow:FlxSprite; + public var bgDad:FlxSprite; var fromResultsParams:Null = null; @@ -232,49 +225,38 @@ class FreeplayState extends MusicBeatSubState stickerSubState = stickers; } + switch (currentCharacterId) + { + case 'bf': + backingCard = new BoyfriendCard(currentCharacter); + case 'pico': + backingCard = new PicoCard(currentCharacter); + default: + backingCard = new BackingCard(currentCharacter); + } + // We build a bunch of sprites BEFORE create() so we can guarantee they aren't null later on. albumRoll = new AlbumRoll(); fp = new FreeplayScore(460, 60, 7, 100); - cardGlow = new FlxSprite(-30, -30).loadGraphic(Paths.image('freeplay/cardGlow')); - confirmGlow = new FlxSprite(-30, 240).loadGraphic(Paths.image('freeplay/confirmGlow')); - confirmTextGlow = new FlxSprite(-8, 115).loadGraphic(Paths.image('freeplay/glowingText')); rankCamera = new FunkinCamera('rankCamera', 0, 0, FlxG.width, FlxG.height); funnyCam = new FunkinCamera('freeplayFunny', 0, 0, FlxG.width, FlxG.height); - funnyScroll = new BGScrollingText(0, 220, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, false, 60); - funnyScroll2 = new BGScrollingText(0, 335, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, false, 60); grpCapsules = new FlxTypedGroup(); grpDifficulties = new FlxTypedSpriteGroup(-300, 80); letterSort = new LetterSort(400, 75); grpSongs = new FlxTypedGroup(); - moreWays = new BGScrollingText(0, 160, currentCharacter.getFreeplayDJText(2), FlxG.width, true, 43); - moreWays2 = new BGScrollingText(0, 397, currentCharacter.getFreeplayDJText(2), FlxG.width, true, 43); - pinkBack = FunkinSprite.create('freeplay/pinkBack'); rankBg = new FunkinSprite(0, 0); rankVignette = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/rankVignette')); sparks = new FlxSprite(0, 0); sparksADD = new FlxSprite(0, 0); txtCompletion = new AtlasText(1185, 87, '69', AtlasFont.FREEPLAY_CLEAR); - txtNuts = new BGScrollingText(0, 285, currentCharacter.getFreeplayDJText(3), FlxG.width / 2, true, 43); ostName = new FlxText(8, 8, FlxG.width - 8 - 8, 'OFFICIAL OST', 48); - orangeBackShit = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFFEDA00); - - bgDad = new FlxSprite(pinkBack.width * 0.74, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad')); - alsoOrangeLOL = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFFFD400); - confirmGlow2 = new FlxSprite(confirmGlow.x, confirmGlow.y).loadGraphic(Paths.image('freeplay/confirmGlow2')); - funnyScroll3 = new BGScrollingText(0, orangeBackShit.y + 10, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, 60); - backingTextYeah = new FlxAtlasSprite(640, 370, Paths.animateAtlas("freeplay/backing-text-yeah"), - { - FrameRate: 24.0, - Reversed: false, - // ?OnComplete:Void -> Void, - ShowPivot: false, - Antialiasing: true, - ScrollFactor: new FlxPoint(1, 1), - }); + bgDad = new FlxSprite(backingCard.pinkBack.width * 0.74, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad')); } + var fadeShader:BlueFade = new BlueFade(); + override function create():Void { super.create(); @@ -283,6 +265,9 @@ class FreeplayState extends MusicBeatSubState FlxTransitionableState.skipNextTransIn = true; + var fadeShaderFilter:ShaderFilter = new ShaderFilter(fadeShader); + funnyCam.setFilters([fadeShaderFilter]); + if (stickerSubState != null) { this.persistentUpdate = true; @@ -360,113 +345,12 @@ class FreeplayState extends MusicBeatSubState trace(FlxG.camera.initialZoom); trace(FlxCamera.defaultZoom); - pinkBack.color = 0xFFFFD4E9; // sets it to pink! - pinkBack.x -= pinkBack.width; - - FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut}); - add(pinkBack); - - add(orangeBackShit); - - add(alsoOrangeLOL); - - exitMovers.set([pinkBack, orangeBackShit, alsoOrangeLOL], - { - x: -pinkBack.width, - y: pinkBack.y, - speed: 0.4, - wait: 0 - }); - - FlxSpriteUtil.alphaMaskFlxSprite(orangeBackShit, pinkBack, orangeBackShit); - orangeBackShit.visible = false; - alsoOrangeLOL.visible = false; - - confirmTextGlow.blend = BlendMode.ADD; - confirmTextGlow.visible = false; - - confirmGlow.blend = BlendMode.ADD; - - confirmGlow.visible = false; - confirmGlow2.visible = false; - - add(confirmGlow2); - add(confirmGlow); - - add(confirmTextGlow); - - var grpTxtScrolls:FlxGroup = new FlxGroup(); - add(grpTxtScrolls); - grpTxtScrolls.visible = false; - - FlxG.debugger.addTrackerProfile(new TrackerProfile(BGScrollingText, ['x', 'y', 'speed', 'size'])); - - moreWays.funnyColor = 0xFFFFF383; - moreWays.speed = 6.8; - grpTxtScrolls.add(moreWays); - - exitMovers.set([moreWays], - { - x: FlxG.width * 2, - speed: 0.4, - }); - - funnyScroll.funnyColor = 0xFFFF9963; - funnyScroll.speed = -3.8; - grpTxtScrolls.add(funnyScroll); - - exitMovers.set([funnyScroll], - { - x: -funnyScroll.width * 2, - y: funnyScroll.y, - speed: 0.4, - wait: 0 - }); - - txtNuts.speed = 3.5; - grpTxtScrolls.add(txtNuts); - exitMovers.set([txtNuts], - { - x: FlxG.width * 2, - speed: 0.4, - }); - - funnyScroll2.funnyColor = 0xFFFF9963; - funnyScroll2.speed = -3.8; - grpTxtScrolls.add(funnyScroll2); - - exitMovers.set([funnyScroll2], - { - x: -funnyScroll2.width * 2, - speed: 0.5, - }); - - moreWays2.funnyColor = 0xFFFFF383; - moreWays2.speed = 6.8; - grpTxtScrolls.add(moreWays2); - - exitMovers.set([moreWays2], - { - x: FlxG.width * 2, - speed: 0.4 - }); - - funnyScroll3.funnyColor = 0xFFFEA400; - funnyScroll3.speed = -3.8; - grpTxtScrolls.add(funnyScroll3); - - exitMovers.set([funnyScroll3], - { - x: -funnyScroll3.width * 2, - speed: 0.3 - }); - - add(backingTextYeah); - - cardGlow.blend = BlendMode.ADD; - cardGlow.visible = false; - - add(cardGlow); + if (backingCard != null) + { + add(backingCard); + backingCard.init(); + backingCard.applyExitMovers(exitMovers, exitMoversCharSel); + } if (currentCharacter?.getFreeplayDJData() != null) { @@ -477,6 +361,12 @@ class FreeplayState extends MusicBeatSubState speed: 0.5 }); add(dj); + exitMoversCharSel.set([dj], + { + y: -175, + speed: 0.8, + wait: 0.1 + }); } bgDad.shader = new AngleMask(); @@ -499,8 +389,16 @@ class FreeplayState extends MusicBeatSubState wait: 0 }); + exitMoversCharSel.set([blackOverlayBullshitLOLXD, bgDad], + { + y: -100, + speed: 0.8, + wait: 0.1 + }); + add(bgDad); - FlxTween.tween(blackOverlayBullshitLOLXD, {x: pinkBack.width * 0.74}, 0.7, {ease: FlxEase.quintOut}); + // backingCard.pinkBack.width * 0.74 + FlxTween.tween(blackOverlayBullshitLOLXD, {x: 387.76}, 0.7, {ease: FlxEase.quintOut}); blackOverlayBullshitLOLXD.shader = bgDad.shader; @@ -520,6 +418,13 @@ class FreeplayState extends MusicBeatSubState wait: 0 }); + exitMoversCharSel.set([grpDifficulties], + { + y: -270, + speed: 0.8, + wait: 0.1 + }); + for (diffId in diffIdsTotal) { var diffSprite:DifficultySprite = new DifficultySprite(diffId); @@ -540,12 +445,12 @@ class FreeplayState extends MusicBeatSubState albumRoll.albumId = null; add(albumRoll); - albumRoll.applyExitMovers(exitMovers); + albumRoll.applyExitMovers(exitMovers, exitMoversCharSel); - var overhangStuff:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 64, FlxColor.BLACK); + var overhangStuff:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 164, FlxColor.BLACK); overhangStuff.y -= overhangStuff.height; - add(overhangStuff); - FlxTween.tween(overhangStuff, {y: 0}, 0.3, {ease: FlxEase.quartOut}); + + FlxTween.tween(overhangStuff, {y: -100}, 0.3, {ease: FlxEase.quartOut}); var fnfFreeplay:FlxText = new FlxText(8, 8, 0, 'FREEPLAY', 48); fnfFreeplay.font = 'VCR OSD Mono'; @@ -563,11 +468,16 @@ class FreeplayState extends MusicBeatSubState wait: 0 }); + exitMoversCharSel.set([overhangStuff, fnfFreeplay, ostName], + { + y: -300, + speed: 0.8, + wait: 0.1 + }); + var sillyStroke:StrokeShader = new StrokeShader(0xFFFFFFFF, 2, 2); fnfFreeplay.shader = sillyStroke; ostName.shader = sillyStroke; - add(fnfFreeplay); - add(ostName); var fnfHighscoreSpr:FlxSprite = new FlxSprite(860, 70); fnfHighscoreSpr.frames = Paths.getSparrowAtlas('freeplay/highscore'); @@ -601,6 +511,13 @@ class FreeplayState extends MusicBeatSubState speed: 0.3 }); + exitMoversCharSel.set([letterSort], + { + y: -270, + speed: 0.8, + wait: 0.1 + }); + letterSort.changeSelectionCallback = (str) -> { switch (str) { @@ -630,6 +547,13 @@ class FreeplayState extends MusicBeatSubState speed: 0.3 }); + exitMoversCharSel.set([fp, txtCompletion, fnfHighscoreSpr, txtCompletion, clearBoxSprite], + { + y: -270, + speed: 0.8, + wait: 0.1 + }); + var diffSelLeft:DifficultySelector = new DifficultySelector(20, grpDifficulties.y - 10, false, controls); var diffSelRight:DifficultySelector = new DifficultySelector(325, grpDifficulties.y - 10, true, controls); diffSelLeft.visible = false; @@ -637,6 +561,11 @@ class FreeplayState extends MusicBeatSubState add(diffSelLeft); add(diffSelRight); + // putting these here to fix the layering + add(overhangStuff); + add(fnfFreeplay); + add(ostName); + // be careful not to "add()" things in here unless it's to a group that's already added to the state // otherwise it won't be properly attatched to funnyCamera (relavent code should be at the bottom of create()) var onDJIntroDone = function() { @@ -656,6 +585,13 @@ class FreeplayState extends MusicBeatSubState speed: 0.26 }); + exitMoversCharSel.set([diffSelLeft, diffSelRight], + { + y: -270, + speed: 0.8, + wait: 0.1 + }); + new FlxTimer().start(1 / 24, function(handShit) { fnfHighscoreSpr.visible = true; fnfFreeplay.visible = true; @@ -674,18 +610,12 @@ class FreeplayState extends MusicBeatSubState }); }); - pinkBack.color = 0xFFFFD863; bgDad.visible = true; - orangeBackShit.visible = true; - alsoOrangeLOL.visible = true; - grpTxtScrolls.visible = true; + backingCard?.introDone(); // render optimisation if (_parentState != null) _parentState.persistentDraw = false; - cardGlow.visible = true; - FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.45, {ease: FlxEase.sineOut}); - if (prepForNewRank && fromResultsParams != null) { rankAnimStart(fromResultsParams); @@ -872,7 +802,7 @@ class FreeplayState extends MusicBeatSubState return str.songName.toLowerCase().startsWith(songFilter.filterData ?? ''); }); case ALL: - // no filter! + // no filter! case FAVORITE: songsToFilter = songsToFilter.filter(str -> { if (str == null) return true; // Random @@ -1174,6 +1104,62 @@ class FreeplayState extends MusicBeatSubState }); } + function goToCharSelect():Void + { + var transitionGradient = new FlxSprite(0, 720).loadGraphic(Paths.image('freeplay/transitionGradient')); + transitionGradient.scale.set(1280, 1); + transitionGradient.updateHitbox(); + transitionGradient.cameras = [rankCamera]; + exitMoversCharSel.set([transitionGradient], + { + y: -720, + speed: 0.8, + wait: 0.1 + }); + add(transitionGradient); + // busy = true; + for (index => capsule in grpCapsules.members) + { + var distFromSelected:Float = Math.abs(index - curSelected) - 1; + if (distFromSelected < 5) + { + capsule.doLerp = false; + exitMoversCharSel.set([capsule], + { + y: -250, + speed: 0.8, + wait: 0.1 + }); + } + } + fadeShader.fade(1.0, 0.0, 0.8, {ease: FlxEase.quadIn}); + FlxG.sound.music.fadeOut(0.9, 0); + // FlxTween.tween(transitionGradient, {y: 0}, 1, {ease: FlxEase.expoOut, startDelay: 0.3}); + new FlxTimer().start(0.9, _ -> { + FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState()); + }); + for (grpSpr in exitMoversCharSel.keys()) + { + var moveData:Null = exitMoversCharSel.get(grpSpr); + if (moveData == null) continue; + + for (spr in grpSpr) + { + if (spr == null) continue; + + var funnyMoveShit:MoveData = moveData; + + var moveDataX = funnyMoveShit.x ?? spr.x; + var moveDataY = funnyMoveShit.y ?? spr.y; + var moveDataSpeed = funnyMoveShit.speed ?? 0.2; + var moveDataWait = funnyMoveShit.wait ?? 0.0; + + FlxTween.tween(spr, {x: moveDataX, y: moveDataY + spr.y}, moveDataSpeed, {ease: FlxEase.backIn}); + // longestTimer = Math.max(longestTimer, moveDataSpeed + moveDataWait); + } + } + } + var touchY:Float = 0; var touchX:Float = 0; var dxTouch:Float = 0; @@ -1199,16 +1185,9 @@ class FreeplayState extends MusicBeatSubState { super.update(elapsed); - #if debug - if (FlxG.keys.justPressed.T) + if (FlxG.keys.justPressed.G) { - rankAnimStart(fromResultsParams ?? - { - playRankAnim: true, - newRank: PERFECT_GOLD, - songId: "tutorial", - difficultyId: "hard" - }); + goToCharSelect(); } if (FlxG.keys.justPressed.P) @@ -1221,6 +1200,18 @@ class FreeplayState extends MusicBeatSubState })); } + #if debug + if (FlxG.keys.justPressed.T) + { + rankAnimStart(fromResultsParams ?? + { + playRankAnim: true, + newRank: PERFECT_GOLD, + songId: "tutorial", + difficultyId: "hard" + }); + } + // if (FlxG.keys.justPressed.H) // { // rankDisplayNew(fromResultsParams); @@ -1495,23 +1486,7 @@ class FreeplayState extends MusicBeatSubState var longestTimer:Float = 0; - // FlxTween.color(bgDad, 0.33, 0xFFFFFFFF, 0xFF555555, {ease: FlxEase.quadOut}); - FlxTween.color(pinkBack, 0.25, 0xFFFFD863, 0xFFFFD0D5, {ease: FlxEase.quadOut}); - - cardGlow.visible = true; - cardGlow.alpha = 1; - cardGlow.scale.set(1, 1); - FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.25, {ease: FlxEase.sineOut}); - - orangeBackShit.visible = false; - alsoOrangeLOL.visible = false; - - moreWays.visible = false; - funnyScroll.visible = false; - txtNuts.visible = false; - funnyScroll2.visible = false; - moreWays2.visible = false; - funnyScroll3.visible = false; + backingCard?.disappear(); for (grpSpr in exitMovers.keys()) { @@ -1787,39 +1762,7 @@ class FreeplayState extends MusicBeatSubState grpCapsules.members[curSelected].forcePosition(); grpCapsules.members[curSelected].confirm(); - // FlxTween.color(bgDad, 0.33, 0xFFFFFFFF, 0xFF555555, {ease: FlxEase.quadOut}); - FlxTween.color(pinkBack, 0.33, 0xFFFFD0D5, 0xFF171831, {ease: FlxEase.quadOut}); - orangeBackShit.visible = false; - alsoOrangeLOL.visible = false; - - confirmGlow.visible = true; - confirmGlow2.visible = true; - - backingTextYeah.playAnimation("BF back card confirm raw", false, false, false, 0); - confirmGlow2.alpha = 0; - confirmGlow.alpha = 0; - - FlxTween.tween(confirmGlow2, {alpha: 0.5}, 0.33, - { - ease: FlxEase.quadOut, - onComplete: function(_) { - confirmGlow2.alpha = 0.6; - confirmGlow.alpha = 1; - confirmTextGlow.visible = true; - confirmTextGlow.alpha = 1; - FlxTween.tween(confirmTextGlow, {alpha: 0.4}, 0.5); - FlxTween.tween(confirmGlow, {alpha: 0}, 0.5); - } - }); - - // confirmGlow - - moreWays.visible = false; - funnyScroll.visible = false; - txtNuts.visible = false; - funnyScroll2.visible = false; - moreWays2.visible = false; - funnyScroll3.visible = false; + backingCard?.confirm(); new FlxTimer().start(1, function(tmr:FlxTimer) { FunkinSound.emptyPartialQueue(); diff --git a/source/funkin/ui/freeplay/backcards/BackingCard.hx b/source/funkin/ui/freeplay/backcards/BackingCard.hx new file mode 100644 index 0000000000..8a2a2c910b --- /dev/null +++ b/source/funkin/ui/freeplay/backcards/BackingCard.hx @@ -0,0 +1,217 @@ +package funkin.ui.freeplay.backcards; + +import funkin.ui.freeplay.FreeplayState; +import flixel.FlxCamera; +import flixel.FlxSprite; +import flixel.group.FlxGroup; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; +import flixel.math.FlxAngle; +import flixel.math.FlxPoint; +import flixel.text.FlxText; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; +import flixel.util.FlxSpriteUtil; +import flixel.util.FlxTimer; +import funkin.graphics.adobeanimate.FlxAtlasSprite; +import funkin.graphics.FunkinSprite; +import funkin.ui.freeplay.charselect.PlayableCharacter; +import funkin.ui.MusicBeatSubState; +import lime.utils.Assets; +import openfl.display.BlendMode; +import flixel.group.FlxSpriteGroup; + +/** + * A class for the backing cards so they dont have to be part of freeplayState...... + */ +class BackingCard extends FlxSpriteGroup +{ + public var backingTextYeah:FlxAtlasSprite; + public var orangeBackShit:FunkinSprite; + public var alsoOrangeLOL:FunkinSprite; + public var pinkBack:FunkinSprite; + public var confirmGlow:FlxSprite; + public var confirmGlow2:FlxSprite; + public var confirmTextGlow:FlxSprite; + public var cardGlow:FlxSprite; + + var _exitMovers:Null; + var _exitMoversCharSel:Null; + + public function new(currentCharacter:PlayableCharacter) + { + super(); + + cardGlow = new FlxSprite(-30, -30).loadGraphic(Paths.image('freeplay/cardGlow')); + confirmGlow = new FlxSprite(-30, 240).loadGraphic(Paths.image('freeplay/confirmGlow')); + confirmTextGlow = new FlxSprite(-8, 115).loadGraphic(Paths.image('freeplay/glowingText')); + pinkBack = FunkinSprite.create('freeplay/pinkBack'); + orangeBackShit = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFFEDA00); + alsoOrangeLOL = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFFFD400); + confirmGlow2 = new FlxSprite(confirmGlow.x, confirmGlow.y).loadGraphic(Paths.image('freeplay/confirmGlow2')); + backingTextYeah = new FlxAtlasSprite(640, 370, Paths.animateAtlas("freeplay/backing-text-yeah"), + { + FrameRate: 24.0, + Reversed: false, + // ?OnComplete:Void -> Void, + ShowPivot: false, + Antialiasing: true, + ScrollFactor: new FlxPoint(1, 1), + }); + + pinkBack.color = 0xFFFFD4E9; // sets it to pink! + pinkBack.x -= pinkBack.width; + } + + /** + * Apply exit movers for the pieces of the backing card. + * @param exitMovers The exit movers to apply. + */ + public function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData, ?exitMoversCharSel:FreeplayState.ExitMoverData):Void + { + if (exitMovers == null) + { + exitMovers = _exitMovers; + } + else + { + _exitMovers = exitMovers; + } + + if (exitMovers == null) return; + + if (exitMoversCharSel == null) + { + exitMoversCharSel = _exitMoversCharSel; + } + else + { + _exitMoversCharSel = exitMoversCharSel; + } + + if (exitMoversCharSel == null) return; + + exitMovers.set([pinkBack, orangeBackShit, alsoOrangeLOL], + { + x: -pinkBack.width, + y: pinkBack.y, + speed: 0.4, + wait: 0 + }); + + exitMoversCharSel.set([pinkBack], + { + y: -100, + speed: 0.8, + wait: 0.1 + }); + + exitMoversCharSel.set([orangeBackShit, alsoOrangeLOL], + { + y: -40, + speed: 0.8, + wait: 0.1 + }); + } + + /** + * Called in create. Adds sprites and tweens. + */ + public function init():Void + { + FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut}); + add(pinkBack); + + add(orangeBackShit); + + add(alsoOrangeLOL); + + FlxSpriteUtil.alphaMaskFlxSprite(orangeBackShit, pinkBack, orangeBackShit); + orangeBackShit.visible = false; + alsoOrangeLOL.visible = false; + + confirmTextGlow.blend = BlendMode.ADD; + confirmTextGlow.visible = false; + + confirmGlow.blend = BlendMode.ADD; + + confirmGlow.visible = false; + confirmGlow2.visible = false; + + add(confirmGlow2); + add(confirmGlow); + + add(confirmTextGlow); + + add(backingTextYeah); + + cardGlow.blend = BlendMode.ADD; + cardGlow.visible = false; + + add(cardGlow); + } + + /** + * Override parts of Freeplay depending on the card class. + */ + public function applyStyle(_freeplayState:FreeplayState):Void {} + + /** + * Called after the dj finishes their start animation. + */ + public function introDone():Void + { + pinkBack.color = 0xFFFFD863; + orangeBackShit.visible = true; + alsoOrangeLOL.visible = true; + cardGlow.visible = true; + FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.45, {ease: FlxEase.sineOut}); + } + + /** + * Called when selecting a song. + */ + public function confirm():Void + { + FlxTween.color(pinkBack, 0.33, 0xFFFFD0D5, 0xFF171831, {ease: FlxEase.quadOut}); + orangeBackShit.visible = false; + alsoOrangeLOL.visible = false; + + confirmGlow.visible = true; + confirmGlow2.visible = true; + + backingTextYeah.playAnimation("BF back card confirm raw", false, false, false, 0); + confirmGlow2.alpha = 0; + confirmGlow.alpha = 0; + + FlxTween.tween(confirmGlow2, {alpha: 0.5}, 0.33, + { + ease: FlxEase.quadOut, + onComplete: function(_) { + confirmGlow2.alpha = 0.6; + confirmGlow.alpha = 1; + confirmTextGlow.visible = true; + confirmTextGlow.alpha = 1; + FlxTween.tween(confirmTextGlow, {alpha: 0.4}, 0.5); + FlxTween.tween(confirmGlow, {alpha: 0}, 0.5); + } + }); + } + + /** + * Called when exiting the freeplay menu + */ + public function disappear():Void + { + FlxTween.color(pinkBack, 0.25, 0xFFFFD863, 0xFFFFD0D5, {ease: FlxEase.quadOut}); + + cardGlow.visible = true; + cardGlow.alpha = 1; + cardGlow.scale.set(1, 1); + FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.25, {ease: FlxEase.sineOut}); + + orangeBackShit.visible = false; + alsoOrangeLOL.visible = false; + } +} diff --git a/source/funkin/ui/freeplay/backcards/BoyfriendCard.hx b/source/funkin/ui/freeplay/backcards/BoyfriendCard.hx new file mode 100644 index 0000000000..737a0a2dc1 --- /dev/null +++ b/source/funkin/ui/freeplay/backcards/BoyfriendCard.hx @@ -0,0 +1,168 @@ +package funkin.ui.freeplay.backcards; + +import funkin.ui.freeplay.FreeplayState; +import flixel.FlxCamera; +import flixel.FlxSprite; +import flixel.group.FlxGroup; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; +import flixel.math.FlxAngle; +import flixel.math.FlxPoint; +import flixel.text.FlxText; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; +import flixel.util.FlxSpriteUtil; +import flixel.util.FlxTimer; +import funkin.graphics.adobeanimate.FlxAtlasSprite; +import funkin.graphics.FunkinSprite; +import funkin.ui.freeplay.charselect.PlayableCharacter; +import funkin.ui.MusicBeatSubState; +import lime.utils.Assets; +import openfl.display.BlendMode; +import flixel.group.FlxSpriteGroup; + +/** + * A class for the backing cards so they dont have to be part of freeplayState...... + */ +class BoyfriendCard extends BackingCard +{ + public var moreWays:BGScrollingText; + public var funnyScroll:BGScrollingText; + public var txtNuts:BGScrollingText; + public var funnyScroll2:BGScrollingText; + public var moreWays2:BGScrollingText; + public var funnyScroll3:BGScrollingText; + + public override function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData, ?exitMoversCharSel:FreeplayState.ExitMoverData):Void + { + super.applyExitMovers(exitMovers, exitMoversCharSel); + if (exitMovers == null || exitMoversCharSel == null) return; + exitMovers.set([moreWays], + { + x: FlxG.width * 2, + speed: 0.4, + }); + exitMovers.set([funnyScroll], + { + x: -funnyScroll.width * 2, + y: funnyScroll.y, + speed: 0.4, + wait: 0 + }); + exitMovers.set([txtNuts], + { + x: FlxG.width * 2, + speed: 0.4, + }); + exitMovers.set([funnyScroll2], + { + x: -funnyScroll2.width * 2, + speed: 0.5, + }); + exitMovers.set([moreWays2], + { + x: FlxG.width * 2, + speed: 0.4 + }); + exitMovers.set([funnyScroll3], + { + x: -funnyScroll3.width * 2, + speed: 0.3 + }); + + exitMoversCharSel.set([moreWays, funnyScroll, txtNuts, funnyScroll2, moreWays2, funnyScroll3], + { + y: -60, + speed: 0.8, + wait: 0.1 + }); + } + + public override function new(currentCharacter:PlayableCharacter) + { + super(currentCharacter); + + funnyScroll = new BGScrollingText(0, 220, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, false, 60); + funnyScroll2 = new BGScrollingText(0, 335, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, false, 60); + moreWays = new BGScrollingText(0, 160, currentCharacter.getFreeplayDJText(2), FlxG.width, true, 43); + moreWays2 = new BGScrollingText(0, 397, currentCharacter.getFreeplayDJText(2), FlxG.width, true, 43); + txtNuts = new BGScrollingText(0, 285, currentCharacter.getFreeplayDJText(3), FlxG.width / 2, true, 43); + funnyScroll3 = new BGScrollingText(0, orangeBackShit.y + 10, currentCharacter.getFreeplayDJText(1), FlxG.width / 2, 60); + } + + public override function init():Void + { + super.init(); + + // var grpTxtScrolls:FlxGroup = new FlxGroup(); + // add(grpTxtScrolls); + + moreWays.visible = false; + funnyScroll.visible = false; + txtNuts.visible = false; + funnyScroll2.visible = false; + moreWays2.visible = false; + funnyScroll3.visible = false; + + moreWays.funnyColor = 0xFFFFF383; + moreWays.speed = 6.8; + add(moreWays); + + funnyScroll.funnyColor = 0xFFFF9963; + funnyScroll.speed = -3.8; + add(funnyScroll); + + txtNuts.speed = 3.5; + add(txtNuts); + + funnyScroll2.funnyColor = 0xFFFF9963; + funnyScroll2.speed = -3.8; + add(funnyScroll2); + + moreWays2.funnyColor = 0xFFFFF383; + moreWays2.speed = 6.8; + add(moreWays2); + + funnyScroll3.funnyColor = 0xFFFEA400; + funnyScroll3.speed = -3.8; + add(funnyScroll3); + } + + public override function introDone():Void + { + super.introDone(); + moreWays.visible = true; + funnyScroll.visible = true; + txtNuts.visible = true; + funnyScroll2.visible = true; + moreWays2.visible = true; + funnyScroll3.visible = true; + // grpTxtScrolls.visible = true; + } + + public override function confirm():Void + { + super.confirm(); + // FlxTween.color(bgDad, 0.33, 0xFFFFFFFF, 0xFF555555, {ease: FlxEase.quadOut}); + + moreWays.visible = false; + funnyScroll.visible = false; + txtNuts.visible = false; + funnyScroll2.visible = false; + moreWays2.visible = false; + funnyScroll3.visible = false; + } + + public override function disappear():Void + { + super.disappear(); + + moreWays.visible = false; + funnyScroll.visible = false; + txtNuts.visible = false; + funnyScroll2.visible = false; + moreWays2.visible = false; + funnyScroll3.visible = false; + } +} diff --git a/source/funkin/ui/freeplay/backcards/PicoCard.hx b/source/funkin/ui/freeplay/backcards/PicoCard.hx new file mode 100644 index 0000000000..a424a76efb --- /dev/null +++ b/source/funkin/ui/freeplay/backcards/PicoCard.hx @@ -0,0 +1,74 @@ +package funkin.ui.freeplay.backcards; + +import funkin.ui.freeplay.FreeplayState; +import flixel.FlxCamera; +import flixel.FlxSprite; +import flixel.group.FlxGroup; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; +import flixel.math.FlxAngle; +import flixel.math.FlxPoint; +import flixel.text.FlxText; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; +import flixel.util.FlxSpriteUtil; +import flixel.util.FlxTimer; +import funkin.graphics.adobeanimate.FlxAtlasSprite; +import funkin.graphics.FunkinSprite; +import funkin.ui.freeplay.charselect.PlayableCharacter; +import funkin.ui.MusicBeatSubState; +import lime.utils.Assets; +import openfl.display.BlendMode; +import flixel.group.FlxSpriteGroup; + +/** + * A class for the backing cards so they dont have to be part of freeplayState...... + */ +class PicoCard extends BackingCard +{ + public override function init():Void + { + FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut}); + add(pinkBack); + + // add(orangeBackShit); + + // add(alsoOrangeLOL); + + // FlxSpriteUtil.alphaMaskFlxSprite(orangeBackShit, pinkBack, orangeBackShit); + // orangeBackShit.visible = false; + // alsoOrangeLOL.visible = false; + + confirmTextGlow.blend = BlendMode.ADD; + confirmTextGlow.visible = false; + + confirmGlow.blend = BlendMode.ADD; + + confirmGlow.visible = false; + confirmGlow2.visible = false; + + add(confirmGlow2); + add(confirmGlow); + + add(confirmTextGlow); + + add(backingTextYeah); + + cardGlow.blend = BlendMode.ADD; + cardGlow.visible = false; + + add(cardGlow); + } + + public override function applyStyle(_freeplayState:FreeplayState):Void {} + + public override function introDone():Void + { + pinkBack.color = 0xFF98A2F3; + // orangeBackShit.visible = true; + // alsoOrangeLOL.visible = true; + cardGlow.visible = true; + FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.45, {ease: FlxEase.sineOut}); + } +} From 9e1d328a0f3ae6236cf036abf78d54bd24c0a7ab Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 23 Aug 2024 13:37:50 +0100 Subject: [PATCH 280/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 2eac236291..0d97c9b20a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 2eac236291775208ce71287a5c27f8f15ae591c9 +Subproject commit 0d97c9b20a989b4517fd6174fe56583ac600522c From 025c186ddc5e9b68109835ac800b0d8cbb23dc39 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 26 Aug 2024 13:18:05 -0400 Subject: [PATCH 281/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index b57d7f8d30..2ad892c766 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit b57d7f8d308e468f7b0947d4784d0efeca44d9aa +Subproject commit 2ad892c76665abad5764137288c606f2f4fcd331 From 0b19b4bc25806ab9ae82376946937c3e95b7feb8 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Mon, 26 Aug 2024 20:34:14 +0200 Subject: [PATCH 282/469] add the maskTest Bizz --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 299f5ac20f..0e4392bcb0 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 299f5ac20fd9ee50fda5cadd6abb22b68d86fb87 +Subproject commit 0e4392bcb0ed1dbb496764cff074635c20c91389 From a1714711e3f85f207b47dab03899543428bce3f1 Mon Sep 17 00:00:00 2001 From: mint Date: Mon, 26 Aug 2024 17:52:37 +0200 Subject: [PATCH 283/469] swap docker image deps --- .github/actions/setup-haxe/action.yml | 5 ++--- .github/workflows/build-game.yml | 1 + build/Dockerfile | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml index b22fc6f693..cf74cfb635 100644 --- a/.github/actions/setup-haxe/action.yml +++ b/.github/actions/setup-haxe/action.yml @@ -56,11 +56,10 @@ runs: shell: bash run: | echo "TIMER_HAXELIB=$(date +%s)" >> "$GITHUB_ENV" - haxelib --debug --never install haxelib 4.1.0 --global - haxelib --debug --never deleterepo || true + haxelib --debug --always git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies --global + rm -rf .haxelib haxelib --debug --never newrepo echo "HAXEPATH=$(haxelib config)" >> "$GITHUB_ENV" - haxelib --debug --always --global git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies haxelib --debug --global --always git hmm https://github.com/FunkinCrew/hmm funkin-patches echo "TIMER_DEPS=$(date +%s)" >> "$GITHUB_ENV" diff --git a/.github/workflows/build-game.yml b/.github/workflows/build-game.yml index ba8167607a..46f478a14d 100644 --- a/.github/workflows/build-game.yml +++ b/.github/workflows/build-game.yml @@ -112,6 +112,7 @@ jobs: run: | git config --global 'url.https://x-access-token:${{ steps.app_token.outputs.token }}@github.com/.insteadOf' https://github.com/ haxelib --global run hmm install -q + cd .haxelib/hxcpp/git/tools/hxcpp && haxe compile.hxml - if: ${{ matrix.target != 'html5' }} name: Restore hxcpp cache diff --git a/build/Dockerfile b/build/Dockerfile index c545d1364b..0e61bd92ce 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -137,8 +137,8 @@ ENV PATH="$HAXEPATH:$PATH" RUN < Date: Mon, 26 Aug 2024 18:12:16 +0200 Subject: [PATCH 284/469] pulseaudio --- .github/actions/setup-haxe/action.yml | 2 +- build/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml index cf74cfb635..298dda631a 100644 --- a/.github/actions/setup-haxe/action.yml +++ b/.github/actions/setup-haxe/action.yml @@ -44,7 +44,7 @@ runs: g++ \ libx11-dev libxi-dev libxext-dev libxinerama-dev libxrandr-dev \ libgl-dev libgl1-mesa-dev \ - libasound2-dev + libasound2-dev libpulse-dev ln -s /usr/lib/x86_64-linux-gnu/libffi.so.8 /usr/lib/x86_64-linux-gnu/libffi.so.6 || true - name: Install linux-specific dependencies if: ${{ runner.os == 'Linux' && contains(inputs.targets, 'linux') }} diff --git a/build/Dockerfile b/build/Dockerfile index 0e61bd92ce..3188701662 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -83,7 +83,7 @@ apt-fast install -y --no-install-recommends \ libc6-dev libffi-dev \ libx11-dev libxi-dev libxext-dev libxinerama-dev libxrandr-dev \ libgl-dev libgl1-mesa-dev \ - libasound2-dev \ + libasound2-dev libpulse-dev \ libvlc-dev libvlccore-dev EOF From 1089764ab62a174525aaa6159e15db04b91df20a Mon Sep 17 00:00:00 2001 From: mint Date: Mon, 26 Aug 2024 18:56:32 +0200 Subject: [PATCH 285/469] suppress useless git warnings --- .github/workflows/build-game.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-game.yml b/.github/workflows/build-game.yml index 46f478a14d..dff9a369d3 100644 --- a/.github/workflows/build-game.yml +++ b/.github/workflows/build-game.yml @@ -111,6 +111,7 @@ jobs: name: Install dependencies run: | git config --global 'url.https://x-access-token:${{ steps.app_token.outputs.token }}@github.com/.insteadOf' https://github.com/ + git config --global advice.detachedHead false haxelib --global run hmm install -q cd .haxelib/hxcpp/git/tools/hxcpp && haxe compile.hxml From 7cac3c95eea321367803ebb77c8e8c4a9de098a8 Mon Sep 17 00:00:00 2001 From: mint Date: Mon, 26 Aug 2024 19:27:11 +0200 Subject: [PATCH 286/469] config the haxelib :eye: --- .github/actions/setup-haxe/action.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml index 298dda631a..918718c55a 100644 --- a/.github/actions/setup-haxe/action.yml +++ b/.github/actions/setup-haxe/action.yml @@ -56,11 +56,14 @@ runs: shell: bash run: | echo "TIMER_HAXELIB=$(date +%s)" >> "$GITHUB_ENV" - haxelib --debug --always git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies --global + haxelib remove --global haxelib git + haxelib remove --global hmm git rm -rf .haxelib + git config -l --show-scope + haxelib --debug --never --global git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies + haxelib --debug --never --global git hmm https://github.com/FunkinCrew/hmm funkin-patches haxelib --debug --never newrepo echo "HAXEPATH=$(haxelib config)" >> "$GITHUB_ENV" - haxelib --debug --global --always git hmm https://github.com/FunkinCrew/hmm funkin-patches echo "TIMER_DEPS=$(date +%s)" >> "$GITHUB_ENV" - name: Restore cached dependencies From 0193bde70058e0017ac253ed053917b0752820bb Mon Sep 17 00:00:00 2001 From: mint Date: Mon, 26 Aug 2024 19:32:38 +0200 Subject: [PATCH 287/469] hm, pain --- .github/actions/setup-haxe/action.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml index 918718c55a..4a81d00790 100644 --- a/.github/actions/setup-haxe/action.yml +++ b/.github/actions/setup-haxe/action.yml @@ -56,13 +56,16 @@ runs: shell: bash run: | echo "TIMER_HAXELIB=$(date +%s)" >> "$GITHUB_ENV" - haxelib remove --global haxelib git - haxelib remove --global hmm git + haxelib --debug --never --global install haxelib 4.1.0 + haxelib --global set haxelib 4.1.0 + haxelib --global remove haxelib git + haxelib --global remove hmm git rm -rf .haxelib git config -l --show-scope haxelib --debug --never --global git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies haxelib --debug --never --global git hmm https://github.com/FunkinCrew/hmm funkin-patches haxelib --debug --never newrepo + haxelib version echo "HAXEPATH=$(haxelib config)" >> "$GITHUB_ENV" echo "TIMER_DEPS=$(date +%s)" >> "$GITHUB_ENV" From 8010441befab58f56e5ef12859555a4c41762d96 Mon Sep 17 00:00:00 2001 From: mint Date: Mon, 26 Aug 2024 19:34:30 +0200 Subject: [PATCH 288/469] haxelib --- .github/actions/setup-haxe/action.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml index 4a81d00790..193c81d1fc 100644 --- a/.github/actions/setup-haxe/action.yml +++ b/.github/actions/setup-haxe/action.yml @@ -56,10 +56,11 @@ runs: shell: bash run: | echo "TIMER_HAXELIB=$(date +%s)" >> "$GITHUB_ENV" + haxelib fixrepo --global haxelib --debug --never --global install haxelib 4.1.0 haxelib --global set haxelib 4.1.0 haxelib --global remove haxelib git - haxelib --global remove hmm git + haxelib --global remove hmm rm -rf .haxelib git config -l --show-scope haxelib --debug --never --global git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies From 514aa576eafdb5fc5444d1689be5765053e940cd Mon Sep 17 00:00:00 2001 From: mint Date: Mon, 26 Aug 2024 19:35:54 +0200 Subject: [PATCH 289/469] haxelib --- .github/actions/setup-haxe/action.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml index 193c81d1fc..aba5371b58 100644 --- a/.github/actions/setup-haxe/action.yml +++ b/.github/actions/setup-haxe/action.yml @@ -56,11 +56,10 @@ runs: shell: bash run: | echo "TIMER_HAXELIB=$(date +%s)" >> "$GITHUB_ENV" - haxelib fixrepo --global + haxelib fixrepo --global || true haxelib --debug --never --global install haxelib 4.1.0 - haxelib --global set haxelib 4.1.0 - haxelib --global remove haxelib git - haxelib --global remove hmm + haxelib --global remove haxelib git || true + haxelib --global remove hmm || true rm -rf .haxelib git config -l --show-scope haxelib --debug --never --global git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies From 266bf939a977c8136bce647a4475cdc6ebe1f69e Mon Sep 17 00:00:00 2001 From: mint Date: Mon, 26 Aug 2024 19:38:21 +0200 Subject: [PATCH 290/469] haxelib --- .github/actions/setup-haxe/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml index aba5371b58..97bc02e669 100644 --- a/.github/actions/setup-haxe/action.yml +++ b/.github/actions/setup-haxe/action.yml @@ -58,6 +58,7 @@ runs: echo "TIMER_HAXELIB=$(date +%s)" >> "$GITHUB_ENV" haxelib fixrepo --global || true haxelib --debug --never --global install haxelib 4.1.0 + haxelib --debug --global set haxelib 4.1.0 haxelib --global remove haxelib git || true haxelib --global remove hmm || true rm -rf .haxelib From a778555a973bc3227689b592aec084f349579640 Mon Sep 17 00:00:00 2001 From: mint Date: Mon, 26 Aug 2024 19:52:53 +0200 Subject: [PATCH 291/469] more debug info --- .github/actions/setup-haxe/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml index 97bc02e669..9da7d32721 100644 --- a/.github/actions/setup-haxe/action.yml +++ b/.github/actions/setup-haxe/action.yml @@ -62,7 +62,7 @@ runs: haxelib --global remove haxelib git || true haxelib --global remove hmm || true rm -rf .haxelib - git config -l --show-scope + git config -l --show-scope --show-origin haxelib --debug --never --global git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies haxelib --debug --never --global git hmm https://github.com/FunkinCrew/hmm funkin-patches haxelib --debug --never newrepo From 3c208ee143c271f4fd1cf8293aa96680e04e135c Mon Sep 17 00:00:00 2001 From: mint Date: Mon, 26 Aug 2024 20:00:53 +0200 Subject: [PATCH 292/469] git cleanup crew --- .github/actions/setup-haxe/action.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml index 9da7d32721..438f330a24 100644 --- a/.github/actions/setup-haxe/action.yml +++ b/.github/actions/setup-haxe/action.yml @@ -62,7 +62,6 @@ runs: haxelib --global remove haxelib git || true haxelib --global remove hmm || true rm -rf .haxelib - git config -l --show-scope --show-origin haxelib --debug --never --global git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies haxelib --debug --never --global git hmm https://github.com/FunkinCrew/hmm funkin-patches haxelib --debug --never newrepo @@ -81,7 +80,11 @@ runs: name: Prep git for dependency install uses: gacts/run-and-post-run@v1 with: - run: git config --global 'url.https://x-access-token:${{ inputs.gh-token }}@github.com/.insteadOf' https://github.com/ + run: | + git config --global --name-only --get-regexp 'url\.https\:\/\/x-access-token:.+@github\.com\/\.insteadOf' \ + | xargs git config --global --unset + git config -l --show-scope --show-origin + git config --global 'url.https://x-access-token:${{ inputs.gh-token }}@github.com/.insteadOf' https://github.com/ post: git config --global --unset 'url.https://x-access-token:${{ inputs.gh-token }}@github.com/.insteadOf' - if: ${{ steps.cache-hmm.outputs.cache-hit != 'true' }} From b51ba61d078b8a4247a0b09a01db53edc536b096 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 26 Aug 2024 17:14:15 -0400 Subject: [PATCH 293/469] Update art submodule --- art | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/art b/art index faeba700c5..0bb988c497 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit faeba700c5526bd4fd57ccc927d875c82b9d3553 +Subproject commit 0bb988c49788fd25a230b56dd9e4448838bc79c9 From f2433bfdcc4fa240687260e7ab7e450e035fd16c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 26 Aug 2024 18:00:59 -0400 Subject: [PATCH 294/469] Updated CHANGELOG --- CHANGELOG.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 653ee203f8..53e981284e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,62 @@ All notable changes will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.0] - 2024-08-?? +### Added +- Added a new Character Select screen to switch between playable characters in Freeplay + - Modding isn't 100% there but we're working on it! +- Added Pico as a playable character! Unlock him by completing Weekend 1 (if you haven't already done that) + - The songs from Weekend 1 have moved; you must now switch to Pico in Freeplay to access them +- Added ## new Pico remixes! Access them by selecting Pico from in the Character Select screen +- Added 2 new Erect remixes! Access them by switching difficulty on the song +- Implemented support for a new Instrumental Selector in Freeplay + - Beating a Pico remix lets you use that instrumental when playing as Boyfriend +- Added the first batch of Erect Stages! These graphical overhauls of the original stages will be used when playing Erect remixes and Pico remixes +- Implemented support for scripted Note Kinds. You can use HScript define a different note style to display for these notes as well as custom behavior. (community feature by lemz1) +- Implemented a new Strumline Background option, to display a darkened background behind the strumline with your choice of opacity. +- Implemented support for Numeric and Selector options in the Options menu. (community feature by FlooferLand) +## Changed +- Girlfriend and Nene now perform previously unused animations when you achieve a large combo, or drop a large combo. +- The pixel character icons in the Freeplay menu now display an animation! +- Altered how Week 6 displays sprites to make things look more retro. +- Character offsets are now independent of the character's scale. + - This should resolve issues with offsets when porting characters from older mods. + - Pixel character offsets have been modified to compensate. +- Note style data can now specify custom combo count graphics, judgement graphics, countdown graphics, and countdown audio. (community feature by anysad) + - These were previously using hardcoded values based on whether the stage was `school` or `schoolEvil`. +- The `danceEvery` property of characters and stage props can now use values with a precision of `0.25`, to play their idle animation up to four times per beat. +- Reworked the JSON merging system in Polymod; you can now include JSONPatch files under `_merge` in your mod folder to add, modify, or remove values in a JSON without replacing it entirely! +- Cutscenes now automatically pause when tabbing out (community fix by AbnormalPoof) +- Characters will now respect the `danceEvery` property (community fix by gamerbross) +- The F5 function now reloads the current song's chart data from disc (community feature by gamerbross) +- Refactored the compilation guide and added common troubleshooting steps (community fix by Hundrec) +- Made several layout improvements and fixes to the Animation Offsets editor in the Debug menu (community fix by gamerbross) +- Fixed a bug where the Back sound would be not played when leaving the Story menu and Options menu (community fix by AppleHair) +- Animation offsets no longer directly modify the `x` and `y` position of props, which makes props work better with tweens (community fix by Sword352) +- The YEAH! events in Tutorial now use chart events rather than being hard-coded (community fix by anysad) +- The player's Score now displays commas in it (community fix by loggo) +## Fixed +- Fixed an issue where songs with no notes would crash on the Results screen. +- Fixed an issue where the old icon easter egg would not work properly on pixel levels. +- Fixed an issue where you could play notes during the Thorns cutscene. +- Fixed an issue where the Heart icon when favoriting a song in Freeplay would be malformed. +- Fixed an issue where Pico's death animation displays a faint blue background (community fix by doggogit) +- Fixed an issue where mod songs would not play a preview in the Freeplay menu (community fix by KarimAkra) +- Fixed an issue where the Memory Usage counter could overflow and display a negative number (community fix by KarimAkra) +- Fixed an issue where pressing the Chart Editor keybind while playtesting a chart would reset the chart editor (community fix by gamerbross) +- Fixed a crash bug when pressing F5 after seeing the sticker transition (community fix by gamerbross) +- Fixed an issue where the Story Mode menu couldn't be scrolled with a mouse (community fix by JVNpixels) +- Fixed an issue causing the song to majorly desync sometimes (community fix by Burgerballs) +- Fixed an issue where the Freeplay song preview would not respect the instrumental ID specified in the song metadata (community fix by AppleHair) +- Fixed an issue where Tankman's icon wouldn't display in the Chart Editor (community fix by hundrec) +- Fixed an issue where pausing the game during a camera zoom would zoom the pause menu. (community fix by gamerbros) +- Fixed an issue where certain UI elements would not flash at a consistent rate (community fix by cyn0x8) +- Fixed an issue where the game would not use the placeholder health icon as a fallback (community fix by gamerbross) +- Fixed an issue where the chart editor could get stuck creating a hold note when using Live Inputs (community fix by gamerbross) +- Fixed an issue where character graphics could not be placed in week folders (community fix by 7oltan) +- Fixed a crash issue when a Freeplay song has no `Normal` difficulty (community fix by Applehair and gamerbross) +- Fixed an issue in Story Mode where a song that isn't valid for the current variation could be selected (community fix by Applehair) + ## [0.4.1] - 2024-06-12 ### Added - Pressing ESCAPE on the title screen on desktop now exits the game, allowing you to exit the game while in fullscreen on desktop From 269f3ecfbe16b32698c0a6adcea4c8648faf509b Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 26 Aug 2024 18:01:06 -0400 Subject: [PATCH 295/469] Updated haxelibs --- hmm.json | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/hmm.json b/hmm.json index 4cd3e27c7d..38e82acf24 100644 --- a/hmm.json +++ b/hmm.json @@ -1,5 +1,12 @@ { "dependencies": [ + { + "name": "FlxPartialSound", + "type": "git", + "dir": null, + "ref": "a1eab7b9bf507b87200a3341719054fe427f3b15", + "url": "https://github.com/FunkinCrew/FlxPartialSound.git" + }, { "name": "discord_rpc", "type": "git", @@ -32,7 +39,7 @@ "name": "flixel-ui", "type": "git", "dir": null, - "ref": "d0afed7293c71ffdb1184751317fc709b44c9056", + "ref": "27f1ba626f80a6282fa8a187115e79a4a2133dc2", "url": "https://github.com/HaxeFlixel/flixel-ui" }, { @@ -42,13 +49,6 @@ "ref": "768740a56b26aa0c072720e0d1236b94afe68e3e", "url": "https://github.com/Dot-Stuff/flxanimate" }, - { - "name": "FlxPartialSound", - "type": "git", - "dir": null, - "ref": "a1eab7b9bf507b87200a3341719054fe427f3b15", - "url": "https://github.com/FunkinCrew/FlxPartialSound.git" - }, { "name": "format", "type": "haxelib", @@ -59,7 +59,7 @@ "type": "git", "dir": null, "ref": "22b1ce089dd924f15cdc4632397ef3504d464e90", - "url": "https://github.com/MidyGamy/funkVis/" + "url": "https://github.com/FunkinCrew/funkVis" }, { "name": "grig.audio", @@ -89,8 +89,10 @@ }, { "name": "hscript", - "type": "haxelib", - "version": "2.5.0" + "type": "git", + "dir": null, + "ref": "59b33162e9dcd7e48b186a9f25ee81b6e47a4dd7", + "url": "https://github.com/haxefoundation/hscript" }, { "name": "hxCodec", @@ -104,7 +106,7 @@ "type": "git", "dir": null, "url": "https://github.com/HaxeFoundation/hxcpp", - "ref": "8dc8020f8465027de6c2aaaed90718bc693651ed" + "ref": "904ea40643b050a5a154c5e4c33a83fd2aec18b1" }, { "name": "hxcpp-debug-server", @@ -185,7 +187,7 @@ "name": "polymod", "type": "git", "dir": null, - "ref": "98945c6c7f5ecde01a32c4623d3515bf012a023a", + "ref": "96cfc5fa693b017e47f7cb13b765cc68698fa6b6", "url": "https://github.com/larsiusprime/polymod" }, { From 6b9bdb5f2adb76ced506e424b8e2a961af02944a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 26 Aug 2024 18:01:23 -0400 Subject: [PATCH 296/469] Upgraded project file to HXP --- .github/labeler.yml | 1 - .vscode/settings.json | 30 +- Project.xml | 266 ---------- project.hxp | 1109 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1124 insertions(+), 282 deletions(-) delete mode 100644 Project.xml create mode 100644 project.hxp diff --git a/.github/labeler.yml b/.github/labeler.yml index e8e4908652..e8250b4e77 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,7 +1,6 @@ # Add Documentation tag to PR's changing markdown files, or anyhting in the docs folder Documentation: - changed-files: - - any-glob-to-any-file: - any-glob-to-any-file: - docs/* - '**/*.md' diff --git a/.vscode/settings.json b/.vscode/settings.json index 26fe0b0427..227cb94ec2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -94,12 +94,12 @@ { "label": "Windows / Debug", "target": "windows", - "args": ["-debug", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "Linux / Debug", "target": "linux", - "args": ["-debug", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "HashLink / Debug", @@ -109,7 +109,7 @@ { "label": "Windows / Debug (FlxAnimate Test)", "target": "windows", - "args": ["-debug", "-DANIMATE", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DANIMATE", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "HashLink / Debug (FlxAnimate Test)", @@ -119,7 +119,7 @@ { "label": "Windows / Debug (Straight to Freeplay)", "target": "windows", - "args": ["-debug", "-DFREEPLAY", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DFREEPLAY", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "HashLink / Debug (Straight to Freeplay)", @@ -132,13 +132,13 @@ "args": [ "-debug", "-DSONG=bopeebo -DDIFFICULTY=normal", - "-DFORCE_DEBUG_VERSION" + "-DFEATURE_DEBUG_FUNCTIONS" ] }, { "label": "Windows / Debug (Straight to Play - 2hot)", "target": "windows", - "args": ["-debug", "-DSONG=2hot", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DSONG=2hot", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "HashLink / Debug (Straight to Play - Bopeebo Normal)", @@ -148,7 +148,7 @@ { "label": "Windows / Debug (Conversation Test)", "target": "windows", - "args": ["-debug", "-DDIALOGUE", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DDIALOGUE", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "HashLink / Debug (Conversation Test)", @@ -163,7 +163,7 @@ { "label": "Windows / Debug (Straight to Chart Editor)", "target": "windows", - "args": ["-debug", "-DCHARTING", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DCHARTING", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "HashLink / Debug (Straight to Chart Editor)", @@ -173,12 +173,12 @@ { "label": "Windows / Debug (Straight to Animation Editor)", "target": "windows", - "args": ["-debug", "-DANIMDEBUG", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DANIMDEBUG", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "Windows / Debug (Debug hxCodec)", "target": "windows", - "args": ["-debug", "-DHXC_LIBVLC_LOGGING", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DHXC_LIBVLC_LOGGING", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "HashLink / Debug (Straight to Animation Editor)", @@ -188,7 +188,7 @@ { "label": "Windows / Debug (Latency Test)", "target": "windows", - "args": ["-debug", "-DLATENCY", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DLATENCY", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "HashLink / Debug (Latency Test)", @@ -198,7 +198,7 @@ { "label": "Windows / Debug (Waveform Test)", "target": "windows", - "args": ["-debug", "-DWAVEFORM", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DWAVEFORM", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "Windows / Release", @@ -218,17 +218,17 @@ { "label": "HTML5 / Debug", "target": "html5", - "args": ["-debug", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "HTML5 / Debug (Watch)", "target": "html5", - "args": ["-debug", "-watch", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-watch", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "macOS / Debug", "target": "mac", - "args": ["-debug", "-DFORCE_DEBUG_VERSION"] + "args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS"] }, { "label": "macOS / Release", diff --git a/Project.xml b/Project.xml deleted file mode 100644 index 656590aec9..0000000000 --- a/Project.xml +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - -
-
- - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - -
-
- - - - - - -
- - - - -
- - -
- - -
- - -
- - - --> - --> - - - -
- -
- - - - - - -
- - - - - - - - - - - - - - - - -
-
diff --git a/project.hxp b/project.hxp new file mode 100644 index 0000000000..0e090a4c95 --- /dev/null +++ b/project.hxp @@ -0,0 +1,1109 @@ +package; + +// I don't think we can import `funkin` classes here. Macros? Recursion? IDK. +import hxp.*; +import lime.tools.*; +import sys.FileSystem; + +using StringTools; + +/** + * This HXP performs the functions of a Lime `project.xml` file, + * but it's written in Haxe rather than XML! + * + * This makes it far easier to organize, reuse, and refactor, + * and improves management of feature flag logic. + */ +@:nullSafety +class Project extends HXProject { + // + // METADATA + // + + /** + * The game's version number, as a Semantic Versioning string with no prefix. + * REMEMBER TO CHANGE THIS WHEN THE GAME UPDATES! + * You only have to change it here, the rest of the game will query this value. + */ + static final VERSION:String = "0.5.0"; + + /** + * The game's name. Used as the default window title. + */ + static final TITLE:String = "Friday Night Funkin'"; + + /** + * The name of the generated executable file. + * For example, `"Funkin"` will create a file called `Funkin.exe`. + */ + static final EXECUTABLE_NAME:String = "Funkin"; + + /** + * The relative location of the source code. + */ + static final SOURCE_DIR:String = "source"; + + /** + * The fully qualified class path for the game's preloader. + * Particularly important on HTML5 but we use it on all platforms. + */ + static final PRELOADER:String = "funkin.ui.transition.preload.FunkinPreloader"; + + /** + * A package name used for identifying the app on various app stores. + */ + static final PACKAGE_NAME:String = "me.funkin.fnf"; + + /** + * The fully qualified class path for the entry point class to execute when launching the game. + * It's where `public static function main():Void` goes. + */ + static final MAIN_CLASS:String = "Main"; + + /** + * The company name for the game. + * This appears in metadata in places I think. + */ + static final COMPANY:String = "The Funkin' Crew"; + + /** + * Path to the Haxe script run before building the game. + */ + static final PREBUILD_HX:String = "source/Prebuild.hx"; + + /** + * Path to the Haxe script run after building the game. + */ + static final POSTBUILD_HX:String = "source/Postbuild.hx"; + + /** + * Asset path globs to always exclude from asset libraries. + */ + static final EXCLUDE_ASSETS:Array = [".*", "cvs", "thumbs.db", "desktop.ini", "*.hash", "*.md"]; + + /** + * Asset path globs to exclude on web platforms. + */ + static final EXCLUDE_ASSETS_WEB:Array = ["*.ogg"]; + /** + * Asset path globs to exclude on native platforms. + */ + static final EXCLUDE_ASSETS_NATIVE:Array = ["*.mp3"]; + + // + // FEATURE FLAGS + // Inverse feature flags are automatically populated. + // + + /** + * `-DGITHUB_BUILD` + * If this flag is enabled, the game will use the configuration used by GitHub Actions + * to generate playtest builds to be pushed to the launcher. + * + * This is generally used to forcibly enable debugging features, + * even when the game is built in release mode for performance reasons. + */ + static final GITHUB_BUILD:FeatureFlag = "GITHUB_BUILD"; + + /** + * `-DREDIRECT_ASSETS_FOLDER` + * If this flag is enabled, the game will redirect the `assets` folder from the `export` folder + * to the `assets` folder at the root of the workspace. + * This is useful for ensuring hot reloaded changes don't get lost when rebuilding the game. + */ + static final REDIRECT_ASSETS_FOLDER:FeatureFlag = "REDIRECT_ASSETS_FOLDER"; + + /** + * `-DTOUCH_HERE_TO_PLAY` + * If this flag is enabled, the game will display a prompt to the user after the preloader completes, + * requiring them to click anywhere on the screen to start the game. + * This is done to ensure that the audio context can initialize properly on HTML5. Not necessary on desktop. + */ + static final TOUCH_HERE_TO_PLAY:FeatureFlag = "TOUCH_HERE_TO_PLAY"; + + /** + * `-DPRELOAD_ALL` + * Whether to preload all asset libraries. + * Disabled on web, enabled on desktop. + */ + static final PRELOAD_ALL:FeatureFlag = "PRELOAD_ALL"; + + /** + * `-DEMBED_ASSETS` + * Whether to embed all asset libraries into the executable. + */ + static final EMBED_ASSETS:FeatureFlag = "EMBED_ASSETS"; + + /** + * `-DHARDCODED_CREDITS` + * If this flag is enabled, the credits will be parsed and encoded in the game at compile time, + * rather than read from JSON data at runtime. + */ + static final HARDCODED_CREDITS:FeatureFlag = "HARDCODED_CREDITS"; + + /** + * `-DFEATURE_DEBUG_FUNCTIONS` + * If this flag is enabled, the game will have all playtester-only debugging functionality enabled. + * This includes debug hotkeys like time travel in the Play State. + * By default, enabled on debug builds or playtester builds and disabled on release builds. + */ + static final FEATURE_DEBUG_FUNCTIONS:FeatureFlag = "FEATURE_DEBUG_FUNCTIONS"; + + /** + * `-DFEATURE_DISCORD_RPC` + * If this flag is enabled, the game will enable the Discord Remote Procedure Call library. + * This is used to provide Discord Rich Presence support. + */ + static final FEATURE_DISCORD_RPC:FeatureFlag = "FEATURE_DISCORD_RPC"; + + /** + * `-DFEATURE_NEWGROUNDS` + * If this flag is enabled, the game will enable the Newgrounds library. + * This is used to provide Medal and Leaderboard support. + */ + static final FEATURE_NEWGROUNDS:FeatureFlag = "FEATURE_NEWGROUNDS"; + + /** + * `-DFEATURE_FUNKVIS` + * If this flag is enabled, the game will enable the Funkin Visualizer library. + * This is used to provide audio visualization like Nene's speaker. + * Disabling this will make some waveforms inactive. + */ + static final FEATURE_FUNKVIS:FeatureFlag = "FEATURE_FUNKVIS"; + + /** + * `-DFEATURE_PARTIAL_SOUNDS` + * If this flag is enabled, the game will enable the FlxPartialSound library. + * This is used to provide audio previews in Freeplay. + * Disabling this will make those previews not play. + */ + static final FEATURE_PARTIAL_SOUNDS:FeatureFlag = "FEATURE_PARTIAL_SOUNDS"; + + /** + * `-DFEATURE_VIDEO_PLAYBACK` + * If this flag is enabled, the game will enable support for video playback. + * This requires the hxCodec library on desktop platforms. + */ + static final FEATURE_VIDEO_PLAYBACK:FeatureFlag = "FEATURE_VIDEO_PLAYBACK"; + + /** + * `-DFEATURE_FILE_DROP` + * If this flag is enabled, the game will support dragging and dropping files onto it for various features. + * Disabled on MacOS. + */ + static final FEATURE_FILE_DROP:FeatureFlag = "FEATURE_FILE_DROP"; + + /** + * `-DFEATURE_OPEN_URL` + * If this flag is enabled, the game will support opening URLs (such as the merch page). + */ + static final FEATURE_OPEN_URL:FeatureFlag = "FEATURE_OPEN_URL"; + + /** + * `-DFEATURE_CHART_EDITOR` + * If this flag is enabled, the Chart Editor will be accessible from the debug menu. + */ + static final FEATURE_CHART_EDITOR:FeatureFlag = "FEATURE_CHART_EDITOR"; + + /** + * `-DFEATURE_STAGE_EDITOR` + * If this flag is enabled, the Stage Editor will be accessible from the debug menu. + */ + static final FEATURE_STAGE_EDITOR:FeatureFlag = "FEATURE_STAGE_EDITOR"; + + /** + * `-DFEATURE_POLYMOD_MODS` + * If this flag is enabled, the game will enable the Polymod library's support for atomic mod loading from the `./mods` folder. + * If this flag is disabled, no mods will be loaded. + */ + static final FEATURE_POLYMOD_MODS:FeatureFlag = "FEATURE_POLYMOD_MODS"; + + /** + * `-DFEATURE_GHOST_TAPPING` + * If this flag is enabled, misses will not be counted when it is not the player's turn. + * Misses are still counted when the player has notes to hit. + */ + static final FEATURE_GHOST_TAPPING:FeatureFlag = "FEATURE_GHOST_TAPPING"; + + // + // CONFIGURATION FUNCTIONS + // + + public function new() { + super(); + + flair(); + configureApp(); + + displayTarget(); + configureFeatureFlags(); + configureCompileDefines(); + configureIncludeMacros(); + configureCustomMacros(); + configureOutputDir(); + configurePolymod(); + configureHaxelibs(); + configureAssets(); + configureIcons(); + } + + /** + * Do something before building, display some ASCII or something IDK + */ + function flair() { + // TODO: Implement this. + info("Friday Night Funkin'"); + info("Initializing build..."); + + info("Target Version: " + VERSION); + info("Git Branch: " + getGitBranch()); + info("Git Commit: " + getGitCommit()); + info("Git Modified? " + getGitModified()); + info("Display? " + isDisplay()); + } + + /** + * Apply basic project metadata, such as the game title and version number, + * as well as info like the package name and company (used by various app stores). + */ + function configureApp() { + this.meta.title = TITLE; + this.meta.version = VERSION; + this.meta.packageName = PACKAGE_NAME; + this.meta.company = COMPANY; + + this.app.main = MAIN_CLASS; + this.app.file = EXECUTABLE_NAME; + this.app.preloader = PRELOADER; + + // Tell Lime where to look for the game's source code. + // If for some reason we have multiple source directories, we can add more entries here. + this.sources.push(SOURCE_DIR); + + // Tell Lime to run some prebuild and postbuild scripts. + this.preBuildCallbacks.push(buildHaxeCLICommand(PREBUILD_HX)); + this.postBuildCallbacks.push(buildHaxeCLICommand(POSTBUILD_HX)); + + // TODO: Should we provide this? + // this.meta.buildNumber = 0; + + // These values are only used by the SWF target I think. + // this.app.path + // this.app.init + // this.app.swfVersion + // this.app.url + + // These values are only used by... FIREFOX MARKETPLACE WHAT? + // this.meta.description = ""; + // this.meta.companyId = COMPANY; + // this.meta.companyUrl = COMPANY; + + // Configure the window. + // Automatically configure FPS. + this.window.fps = 60; + // Set the window size. + this.window.width = 1280; + this.window.height = 720; + // Black background on release builds, magenta on debug builds. + this.window.background = FEATURE_DEBUG_FUNCTIONS.isEnabled(this) ? 0xFFFF00FF : 0xFF000000; + + this.window.hardware = true; + this.window.vsync = false; + + if (isWeb()) { + this.window.resizable = true; + } + + if (isDesktop()) { + this.window.orientation = Orientation.LANDSCAPE; + this.window.fullscreen = false; + this.window.resizable = true; + this.window.vsync = false; + } + + if (isMobile()) { + this.window.orientation = Orientation.LANDSCAPE; + this.window.fullscreen = false; + this.window.resizable = false; + this.window.width = 0; + this.window.height = 0; + } + } + + /** + * Log information about the configured target platform. + */ + function displayTarget() { + // Display the target operating system. + switch (this.target) { + case Platform.WINDOWS: + info('Target Platform: Windows'); + case Platform.MAC: + info('Target Platform: MacOS'); + case Platform.LINUX: + info('Target Platform: Linux'); + case Platform.ANDROID: + info('Target Platform: Android'); + case Platform.IOS: + info('Target Platform: IOS'); + case Platform.HTML5: + info('Target Platform: HTML5'); + // See lime.tools.Platform for a full list. + // case Platform.EMSCRITEN: // A WebAssembly build might be interesting... + // case Platform.AIR: + // case Platform.BLACKBERRY: + // case Platform.CONSOLE_PC: + // case Platform.FIREFOX: + // case Platform.FLASH: + // case Platform.PS3: + // case Platform.PS4: + // case Platform.TIZEN: + // case Platform.TVOS: + // case Platform.VITA: + // case Platform.WEBOS: + // case Platform.WIIU: + // case Platform.XBOX1: + default: + error('Unsupported platform (got ${target})'); + } + + switch (this.platformType) { + case PlatformType.DESKTOP: + info('Platform Type: Desktop'); + case PlatformType.MOBILE: + info('Platform Type: Mobile'); + case PlatformType.WEB: + info('Platform Type: Web'); + case PlatformType.CONSOLE: + info('Platform Type: Console'); + default: + error('Unknown platform type (got ${platformType})'); + } + + // Print whether we are using HXCPP, HashLink, or something else. + if (isWeb()) { + info('Target Language: JavaScript (HTML5)'); + } else if (isHashLink()) { + info('Target Language: HashLink'); + } else if (isNeko()) { + info('Target Language: Neko'); + } else if (isJava()) { + info('Target Language: Java'); + } else if (isNodeJS()) { + info('Target Language: JavaScript (NodeJS)'); + } else if (isCSharp()) { + info('Target Language: C#'); + } else { + info('Target Language: C++'); + } + + for (arch in this.architectures) { + // Display the list of target architectures. + switch (arch) { + case Architecture.X86: + info('Architecture: x86'); + case Architecture.X64: + info('Architecture: x64'); + case Architecture.ARMV5: + info('Architecture: ARMv5'); + case Architecture.ARMV6: + info('Architecture: ARMv6'); + case Architecture.ARMV7: + info('Architecture: ARMv7'); + case Architecture.ARMV7S: + info('Architecture: ARMv7S'); + case Architecture.ARM64: + info('Architecture: ARMx64'); + case Architecture.MIPS: + info('Architecture: MIPS'); + case Architecture.MIPSEL: + info('Architecture: MIPSEL'); + case null: + if (!isWeb()) { + error('Unsupported architecture (got null on non-web platform)'); + } else { + info('Architecture: Web'); + } + default: + error('Unsupported architecture (got ${arch})'); + } + } + } + + /** + * Apply various feature flags based on the target platform and the user-provided build flags. + */ + function configureFeatureFlags() { + // You can explicitly override any of these. + // For example, `-DGITHUB_BUILD` or `-DNO_HARDCODED_CREDITS` + + // Should be false unless explicitly requested. + GITHUB_BUILD.apply(this, false); + FEATURE_STAGE_EDITOR.apply(this, false); + FEATURE_NEWGROUNDS.apply(this, false); + FEATURE_GHOST_TAPPING.apply(this, false); + + // Should be true unless explicitly requested. + HARDCODED_CREDITS.apply(this, true); + FEATURE_OPEN_URL.apply(this, true); + FEATURE_POLYMOD_MODS.apply(this, true); + FEATURE_FUNKVIS.apply(this, true); + FEATURE_PARTIAL_SOUNDS.apply(this, true); + FEATURE_VIDEO_PLAYBACK.apply(this, true); + + // Should be true on debug builds or if GITHUB_BUILD is enabled. + FEATURE_DEBUG_FUNCTIONS.apply(this, isDebug() || GITHUB_BUILD.isEnabled(this)); + + // Should default to true on workspace builds and false on release builds. + REDIRECT_ASSETS_FOLDER.apply(this, isDebug() && isDesktop()); + + // Should be true on release, non-tester builds. + // We don't want testers to accidentally leak songs to their Discord friends! + // TODO: Re-enable this. + FEATURE_DISCORD_RPC.apply(this, false && !FEATURE_DEBUG_FUNCTIONS.isEnabled(this)); + + // Should be true only on web builds. + // Audio context issues only exist there. + TOUCH_HERE_TO_PLAY.apply(this, isWeb()); + + // Should be true only on web builds. + // Enabling embedding and preloading is required to preload assets properly. + EMBED_ASSETS.apply(this, isWeb()); + PRELOAD_ALL.apply(this, true); + + // Should be true except on MacOS. + // File drop doesn't work there. + FEATURE_FILE_DROP.apply(this, !isMac()); + + // Should be true except on web builds. + // Chart editor doesn't work there. + FEATURE_CHART_EDITOR.apply(this, !isWeb()); + } + + /** + * Set compilation flags which are not feature flags. + */ + function configureCompileDefines() { + // Enable OpenFL's error handler. Required for the crash logger. + setHaxedef("openfl-enable-handle-error"); + + // Enable stack trace tracking. Good for debugging but has a (minor) performance impact. + setHaxedef("HXCPP_CHECK_POINTER"); + setHaxedef("HXCPP_STACK_LINE"); + setHaxedef("HXCPP_STACK_TRACE"); + setHaxedef("hscriptPos"); + + setHaxedef("safeMode"); + + // If we aren't using the Flixel debugger, strip it out. + if (FEATURE_DEBUG_FUNCTIONS.isDisabled(this)) { + setHaxedef("FLX_NO_DEBUG"); + } + + // Disable the built in pause screen when unfocusing the game. + setHaxedef("FLX_NO_FOCUS_LOST_SCREEN"); + + // HaxeUI configuration. + setHaxedef("haxeui_no_mouse_reset"); + setHaxedef("haxeui_focus_out_on_click"); // Unfocus a dialog when clicking out of it + setHaxedef("haxeui_dont_impose_base_class"); // Suppress a macro error + + if (isRelease()) { + // Improve performance on Nape + // TODO: Do we even use Nape? + setHaxedef("NAPE_RELEASE_BUILD"); + } + + // Cleaner looking compiler errors. + setHaxedef("message.reporting", "pretty"); + } + + /** + * Set compilation flags which manage dead code elimination. + */ + function configureIncludeMacros() { + // Disable dead code elimination. + // This prevents functions that are unused by the base game from being unavailable to HScript. + addHaxeFlag("-dce no"); + + // Forcibly include all Funkin' classes in builds. + // This prevents classes that are unused by the base game from being unavailable to HScript. + addHaxeMacro("include('funkin')"); + + // Ensure all HaxeUI components are available at runtime. + addHaxeMacro("include('haxe.ui.backend.flixel.components')"); + addHaxeMacro("include('haxe.ui.core')"); + addHaxeMacro("include('haxe.ui.components')"); + addHaxeMacro("include('haxe.ui.containers')"); + addHaxeMacro("include('haxe.ui.containers.dialogs')"); + addHaxeMacro("include('haxe.ui.containers.menus')"); + addHaxeMacro("include('haxe.ui.containers.properties')"); + + // Ensure all Flixel classes are available at runtime. + // Explicitly ignore packages which require additional dependencies. + addHaxeMacro("include('flixel', true, [ 'flixel.addons.editors.spine.*', 'flixel.addons.nape.*', 'flixel.system.macros.*' ])"); + } + + /** + * Set compilation flags which manage bespoke build-time macros. + */ + function configureCustomMacros() { + // This macro allows addition of new functionality to existing Flixel. --> + addHaxeMacro("addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')"); + } + + function configureOutputDir() { + // Set the output directory. Depends on the target platform and build type. + + var buildDir = 'export/${isDebug() ? 'debug' : 'release'}/'; + + info('Output directory: $buildDir'); + // setenv('BUILD_DIR', buildDir); + app.path = buildDir; + } + + function configurePolymod() { + // The file extension to use for script files. + setHaxedef("POLYMOD_SCRIPT_EXT", ".hscript"); + // Which asset library to use for scripts. + setHaxedef("POLYMOD_SCRIPT_LIBRARY", "scripts"); + // The base path from which scripts should be accessed. + setHaxedef("POLYMOD_ROOT_PATH", "scripts/"); + // Determines the subdirectory of the mod folder used for file appending. + setHaxedef("POLYMOD_APPEND_FOLDER", "_append"); + // Determines the subdirectory of the mod folder used for file merges. + setHaxedef("POLYMOD_MERGE_FOLDER", "_merge"); + // Determines the file in the mod folder used for metadata. + setHaxedef("POLYMOD_MOD_METADATA_FILE", "_polymod_meta.json"); + // Determines the file in the mod folder used for the icon. + setHaxedef("POLYMOD_MOD_ICON_FILE", "_polymod_icon.png"); + + if (isDebug()) { + // Turns on additional debug logging. + setHaxedef("POLYMOD_DEBUG"); + } + } + + function configureHaxelibs() { + // Don't enforce + addHaxelib('lime'); // Game engine backend + addHaxelib('openfl'); // Game engine backend + + addHaxelib('flixel'); // Game engine + + addHaxelib('flixel-addons'); // Additional utilities for Flixel + addHaxelib('hscript'); // Scripting + // addHaxelib('flixel-ui'); // UI framework (DEPRECATED) + addHaxelib('haxeui-core'); // UI framework + addHaxelib('haxeui-flixel'); // Integrate HaxeUI with Flixel + addHaxelib('flixel-text-input'); // Improved text field rendering for HaxeUI + + addHaxelib('polymod'); // Modding framework + addHaxelib('flxanimate'); // Texture atlas rendering + + addHaxelib('json2object'); // JSON parsing + addHaxelib('jsonpath'); // JSON parsing + addHaxelib('jsonpatch'); // JSON parsing + addHaxelib('thx.core'); // General utility library, "the lodash of Haxe" + addHaxelib('thx.semver'); // Version string handling + + if (isDebug()) { + addHaxelib('hxcpp-debug-server'); // VSCode debug support + } + + if (isDesktop() && !isHashLink() && FEATURE_VIDEO_PLAYBACK.isEnabled(this)) { + // hxCodec doesn't function on HashLink or non-desktop platforms + // It's also unnecessary if video playback is disabled + addHaxelib('hxCodec'); // Video playback + } + + if (FEATURE_DISCORD_RPC.isEnabled(this)) { + addHaxelib('discord_rpc'); // Discord API + } + + if (FEATURE_NEWGROUNDS.isEnabled(this)) { + addHaxelib('newgrounds'); // Newgrounds API + } + + if (FEATURE_FUNKVIS.isEnabled(this)) { + addHaxelib('funkin.vis'); // Audio visualization + addHaxelib('grig.audio'); // Audio data utilities + } + + if (FEATURE_PARTIAL_SOUNDS.isEnabled(this)) { + addHaxelib('FlxPartialSound'); // Partial sound + } + } + + function configureAssets() { + var exclude = EXCLUDE_ASSETS.concat(isWeb() ? EXCLUDE_ASSETS_WEB : EXCLUDE_ASSETS_NATIVE); + var shouldPreload = PRELOAD_ALL.isEnabled(this); + var shouldEmbed = EMBED_ASSETS.isEnabled(this); + + if (shouldEmbed) { + info('Embedding assets into executable...'); + } else { + info('Including assets alongside executable...'); + } + + // Default asset library + var shouldPreloadDefault = true; + addAssetLibrary("default", shouldEmbed, shouldPreloadDefault); + addAssetPath("assets/preload", "assets", "default", ["*"], exclude, shouldEmbed); + + // Font assets + var shouldEmbedFonts = true; + addAssetPath("assets/fonts", null, "default", ["*"], exclude, shouldEmbedFonts); + + // Shared asset libraries + addAssetLibrary("songs", shouldEmbed, shouldPreload); + addAssetPath("assets/songs", "assets/songs", "songs", ["*"], exclude, shouldEmbed); + addAssetLibrary("shared", shouldEmbed, shouldPreload); + addAssetPath("assets/shared", "assets/shared", "shared", ["*"], exclude, shouldEmbed); + if (FEATURE_VIDEO_PLAYBACK.isEnabled(this)) { + var shouldEmbedVideos = false; + addAssetLibrary("videos", shouldEmbedVideos, shouldPreload); + addAssetPath("assets/videos", "assets/videos", "videos", ["*"], exclude, shouldEmbedVideos); + } + + // Level asset libraries + addAssetLibrary("tutorial", shouldEmbed, shouldPreload); + addAssetPath("assets/tutorial", "assets/tutorial", "tutorial", ["*"], exclude, shouldEmbed); + addAssetLibrary("week1", shouldEmbed, shouldPreload); + addAssetPath("assets/week1", "assets/week1", "week1", ["*"], exclude, shouldEmbed); + addAssetLibrary("week2", shouldEmbed, shouldPreload); + addAssetPath("assets/week2", "assets/week2", "week2", ["*"], exclude, shouldEmbed); + addAssetLibrary("week3", shouldEmbed, shouldPreload); + addAssetPath("assets/week3", "assets/week3", "week3", ["*"], exclude, shouldEmbed); + addAssetLibrary("week4", shouldEmbed, shouldPreload); + addAssetPath("assets/week4", "assets/week4", "week4", ["*"], exclude, shouldEmbed); + addAssetLibrary("week5", shouldEmbed, shouldPreload); + addAssetPath("assets/week5", "assets/week5", "week5", ["*"], exclude, shouldEmbed); + addAssetLibrary("week6", shouldEmbed, shouldPreload); + addAssetPath("assets/week6", "assets/week6", "week6", ["*"], exclude, shouldEmbed); + addAssetLibrary("week7", shouldEmbed, shouldPreload); + addAssetPath("assets/week7", "assets/week7", "week7", ["*"], exclude, shouldEmbed); + addAssetLibrary("weekend1", shouldEmbed, shouldPreload); + addAssetPath("assets/weekend1", "assets/weekend1", "weekend1", ["*"], exclude, shouldEmbed); + + // Art asset library (where README and CHANGELOG pull from) + var shouldEmbedArt = false; + var shouldPreloadArt = false; + addAssetLibrary("art", shouldEmbedArt, shouldPreloadArt); + addAsset("art/readme.txt", "do NOT readme.txt", "art", shouldEmbedArt); + addAsset("LICENSE.md", "LICENSE.md", "art", shouldEmbedArt); + addAsset("CHANGELOG.md", "CHANGELOG.md", "art", shouldEmbedArt); + } + + /** + * Configure the application's favicon and executable icon. + */ + function configureIcons() { + addIcon("art/icon16.png", 16); + addIcon("art/icon32.png", 32); + addIcon("art/icon64.png", 64); + addIcon("art/iconOG.png"); + } + + // + // HELPER FUNCTIONS + // Easy functions to make the code more readable. + // + + public function isWeb():Bool { + return this.platformType == PlatformType.WEB; + } + + public function isMobile():Bool { + return this.platformType == PlatformType.MOBILE; + } + + public function isDesktop():Bool { + return this.platformType == PlatformType.DESKTOP; + } + + public function isConsole():Bool { + return this.platformType == PlatformType.CONSOLE; + } + + public function is32Bit():Bool { + return this.architectures.contains(Architecture.X86); + } + + public function is64Bit():Bool { + return this.architectures.contains(Architecture.X64); + } + + public function isWindows():Bool { + return this.target == Platform.WINDOWS; + } + + public function isMac():Bool { + return this.target == Platform.MAC; + } + + public function isLinux():Bool { + return this.target == Platform.LINUX; + } + + public function isAndroid():Bool { + return this.target == Platform.ANDROID; + } + + public function isIOS():Bool { + return this.target == Platform.IOS; + } + + public function isHashLink():Bool { + return this.targetFlags.exists("hl"); + } + + public function isNeko():Bool { + return this.targetFlags.exists("neko"); + } + + public function isJava():Bool { + return this.targetFlags.exists("java"); + } + + public function isNodeJS():Bool { + return this.targetFlags.exists("nodejs"); + } + + public function isCSharp():Bool { + return this.targetFlags.exists("cs"); + } + + public function isDisplay():Bool { + return this.command == "display"; + } + + public function isDebug():Bool { + return this.debug; + } + + public function isRelease():Bool { + return !isDebug(); + } + + public function getHaxedef(name:String):Null { + return this.haxedefs.get(name); + } + + public function setHaxedef(name:String, ?value:String):Void { + if (value == null) value = ""; + + this.haxedefs.set(name, value); + } + + public function unsetHaxedef(name:String):Void { + this.haxedefs.remove(name); + } + + public function getDefine(name:String):Null { + return this.defines.get(name); + } + + public function hasDefine(name:String):Bool { + return this.defines.exists(name); + } + + /** + * Add a library to the list of dependencies for the project. + * @param name The name of the library to add. + * @param version The version of the library to add. Optional. + */ + public function addHaxelib(name:String, version:String = ""):Void { + this.haxelibs.push(new Haxelib(name, version)); + } + + /** + * Add a `haxeflag` to the project. + */ + public function addHaxeFlag(value:String):Void { + this.haxeflags.push(value); + } + + /** + * Call a Haxe build macro. + */ + public function addHaxeMacro(value:String):Void { + addHaxeFlag('--macro ${value}'); + } + + /** + * Add an icon to the project. + * @param icon The path to the icon. + * @param size The size of the icon. Optional. + */ + public function addIcon(icon:String, ?size:Int):Void { + this.icons.push(new Icon(icon, size)); + } + + /** + * Add an asset to the game build. + * @param path The path the asset is located at. + * @param rename The path the asset should be placed. + * @param library The asset library to add the asset to. `null` = "default" + * @param embed Whether to embed the asset in the executable. + */ + public function addAsset(path:String, ?rename:String, ?library:String, embed:Bool = false):Void { + // path, rename, type, embed, setDefaults + var asset = new Asset(path, rename, null, embed, true); + @:nullSafety(Off) + { + asset.library = library ?? "default"; + } + this.assets.push(asset); + } + + /** + * Add an entire path of assets to the game build. + * @param path The path the assets are located at. + * @param rename The path the assets should be placed. + * @param library The asset library to add the assets to. `null` = "default" + * @param include An optional array to include specific asset names. + * @param exclude An optional array to exclude specific asset names. + * @param embed Whether to embed the assets in the executable. + */ + public function addAssetPath(path:String, ?rename:String, library:String, ?include:Array, ?exclude:Array, embed:Bool = false):Void { + // Argument parsing. + if (path == "") return; + + if (include == null) include = []; + + if (exclude == null) exclude = []; + + var targetPath = rename ?? path; + if (targetPath != "") targetPath += "/"; + + // Validate path. + if (!sys.FileSystem.exists(path)) { + error('Could not find asset path "${path}".'); + } else if (!sys.FileSystem.isDirectory(path)) { + error('Could not parse asset path "${path}", expected a directory.'); + } else { + // info(' Found asset path "${path}".'); + } + + for (file in sys.FileSystem.readDirectory(path)) { + if (sys.FileSystem.isDirectory('${path}/${file}')) { + // Attempt to recursively add all assets in the directory. + if (this.filter(file, ["*"], exclude)) { + addAssetPath('${path}/${file}', '${targetPath}${file}', library, include, exclude, embed); + } + } else { + if (this.filter(file, include, exclude)) { + addAsset('${path}/${file}', '${targetPath}${file}', library, embed); + } + } + } + } + + /** + * Add an asset library to the game build. + * @param name The name of the library. + * @param embed + * @param preload + */ + public function addAssetLibrary(name:String, embed:Bool = false, preload:Bool = false):Void { + // sourcePath, name, type, embed, preload, generate, prefix + var sourcePath = ''; + this.libraries.push(new Library(sourcePath, name, null, embed, preload, false, "")); + } + + // + // PROCESS FUNCTIONS + // + + /** + * A CLI command to run a command in the shell. + */ + public function buildCLICommand(cmd:String):CLICommand { + return CommandHelper.fromSingleString(cmd); + } + + /** + * A CLI command to run a Haxe script via `--interp`. + */ + public function buildHaxeCLICommand(path:String):CLICommand { + return CommandHelper.interpretHaxe(path); + } + + public function getGitCommit():String { + // Cannibalized from GitCommit.hx + var process = new sys.io.Process('git', ['rev-parse', 'HEAD']); + if (process.exitCode() != 0) { + var message = process.stderr.readAll().toString(); + error('[ERROR] Could not determine current git commit; is this a proper Git repository?'); + } + + var commitHash:String = process.stdout.readLine(); + var commitHashSplice:String = commitHash.substr(0, 7); + + return commitHashSplice; + } + + public function getGitBranch():String { + // Cannibalized from GitCommit.hx + var branchProcess = new sys.io.Process('git', ['rev-parse', '--abbrev-ref', 'HEAD']); + + if (branchProcess.exitCode() != 0) { + var message = branchProcess.stderr.readAll().toString(); + error('Could not determine current git branch; is this a proper Git repository?'); + } + + var branchName:String = branchProcess.stdout.readLine(); + + return branchName; + } + + public function getGitModified():Bool { + var branchProcess = new sys.io.Process('git', ['status', '--porcelain']); + + if (branchProcess.exitCode() != 0) { + var message = branchProcess.stderr.readAll().toString(); + error('Could not determine current git status; is this a proper Git repository?'); + } + + var output:String = ''; + try { + output = branchProcess.stdout.readLine(); + } catch (e) { + if (e.message == 'Eof') { + // Do nothing. + // Eof = No output. + } else { + // Rethrow other exceptions. + throw e; + } + } + + return output.length > 0; + } + + // + // LOGGING FUNCTIONS + // + + /** + * Display an error message. This should stop the build process. + */ + public function error(message:String):Void { + Log.error('${message}'); + } + + /** + * Display an info message. This should not interfere with the build process. + */ + public function info(message:String):Void { + // CURSED: We have to disable info() log calls because of a bug. + // https://github.com/haxelime/lime-vscode-extension/issues/88 + + // Log.info('[INFO] ${message}'); + + // trace(message); + // Sys.println(message); + // Sys.stdout().writeString(message); + // Sys.stderr().writeString(message); + } +} + +/** + * An object representing a feature flag, which can be enabled or disabled. + * Includes features such as automatic generation of compile defines and inversion. + */ +abstract FeatureFlag(String) { + static final INVERSE_PREFIX:String = "NO_"; + + public function new(input:String) { + this = input; + } + + @:from + public static function fromString(input:String):FeatureFlag { + return new FeatureFlag(input); + } + + /** + * Enable/disable a feature flag if it is unset, and handle the inverse flag. + * Doesn't override a feature flag that was set explicitly. + * @param enableByDefault Whether to enable this feature flag if it is unset. + */ + public function apply(project:Project, enableByDefault:Bool = false):Void { + // TODO: Name this function better? + + if (isEnabled(project)) { + // If this flag was already enabled, disable the inverse. + project.info('Enabling feature flag ${this}'); + getInverse().disable(project, false); + } else if (getInverse().isEnabled(project)) { + // If the inverse flag was already enabled, disable this flag. + project.info('Disabling feature flag ${this}'); + disable(project, false); + } else { + if (enableByDefault) { + // Enable this flag if it was unset, and disable the inverse. + project.info('Enabling feature flag ${this}'); + enable(project, true); + } else { + // Disable this flag if it was unset, and enable the inverse. + project.info('Disabling feature flag ${this}'); + disable(project, true); + } + } + } + + /** + * Enable this feature flag by setting the appropriate compile define. + * + * @param project The project to modify. + * @param andInverse Also disable the feature flag's inverse. + */ + public function enable(project:Project, andInverse:Bool = true) { + project.setHaxedef(this, ""); + if (andInverse) { + getInverse().disable(project, false); + } + } + + /** + * Disable this feature flag by removing the appropriate compile define. + * + * @param project The project to modify. + * @param andInverse Also enable the feature flag's inverse. + */ + public function disable(project:Project, andInverse:Bool = true) { + project.unsetHaxedef(this); + if (andInverse) { + getInverse().enable(project, false); + } + } + + /** + * Query if this feature flag is enabled. + * @param project The project to query. + */ + public function isEnabled(project:Project):Bool { + // Check both Haxedefs and Defines for this flag. + return project.haxedefs.exists(this) || project.defines.exists(this); + } + + /** + * Query if this feature flag's inverse is enabled. + */ + public function isDisabled(project:Project):Bool { + return getInverse().isEnabled(project); + } + + /** + * Return the inverse of this feature flag. + * @return A new feature flag that is the inverse of this one. + */ + public function getInverse():FeatureFlag { + if (this.startsWith(INVERSE_PREFIX)) { + return this.substring(INVERSE_PREFIX.length); + } + return INVERSE_PREFIX + this; + } +} From 200b3c963b6f3793c231eb8b4792af7b7d24e98e Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 26 Aug 2024 18:01:36 -0400 Subject: [PATCH 297/469] Updated compile defines to use feature flag system --- source/Main.hx | 2 +- source/funkin/InitState.hx | 17 +- source/funkin/api/discord/Discord.hx | 4 +- source/funkin/api/newgrounds/NGUnsafe.hx | 6 +- source/funkin/api/newgrounds/NGio.hx | 6 +- source/funkin/data/story/level/LevelData.hx | 4 +- .../graphics/adobeanimate/FlxAtlasSprite.hx | 2 +- source/funkin/input/Controls.hx | 7 +- source/funkin/modding/PolymodHandler.hx | 33 +- source/funkin/play/PauseSubState.hx | 2 +- source/funkin/play/PlayState.hx | 282 +++++++++--------- source/funkin/play/stage/Stage.hx | 6 +- source/funkin/ui/debug/DebugMenuSubState.hx | 2 +- .../ui/debug/charting/ChartEditorState.hx | 2 +- source/funkin/ui/freeplay/FreeplayDJ.hx | 2 +- source/funkin/ui/freeplay/FreeplayState.hx | 14 +- source/funkin/ui/mainmenu/MainMenuState.hx | 6 +- source/funkin/ui/story/StoryMenuState.hx | 2 +- source/funkin/ui/transition/LoadingState.hx | 2 +- source/funkin/util/Constants.hx | 8 +- 20 files changed, 216 insertions(+), 193 deletions(-) diff --git a/source/Main.hx b/source/Main.hx index add5bbc67b..2426fa0d99 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -113,7 +113,7 @@ class Main extends Sprite addChild(game); - #if debug + #if FEATURE_DEBUG_FUNCTIONS game.debugger.interaction.addTool(new funkin.util.TrackerToolButtonUtil()); #end diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index e53499e3c9..21f83022e5 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -34,7 +34,7 @@ import funkin.util.CLIUtil; import funkin.util.CLIUtil.CLIParams; import funkin.util.TimerUtil; import funkin.util.TrackerUtil; -#if discord_rpc +#if FEATURE_DISCORD_RPC import Discord.DiscordClient; #end @@ -123,7 +123,7 @@ class InitState extends FlxState // // DISCORD API SETUP // - #if discord_rpc + #if FEATURE_DISCORD_RPC DiscordClient.initialize(); Application.current.onExit.add(function(exitCode) { @@ -144,7 +144,7 @@ class InitState extends FlxState // Plugins provide a useful interface for globally active Flixel objects, // that receive update events regardless of the current state. // TODO: Move scripted Module behavior to a Flixel plugin. - #if debug + #if FEATURE_DEBUG_FUNCTIONS funkin.util.plugins.MemoryGCPlugin.initialize(); #end funkin.util.plugins.EvacuateDebugPlugin.initialize(); @@ -374,11 +374,16 @@ class InitState extends FlxState // // FLIXEL DEBUG SETUP // - #if (debug || FORCE_DEBUG_VERSION) - // Make errors and warnings less annoying. - // Forcing this always since I have never been happy to have the debugger to pop up + #if FEATURE_DEBUG_FUNCTIONS + trace('Initializing Flixel debugger...'); + + #if !debug + // Make errors less annoying on release builds. LogStyle.ERROR.openConsole = false; LogStyle.ERROR.errorSound = null; + #end + + // Make errors and warnings less annoying. LogStyle.WARNING.openConsole = false; LogStyle.WARNING.errorSound = null; diff --git a/source/funkin/api/discord/Discord.hx b/source/funkin/api/discord/Discord.hx index a4d65684ee..9dd513bf79 100644 --- a/source/funkin/api/discord/Discord.hx +++ b/source/funkin/api/discord/Discord.hx @@ -1,13 +1,13 @@ package funkin.api.discord; import Sys.sleep; -#if discord_rpc +#if FEATURE_DISCORD_RPC import discord_rpc.DiscordRpc; #end class DiscordClient { - #if discord_rpc + #if FEATURE_DISCORD_RPC public function new() { trace("Discord Client starting..."); diff --git a/source/funkin/api/newgrounds/NGUnsafe.hx b/source/funkin/api/newgrounds/NGUnsafe.hx index 77e44bd1d4..81da7359e7 100644 --- a/source/funkin/api/newgrounds/NGUnsafe.hx +++ b/source/funkin/api/newgrounds/NGUnsafe.hx @@ -24,7 +24,7 @@ class NGUnsafe NG.core.calls.event.logEvent(event).send(); trace('should have logged: ' + event); #else - #if debug + #if FEATURE_DEBUG_FUNCTIONS trace('event:$event - not logged, missing NG.io lib'); #end #end @@ -39,7 +39,7 @@ class NGUnsafe if (!medal.unlocked) medal.sendUnlock(); } #else - #if debug + #if FEATURE_DEBUG_FUNCTIONS trace('medal:$id - not unlocked, missing NG.io lib'); #end #end @@ -63,7 +63,7 @@ class NGUnsafe } } #else - #if debug + #if FEATURE_DEBUG_FUNCTIONS trace('Song:$song, Score:$score - not posted, missing NG.io lib'); #end #end diff --git a/source/funkin/api/newgrounds/NGio.hx b/source/funkin/api/newgrounds/NGio.hx index 3f5fc078ac..a866783c11 100644 --- a/source/funkin/api/newgrounds/NGio.hx +++ b/source/funkin/api/newgrounds/NGio.hx @@ -239,7 +239,7 @@ class NGio NG.core.calls.event.logEvent(event).send(); trace('should have logged: ' + event); #else - #if debug + #if FEATURE_DEBUG_FUNCTIONS trace('event:$event - not logged, missing NG.io lib'); #end #end @@ -254,7 +254,7 @@ class NGio if (!medal.unlocked) medal.sendUnlock(); } #else - #if debug + #if FEATURE_DEBUG_FUNCTIONS trace('medal:$id - not unlocked, missing NG.io lib'); #end #end @@ -278,7 +278,7 @@ class NGio } } #else - #if debug + #if FEATURE_DEBUG_FUNCTIONS trace('Song:$song, Score:$score - not posted, missing NG.io lib'); #end #end diff --git a/source/funkin/data/story/level/LevelData.hx b/source/funkin/data/story/level/LevelData.hx index d01689a826..d1b00bbe6e 100644 --- a/source/funkin/data/story/level/LevelData.hx +++ b/source/funkin/data/story/level/LevelData.hx @@ -93,9 +93,9 @@ typedef LevelPropData = * The frequency to bop at, in beats. * 1 = every beat, 2 = every other beat, etc. * Supports up to 0.25 precision. - * @default 0.0 + * @default 1.0 */ - @:default(0.0) + @:default(1.0) @:optional var danceEvery:Float; diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index 049c6e2064..194584992f 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -21,7 +21,7 @@ class FlxAtlasSprite extends FlxAnimate FrameRate: 24.0, Reversed: false, // ?OnComplete:Void -> Void, - ShowPivot: #if debug false #else false #end, + ShowPivot: false, Antialiasing: true, ScrollFactor: null, // Offset: new FlxPoint(0, 0), // This is just FlxSprite.offset diff --git a/source/funkin/input/Controls.hx b/source/funkin/input/Controls.hx index 6b8e9aa3e6..b5aefd08d7 100644 --- a/source/funkin/input/Controls.hx +++ b/source/funkin/input/Controls.hx @@ -356,9 +356,10 @@ class Controls extends FlxActionSet public function check(name:Action, trigger:FlxInputState = JUST_PRESSED, gamepadOnly:Bool = false):Bool { - #if debug + #if FEATURE_DEBUG_FUNCTIONS if (!byName.exists(name)) throw 'Invalid name: $name'; #end + var action = byName[name]; if (gamepadOnly) return action.checkFiltered(trigger, GAMEPAD); else @@ -367,7 +368,7 @@ class Controls extends FlxActionSet public function getKeysForAction(name:Action):Array { - #if debug + #if FEATURE_DEBUG_FUNCTIONS if (!byName.exists(name)) throw 'Invalid name: $name'; #end @@ -382,7 +383,7 @@ class Controls extends FlxActionSet public function getButtonsForAction(name:Action):Array { - #if debug + #if FEATURE_DEBUG_FUNCTIONS if (!byName.exists(name)) throw 'Invalid name: $name'; #end diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index 5767199baa..52d4624cbf 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -28,11 +28,10 @@ class PolymodHandler { /** * The API version that mods should comply with. - * Format this with Semantic Versioning; ... - * Bug fixes increment the patch version, new features increment the minor version. - * Changes that break old mods increment the major version. + * Indicates which mods are compatible with this version of the game. + * Minor updates rarely impact mods but major versions often do. */ - static final API_VERSION:String = '0.1.0'; + static final API_VERSION:String = "0.5.0"; // Constants.VERSION; /** * Where relative to the executable that mods are located. @@ -178,7 +177,7 @@ class PolymodHandler loadedModIds.push(mod.id); } - #if debug + #if FEATURE_DEBUG_FUNCTIONS var fileList:Array = Polymod.listModFiles(PolymodAssetType.IMAGE); trace('Installed mods have replaced ${fileList.length} images.'); for (item in fileList) @@ -258,8 +257,29 @@ class PolymodHandler // Unserializerr.DEFAULT_RESOLVER.resolveClass() can access blacklisted packages Polymod.blacklistImport('Unserializer'); + // `lime.system.CFFI` + // Can load and execute compiled binaries. + Polymod.blacklistImport('lime.system.CFFI'); + + // `lime.system.JNI` + // Can load and execute compiled binaries. + Polymod.blacklistImport('lime.system.JNI'); + + // `lime.system.System` + // System.load() can load malicious DLLs + Polymod.blacklistImport('lime.system.System'); + + // `lime.utils.Assets` + // Literally just has a private `resolveClass` function for some reason? + Polymod.blacklistImport('lime.utils.Assets'); + Polymod.blacklistImport('openfl.utils.Assets'); + + // `openfl.desktop.NativeProcess` + // Can load native processes on the host operating system. + Polymod.blacklistImport('openfl.desktop.NativeProcess'); + // `polymod.*` - // You can probably unblacklist a module + // Contains functions which may allow for un-blacklisting other modules. for (cls in ClassMacro.listClassesInPackage('polymod')) { if (cls == null) continue; @@ -268,6 +288,7 @@ class PolymodHandler } // `sys.*` + // Access to system utilities such as the file system. for (cls in ClassMacro.listClassesInPackage('sys')) { if (cls == null) continue; diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index 6f8908eea9..9ff70bee57 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -430,7 +430,7 @@ class PauseSubState extends MusicBeatSubState resume(this); } - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS // to pause the game and get screenshots easy, press H on pause menu! if (FlxG.keys.justPressed.H) { diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index b6a17947fd..928f392e5a 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -67,7 +67,7 @@ import lime.ui.Haptic; import openfl.display.BitmapData; import openfl.geom.Rectangle; import openfl.Lib; -#if discord_rpc +#if FEATURE_DISCORD_RPC import Discord.DiscordClient; #end @@ -445,7 +445,7 @@ class PlayState extends MusicBeatSubState */ public var vocals:VoicesGroup; - #if discord_rpc + #if FEATURE_DISCORD_RPC // Discord RPC variables var storyDifficultyText:String = ''; var iconRPC:String = ''; @@ -698,7 +698,7 @@ class PlayState extends MusicBeatSubState initStrumlines(); initPopups(); - #if discord_rpc + #if FEATURE_DISCORD_RPC // Initialize Discord Rich Presence. initDiscord(); #end @@ -738,7 +738,7 @@ class PlayState extends MusicBeatSubState rightWatermarkText.cameras = [camHUD]; // Initialize some debug stuff. - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS // Display the version number (and git commit hash) in the bottom right corner. this.rightWatermarkText.text = Constants.VERSION; @@ -977,7 +977,7 @@ class PlayState extends MusicBeatSubState // boyfriendPos.put(); // TODO: Why is this here? } - #if discord_rpc + #if FEATURE_DISCORD_RPC DiscordClient.changePresence(detailsPausedText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); #end } @@ -1036,7 +1036,7 @@ class PlayState extends MusicBeatSubState // Disable updates, preventing animations in the background from playing. persistentUpdate = false; - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS if (FlxG.keys.pressed.THREE) { // TODO: Change the key or delete this? @@ -1047,7 +1047,7 @@ class PlayState extends MusicBeatSubState { #end persistentDraw = false; - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS } #end @@ -1066,7 +1066,7 @@ class PlayState extends MusicBeatSubState moveToGameOver(); } - #if discord_rpc + #if FEATURE_DISCORD_RPC // Game Over doesn't get his own variable because it's only used here DiscordClient.changePresence('Game Over - ' + detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); #end @@ -1179,9 +1179,9 @@ class PlayState extends MusicBeatSubState } /** - * Function called before opening a new substate. - * @param subState The substate to open. - */ + * Function called before opening a new substate. + * @param subState The substate to open. + */ public override function openSubState(subState:FlxSubState):Void { // If there is a substate which requires the game to continue, @@ -1237,9 +1237,9 @@ class PlayState extends MusicBeatSubState } /** - * Function called before closing the current substate. - * @param subState - */ + * Function called before closing the current substate. + * @param subState + */ public override function closeSubState():Void { if (Std.isOfType(subState, PauseSubState)) @@ -1278,7 +1278,7 @@ class PlayState extends MusicBeatSubState // Resume the countdown. Countdown.resumeCountdown(); - #if discord_rpc + #if FEATURE_DISCORD_RPC if (startTimer.finished) { DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true, @@ -1301,8 +1301,8 @@ class PlayState extends MusicBeatSubState } /** - * Function called when the game window gains focus. - */ + * Function called when the game window gains focus. + */ public override function onFocus():Void { if (VideoCutscene.isPlaying() && FlxG.autoPause && isGamePaused) VideoCutscene.pauseVideo(); @@ -1311,7 +1311,7 @@ class PlayState extends MusicBeatSubState VideoCutscene.resumeVideo(); #end - #if discord_rpc + #if FEATURE_DISCORD_RPC if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause) { if (Conductor.instance.songPosition > 0.0) DiscordClient.changePresence(detailsText, currentSong.song @@ -1329,15 +1329,15 @@ class PlayState extends MusicBeatSubState } /** - * Function called when the game window loses focus. - */ + * Function called when the game window loses focus. + */ public override function onFocusLost():Void { #if html5 if (FlxG.autoPause) VideoCutscene.pauseVideo(); #end - #if discord_rpc + #if FEATURE_DISCORD_RPC if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause) DiscordClient.changePresence(detailsPausedText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); #end @@ -1346,8 +1346,8 @@ class PlayState extends MusicBeatSubState } /** - * Call this by pressing F5 on a debug build. - */ + * Call this by pressing F5 on a debug build. + */ override function reloadAssets():Void { funkin.modding.PolymodHandler.forceReloadAssets(); @@ -1459,8 +1459,8 @@ class PlayState extends MusicBeatSubState } /** - * Initializes the game and HUD cameras. - */ + * Initializes the game and HUD cameras. + */ function initCameras():Void { camGame = new FunkinCamera('playStateCamGame'); @@ -1484,8 +1484,8 @@ class PlayState extends MusicBeatSubState } /** - * Initializes the health bar on the HUD. - */ + * Initializes the health bar on the HUD. + */ function initHealthBar():Void { var healthBarYPos:Float = Preferences.downscroll ? FlxG.height * 0.1 : FlxG.height * 0.9; @@ -1516,8 +1516,8 @@ class PlayState extends MusicBeatSubState } /** - * Generates the stage and all its props. - */ + * Generates the stage and all its props. + */ function initStage():Void { loadStage(currentStageId); @@ -1537,10 +1537,10 @@ class PlayState extends MusicBeatSubState } /** - * Loads stage data from cache, assembles the props, - * and adds it to the state. - * @param id - */ + * Loads stage data from cache, assembles the props, + * and adds it to the state. + * @param id + */ function loadStage(id:String):Void { currentStage = StageRegistry.instance.fetchEntry(id); @@ -1558,7 +1558,7 @@ class PlayState extends MusicBeatSubState // Add the stage to the scene. this.add(currentStage); - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS FlxG.console.registerObject('stage', currentStage); #end } @@ -1581,8 +1581,8 @@ class PlayState extends MusicBeatSubState } /** - * Generates the character sprites and adds them to the stage. - */ + * Generates the character sprites and adds them to the stage. + */ function initCharacters():Void { if (currentSong == null || currentChart == null) @@ -1661,7 +1661,7 @@ class PlayState extends MusicBeatSubState { currentStage.addCharacter(girlfriend, GF); - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS FlxG.console.registerObject('gf', girlfriend); #end } @@ -1670,7 +1670,7 @@ class PlayState extends MusicBeatSubState { currentStage.addCharacter(boyfriend, BF); - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS FlxG.console.registerObject('bf', boyfriend); #end } @@ -1681,7 +1681,7 @@ class PlayState extends MusicBeatSubState // Camera starts at dad. cameraFollowPoint.setPosition(dad.cameraFocusPoint.x, dad.cameraFocusPoint.y); - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS FlxG.console.registerObject('dad', dad); #end } @@ -1692,8 +1692,8 @@ class PlayState extends MusicBeatSubState } /** - * Constructs the strumlines for each player. - */ + * Constructs the strumlines for each player. + */ function initStrumlines():Void { var noteStyleId:String = currentChart.noteStyle; @@ -1725,8 +1725,8 @@ class PlayState extends MusicBeatSubState } /** - * Configures the judgement and combo popups. - */ + * Configures the judgement and combo popups. + */ function initPopups():Void { var noteStyleId:String = currentChart.noteStyle; @@ -1740,11 +1740,11 @@ class PlayState extends MusicBeatSubState } /** - * Initializes the Discord Rich Presence. - */ + * Initializes the Discord Rich Presence. + */ function initDiscord():Void { - #if discord_rpc + #if FEATURE_DISCORD_RPC storyDifficultyText = difficultyString(); iconRPC = currentSong.player2; @@ -1775,9 +1775,9 @@ class PlayState extends MusicBeatSubState } /** - * Initializes the song (applying the chart, generating the notes, etc.) - * Should be done before the countdown starts. - */ + * Initializes the song (applying the chart, generating the notes, etc.) + * Should be done before the countdown starts. + */ function generateSong():Void { if (currentChart == null) @@ -1808,8 +1808,8 @@ class PlayState extends MusicBeatSubState } /** - * Read note data from the chart and generate the notes. - */ + * Read note data from the chart and generate the notes. + */ function regenNoteData(startTime:Float = 0):Void { Highscore.tallies.combo = 0; @@ -1862,10 +1862,10 @@ class PlayState extends MusicBeatSubState } /** - * Prepares to start the countdown. - * Ends any running cutscenes, creates the strumlines, and starts the countdown. - * This is public so that scripts can call it. - */ + * Prepares to start the countdown. + * Ends any running cutscenes, creates the strumlines, and starts the countdown. + * This is public so that scripts can call it. + */ public function startCountdown():Void { // If Countdown.performCountdown returns false, then the countdown was canceled by a script. @@ -1879,9 +1879,9 @@ class PlayState extends MusicBeatSubState } /** - * Displays a dialogue cutscene with the given ID. - * This is used by song scripts to display dialogue. - */ + * Displays a dialogue cutscene with the given ID. + * This is used by song scripts to display dialogue. + */ public function startConversation(conversationId:String):Void { isInCutscene = true; @@ -1901,8 +1901,8 @@ class PlayState extends MusicBeatSubState } /** - * Handler function called when a conversation ends. - */ + * Handler function called when a conversation ends. + */ function onConversationComplete():Void { isInCutscene = false; @@ -1921,8 +1921,8 @@ class PlayState extends MusicBeatSubState } /** - * Starts playing the song after the countdown has completed. - */ + * Starts playing the song after the countdown has completed. + */ function startSong():Void { startingSong = false; @@ -1957,7 +1957,7 @@ class PlayState extends MusicBeatSubState vocals.pitch = playbackRate; resyncVocals(); - #if discord_rpc + #if FEATURE_DISCORD_RPC // Updating Discord Rich Presence (with Time Left) DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true, currentSongLengthMs); #end @@ -1972,8 +1972,8 @@ class PlayState extends MusicBeatSubState } /** - * Resyncronize the vocal tracks if they have become offset from the instrumental. - */ + * Resyncronize the vocal tracks if they have become offset from the instrumental. + */ function resyncVocals():Void { if (vocals == null) return; @@ -1992,8 +1992,8 @@ class PlayState extends MusicBeatSubState } /** - * Updates the position and contents of the score display. - */ + * Updates the position and contents of the score display. + */ function updateScoreText():Void { // TODO: Add functionality for modules to update the score text. @@ -2010,8 +2010,8 @@ class PlayState extends MusicBeatSubState } /** - * Updates the values of the health bar. - */ + * Updates the values of the health bar. + */ function updateHealthBar():Void { if (isBotPlayMode) @@ -2025,8 +2025,8 @@ class PlayState extends MusicBeatSubState } /** - * Callback executed when one of the note keys is pressed. - */ + * Callback executed when one of the note keys is pressed. + */ function onKeyPress(event:PreciseInputEvent):Void { if (isGamePaused) return; @@ -2036,8 +2036,8 @@ class PlayState extends MusicBeatSubState } /** - * Callback executed when one of the note keys is released. - */ + * Callback executed when one of the note keys is released. + */ function onKeyRelease(event:PreciseInputEvent):Void { if (isGamePaused) return; @@ -2047,8 +2047,8 @@ class PlayState extends MusicBeatSubState } /** - * Handles opponent note hits and player note misses. - */ + * Handles opponent note hits and player note misses. + */ function processNotes(elapsed:Float):Void { if (playerStrumline?.notes?.members == null || opponentStrumline?.notes?.members == null) return; @@ -2266,8 +2266,8 @@ class PlayState extends MusicBeatSubState } /** - * Spitting out the input for ravy 🙇‍♂️!! - */ + * Spitting out the input for ravy 🙇‍♂️!! + */ var inputSpitter:Array = []; function handleSkippedNotes():Void @@ -2291,9 +2291,9 @@ class PlayState extends MusicBeatSubState } /** - * PreciseInputEvents are put into a queue between update() calls, - * and then processed here. - */ + * PreciseInputEvents are put into a queue between update() calls, + * and then processed here. + */ function processInputQueue():Void { if (inputPressQueue.length + inputReleaseQueue.length == 0) return; @@ -2435,9 +2435,9 @@ class PlayState extends MusicBeatSubState } /** - * Called when a note leaves the screen and is considered missed by the player. - * @param note - */ + * Called when a note leaves the screen and is considered missed by the player. + * @param note + */ function onNoteMiss(note:NoteSprite, playSound:Bool = false, healthChange:Float):Void { // If we are here, we already CALLED the onNoteMiss script hook! @@ -2493,13 +2493,13 @@ class PlayState extends MusicBeatSubState } /** - * Called when a player presses a key with no note present. - * Scripts can modify the amount of health/score lost, whether player animations or sounds are used, - * or even cancel the event entirely. - * - * @param direction - * @param hasPossibleNotes - */ + * Called when a player presses a key with no note present. + * Scripts can modify the amount of health/score lost, whether player animations or sounds are used, + * or even cancel the event entirely. + * + * @param direction + * @param hasPossibleNotes + */ function ghostNoteMiss(direction:NoteDirection, hasPossibleNotes:Bool = true):Void { var event:GhostMissNoteScriptEvent = new GhostMissNoteScriptEvent(direction, // Direction missed in. @@ -2548,11 +2548,11 @@ class PlayState extends MusicBeatSubState } /** - * Debug keys. Disabled while in cutscenes. - */ + * Debug keys. Disabled while in cutscenes. + */ function debugKeyShit():Void { - #if CHART_EDITOR_SUPPORTED + #if FEATURE_CHART_EDITOR // Open the stage editor overlaying the current state. if (controls.DEBUG_STAGE) { @@ -2582,7 +2582,7 @@ class PlayState extends MusicBeatSubState } #end - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS // H: Hide the HUD. if (FlxG.keys.justPressed.H) camHUD.visible = !camHUD.visible; @@ -2599,7 +2599,7 @@ class PlayState extends MusicBeatSubState // 9: Toggle the old icon. if (FlxG.keys.justPressed.NINE) iconP1.toggleOldIcon(); - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS // PAGEUP: Skip forward two sections. // SHIFT+PAGEUP: Skip forward twenty sections. if (FlxG.keys.justPressed.PAGEUP) changeSection(FlxG.keys.pressed.SHIFT ? 20 : 2); @@ -2612,8 +2612,8 @@ class PlayState extends MusicBeatSubState } /** - * Handles applying health, score, and ratings. - */ + * Handles applying health, score, and ratings. + */ function applyScore(score:Int, daRating:String, healthChange:Float, isComboBreak:Bool) { switch (daRating) @@ -2645,8 +2645,8 @@ class PlayState extends MusicBeatSubState } /** - * Handles rating popups when a note is hit. - */ + * Handles rating popups when a note is hit. + */ function popUpScore(daRating:String, ?combo:Int):Void { if (daRating == 'miss') @@ -2703,10 +2703,10 @@ class PlayState extends MusicBeatSubState } /** - * Handle keyboard inputs during cutscenes. - * This includes advancing conversations and skipping videos. - * @param elapsed Time elapsed since last game update. - */ + * Handle keyboard inputs during cutscenes. + * This includes advancing conversations and skipping videos. + * @param elapsed Time elapsed since last game update. + */ function handleCutsceneKeys(elapsed:Float):Void { if (isGamePaused) return; @@ -2750,20 +2750,20 @@ class PlayState extends MusicBeatSubState } /** - * Handle logic for actually skipping a video cutscene after it has been held. - */ + * Handle logic for actually skipping a video cutscene after it has been held. + */ function skipVideoCutscene():Void { VideoCutscene.finishVideo(); } /** - * End the song. Handle saving high scores and transitioning to the results screen. - * - * Broadcasts an `onSongEnd` event, which can be cancelled to prevent the song from ending (for a cutscene or something). - * Remember to call `endSong` again when the song should actually end! - * @param rightGoddamnNow If true, don't play the fancy animation where you zoom onto Girlfriend. Used after a cutscene. - */ + * End the song. Handle saving high scores and transitioning to the results screen. + * + * Broadcasts an `onSongEnd` event, which can be cancelled to prevent the song from ending (for a cutscene or something). + * Remember to call `endSong` again when the song should actually end! + * @param rightGoddamnNow If true, don't play the fancy animation where you zoom onto Girlfriend. Used after a cutscene. + */ public function endSong(rightGoddamnNow:Bool = false):Void { if (FlxG.sound.music != null) FlxG.sound.music.volume = 0; @@ -2978,8 +2978,8 @@ class PlayState extends MusicBeatSubState } /** - * Perform necessary cleanup before leaving the PlayState. - */ + * Perform necessary cleanup before leaving the PlayState. + */ function performCleanup():Void { // If the camera is being tweened, stop it. @@ -3037,8 +3037,8 @@ class PlayState extends MusicBeatSubState } /** - * Play the camera zoom animation and then move to the results screen once it's done. - */ + * Play the camera zoom animation and then move to the results screen once it's done. + */ function zoomIntoResultsScreen(isNewHighscore:Bool, ?prevScoreData:SaveScoreData):Void { trace('WENT TO RESULTS SCREEN!'); @@ -3102,17 +3102,17 @@ class PlayState extends MusicBeatSubState // Zoom over to the Results screen. // TODO: Re-enable this. /* - FlxTween.tween(FlxG.camera, {zoom: 1200}, 1.1, - { - ease: FlxEase.expoIn, - }); - */ + FlxTween.tween(FlxG.camera, {zoom: 1200}, 1.1, + { + ease: FlxEase.expoIn, + }); + */ }); } /** - * Move to the results screen right goddamn now. - */ + * Move to the results screen right goddamn now. + */ function moveToResultsScreen(isNewHighscore:Bool, ?prevScoreData:SaveScoreData):Void { persistentUpdate = false; @@ -3152,8 +3152,8 @@ class PlayState extends MusicBeatSubState } /** - * Pauses music and vocals easily. - */ + * Pauses music and vocals easily. + */ public function pauseMusic():Void { if (FlxG.sound.music != null) FlxG.sound.music.pause(); @@ -3161,8 +3161,8 @@ class PlayState extends MusicBeatSubState } /** - * Resets the camera's zoom level and focus point. - */ + * Resets the camera's zoom level and focus point. + */ public function resetCamera(?resetZoom:Bool = true, ?cancelTweens:Bool = true):Void { // Cancel camera tweens if any are active. @@ -3184,8 +3184,8 @@ class PlayState extends MusicBeatSubState } /** - * Sets the camera follow point's position and tweens the camera there. - */ + * Sets the camera follow point's position and tweens the camera there. + */ public function tweenCameraToPosition(?x:Float, ?y:Float, ?duration:Float, ?ease:NullFloat>):Void { cameraFollowPoint.setPosition(x, y); @@ -3193,8 +3193,8 @@ class PlayState extends MusicBeatSubState } /** - * Disables camera following and tweens the camera to the follow point manually. - */ + * Disables camera following and tweens the camera to the follow point manually. + */ public function tweenCameraToFollowPoint(?duration:Float, ?ease:NullFloat>):Void { // Cancel the current tween if it's active. @@ -3231,8 +3231,8 @@ class PlayState extends MusicBeatSubState } /** - * Tweens the camera zoom to the desired amount. - */ + * Tweens the camera zoom to the desired amount. + */ public function tweenCameraZoom(?zoom:Float, ?duration:Float, ?direct:Bool, ?ease:NullFloat>):Void { // Cancel the current tween if it's active. @@ -3263,8 +3263,8 @@ class PlayState extends MusicBeatSubState } /** - * Cancel all active camera tweens simultaneously. - */ + * Cancel all active camera tweens simultaneously. + */ public function cancelAllCameraTweens() { cancelCameraFollowTween(); @@ -3274,8 +3274,8 @@ class PlayState extends MusicBeatSubState var prevScrollTargets:Array = []; // used to snap scroll speed when things go unruely /** - * The magical function that shall tween the scroll speed. - */ + * The magical function that shall tween the scroll speed. + */ public function tweenScrollSpeed(?speed:Float, ?duration:Float, ?ease:NullFloat>, strumlines:Array):Void { // Cancel the current tween if it's active. @@ -3325,12 +3325,12 @@ class PlayState extends MusicBeatSubState scrollSpeedTweens = []; } - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS /** - * Jumps forward or backward a number of sections in the song. - * Accounts for BPM changes, does not prevent death from skipped notes. - * @param sections The number of sections to jump, negative to go backwards. - */ + * Jumps forward or backward a number of sections in the song. + * Accounts for BPM changes, does not prevent death from skipped notes. + * @param sections The number of sections to jump, negative to go backwards. + */ function changeSection(sections:Int):Void { // FlxG.sound.music.pause(); diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index 85b0056ca7..dc24b7a5b4 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -386,7 +386,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements { if (character == null) return; - #if debug + #if FEATURE_DEBUG_FUNCTIONS // Temporary marker that shows where the character's location is relative to. // Should display at the stage position of the character (before any offsets). // TODO: Make this a toggle? It's useful to turn on from time to time. @@ -451,7 +451,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements character.cameraFocusPoint.x += stageCharData.cameraOffsets[0]; character.cameraFocusPoint.y += stageCharData.cameraOffsets[1]; - #if debug + #if FEATURE_DEBUG_FUNCTIONS // Draw the debug icon at the character's feet. if (charType == BF || charType == DAD) { @@ -468,7 +468,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements ScriptEventDispatcher.callEvent(character, new ScriptEvent(ADDED, false)); - #if debug + #if FEATURE_DEBUG_FUNCTIONS debugIconGroup.add(debugIcon); debugIconGroup.add(debugIcon2); #end diff --git a/source/funkin/ui/debug/DebugMenuSubState.hx b/source/funkin/ui/debug/DebugMenuSubState.hx index 590cce88b8..fc5f3aa37c 100644 --- a/source/funkin/ui/debug/DebugMenuSubState.hx +++ b/source/funkin/ui/debug/DebugMenuSubState.hx @@ -54,7 +54,7 @@ class DebugMenuSubState extends MusicBeatSubState // Create each menu item. // Call onMenuChange when the first item is created to move the camera . - #if CHART_EDITOR_SUPPORTED + #if FEATURE_CHART_EDITOR onMenuChange(createItem("CHART EDITOR", openChartEditor)); #end // createItem("Input Offset Testing", openInputOffsetTesting); diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index ebfbe5eac5..a1d7aca6be 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -3327,7 +3327,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState handleTestKeybinds(); handleHelpKeybinds(); - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS handleQuickWatch(); #end diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index 317a523084..fd00a95493 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -148,7 +148,7 @@ class FreeplayDJ extends FlxAtlasSprite // I shit myself. } - #if debug + #if FEATURE_DEBUG_FUNCTIONS if (FlxG.keys.pressed.CONTROL) { if (FlxG.keys.justPressed.LEFT) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 9e0e150191..cffb0b5a43 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -291,14 +291,14 @@ class FreeplayState extends MusicBeatSubState stickerSubState.degenStickers(); } - #if discord_rpc + #if FEATURE_DISCORD_RPC // Updating Discord Rich Presence DiscordClient.changePresence('In the Menus', null); #end var isDebug:Bool = false; - #if debug + #if FEATURE_DEBUG_FUNCTIONS isDebug = true; #end @@ -871,7 +871,7 @@ class FreeplayState extends MusicBeatSubState return str.songName.toLowerCase().startsWith(songFilter.filterData ?? ''); }); case ALL: - // no filter! + // no filter! case FAVORITE: songsToFilter = songsToFilter.filter(str -> { if (str == null) return true; // Random @@ -1198,7 +1198,7 @@ class FreeplayState extends MusicBeatSubState { super.update(elapsed); - #if debug + #if FEATURE_DEBUG_FUNCTIONS if (FlxG.keys.justPressed.T) { rankAnimStart(fromResultsParams ?? @@ -1772,7 +1772,7 @@ class FreeplayState extends MusicBeatSubState var targetInstId:String = baseInstrumentalId; // TODO: Make this a UI element. - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS if (altInstrumentalIds.length > 0 && FlxG.keys.pressed.CONTROL) { targetInstId = altInstrumentalIds[0]; @@ -1833,7 +1833,7 @@ class FreeplayState extends MusicBeatSubState practiceMode: false, minimalMode: false, - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS botPlayMode: FlxG.keys.pressed.SHIFT, #else botPlayMode: false, @@ -1942,7 +1942,7 @@ class FreeplayState extends MusicBeatSubState var instSuffix:String = baseInstrumentalId; // TODO: Make this a UI element. - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS if (altInstrumentalIds.length > 0 && FlxG.keys.pressed.CONTROL) { instSuffix = altInstrumentalIds[0]; diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index d219bfbcfa..2d809354a4 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -27,7 +27,7 @@ import funkin.ui.title.TitleState; import funkin.ui.story.StoryMenuState; import funkin.ui.Prompt; import funkin.util.WindowUtil; -#if discord_rpc +#if FEATURE_DISCORD_RPC import Discord.DiscordClient; #end #if newgrounds @@ -54,7 +54,7 @@ class MainMenuState extends MusicBeatState override function create():Void { - #if discord_rpc + #if FEATURE_DISCORD_RPC // Updating Discord Rich Presence DiscordClient.changePresence("In the Menus", null); #end @@ -344,7 +344,7 @@ class MainMenuState extends MusicBeatState } } - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS // Open the debug menu, defaults to ` / ~ if (controls.DEBUG_MENU) { diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 4e51fb229e..18614d414b 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -216,7 +216,7 @@ class StoryMenuState extends MusicBeatState changeLevel(); refresh(); - #if discord_rpc + #if FEATURE_DISCORD_RPC // Updating Discord Rich Presence DiscordClient.changePresence('In the Menus', null); #end diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index d48ca1c0b8..067de0e7fc 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -174,7 +174,7 @@ class LoadingState extends MusicBeatSubState FlxG.watch.addQuick('percentage?', callbacks.numRemaining / callbacks.length); } - #if debug + #if FEATURE_DEBUG_FUNCTIONS if (FlxG.keys.justPressed.SPACE) trace('fired: ' + callbacks.getFired() + ' unfired:' + callbacks.getUnfired()); #end } diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index 85cd1a27b6..80318f1a42 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -41,9 +41,9 @@ class Constants * A suffix to add to the game version. * Add a suffix to prototype builds and remove it for releases. */ - public static final VERSION_SUFFIX:String = #if (DEBUG || FORCE_DEBUG_VERSION) ' PROTOTYPE' #else '' #end; + public static final VERSION_SUFFIX:String = #if FEATURE_DEBUG_FUNCTIONS ' PROTOTYPE' #else '' #end; - #if (debug || FORCE_DEBUG_VERSION) + #if FEATURE_DEBUG_FUNCTIONS static function get_VERSION():String { return 'v${Application.current.meta.get('version')} (${GIT_BRANCH} : ${GIT_HASH}${GIT_HAS_LOCAL_CHANGES ? ' : MODIFIED' : ''})' + VERSION_SUFFIX; @@ -384,11 +384,7 @@ class Constants * 1 = The preloader waits for 1 second before moving to the next step. * The progress bare is automatically rescaled to match. */ - #if debug - public static final PRELOADER_MIN_STAGE_TIME:Float = 0.0; - #else public static final PRELOADER_MIN_STAGE_TIME:Float = 0.1; - #end /** * HEALTH VALUES From 91e4043ce99b4370ffbc01feaf0e2862ecabb508 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 26 Aug 2024 21:58:35 -0400 Subject: [PATCH 298/469] small hmm updates --- hmm.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hmm.json b/hmm.json index 4cd3e27c7d..1a1fd22032 100644 --- a/hmm.json +++ b/hmm.json @@ -59,7 +59,7 @@ "type": "git", "dir": null, "ref": "22b1ce089dd924f15cdc4632397ef3504d464e90", - "url": "https://github.com/MidyGamy/funkVis/" + "url": "https://github.com/FunkinCrew/funkVis/" }, { "name": "grig.audio", @@ -104,7 +104,7 @@ "type": "git", "dir": null, "url": "https://github.com/HaxeFoundation/hxcpp", - "ref": "8dc8020f8465027de6c2aaaed90718bc693651ed" + "ref": "904ea40643b050a5a154c5e4c33a83fd2aec18b1" }, { "name": "hxcpp-debug-server", @@ -143,7 +143,7 @@ "name": "lime", "type": "git", "dir": null, - "ref": "f6153ffcb1ffcf733f91d531eac5fda4189e07f7", + "ref": "e0b2339e02fff91168789dbd1a0dd019ea3dda39", "url": "https://github.com/FunkinCrew/lime" }, { From 5caba54e19b68b2e7eb220d127386fd3d5dc0770 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 27 Aug 2024 02:33:17 -0400 Subject: [PATCH 299/469] Update HScript to use the organization's fork --- hmm.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hmm.json b/hmm.json index 38e82acf24..017b8714c9 100644 --- a/hmm.json +++ b/hmm.json @@ -91,8 +91,8 @@ "name": "hscript", "type": "git", "dir": null, - "ref": "59b33162e9dcd7e48b186a9f25ee81b6e47a4dd7", - "url": "https://github.com/haxefoundation/hscript" + "ref": "12785398e2f07082f05034cb580682e5671442a2", + "url": "https://github.com/FunkinCrew/hscript" }, { "name": "hxCodec", From df42ac75311165fd1348b807365b73846e11b0fe Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 27 Aug 2024 04:20:49 -0400 Subject: [PATCH 300/469] Fixed issues with Nene offsets in Pico mode --- assets | 2 +- source/funkin/play/character/BaseCharacter.hx | 2 -- source/funkin/play/character/MultiSparrowCharacter.hx | 2 ++ 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets b/assets index 2ad892c766..8573b2039b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 2ad892c76665abad5764137288c606f2f4fcd331 +Subproject commit 8573b2039b9fb37551a41f5195aaa2abdc63770e diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index 15e196dc1d..55b849bcbc 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -411,7 +411,6 @@ class BaseCharacter extends Bopper else { // Play the idle animation. - trace('${characterId}: attempting dance'); dance(true); } } @@ -633,7 +632,6 @@ class BaseCharacter extends Bopper public override function playAnimation(name:String, restart:Bool = false, ignoreOther:Bool = false, reversed:Bool = false):Void { - // FlxG.watch.addQuick('playAnim(${characterName})', name); super.playAnimation(name, restart, ignoreOther, reversed); } } diff --git a/source/funkin/play/character/MultiSparrowCharacter.hx b/source/funkin/play/character/MultiSparrowCharacter.hx index e23afe18c5..759d17a6b8 100644 --- a/source/funkin/play/character/MultiSparrowCharacter.hx +++ b/source/funkin/play/character/MultiSparrowCharacter.hx @@ -67,6 +67,8 @@ class MultiSparrowCharacter extends BaseCharacter if (texture == null) { trace('Multi-Sparrow atlas could not load PRIMARY texture: ${_data.assetPath}'); + FlxG.log.error('Multi-Sparrow atlas could not load PRIMARY texture: ${_data.assetPath}'); + return; } else { From d02f632c0e706a3454182438b138d42b41e227b9 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 27 Aug 2024 04:54:02 -0400 Subject: [PATCH 301/469] Fix a bug where stage props would receive events twice --- source/funkin/play/stage/Stage.hx | 152 ++++-------------------------- 1 file changed, 20 insertions(+), 132 deletions(-) diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index 85b0056ca7..bece8b5304 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -769,39 +769,15 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements * A function that gets called once per step in the song. * @param curStep The current step number. */ - public function onStepHit(event:SongTimeScriptEvent):Void - { - // Override me in your scripted stage to perform custom behavior! - // Make sure to call super.onStepHit(event) if you want to keep the boppers dancing. - - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onStepHit(event:SongTimeScriptEvent):Void {} /** * A function that gets called once per beat in the song (once every four steps). * @param curStep The current beat number. */ - public function onBeatHit(event:SongTimeScriptEvent):Void - { - // Override me in your scripted stage to perform custom behavior! - // Make sure to call super.onBeatHit(event) if you want to keep the boppers dancing. - - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onBeatHit(event:SongTimeScriptEvent):Void {} - public function onUpdate(event:UpdateScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onUpdate(event:UpdateScriptEvent) {} public override function kill() { @@ -883,129 +859,41 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements public function onScriptEvent(event:ScriptEvent) { + // Ensure all custom events get broadcast to the elements of the stage. + // If we do it here, we don't have to add a handler to EACH script event function. for (bopper in boppers) { ScriptEventDispatcher.callEvent(bopper, event); } } - public function onPause(event:PauseScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onPause(event:PauseScriptEvent) {} - public function onResume(event:ScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onResume(event:ScriptEvent) {} - public function onSongStart(event:ScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onSongStart(event:ScriptEvent) {} - public function onSongEnd(event:ScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onSongEnd(event:ScriptEvent) {} - public function onGameOver(event:ScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onGameOver(event:ScriptEvent) {} - public function onCountdownStart(event:CountdownScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onCountdownStart(event:CountdownScriptEvent) {} - public function onCountdownStep(event:CountdownScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onCountdownStep(event:CountdownScriptEvent) {} - public function onCountdownEnd(event:CountdownScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onCountdownEnd(event:CountdownScriptEvent) {} - public function onNoteIncoming(event:NoteScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onNoteIncoming(event:NoteScriptEvent) {} - public function onNoteHit(event:HitNoteScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onNoteHit(event:HitNoteScriptEvent) {} - public function onNoteMiss(event:NoteScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onNoteMiss(event:NoteScriptEvent) {} - public function onSongEvent(event:SongEventScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onSongEvent(event:SongEventScriptEvent) {} - public function onNoteGhostMiss(event:GhostMissNoteScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onNoteGhostMiss(event:GhostMissNoteScriptEvent) {} - public function onSongLoaded(event:SongLoadScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onSongLoaded(event:SongLoadScriptEvent) {} - public function onSongRetry(event:ScriptEvent) - { - for (bopper in boppers) - { - ScriptEventDispatcher.callEvent(bopper, event); - } - } + public function onSongRetry(event:ScriptEvent) {} } From 82496299e79e39729df70b1cdff28ffa12c1c13a Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 27 Aug 2024 14:21:40 -0400 Subject: [PATCH 302/469] Display syntax formatting in project.hxp on Github.com --- .gitattributes | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 94f480de94..e7a218c89d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,3 @@ -* text=auto eol=lf \ No newline at end of file +* text=auto eol=lf +*.hxc linguist-language=Haxe +*.hxp linguist-language=Haxe From 861b869b2d88784b3fcbde951dbb776ef5ce182f Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 27 Aug 2024 15:23:27 -0400 Subject: [PATCH 303/469] fonts fix for mac --- source/funkin/ui/credits/CreditsState.hx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/funkin/ui/credits/CreditsState.hx b/source/funkin/ui/credits/CreditsState.hx index 44769e9b33..b379856505 100644 --- a/source/funkin/ui/credits/CreditsState.hx +++ b/source/funkin/ui/credits/CreditsState.hx @@ -34,7 +34,13 @@ class CreditsState extends MusicBeatState * To use a font from the `assets` folder, use `Paths.font(...)`. * Choose something that will render Unicode properly. */ + #if windows static final CREDITS_FONT = 'Consolas'; + #elseif mac + static final CREDITS_FONT = 'Menlo'; + #else + static final CREDITS_FONT = "Courier New"; + #end /** * The size of the font. From 9b712ce2aa407ba2ac01715e76eaceadb02207b9 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 27 Aug 2024 16:13:52 -0400 Subject: [PATCH 304/469] bump flixel --- hmm.json | 8 +++----- source/funkin/ui/charSelect/CharSelectSubState.hx | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/hmm.json b/hmm.json index c6fb67eba4..0520b85c98 100644 --- a/hmm.json +++ b/hmm.json @@ -18,7 +18,7 @@ "name": "flixel", "type": "git", "dir": null, - "ref": "10c2a203c43a78ff1ff26b8368fd736576829d8d", + "ref": "599f38eeb502a8ba6439784036c2cfdc7b485260", "url": "https://github.com/FunkinCrew/flixel" }, { @@ -59,9 +59,7 @@ "type": "git", "dir": null, "ref": "22b1ce089dd924f15cdc4632397ef3504d464e90", - "url": "https://github.com/FunkinCrew/funkVis" - }, { "name": "grig.audio", @@ -107,8 +105,8 @@ "name": "hxcpp", "type": "git", "dir": null, - "url": "https://github.com/HaxeFoundation/hxcpp", - "ref": "904ea40643b050a5a154c5e4c33a83fd2aec18b1" + "ref": "904ea40643b050a5a154c5e4c33a83fd2aec18b1", + "url": "https://github.com/HaxeFoundation/hxcpp" }, { "name": "hxcpp-debug-server", diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 8b1f050f54..41ed7ef1e5 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -245,7 +245,7 @@ class CharSelectSubState extends MusicBeatSubState cursorBlue.scrollFactor.set(); cursorDarkBlue.scrollFactor.set(); - FlxTween.color(cursor, 0.2, 0xFFFFFF00, 0xFFFFCC00, {type: FlxTween.PINGPONG}); + FlxTween.color(cursor, 0.2, 0xFFFFFF00, 0xFFFFCC00, {type: PINGPONG}); // FlxG.debugger.track(cursor); From 353aa42f5b46122b2115b7be0ca54ed416adb47f Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 28 Aug 2024 02:48:56 +0200 Subject: [PATCH 305/469] Fixes for the atlasSprite bizz --- assets | 2 +- .../graphics/adobeanimate/FlxAtlasSprite.hx | 32 ++++++- source/funkin/ui/charSelect/CharSelectGF.hx | 34 +------- .../funkin/ui/charSelect/CharSelectPlayer.hx | 85 +++++-------------- .../ui/charSelect/CharSelectSubState.hx | 17 ++-- source/funkin/ui/freeplay/AlbumRoll.hx | 4 +- source/funkin/ui/freeplay/FreeplayState.hx | 3 + 7 files changed, 66 insertions(+), 111 deletions(-) diff --git a/assets b/assets index 0e4392bcb0..f986e52819 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0e4392bcb0ed1dbb496764cff074635c20c91389 +Subproject commit f986e52819c5fd2e47f4d8d3297f326351682675 diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index fdb53e30e5..9a9ba70361 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -9,6 +9,7 @@ import flixel.system.FlxAssets.FlxGraphicAsset; import openfl.display.BitmapData; import openfl.utils.Assets; import flixel.math.FlxPoint; +import flxanimate.animate.FlxKeyFrame; /** * A sprite which provides convenience functions for rendering a texture atlas with animations. @@ -121,6 +122,12 @@ class FlxAtlasSprite extends FlxAnimate return false; } + var _completeAnim:Bool = false; + + var fr:FlxKeyFrame = null; + + var looping:Bool = false; + /** * Plays an animation. * @param id A string ID of the animation to play. @@ -174,6 +181,8 @@ class FlxAtlasSprite extends FlxAnimate } }); + looping = loop; + // Prevent other animations from playing if `ignoreOther` is true. if (ignoreOther) canPlayOtherAnims = false; @@ -182,6 +191,9 @@ class FlxAtlasSprite extends FlxAnimate trace('Playing animation $id'); this.anim.play(id, restart, false, startFrame); goToFrameLabel(id); + + fr = anim.getFrameLabel(id); + anim.curFrame += startFrame; this.currentAnimation = id; } @@ -259,7 +271,21 @@ class FlxAtlasSprite extends FlxAnimate if (currentAnimation != null) { onAnimationFrame.dispatch(currentAnimation, frame); - if (isLoopComplete()) onAnimationLoopComplete.dispatch(currentAnimation); + + if (fr != null && frame > (fr.index + fr.duration - 1) || isLoopFinished()) + { + anim.pause(); + _onAnimationComplete(); + if (looping) + { + anim.curFrame = (fr != null) ? fr.index : 0; + anim.resume(); + } + else + { + anim.curFrame--; + } + } } } @@ -267,7 +293,9 @@ class FlxAtlasSprite extends FlxAnimate { if (currentAnimation != null) { - onAnimationComplete.dispatch(currentAnimation); + if (looping) onAnimationLoopComplete.dispatch(currentAnimation); + else + onAnimationComplete.dispatch(currentAnimation); } } diff --git a/source/funkin/ui/charSelect/CharSelectGF.hx b/source/funkin/ui/charSelect/CharSelectGF.hx index 44a9a6088b..cb4eda8be2 100644 --- a/source/funkin/ui/charSelect/CharSelectGF.hx +++ b/source/funkin/ui/charSelect/CharSelectGF.hx @@ -29,39 +29,12 @@ class CharSelectGF extends FlxAtlasSprite public function new() { super(0, 0, Paths.animateAtlas("charSelect/gfChill")); - anim.play(""); + list = anim.curSymbol.getFrameLabelNames(); switchGF("bf"); } - var _addedCallback:String = ""; - - override public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false, loop:Bool = false, startFrame:Int = 0):Void - { - if (id == null) id = "idle"; - // var fr = anim.getFrameLabel("confirm"); - // fr.removeCallbacks(); - // fr.add(() -> trace("HEY")); - - if (id != _addedCallback) - { - var next = list[list.indexOf(_addedCallback) + 1]; - if (next != null) anim.getFrameLabel(next).removeCallbacks(); - - var index:Int = list.indexOf(id); - - _addedCallback = list[index]; - if (index != -1 && index + 1 < list.length) - { - var lb = anim.getFrameLabel(list[index + 1]); - @:privateAccess - lb.add(() -> playAnimation(list[index], true, false, false)); - } - } - super.playAnimation(id, restart, ignoreOther, loop, startFrame); - } - override public function update(elapsed:Float) { super.update(elapsed); @@ -190,9 +163,8 @@ class CharSelectGF extends FlxAtlasSprite animInInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + str + "AnimInfo/" + str + "In.txt")); animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + str + "AnimInfo/" + str + "Out.txt")); - anim.play(""); - playAnimation("idle", true, false, false); - addFrameCallback(getNextFrameLabel("idle"), () -> playAnimation("idle", true, false, false)); + playAnimation("idle", true, false, true); + // addFrameCallback(getNextFrameLabel("idle"), () -> playAnimation("idle", true, false, false)); updateHitbox(); } diff --git a/source/funkin/ui/charSelect/CharSelectPlayer.hx b/source/funkin/ui/charSelect/CharSelectPlayer.hx index 8a5d575301..07c82d038d 100644 --- a/source/funkin/ui/charSelect/CharSelectPlayer.hx +++ b/source/funkin/ui/charSelect/CharSelectPlayer.hx @@ -2,82 +2,33 @@ package funkin.ui.charSelect; import flixel.FlxSprite; import funkin.graphics.adobeanimate.FlxAtlasSprite; +import flxanimate.animate.FlxKeyFrame; class CharSelectPlayer extends FlxAtlasSprite { + var desLp:FlxKeyFrame = null; + public function new(x:Float, y:Float) { super(x, y, Paths.animateAtlas("charSelect/bfChill")); + desLp = anim.getFrameLabel("deselect loop start"); + onAnimationComplete.add(function(animLabel:String) { - if (hasAnimation("slidein idle point")) playAnimation("slidein idle point", true, false, false); - else - playAnimation("idle"); + switch (animLabel) + { + case "slidein": + if (hasAnimation("slidein idle point")) playAnimation("slidein idle point", true, false, false); + else + playAnimation("idle", true, false, true); + case "slidein idle point": + playAnimation("idle", true, false, true); + } }); - } - - var _addedCall = false; - - override public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false, loop:Bool = false, startFrame:Int = 0):Void - { - if (id == null || id == "") id = "idle"; - switch (id) - { - case "idle", "slidein idle point": - if (!_addedCall) - { - var fr = anim.getFrameLabel("idle end"); - if (fr != null) fr.add(() -> { - playAnimation("idle", true, false, false); - }); - } - _addedCall = true; - - case "select": - if (_addedCall) - { - anim.getFrameLabel("idle end").removeCallbacks(); - _addedCall = false; - } - - var fr = anim.getFrameLabel("deselect"); - - fr.add(() -> { - anim.pause(); - anim.curFrame--; - }); - _addedCall = true; - - case "deselect": - var og = anim.getFrameLabel("deselect"); - if (_addedCall) - { - og.removeCallbacks(); - _addedCall = false; - } - - var fr = anim.getFrameLabel("deselect loop end"); - - fr.removeCallbacks(); - fr.add(() -> playAnimation("deselect loop start", true, false, false)); - - _addedCall = true; - - case "slidein", "slideout": - if (_addedCall) - { - anim.getFrameLabel("deselect loop end").removeCallbacks(); - _addedCall = false; - } - default: - if (_addedCall) - { - anim.getFrameLabel("idle end").removeCallbacks(); - _addedCall = false; - } - } - super.playAnimation(id, restart, ignoreOther, loop, startFrame); + onAnimationFrame.add(function(animLabel:String, frame:Int) { + if (animLabel == "deselect" && desLp != null && frame >= desLp.index) playAnimation("deselect loop start", true, false, true); + }); } public function updatePosition(str:String) @@ -104,6 +55,8 @@ class CharSelectPlayer extends FlxAtlasSprite playAnimation("slidein", true, false, false); + desLp = anim.getFrameLabel("deselect loop start"); + updateHitbox(); updatePosition(str); diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 2b577dd952..9aed0ef020 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -499,12 +499,8 @@ class CharSelectSubState extends MusicBeatSubState { ease: FlxEase.quartInOut, onComplete: (_) -> { - var fr = playerChill.anim.getFrameLabel("deselect loop end"); - if (fr != null) fr.removeCallbacks(); - @:privateAccess - playerChill._addedCall = false; - playerChill.playAnimation("idle"); - gfChill.playAnimation("idle"); + playerChill.playAnimation("idle", true, false, true); + gfChill.playAnimation("idle", true, false, true); } }); pressedSelect = false; @@ -645,20 +641,23 @@ class CharSelectSubState extends MusicBeatSubState nametag.switchChar(value); playerChill.visible = false; playerChillOut.visible = true; - playerChillOut.anim.goToFrameLabel("slideout"); + playerChillOut.playAnimation("slideout"); + var index = playerChillOut.anim.getFrameLabel("slideout").index; playerChillOut.onAnimationFrame.add((_, frame:Int) -> { - if (frame == playerChillOut.anim.getFrameLabel("slideout").index + 1) + if (frame == index + 1) { playerChill.visible = true; playerChill.switchChar(value); gfChill.switchGF(value); } - if (frame == playerChillOut.anim.getFrameLabel("slideout").index + 2) + if (frame == index + 2) { playerChillOut.switchChar(value); playerChillOut.visible = false; + playerChillOut.onAnimationFrame.removeAll(); } }); + return value; } diff --git a/source/funkin/ui/freeplay/AlbumRoll.hx b/source/funkin/ui/freeplay/AlbumRoll.hx index 36dba0054a..64b86120cb 100644 --- a/source/funkin/ui/freeplay/AlbumRoll.hx +++ b/source/funkin/ui/freeplay/AlbumRoll.hx @@ -60,7 +60,7 @@ class AlbumRoll extends FlxSpriteGroup { super(); - newAlbumArt = new FlxAtlasSprite(640, 350, Paths.animateAtlas("freeplay/albumRoll/freeplayAlbum")); + newAlbumArt = new FlxAtlasSprite(640, 360, Paths.animateAtlas("freeplay/albumRoll/freeplayAlbum")); newAlbumArt.visible = false; newAlbumArt.onAnimationComplete.add(onAlbumFinish); @@ -79,7 +79,7 @@ class AlbumRoll extends FlxSpriteGroup // Play the idle animation for the current album. if (animName != "idle") { - // newAlbumArt.playAnimation('idle', true); + newAlbumArt.playAnimation('idle', true); } } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 63767d28e0..d30bb3e8c0 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -643,6 +643,9 @@ class FreeplayState extends MusicBeatSubState // when boyfriend hits dat shiii albumRoll.playIntro(); + var daSong = grpCapsules.members[curSelected].songData; + albumRoll.albumId = daSong?.albumId; + FlxTween.tween(grpDifficulties, {x: 90}, 0.6, {ease: FlxEase.quartOut}); From 469ff6690f93ebb93fa1c4f11b9eef0520fba62b Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 05:40:50 -0400 Subject: [PATCH 306/469] Fix a crash when skipping in the song before it started. --- source/funkin/play/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 928f392e5a..fa059f73ae 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1979,7 +1979,7 @@ class PlayState extends MusicBeatSubState if (vocals == null) return; // Skip this if the music is paused (GameOver, Pause menu, start-of-song offset, etc.) - if (!FlxG.sound.music.playing) return; + if (!(FlxG.sound.music?.playing ?? false)) return; var timeToPlayAt:Float = Conductor.instance.songPosition - Conductor.instance.instrumentalOffset; FlxG.sound.music.pause(); vocals.pause(); From 382af3b4851c63839bca5eed672bf0c2d5a2ed1a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 05:42:14 -0400 Subject: [PATCH 307/469] Reimplement ghost tapping (disabled via compile define) --- source/funkin/play/PlayState.hx | 16 +++---- source/funkin/play/notes/Strumline.hx | 65 ++++++++++++++++++++++++--- source/funkin/util/Constants.hx | 10 +++-- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index fa059f73ae..b3beba4e24 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2323,7 +2323,11 @@ class PlayState extends MusicBeatSubState var notesInDirection:Array = notesByDirection[input.noteDirection]; - if (!Constants.GHOST_TAPPING && notesInDirection.length == 0) + #if FEATURE_GHOST_TAPPING + if ((!playerStrumline.mayGhostTap()) && notesInDirection.length == 0) + #else + if (notesInDirection.length == 0) + #end { // Pressed a wrong key with no notes nearby. // Perform a ghost miss (anti-spam). @@ -2333,16 +2337,6 @@ class PlayState extends MusicBeatSubState playerStrumline.playPress(input.noteDirection); trace('PENALTY Score: ${songScore}'); } - else if (Constants.GHOST_TAPPING && (!playerStrumline.mayGhostTap()) && notesInDirection.length == 0) - { - // Pressed a wrong key with notes visible on-screen. - // Perform a ghost miss (anti-spam). - ghostNoteMiss(input.noteDirection, notesInRange.length > 0); - - // Play the strumline animation. - playerStrumline.playPress(input.noteDirection); - trace('PENALTY Score: ${songScore}'); - } else if (notesInDirection.length == 0) { // Press a key with no penalty. diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index 1e5782ad2f..e894f9c627 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -94,6 +94,10 @@ class Strumline extends FlxSpriteGroup final noteStyle:NoteStyle; + #if FEATURE_GHOST_TAPPING + var ghostTapTimer:Float = 0.0; + #end + /** * The note data for the song. Should NOT be altered after the song starts, * so we can easily rewind. @@ -179,21 +183,36 @@ class Strumline extends FlxSpriteGroup super.update(elapsed); updateNotes(); + + #if FEATURE_GHOST_TAPPING + updateGhostTapTimer(elapsed); + #end } + #if FEATURE_GHOST_TAPPING /** * Returns `true` if no notes are in range of the strumline and the player can spam without penalty. */ public function mayGhostTap():Bool { - // TODO: Refine this. Only querying "can be hit" is too tight but "is being rendered" is too loose. - // Also, if you just hit a note, there should be a (short) period where this is off so you can't spam. + // Any notes in range of the strumline. + if (getNotesMayHit().length > 0) + { + return false; + } + // Any hold notes in range of the strumline. + if (getHoldNotesHitOrMissed().length > 0) + { + return false; + } - // If there are any notes on screen, we can't ghost tap. - return notes.members.filter(function(note:NoteSprite) { - return note != null && note.alive && !note.hasBeenHit; - }).length == 0; + // Note has been hit recently. + if (ghostTapTimer > 0.0) return false; + + // **yippee** + return true; } + #end /** * Return notes that are within `Constants.HIT_WINDOW` ms of the strumline. @@ -492,6 +511,32 @@ class Strumline extends FlxSpriteGroup } } + /** + * Return notes that are within, or way after, `Constants.HIT_WINDOW` ms of the strumline. + * @return An array of `NoteSprite` objects. + */ + public function getNotesOnScreen():Array + { + return notes.members.filter(function(note:NoteSprite) { + return note != null && note.alive && !note.hasBeenHit; + }); + } + + #if FEATURE_GHOST_TAPPING + function updateGhostTapTimer(elapsed:Float):Void + { + // If it's still our turn, don't update the ghost tap timer. + if (getNotesOnScreen().length > 0) return; + + ghostTapTimer -= elapsed; + + if (ghostTapTimer <= 0) + { + ghostTapTimer = 0; + } + } + #end + /** * Called when the PlayState skips a large amount of time forward or backward. */ @@ -563,6 +608,10 @@ class Strumline extends FlxSpriteGroup playStatic(dir); } resetScrollSpeed(); + + #if FEATURE_GHOST_TAPPING + ghostTapTimer = 0; + #end } public function applyNoteData(data:Array):Void @@ -602,6 +651,10 @@ class Strumline extends FlxSpriteGroup note.holdNoteSprite.sustainLength = (note.holdNoteSprite.strumTime + note.holdNoteSprite.fullSustainLength) - conductorInUse.songPosition; } + + #if FEATURE_GHOST_TAPPING + ghostTapTimer = Constants.GHOST_TAP_DELAY; + #end } public function killNote(note:NoteSprite):Void diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index 80318f1a42..fa03b229da 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -524,12 +524,16 @@ class Constants * OTHER */ // ============================== + #if FEATURE_GHOST_TAPPING + // Hey there, Eric here. + // This feature is currently still in development. You can test it out by creating a special debug build! + // lime build windows -DFEATURE_GHOST_TAPPING /** - * If true, the player will not receive the ghost miss penalty if there are no notes within the hit window. - * This is the thing people have been begging for forever lolol. + * Duration, in seconds, after the player's section ends before the player can spam without penalty. */ - public static final GHOST_TAPPING:Bool = false; + public static final GHOST_TAP_DELAY:Float = 3 / 8; + #end /** * The maximum number of previous file paths for the Chart Editor to remember. From b0f04779dc49a5a9e9dd40d194e57e82efa5f8d9 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 05:43:01 -0400 Subject: [PATCH 308/469] Fix to Spooky icon in Freeplay menu --- source/funkin/play/character/CharacterData.hx | 2 ++ source/funkin/ui/PixelatedIcon.hx | 28 +++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/source/funkin/play/character/CharacterData.hx b/source/funkin/play/character/CharacterData.hx index d447eb97f2..bac2c7141e 100644 --- a/source/funkin/play/character/CharacterData.hx +++ b/source/funkin/play/character/CharacterData.hx @@ -305,6 +305,8 @@ class CharacterDataParser icon = "darnell"; case "senpai-angry": icon = "senpai"; + case "spooky-dark": + icon = "spooky"; case "tankman-atlas": icon = "tankman"; } diff --git a/source/funkin/ui/PixelatedIcon.hx b/source/funkin/ui/PixelatedIcon.hx index 8d9b97d9ca..d1ea652c3b 100644 --- a/source/funkin/ui/PixelatedIcon.hx +++ b/source/funkin/ui/PixelatedIcon.hx @@ -22,14 +22,26 @@ class PixelatedIcon extends FlxSprite switch (char) { - case 'monster-christmas': - charPath += 'monsterpixel'; - case 'mom-car': - charPath += 'mommypixel'; - case 'darnell-blazin': - charPath += 'darnellpixel'; - case 'senpai-angry': - charPath += 'senpaipixel'; + case "bf-christmas" | "bf-car" | "bf-pixel" | "bf-holding-gf": + charPath += "bfpixel"; + case "monster-christmas": + charPath += "monsterpixel"; + case "mom" | "mom-car": + charPath += "mommypixel"; + case "pico-blazin" | "pico-playable" | "pico-speaker": + charPath += "picopixel"; + case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen": + charPath += "gfpixel"; + case "dad": + charPath += "dadpixel"; + case "darnell-blazin": + charPath += "darnellpixel"; + case "senpai-angry": + charPath += "senpaipixel"; + case "spooky-dark": + charPath += "spookypixel"; + case "tankman-atlas": + charPath += "tankmanpixel"; default: charPath += '${char}pixel'; } From af93da6cd4d6ebd414ed196448c5549264e7d932 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 05:43:23 -0400 Subject: [PATCH 309/469] Fixes to Chart Editor toolboxes --- source/funkin/play/event/ZoomCameraSongEvent.hx | 2 +- .../debug/charting/toolboxes/ChartEditorEventDataToolbox.hx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/funkin/play/event/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx index 748abda198..ee2eea8ad5 100644 --- a/source/funkin/play/event/ZoomCameraSongEvent.hx +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -114,7 +114,7 @@ class ZoomCameraSongEvent extends SongEvent name: 'zoom', title: 'Zoom Level', defaultValue: 1.0, - step: 0.1, + step: 0.05, type: SongEventFieldType.FLOAT, units: 'x' }, diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx index 8f021840ac..70580300ea 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx @@ -190,8 +190,8 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox var numberStepper:NumberStepper = new NumberStepper(); numberStepper.id = field.name; numberStepper.step = field.step ?? 1.0; - numberStepper.min = field.min ?? 0.0; - numberStepper.max = field.max ?? 10.0; + if (field.min != null) numberStepper.min = field.min; + if (field.min != null) numberStepper.max = field.max; if (field.defaultValue != null) numberStepper.value = field.defaultValue; input = numberStepper; case FLOAT: From ccabded2cc5a2c81666b827148388c5bafc46f82 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 05:43:41 -0400 Subject: [PATCH 310/469] Fix warning related to bad asset caching for judgements. --- source/funkin/ui/transition/LoadingState.hx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index 067de0e7fc..5b82cc7416 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -314,25 +314,28 @@ class LoadingState extends MusicBeatSubState FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num7')); FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num8')); FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num9')); + FunkinSprite.cacheTexture(Paths.image('notes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared')); FunkinSprite.cacheTexture(Paths.image('NOTE_hold_assets')); + FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/ready', 'shared')); FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/set', 'shared')); FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/go', 'shared')); FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/ready', 'shared')); FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/set', 'shared')); FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/go', 'shared')); - FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/sick')); - FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/good')); - FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/bad')); - FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/shit')); + + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/sick')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/good')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/bad')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/shit')); + FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/sick')); FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/good')); FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/bad')); FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/shit')); - FunkinSprite.cacheTexture(Paths.image('miss', 'shared')); // TODO: remove this // List all image assets in the level's library. // This is crude and I want to remove it when we have a proper asset caching system. From 89402f8bb839827f7cf6322c320d92aea9f57f99 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 05:47:34 -0400 Subject: [PATCH 311/469] Fix a bug where Bot Play could miss (via lag or the player mashing) and lose the song. --- source/funkin/play/PlayState.hx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index b3beba4e24..f4b177763b 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2221,10 +2221,14 @@ class PlayState extends MusicBeatSubState // Calling event.cancelEvent() skips all the other logic! Neat! if (event.eventCanceled) continue; + // Skip handling the miss in botplay! + if (!isBotPlayMode) + { // Judge the miss. // NOTE: This is what handles the scoring. trace('Missed note! ${note.noteData}'); onNoteMiss(note, event.playSound, event.healthChange); + } note.handledMiss = true; } @@ -2321,6 +2325,9 @@ class PlayState extends MusicBeatSubState playerStrumline.pressKey(input.noteDirection); + // Don't credit or penalize inputs in Bot Play. + if (isBotPlayMode) continue; + var notesInDirection:Array = notesByDirection[input.noteDirection]; #if FEATURE_GHOST_TAPPING From 152820f2e1961684ed8607cbcb5c4ab8773c2ce3 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 06:03:12 -0400 Subject: [PATCH 312/469] Fix some warnings with props in StoryMenu --- source/funkin/ui/story/LevelProp.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/story/LevelProp.hx b/source/funkin/ui/story/LevelProp.hx index 4e78415e36..dfb11dd204 100644 --- a/source/funkin/ui/story/LevelProp.hx +++ b/source/funkin/ui/story/LevelProp.hx @@ -16,7 +16,7 @@ class LevelProp extends Bopper this.propData = value; this.visible = this.propData != null; - danceEvery = this.propData?.danceEvery ?? 0.0; + danceEvery = this.propData?.danceEvery ?? 1.0; applyData(); } @@ -32,7 +32,7 @@ class LevelProp extends Bopper public function playConfirm():Void { - playAnimation('confirm', true, true); + if (hasAnimation('confirm')) playAnimation('confirm', true, true); } function applyData():Void From 570e672296d02522399e249c5c8509f966db4cca Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 06:03:26 -0400 Subject: [PATCH 313/469] Fix magenta flicker on the main menu. --- source/funkin/ui/mainmenu/MainMenuState.hx | 36 ++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 2d809354a4..c426fd0489 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -98,14 +98,7 @@ class MainMenuState extends MusicBeatState add(menuItems); menuItems.onChange.add(onMenuItemChange); menuItems.onAcceptPress.add(function(_) { - if (_.name == 'freeplay') - { - magenta.visible = true; - } - else - { - FlxFlicker.flicker(magenta, 1.1, 0.15, false, true); - } + FlxFlicker.flicker(magenta, 1.1, 0.15, false, true); }); menuItems.enabled = true; // can move on intro @@ -117,10 +110,7 @@ class MainMenuState extends MusicBeatState FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - openSubState(new FreeplayState( - { - character: FlxG.keys.pressed.SHIFT ? 'pico' : 'bf', - })); + openSubState(new FreeplayState()); }); #if CAN_OPEN_LINKS @@ -375,6 +365,28 @@ class MainMenuState extends MusicBeatState }); } + if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.L) + { + // Give the user a score of 0 points on Weekend 1 story mode. + // This makes the level count as uncleared and no longer displays the songs in Freeplay. + funkin.save.Save.instance.setLevelScore('weekend1', 'easy', + { + score: 1, + tallies: + { + sick: 0, + good: 0, + bad: 0, + shit: 0, + missed: 0, + combo: 0, + maxCombo: 0, + totalNotesHit: 0, + totalNotes: 0, + } + }); + } + if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.R) { // Give the user a hypothetical overridden score, From 2e7eabc4947ae53752b06b4341723fa4e3fdb404 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 06:05:28 -0400 Subject: [PATCH 314/469] Remember character ID between visits to Freeplay, plus some fixes to prevent spamming keys --- source/funkin/ui/freeplay/FreeplayState.hx | 43 +++++++++++++++------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index cffb0b5a43..f04fd8ff61 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -177,9 +177,22 @@ class FreeplayState extends MusicBeatSubState var stickerSubState:Null = null; - public static var rememberedDifficulty:Null = Constants.DEFAULT_DIFFICULTY; + /** + * The difficulty we were on when this menu was last accessed. + */ + public static var rememberedDifficulty:String = Constants.DEFAULT_DIFFICULTY; + + /** + * The song we were on when this menu was last accessed. + * NOTE: `null` if the last song was `Random`. + */ public static var rememberedSongId:Null = 'tutorial'; + /** + * The character we were on when this menu was last accessed. + */ + public static var rememberedCharacterId:String = Constants.DEFAULT_CHARACTER; + var funnyCam:FunkinCamera; var rankCamera:FunkinCamera; var rankBg:FunkinSprite; @@ -209,14 +222,16 @@ class FreeplayState extends MusicBeatSubState public function new(?params:FreeplayStateParams, ?stickers:StickerSubState) { - currentCharacterId = params?.character ?? Constants.DEFAULT_CHARACTER; + currentCharacterId = params?.character ?? rememberedCharacterId; var fetchPlayableCharacter = function():PlayableCharacter { - var result = PlayerRegistry.instance.fetchEntry(params?.character ?? Constants.DEFAULT_CHARACTER); + var result = PlayerRegistry.instance.fetchEntry(params?.character ?? rememberedCharacterId); if (result == null) throw 'No valid playable character with id ${params?.character}'; return result; }; currentCharacter = fetchPlayableCharacter(); + rememberedCharacterId = currentCharacter?.id ?? Constants.DEFAULT_CHARACTER; + fromResultsParams = params?.fromResults; if (fromResultsParams?.playRankAnim == true) @@ -743,10 +758,7 @@ class FreeplayState extends MusicBeatSubState var tempSongs:Array> = songs; // Remember just the difficulty because it's important for song sorting. - if (rememberedDifficulty != null) - { - currentDifficulty = rememberedDifficulty; - } + currentDifficulty = rememberedDifficulty; if (filterStuff != null) tempSongs = sortSongs(tempSongs, filterStuff); @@ -1190,7 +1202,7 @@ class FreeplayState extends MusicBeatSubState /** * If true, disable interaction with the interface. */ - var busy:Bool = false; + public var busy:Bool = false; var originalPos:FlxPoint = new FlxPoint(); @@ -1326,6 +1338,8 @@ class FreeplayState extends MusicBeatSubState } handleInputs(elapsed); + + if (dj != null) FlxG.watch.addQuick('dj-anim', dj.getCurrentAnimation()); } function handleInputs(elapsed:Float):Void @@ -1483,7 +1497,7 @@ class FreeplayState extends MusicBeatSubState generateSongList(currentFilter, true); } - if (controls.BACK) + if (controls.BACK && !busy) { busy = true; FlxTween.globalManager.clear(); @@ -1891,7 +1905,7 @@ class FreeplayState extends MusicBeatSubState intendedCompletion = 0.0; diffIdsCurrent = diffIdsTotal; rememberedSongId = null; - rememberedDifficulty = null; + rememberedDifficulty = Constants.DEFAULT_DIFFICULTY; albumRoll.albumId = null; } @@ -2000,10 +2014,13 @@ class DifficultySelector extends FlxSprite var controls:Controls; var whiteShader:PureColor; - public function new(x:Float, y:Float, flipped:Bool, controls:Controls) + var parent:FreeplayState; + + public function new(parent:FreeplayState, x:Float, y:Float, flipped:Bool, controls:Controls) { super(x, y); + this.parent = parent; this.controls = controls; frames = Paths.getSparrowAtlas('freeplay/freeplaySelector'); @@ -2019,8 +2036,8 @@ class DifficultySelector extends FlxSprite override function update(elapsed:Float):Void { - if (flipX && controls.UI_RIGHT_P) moveShitDown(); - if (!flipX && controls.UI_LEFT_P) moveShitDown(); + if (flipX && controls.UI_RIGHT_P && !parent.busy) moveShitDown(); + if (!flipX && controls.UI_LEFT_P && !parent.busy) moveShitDown(); super.update(elapsed); } From 16a6dba9df36fecd81b879df2924db472042ba4c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 06:08:30 -0400 Subject: [PATCH 315/469] Pico Fist Pump animation (with loss animation) --- .../funkin/data/freeplay/player/PlayerData.hx | 75 +++++++++++++ source/funkin/play/stage/Bopper.hx | 2 +- source/funkin/ui/freeplay/FreeplayDJ.hx | 105 ++++++++++++++++-- source/funkin/ui/freeplay/FreeplayState.hx | 18 ++- 4 files changed, 183 insertions(+), 17 deletions(-) diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx index f6c0850180..de55fdf5fc 100644 --- a/source/funkin/data/freeplay/player/PlayerData.hx +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -38,6 +38,11 @@ class PlayerData @:optional public var freeplayDJ:Null = null; + + /** + * Data for displaying this character in the results screen. + */ + @:optional public var results:Null = null; /** @@ -97,6 +102,9 @@ class PlayerFreeplayDJData @:optional var cartoon:Null; + @:optional + var fistPump:Null; + public function new() { animationMap = new Map(); @@ -183,6 +191,46 @@ class PlayerFreeplayDJData { return cartoon?.channelChangeFrame ?? 60; } + + public function getFistPumpIntroStartFrame():Int + { + return fistPump?.introStartFrame ?? 0; + } + + public function getFistPumpIntroEndFrame():Int + { + return fistPump?.introEndFrame ?? 0; + } + + public function getFistPumpLoopStartFrame():Int + { + return fistPump?.loopStartFrame ?? 0; + } + + public function getFistPumpLoopEndFrame():Int + { + return fistPump?.loopEndFrame ?? 0; + } + + public function getFistPumpIntroBadStartFrame():Int + { + return fistPump?.introBadStartFrame ?? 0; + } + + public function getFistPumpIntroBadEndFrame():Int + { + return fistPump?.introBadEndFrame ?? 0; + } + + public function getFistPumpLoopBadStartFrame():Int + { + return fistPump?.loopBadStartFrame ?? 0; + } + + public function getFistPumpLoopBadEndFrame():Int + { + return fistPump?.loopBadEndFrame ?? 0; + } } typedef PlayerResultsData = @@ -242,3 +290,30 @@ typedef PlayerFreeplayDJCartoonData = var loopFrame:Int; var channelChangeFrame:Int; } + +typedef PlayerFreeplayDJFistPumpData = +{ + @:default(0) + var introStartFrame:Int; + + @:default(4) + var introEndFrame:Int; + + @:default(4) + var loopStartFrame:Int; + + @:default(-1) + var loopEndFrame:Int; + + @:default(0) + var introBadStartFrame:Int; + + @:default(4) + var introBadEndFrame:Int; + + @:default(4) + var loopBadStartFrame:Int; + + @:default(-1) + var loopBadEndFrame:Int; +}; diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index de19c51b4d..96a217d31d 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -315,7 +315,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass public function isAnimationFinished():Bool { - return this.animation.finished; + return this.animation?.finished ?? false; } public function setAnimationOffsets(name:String, xOffset:Float, yOffset:Float):Void diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index fd00a95493..6e2f83a63a 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -116,13 +116,54 @@ class FreeplayDJ extends FlxAtlasSprite if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, false); timeIdling = 0; case FistPumpIntro: - var animPrefix = playableCharData.getAnimationPrefix('fistPump'); - if (getCurrentAnimation() != animPrefix) playFlashAnimation('Boyfriend DJ fist pump', false); - if (getCurrentAnimation() == animPrefix && anim.curFrame >= 4) + var animPrefixA = playableCharData.getAnimationPrefix('fistPump'); + var animPrefixB = playableCharData.getAnimationPrefix('loss'); + + if (getCurrentAnimation() == animPrefixA) + { + var endFrame = playableCharData.getFistPumpIntroEndFrame(); + if (endFrame > -1 && anim.curFrame >= endFrame) + { + playFlashAnimation(animPrefixA, true, false, false, playableCharData.getFistPumpIntroStartFrame()); + } + } + else if (getCurrentAnimation() == animPrefixB) + { + var endFrame = playableCharData.getFistPumpIntroBadEndFrame(); + if (endFrame > -1 && anim.curFrame >= endFrame) + { + playFlashAnimation(animPrefixB, true, false, false, playableCharData.getFistPumpIntroBadStartFrame()); + } + } + else { - playAnimation("Boyfriend DJ fist pump", true, false, false, 0); + FlxG.log.warn("Unrecognized animation in FistPumpIntro: " + getCurrentAnimation()); } + case FistPump: + var animPrefixA = playableCharData.getAnimationPrefix('fistPump'); + var animPrefixB = playableCharData.getAnimationPrefix('loss'); + + if (getCurrentAnimation() == animPrefixA) + { + var endFrame = playableCharData.getFistPumpLoopEndFrame(); + if (endFrame > -1 && anim.curFrame >= endFrame) + { + playFlashAnimation(animPrefixA, true, false, false, playableCharData.getFistPumpLoopStartFrame()); + } + } + else if (getCurrentAnimation() == animPrefixB) + { + var endFrame = playableCharData.getFistPumpLoopBadEndFrame(); + if (endFrame > -1 && anim.curFrame >= endFrame) + { + playFlashAnimation(animPrefixB, true, false, false, playableCharData.getFistPumpLoopBadStartFrame()); + } + } + else + { + FlxG.log.warn("Unrecognized animation in FistPump: " + getCurrentAnimation()); + } case IdleEasterEgg: var animPrefix = playableCharData.getAnimationPrefix('idleEasterEgg'); @@ -271,7 +312,7 @@ class FreeplayDJ extends FlxAtlasSprite function loadCartoon() { cartoonSnd = FunkinSound.load(Paths.sound(getRandomFlashToon()), 1.0, false, true, true, function() { - playAnimation("Boyfriend DJ watchin tv OG", true, false, false, 60); + playFlashAnimation(playableCharData.getAnimationPrefix('cartoon'), true, false, false, 60); }); // Fade out music to 40% volume over 1 second. @@ -301,21 +342,32 @@ class FreeplayDJ extends FlxAtlasSprite currentState = Confirm; } - public function fistPump():Void + public function fistPumpIntro():Void { currentState = FistPumpIntro; + var animPrefix = playableCharData.getAnimationPrefix('fistPump'); + playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpIntroStartFrame()); } - public function pumpFist():Void + public function fistPump():Void { currentState = FistPump; - playAnimation("Boyfriend DJ fist pump", true, false, false, 4); + var animPrefix = playableCharData.getAnimationPrefix('fistPump'); + playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpLoopStartFrame()); + } + + public function fistPumpLossIntro():Void + { + currentState = FistPumpIntro; + var animPrefix = playableCharData.getAnimationPrefix('loss'); + playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpIntroBadStartFrame()); } - public function pumpFistBad():Void + public function fistPumpLoss():Void { currentState = FistPump; - playAnimation("Boyfriend DJ loss reaction 1", true, false, false, 4); + var animPrefix = playableCharData.getAnimationPrefix('loss'); + playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpLoopBadStartFrame()); } override public function getCurrentAnimation():String @@ -366,12 +418,43 @@ class FreeplayDJ extends FlxAtlasSprite } } -enum DJBoyfriendState +enum FreeplayDJState { + /** + * Character enters the frame and transitions to Idle. + */ Intro; + + /** + * Character loops in idle. + */ Idle; + + /** + * Plays an easter egg animation after a period in Idle, then reverts to Idle. + */ + IdleEasterEgg; + + /** + * Plays an elaborate easter egg animation. Does not revert until another animation is triggered. + */ + Cartoon; + + /** + * Player has selected a song. + */ Confirm; + + /** + * Character preps to play the fist pump animation; plays after the Results screen. + * The actual frame label that gets played may vary based on the player's success. + */ FistPumpIntro; + + /** + * Character plays the fist pump animation. + * The actual frame label that gets played may vary based on the player's success. + */ FistPump; IdleEasterEgg; Cartoon; diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index f04fd8ff61..6d612b7fc3 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -644,8 +644,8 @@ class FreeplayState extends MusicBeatSubState speed: 0.3 }); - var diffSelLeft:DifficultySelector = new DifficultySelector(20, grpDifficulties.y - 10, false, controls); - var diffSelRight:DifficultySelector = new DifficultySelector(325, grpDifficulties.y - 10, true, controls); + var diffSelLeft:DifficultySelector = new DifficultySelector(this, 20, grpDifficulties.y - 10, false, controls); + var diffSelRight:DifficultySelector = new DifficultySelector(this, 325, grpDifficulties.y - 10, true, controls); diffSelLeft.visible = false; diffSelRight.visible = false; add(diffSelLeft); @@ -913,7 +913,15 @@ class FreeplayState extends MusicBeatSubState changeSelection(); changeDiff(); - if (dj != null) dj.fistPump(); + if (fromResultsParams?.newRank == SHIT) + { + if (dj != null) dj.fistPumpLossIntro(); + } + else + { + if (dj != null) dj.fistPumpIntro(); + } + // rankCamera.fade(FlxColor.BLACK, 0.5, true); rankCamera.fade(0xFF000000, 0.5, true, null, true); if (FlxG.sound.music != null) FlxG.sound.music.volume = 0; @@ -1095,11 +1103,11 @@ class FreeplayState extends MusicBeatSubState if (fromResultsParams?.newRank == SHIT) { - if (dj != null) dj.pumpFistBad(); + if (dj != null) dj.fistPumpLoss(); } else { - if (dj != null) dj.pumpFist(); + if (dj != null) dj.fistPump(); } rankCamera.zoom = 0.8; From 7e39687994a85bb9c458e4eac7f5cac915527dc8 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 06:09:14 -0400 Subject: [PATCH 316/469] Data driven char select grid positions --- .../funkin/data/freeplay/player/PlayerData.hx | 18 +++++++++++++ .../ui/charSelect/CharSelectSubState.hx | 25 +++++++++++++++++-- .../freeplay/charselect/PlayableCharacter.hx | 5 ++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx index de55fdf5fc..55657ba464 100644 --- a/source/funkin/data/freeplay/player/PlayerData.hx +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -38,6 +38,12 @@ class PlayerData @:optional public var freeplayDJ:Null = null; + /** + * Data for displaying this character in the Character Select menu. + * If null, exclude from Character Select. + */ + @:optional + public var charSelect:Null = null; /** * Data for displaying this character in the results screen. @@ -233,6 +239,18 @@ class PlayerFreeplayDJData } } +class PlayerCharSelectData +{ + /** + * A zero-indexed number for the character's preferred position in the grid. + * 0 = top left, 4 = center, 8 = bottom right + * In the event of a conflict, the first character alphabetically gets it, + * and others get shifted over. + */ + @:optional + public var position:Null; +} + typedef PlayerResultsData = { var perfect:Array; diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 8b1f050f54..4717c91ca7 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -67,8 +67,29 @@ class CharSelectSubState extends MusicBeatSubState { super(); - availableChars.set(4, "bf"); - availableChars.set(3, "pico"); + loadAvailableCharacters(); + } + + function loadAvailableCharacters():Void + { + var playerIds:Array = PlayerRegistry.instance.listEntryIds(); + + for (playerId in playerIds) + { + var player:Null = PlayerRegistry.instance.fetchEntry(playerId); + if (player == null) continue; + var playerData = player.getCharSelectData(); + if (playerData == null) continue; + + var targetPosition:Int = playerData.position ?? 0; + while (availableChars.exists(targetPosition)) + { + targetPosition += 1; + } + + trace('Placing player ${playerId} at position ${targetPosition}'); + availableChars.set(targetPosition, playerId); + } } override public function create():Void diff --git a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx index c46b4b9300..d4dd7aaa48 100644 --- a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx +++ b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx @@ -88,6 +88,11 @@ class PlayableCharacter implements IRegistryEntry return _data.freeplayDJ.getFreeplayDJText(index); } + public function getCharSelectData():PlayerCharSelectData + { + return _data.charSelect; + } + /** * @param rank Which rank to get info for * @return An array of animations. For example, BF Great has two animations, one for BF and one for GF From 1fb5c31c2279a699654e33f7992db75b2a12ad64 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 06:09:34 -0400 Subject: [PATCH 317/469] Don't apply global offset twice (NOTE: this may break Nene again!) --- source/funkin/play/stage/Stage.hx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index c547b9f5f0..c42e41cadb 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -436,8 +436,9 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements // Start with the per-stage character position. // Subtracting the origin ensures characters are positioned relative to their feet. // Subtracting the global offset allows positioning on a per-character basis. - character.x = stageCharData.position[0] - character.characterOrigin.x + character.globalOffsets[0]; - character.y = stageCharData.position[1] - character.characterOrigin.y + character.globalOffsets[1]; + // We previously applied the global offset here but that is now done elsewhere. + character.x = stageCharData.position[0] - character.characterOrigin.x; + character.y = stageCharData.position[1] - character.characterOrigin.y; @:privateAccess(funkin.play.stage.Bopper) { From e7fca119f8e4ce7c795aa4729a5f0a5b89961aec Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 06:11:01 -0400 Subject: [PATCH 318/469] Logic + animations for new unlocks --- .../data/freeplay/player/PlayerRegistry.hx | 35 ++++++++++ source/funkin/save/Save.hx | 57 +++++++++++++-- .../ui/charSelect/CharSelectSubState.hx | 1 - source/funkin/ui/freeplay/FreeplayDJ.hx | 69 +++++++++++++++++-- source/funkin/ui/freeplay/FreeplayState.hx | 27 +++++++- 5 files changed, 178 insertions(+), 11 deletions(-) diff --git a/source/funkin/data/freeplay/player/PlayerRegistry.hx b/source/funkin/data/freeplay/player/PlayerRegistry.hx index be8730ccd2..62c05fc91b 100644 --- a/source/funkin/data/freeplay/player/PlayerRegistry.hx +++ b/source/funkin/data/freeplay/player/PlayerRegistry.hx @@ -53,6 +53,41 @@ class PlayerRegistry extends BaseRegistry log('Loaded ${countEntries()} playable characters with ${ownedCharacterIds.size()} associations.'); } + public function countUnlockedCharacters():Int + { + var count = 0; + + for (charId in listEntryIds()) + { + var player = fetchEntry(charId); + if (player == null) continue; + + if (player.isUnlocked()) count++; + } + + return count; + } + + public function hasNewCharacter():Bool + { + var characters = Save.instance.charactersSeen.clone(); + + for (charId in listEntryIds()) + { + var player = fetchEntry(charId); + if (player == null) continue; + + if (!player.isUnlocked()) continue; + if (characters.contains(charId)) continue; + + // This character is unlocked but we haven't seen them in Freeplay yet. + return true; + } + + // Fallthrough case. + return false; + } + /** * Get the playable character associated with a given stage character. * @param characterId The stage character ID. diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index 2900ce2be6..a3d9455944 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -121,6 +121,12 @@ class Save modOptions: [], }, + unlocks: + { + // Default to having seen the default character. + charactersSeen: ["bf"], + }, + optionsChartEditor: { // Reasonable defaults. @@ -393,6 +399,22 @@ class Save return data.optionsChartEditor.playbackSpeed; } + public var charactersSeen(get, never):Array; + + function get_charactersSeen():Array + { + return data.unlocks.charactersSeen; + } + + /** + * When we've seen a character unlock, add it to the list of characters seen. + * @param character + */ + public function addCharacterSeen(character:String):Void + { + data.unlocks.charactersSeen.push(character); + } + /** * Return the score the user achieved for a given level on a given difficulty. * @@ -471,10 +493,18 @@ class Save for (difficulty in difficultyList) { var score:Null = getLevelScore(levelId, difficulty); - // TODO: Do we need to check accuracy/score here? if (score != null) { - return true; + if (score.score > 0) + { + // Level has score data, which means we cleared it! + return true; + } + else + { + // Level has score data, but the score is 0. + return false; + } } } return false; @@ -630,10 +660,18 @@ class Save for (difficulty in difficultyList) { var score:Null = getSongScore(songId, difficulty); - // TODO: Do we need to check accuracy/score here? if (score != null) { - return true; + if (score.score > 0) + { + // Level has score data, which means we cleared it! + return true; + } + else + { + // Level has score data, but the score is 0. + return false; + } } } return false; @@ -956,6 +994,8 @@ typedef RawSaveData = */ var options:SaveDataOptions; + var unlocks:SaveDataUnlocks; + /** * The user's favorited songs in the Freeplay menu, * as a list of song IDs. @@ -980,6 +1020,15 @@ typedef SaveApiNewgroundsData = var sessionId:Null; } +typedef SaveDataUnlocks = +{ + /** + * Every time we see the unlock animation for a character, + * add it to this list so that we don't show it again. + */ + var charactersSeen:Array; +} + /** * An anoymous structure containing options about the user's high scores. */ diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 4717c91ca7..e6cb3fb775 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -290,7 +290,6 @@ class CharSelectSubState extends MusicBeatSubState } var grpIcons:FlxSpriteGroup; - var grpXSpread(default, set):Float = 107; var grpYSpread(default, set):Float = 127; diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index 6e2f83a63a..21bd89f933 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -99,7 +99,7 @@ class FreeplayDJ extends FlxAtlasSprite playFlashAnimation(animPrefix, true, false, true); } - if (getCurrentAnimation() == animPrefix && this.isLoopFinished()) + if (getCurrentAnimation() == animPrefix && this.isLoopComplete()) { if (timeIdling >= IDLE_EGG_PERIOD && !seenIdleEasterEgg) { @@ -111,6 +111,16 @@ class FreeplayDJ extends FlxAtlasSprite } } timeIdling += elapsed; + case NewUnlock: + var animPrefix = playableCharData.getAnimationPrefix('newUnlock'); + if (!hasAnimation(animPrefix)) + { + currentState = Idle; + } + if (getCurrentAnimation() != animPrefix) + { + playFlashAnimation(animPrefix, true, false, true); + } case Confirm: var animPrefix = playableCharData.getAnimationPrefix('confirm'); if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, false); @@ -226,7 +236,14 @@ class FreeplayDJ extends FlxAtlasSprite if (name == playableCharData.getAnimationPrefix('intro')) { - currentState = Idle; + if (PlayerRegistry.instance.hasNewCharacter()) + { + currentState = NewUnlock; + } + else + { + currentState = Idle; + } onIntroDone.dispatch(); } else if (name == playableCharData.getAnimationPrefix('idle')) @@ -266,9 +283,17 @@ class FreeplayDJ extends FlxAtlasSprite // runTvLogic(); } trace('Replay idle: ${frame}'); - playAnimation(playableCharData.getAnimationPrefix('cartoon'), true, false, false, frame); + playFlashAnimation(playableCharData.getAnimationPrefix('cartoon'), true, false, false, frame); // trace('Finished confirm'); } + else if (name == playableCharData.getAnimationPrefix('newUnlock')) + { + // Animation should loop. + } + else if (name == playableCharData.getAnimationPrefix('charSelect')) + { + onCharSelectComplete(); + } else { trace('Finished ${name}'); @@ -281,6 +306,15 @@ class FreeplayDJ extends FlxAtlasSprite seenIdleEasterEgg = false; } + /** + * Dynamic function, it's actually a variable you can reassign! + * `dj.onCharSelectComplete = function() {};` + */ + public dynamic function onCharSelectComplete():Void + { + trace('onCharSelectComplete()'); + } + var offsetX:Float = 0.0; var offsetY:Float = 0.0; @@ -342,6 +376,22 @@ class FreeplayDJ extends FlxAtlasSprite currentState = Confirm; } + public function toCharSelect():Void + { + if (hasAnimation('charSelect')) + { + currentState = CharSelect; + var animPrefix = playableCharData.getAnimationPrefix('charSelect'); + playFlashAnimation(animPrefix, true, false, false, 0); + } + else + { + currentState = Confirm; + // Call this immediately; otherwise, we get locked out of Character Select. + onCharSelectComplete(); + } + } + public function fistPumpIntro():Void { currentState = FistPumpIntro; @@ -456,6 +506,15 @@ enum FreeplayDJState * The actual frame label that gets played may vary based on the player's success. */ FistPump; - IdleEasterEgg; - Cartoon; + + /** + * Plays an animation to indicate that the player has a new unlock in Character Select. + * Overrides all idle animations as well as the fist pump. Only Confirm and CharSelect will override this. + */ + NewUnlock; + + /** + * Plays an animation to transition to the Character Select screen. + */ + CharSelect; } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 6d612b7fc3..79ca758c5d 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1253,7 +1253,32 @@ class FreeplayState extends MusicBeatSubState if (controls.FREEPLAY_CHAR_SELECT && !busy) { - FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState()); + // Check if we have ACCESS to character select! + trace('Is Pico unlocked? ${PlayerRegistry.instance.fetchEntry('pico')?.isUnlocked()}'); + trace('Number of characters: ${PlayerRegistry.instance.countUnlockedCharacters()}'); + + if (PlayerRegistry.instance.countUnlockedCharacters() > 1) + { + if (dj != null) + { + busy = true; + // Transition to character select after animation + dj.onCharSelectComplete = function() { + FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState()); + } + dj.toCharSelect(); + } + else + { + // Transition to character select immediately + FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState()); + } + } + else + { + trace('Not enough characters unlocked to open character select!'); + FunkinSound.playOnce(Paths.sound('cancelMenu')); + } } if (controls.FREEPLAY_FAVORITE && !busy) From 6cff1efc722be13a063ca69d6f36f7a95e5ef2b0 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 06:45:18 -0400 Subject: [PATCH 319/469] Fix several compile errors, and update assets submodule. --- assets | 2 +- .../data/freeplay/player/PlayerRegistry.hx | 1 + .../ui/charSelect/CharSelectSubState.hx | 38 ++++++++++--------- source/funkin/ui/freeplay/FreeplayDJ.hx | 2 +- source/funkin/ui/mainmenu/MainMenuState.hx | 2 + 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/assets b/assets index 8573b2039b..33c9137935 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8573b2039b9fb37551a41f5195aaa2abdc63770e +Subproject commit 33c91379353de42f2c11f1c7a9aebf66069ec13d diff --git a/source/funkin/data/freeplay/player/PlayerRegistry.hx b/source/funkin/data/freeplay/player/PlayerRegistry.hx index 62c05fc91b..c0a15ed1c3 100644 --- a/source/funkin/data/freeplay/player/PlayerRegistry.hx +++ b/source/funkin/data/freeplay/player/PlayerRegistry.hx @@ -3,6 +3,7 @@ package funkin.data.freeplay.player; import funkin.data.freeplay.player.PlayerData; import funkin.ui.freeplay.charselect.PlayableCharacter; import funkin.ui.freeplay.charselect.ScriptedPlayableCharacter; +import funkin.save.Save; class PlayerRegistry extends BaseRegistry { diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index e6cb3fb775..1374c64ef2 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -1,27 +1,31 @@ package funkin.ui.charSelect; -import funkin.ui.freeplay.FreeplayState; -import flixel.text.FlxText; -import funkin.ui.PixelatedIcon; -import flixel.system.debug.watch.Tracker.TrackerProfile; -import flixel.math.FlxPoint; -import flixel.tweens.FlxTween; -import openfl.display.BlendMode; -import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.FlxObject; import flixel.FlxSprite; +import flixel.group.FlxGroup; +import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxSpriteGroup; -import funkin.play.stage.Stage; +import flixel.math.FlxPoint; +import flixel.sound.FlxSound; +import flixel.system.debug.watch.Tracker.TrackerProfile; +import flixel.text.FlxText; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.util.FlxTimer; +import funkin.audio.FunkinSound; +import funkin.data.freeplay.player.PlayerData; +import funkin.data.freeplay.player.PlayerRegistry; +import funkin.graphics.adobeanimate.FlxAtlasSprite; +import funkin.graphics.FunkinCamera; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEventDispatcher; -import funkin.graphics.adobeanimate.FlxAtlasSprite; -import flixel.FlxObject; -import openfl.display.BlendMode; -import flixel.group.FlxGroup; +import funkin.play.stage.Stage; +import funkin.ui.freeplay.charselect.PlayableCharacter; +import funkin.ui.freeplay.FreeplayState; +import funkin.ui.PixelatedIcon; import funkin.util.MathUtil; -import flixel.util.FlxTimer; -import flixel.tweens.FlxEase; -import flixel.sound.FlxSound; -import funkin.audio.FunkinSound; +import funkin.vis.dsp.SpectralAnalyzer; +import openfl.display.BlendMode; class CharSelectSubState extends MusicBeatSubState { diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index 21bd89f933..b1528d906b 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -15,7 +15,7 @@ class FreeplayDJ extends FlxAtlasSprite { // Represents the sprite's current status. // Without state machines I would have driven myself crazy years ago. - public var currentState:DJBoyfriendState = Intro; + public var currentState:FreeplayDJState = Intro; // A callback activated when the intro animation finishes. public var onIntroDone:FlxSignal = new FlxSignal(); diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index c426fd0489..83da967b0b 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -345,6 +345,7 @@ class MainMenuState extends MusicBeatState if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.W) { + FunkinSound.playOnce(Paths.sound('confirmMenu')); // Give the user a score of 1 point on Weekend 1 story mode. // This makes the level count as cleared and displays the songs in Freeplay. funkin.save.Save.instance.setLevelScore('weekend1', 'easy', @@ -367,6 +368,7 @@ class MainMenuState extends MusicBeatState if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.L) { + FunkinSound.playOnce(Paths.sound('confirmMenu')); // Give the user a score of 0 points on Weekend 1 story mode. // This makes the level count as uncleared and no longer displays the songs in Freeplay. funkin.save.Save.instance.setLevelScore('weekend1', 'easy', From 452747139eabd92115ea3823851926081011db3c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 09:41:58 -0400 Subject: [PATCH 320/469] Updated Pico and Erect charts, sent on August 27th --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 8573b2039b..e19aaca192 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8573b2039b9fb37551a41f5195aaa2abdc63770e +Subproject commit e19aaca19254ec2717a4f942005f32b42b6ecb18 From 5d590654f4b1931a7186371f18435adac6f9cf78 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 28 Aug 2024 16:32:41 -0400 Subject: [PATCH 321/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 33c9137935..7210562035 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 33c91379353de42f2c11f1c7a9aebf66069ec13d +Subproject commit 7210562035c7ab6d2122606ec607b3e897a5ef20 From 560badd6b502d1dea6c899c19bc428ad10540888 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 29 Aug 2024 06:59:10 -0400 Subject: [PATCH 322/469] File optimization and Expansion 2 album art --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 7210562035..fda4eaaf3b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 7210562035c7ab6d2122606ec607b3e897a5ef20 +Subproject commit fda4eaaf3bab4e6ebcf66548517ee89de0438e3d From 0a316c815fac3897309eae10f13516599c52580e Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 29 Aug 2024 13:22:56 -0400 Subject: [PATCH 323/469] stop main menu music from playing when entering freeplay from pausemenu --- source/funkin/ui/freeplay/FreeplayState.hx | 14 +------------- source/funkin/ui/mainmenu/MainMenuState.hx | 2 +- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 79ca758c5d..09d7fb7b8c 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -317,15 +317,6 @@ class FreeplayState extends MusicBeatSubState isDebug = true; #end - if (prepForNewRank == false) - { - FunkinSound.playMusic('freakyMenu', - { - overrideExisting: true, - restartTrack: false - }); - } - // Add a null entry that represents the RANDOM option songs.push(null); @@ -2028,10 +2019,7 @@ class FreeplayState extends MusicBeatSubState public static function build(?params:FreeplayStateParams, ?stickers:StickerSubState):MusicBeatState { var result:MainMenuState; - if (params?.fromResults?.playRankAnim ?? false) result = new MainMenuState(true); - else - result = new MainMenuState(false); - + result = new MainMenuState(true); result.openSubState(new FreeplayState(params, stickers)); result.persistentUpdate = false; result.persistentDraw = true; diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 83da967b0b..c777e685b4 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -64,7 +64,7 @@ class MainMenuState extends MusicBeatState transIn = FlxTransitionableState.defaultTransIn; transOut = FlxTransitionableState.defaultTransOut; - if (overrideMusic == false) playMenuMusic(); + if (!overrideMusic) playMenuMusic(); // We want the state to always be able to begin with being able to accept inputs and show the anims of the menu items. persistentUpdate = true; From 0392eddba649ec4842a91f508813355a70ddbf6b Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 29 Aug 2024 14:32:10 -0400 Subject: [PATCH 324/469] gf/nene char select mini optimization --- source/funkin/ui/charSelect/CharSelectGF.hx | 40 +++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/source/funkin/ui/charSelect/CharSelectGF.hx b/source/funkin/ui/charSelect/CharSelectGF.hx index 6d8e3e657b..ac8dafabc3 100644 --- a/source/funkin/ui/charSelect/CharSelectGF.hx +++ b/source/funkin/ui/charSelect/CharSelectGF.hx @@ -21,6 +21,8 @@ class CharSelectGF extends FlxAtlasSprite var intendedYPos:Float = 0; var intendedAlpha:Float = 0; + var curGF:GFChar = GF; + public function new() { super(0, 0, Paths.animateAtlas("charSelect/gfChill")); @@ -28,7 +30,7 @@ class CharSelectGF extends FlxAtlasSprite switchGF("bf"); } - override public function update(elapsed:Float) + override public function update(elapsed:Float):Void { super.update(elapsed); @@ -63,7 +65,7 @@ class CharSelectGF extends FlxAtlasSprite * @param animInfo Should not be confused with animInInfo! * This is merely a local var for the function! */ - function doFade(animInfo:FramesJSFLInfo) + function doFade(animInfo:FramesJSFLInfo):Void { fadeTimer += FlxG.elapsed; if (fadeTimer >= 1 / 24) @@ -101,26 +103,30 @@ class CharSelectGF extends FlxAtlasSprite fadeAnimIndex = 0; } - public function switchGF(str:String) + /** + * For switching between "GFs" such as gf, nene, etc + * @param bf Which BF we are selecting, so that we know the accompyaning GF + */ + public function switchGF(bf:String):Void { - str = switch (str) + var prevGF:GFChar = curGF; + switch (bf) { case "pico": - "nene"; + curGF = NENE; case "bf": - "gf"; + curGF = GF; default: - "gf"; + curGF = GF; } - switch str - { - default: - loadAtlas(Paths.animateAtlas("charSelect/" + str + "Chill")); - } + // We don't need to update any anims if we didn't change GF + if (prevGF == curGF) return; + + loadAtlas(Paths.animateAtlas("charSelect/" + curGF + "Chill")); - animInInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + str + "AnimInfo/" + str + "In.txt")); - animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + str + "AnimInfo/" + str + "Out.txt")); + animInInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "In.txt")); + animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "Out.txt")); anim.play(""); playAnimation("idle", true, false, true); @@ -135,3 +141,9 @@ enum FadeStatus FADE_OUT; FADE_IN; } + +enum abstract GFChar(String) from String to String +{ + var GF = "gf"; + var NENE = "nene"; +} From 364a5d5a55229acee3246e54a7dbe7554e5123a3 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 29 Aug 2024 15:06:11 -0400 Subject: [PATCH 325/469] hmm flxanimate version fix --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index d677608827..b59a5d2586 100644 --- a/hmm.json +++ b/hmm.json @@ -39,7 +39,7 @@ "name": "flxanimate", "type": "git", "dir": null, - "ref": "768740a56b26aa0c072720e0d1236b94afe68e3e", + "ref": "c3340af27d8e5bc11b42362d0ba33504bd2f3d35", "url": "https://github.com/Dot-Stuff/flxanimate" }, { From 737a679196bdffc7edc3f6d293523fbca091722d Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 29 Aug 2024 15:26:35 -0400 Subject: [PATCH 326/469] merge conflict fixins --- assets | 2 +- source/Main.hx | 2 +- source/funkin/ui/charSelect/CharSelectGF.hx | 3 +-- source/funkin/ui/charSelect/CharSelectSubState.hx | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/assets b/assets index 8558bc906e..b7f132a4e6 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8558bc906ecd48ba5d7b1c213982f15b5d033227 +Subproject commit b7f132a4e6c528c2d55831f88a11781df0149fd5 diff --git a/source/Main.hx b/source/Main.hx index 475fa9234d..2426fa0d99 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -26,7 +26,7 @@ class Main extends Sprite var framerate:Int = 60; // How many frames per second the game should run at. #else // TODO: This should probably be in the options menu? - var framerate:Int = 60; // How many frames per second the game should run at. + var framerate:Int = 144; // How many frames per second the game should run at. #end var skipSplash:Bool = true; // Whether to skip the flixel splash screen that appears in release mode. var startFullscreen:Bool = false; // Whether to start the game in fullscreen on desktop targets diff --git a/source/funkin/ui/charSelect/CharSelectGF.hx b/source/funkin/ui/charSelect/CharSelectGF.hx index 7122a8b90a..2e0e3980c4 100644 --- a/source/funkin/ui/charSelect/CharSelectGF.hx +++ b/source/funkin/ui/charSelect/CharSelectGF.hx @@ -22,7 +22,6 @@ class CharSelectGF extends FlxAtlasSprite var intendedYPos:Float = 0; var intendedAlpha:Float = 0; var list:Array = []; - var char:String = "gf"; var analyzer:SpectralAnalyzer; @@ -76,7 +75,7 @@ class CharSelectGF extends FlxAtlasSprite function drawFFT() { - if (char == "nene") + if (curGF == NENE) { var levels = analyzer.getLevels(); var frame = anim.curSymbol.timeline.get("VIZ_bars").get(anim.curFrame); diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 832a56f996..c2850916ac 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -309,7 +309,7 @@ class CharSelectSubState extends MusicBeatSubState var grpXSpread(default, set):Float = 107; var grpYSpread(default, set):Float = 127; - function initLocks() + function initLocks():Void { grpIcons = new FlxSpriteGroup(); add(grpIcons); From 3e339916d2d73120cce2b83a79c9178b79f42258 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 29 Aug 2024 18:11:51 -0400 Subject: [PATCH 327/469] flxanimate bump --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index f84af9f4fe..3cd8d5d3d4 100644 --- a/hmm.json +++ b/hmm.json @@ -46,7 +46,7 @@ "name": "flxanimate", "type": "git", "dir": null, - "ref": "c3340af27d8e5bc11b42362d0ba33504bd2f3d35", + "ref": "27a0a1f0afc1074f619991472fb3ee209aad28df", "url": "https://github.com/Dot-Stuff/flxanimate" }, { From ecd6100cf95b13e0b19b481356f8c9fe7ad9700e Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 29 Aug 2024 18:26:04 -0400 Subject: [PATCH 328/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index b7f132a4e6..5a0c8da321 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit b7f132a4e6c528c2d55831f88a11781df0149fd5 +Subproject commit 5a0c8da32111571648b8fd07deabe2f78ae99eaa From a4f3c2cb84ed51b0185ae3bbf630f243c3d48600 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 30 Aug 2024 00:55:24 +0100 Subject: [PATCH 329/469] freeplay style code --- source/funkin/InitState.hx | 2 + .../data/freeplay/style/FreeplayStyleData.hx | 48 +++++++ .../freeplay/style/FreeplayStyleRegistry.hx | 84 ++++++++++++ source/funkin/ui/freeplay/FreeplayStyle.hx | 121 ++++++++++++++++++ .../ui/freeplay/ScriptedFreeplayStyle.hx | 9 ++ 5 files changed, 264 insertions(+) create mode 100644 source/funkin/data/freeplay/style/FreeplayStyleData.hx create mode 100644 source/funkin/data/freeplay/style/FreeplayStyleRegistry.hx create mode 100644 source/funkin/ui/freeplay/FreeplayStyle.hx create mode 100644 source/funkin/ui/freeplay/ScriptedFreeplayStyle.hx diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index e53499e3c9..f8dc66b1b0 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -19,6 +19,7 @@ import funkin.play.PlayStatePlaylist; import openfl.display.BitmapData; import funkin.data.story.level.LevelRegistry; import funkin.data.notestyle.NoteStyleRegistry; +import funkin.data.freeplay.style.FreeplayStyleRegistry; import funkin.data.event.SongEventRegistry; import funkin.data.stage.StageRegistry; import funkin.data.dialogue.conversation.ConversationRegistry; @@ -170,6 +171,7 @@ class InitState extends FlxState ConversationRegistry.instance.loadEntries(); DialogueBoxRegistry.instance.loadEntries(); SpeakerRegistry.instance.loadEntries(); + FreeplayStyleRegistry.instance.loadEntries(); AlbumRegistry.instance.loadEntries(); StageRegistry.instance.loadEntries(); diff --git a/source/funkin/data/freeplay/style/FreeplayStyleData.hx b/source/funkin/data/freeplay/style/FreeplayStyleData.hx new file mode 100644 index 0000000000..1af1982173 --- /dev/null +++ b/source/funkin/data/freeplay/style/FreeplayStyleData.hx @@ -0,0 +1,48 @@ +package funkin.data.freeplay.style; + +import funkin.data.animation.AnimationData; + +/** + * A type definition for the data for an album of songs. + * It includes things like what graphics to display in Freeplay. + * @see https://lib.haxe.org/p/json2object/ + */ +typedef FreeplayStyleData = +{ + /** + * Semantic version for style data. + */ + public var version:String; + + /** + * Asset key for the background image. + */ + public var bgAsset:String; + + /** + * Asset key for the difficulty selector image. + */ + public var selectorAsset:String; + + /** + * Asset key for the numbers shown at the top right of the screen. + */ + public var numbersAsset:String; + + /** + * Asset key for the freeplay capsules. + */ + public var capsuleAsset:String; + + /** + * Color data for the capsule text outline. + * the order of this array goes as follows: [DESELECTED, SELECTED] + */ + public var capsuleTextColors:Array; + + /** + * Delay time after confirming a song selection, before entering PlayState. + * Useful for letting longer animations play out. + */ + public var startDelay:Float; +} diff --git a/source/funkin/data/freeplay/style/FreeplayStyleRegistry.hx b/source/funkin/data/freeplay/style/FreeplayStyleRegistry.hx new file mode 100644 index 0000000000..626e2ac393 --- /dev/null +++ b/source/funkin/data/freeplay/style/FreeplayStyleRegistry.hx @@ -0,0 +1,84 @@ +package funkin.data.freeplay.style; + +import funkin.ui.freeplay.FreeplayStyle; +import funkin.data.freeplay.style.FreeplayStyleData; +import funkin.ui.freeplay.ScriptedFreeplayStyle; + +class FreeplayStyleRegistry extends BaseRegistry +{ + /** + * The current version string for the style data format. + * Handle breaking changes by incrementing this value + * and adding migration to the `migrateStyleData()` function. + */ + public static final FREEPLAYSTYLE_DATA_VERSION:thx.semver.Version = '1.0.0'; + + public static final FREEPLAYSTYLE_DATA_VERSION_RULE:thx.semver.VersionRule = '1.0.x'; + + public static final instance:FreeplayStyleRegistry = new FreeplayStyleRegistry(); + + public function new() + { + super('FREEPLAYSTYLE', 'ui/freeplay/styles', FREEPLAYSTYLE_DATA_VERSION_RULE); + } + + /** + * Read, parse, and validate the JSON data and produce the corresponding data object. + * @param id The ID of the entry to load. + * @return The parsed data object. + */ + public function parseEntryData(id:String):Null + { + // JsonParser does not take type parameters, + // otherwise this function would be in BaseRegistry. + var parser:json2object.JsonParser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + + switch (loadEntryFile(id)) + { + case {fileName: fileName, contents: contents}: + parser.fromJson(contents, fileName); + default: + return null; + } + + if (parser.errors.length > 0) + { + printErrors(parser.errors, id); + return null; + } + return parser.value; + } + + /** + * Parse and validate the JSON data and produce the corresponding data object. + * + * NOTE: Must be implemented on the implementation class. + * @param contents The JSON as a string. + * @param fileName An optional file name for error reporting. + * @return The parsed data object. + */ + public function parseEntryDataRaw(contents:String, ?fileName:String):Null + { + var parser:json2object.JsonParser = new json2object.JsonParser(); + parser.ignoreUnknownVariables = false; + parser.fromJson(contents, fileName); + + if (parser.errors.length > 0) + { + printErrors(parser.errors, fileName); + return null; + } + return parser.value; + } + + function createScriptedEntry(clsName:String):FreeplayStyle + { + return ScriptedFreeplayStyle.init(clsName, 'unknown'); + } + + function getScriptedClassNames():Array + { + return ScriptedFreeplayStyle.listScriptClasses(); + } +} diff --git a/source/funkin/ui/freeplay/FreeplayStyle.hx b/source/funkin/ui/freeplay/FreeplayStyle.hx new file mode 100644 index 0000000000..5ced51e1cb --- /dev/null +++ b/source/funkin/ui/freeplay/FreeplayStyle.hx @@ -0,0 +1,121 @@ +package funkin.ui.freeplay; + +import funkin.data.freeplay.style.FreeplayStyleData; +import funkin.data.freeplay.style.FreeplayStyleRegistry; +import funkin.data.animation.AnimationData; +import funkin.data.IRegistryEntry; +import flixel.graphics.FlxGraphic; +import flixel.util.FlxColor; + +/** + * A class representing the data for a style of the Freeplay menu. + */ +class FreeplayStyle implements IRegistryEntry +{ + /** + * The internal ID for this freeplay style. + */ + public final id:String; + + /** + * The full data for a freeplay style. + */ + public final _data:FreeplayStyleData; + + public function new(id:String) + { + this.id = id; + this._data = _fetchData(id); + + if (_data == null) + { + throw 'Could not parse album data for id: $id'; + } + } + + /** + * Get the background art as a graphic, ready to apply to a sprite. + * @return The built graphic + */ + public function getBgAssetGraphic():FlxGraphic + { + return FlxG.bitmap.add(Paths.image(getBgAssetKey())); + } + + /** + * Get the asset key for the background. + * @return The asset key + */ + public function getBgAssetKey():String + { + return _data.bgAsset; + } + + /** + * Get the asset key for the background. + * @return The asset key + */ + public function getSelectorAssetKey():String + { + return _data.selectorAsset; + } + + /** + * Get the asset key for the number assets. + * @return The asset key + */ + public function getCapsuleAssetKey():String + { + return _data.capsuleAsset; + } + + /** + * Get the asset key for the capsule art. + * @return The asset key + */ + public function getNumbersAssetKey():String + { + return _data.numbersAsset; + } + + /** + * Return the deselected color of the text outline + * for freeplay capsules. + * @return The deselected color + */ + public function getCapsuleDeselCol():FlxColor + { + return FlxColor.fromString(_data.capsuleTextColors[0]); + } + + /** + * Return the song selection transition delay. + * @return The start delay + */ + public function getStartDelay():Float + { + return _data.startDelay; + } + + public function toString():String + { + return 'Style($id)'; + } + + /** + * Return the selected color of the text outline + * for freeplay capsules. + * @return The selected color + */ + public function getCapsuleSelCol():FlxColor + { + return FlxColor.fromString(_data.capsuleTextColors[1]); + } + + public function destroy():Void {} + + static function _fetchData(id:String):Null + { + return FreeplayStyleRegistry.instance.parseEntryDataWithMigration(id, FreeplayStyleRegistry.instance.fetchEntryVersion(id)); + } +} diff --git a/source/funkin/ui/freeplay/ScriptedFreeplayStyle.hx b/source/funkin/ui/freeplay/ScriptedFreeplayStyle.hx new file mode 100644 index 0000000000..b7013a6b24 --- /dev/null +++ b/source/funkin/ui/freeplay/ScriptedFreeplayStyle.hx @@ -0,0 +1,9 @@ +package funkin.ui.freeplay; + +/** + * A script that can be tied to a Freeplay style. + * Create a scripted class that extends FreeplayStyle to use this. + * This allows you to customize how a specific style works. + */ +@:hscriptClass +class ScriptedFreeplayStyle extends funkin.ui.freeplay.FreeplayStyle implements polymod.hscript.HScriptedClass {} From e7448cb8f7a804775f0d2af11f4c7ab5e7bca430 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 30 Aug 2024 00:58:19 +0100 Subject: [PATCH 330/469] style support --- source/funkin/ui/freeplay/CapsuleText.hx | 19 ++++++++++-- source/funkin/ui/freeplay/FreeplayScore.hx | 34 +++++++++++++++------- source/funkin/ui/freeplay/SongMenuItem.hx | 27 ++++++++++++----- 3 files changed, 59 insertions(+), 21 deletions(-) diff --git a/source/funkin/ui/freeplay/CapsuleText.hx b/source/funkin/ui/freeplay/CapsuleText.hx index c3bcdb09bb..aae72544e5 100644 --- a/source/funkin/ui/freeplay/CapsuleText.hx +++ b/source/funkin/ui/freeplay/CapsuleText.hx @@ -10,6 +10,7 @@ import flixel.tweens.FlxEase; import flixel.util.FlxTimer; import flixel.tweens.FlxTween; import openfl.display.BlendMode; +import flixel.util.FlxColor; class CapsuleText extends FlxSpriteGroup { @@ -25,6 +26,8 @@ class CapsuleText extends FlxSpriteGroup public var tooLong:Bool = false; + var glowColor:FlxColor = 0xFF00ccff; + // 255, 27 normal // 220, 27 favourited @@ -38,7 +41,7 @@ class CapsuleText extends FlxSpriteGroup // whiteText.shader = new GaussianBlurShader(0.3); text = songTitle; - blurredText.color = 0xFF00ccff; + blurredText.color = glowColor; whiteText.color = 0xFFFFFFFF; add(blurredText); add(whiteText); @@ -51,6 +54,16 @@ class CapsuleText extends FlxSpriteGroup return text; } + public function applyStyle(styleData:FreeplayStyle):Void + { + glowColor = styleData.getCapsuleSelCol(); + blurredText.color = glowColor; + whiteText.textField.filters = [ + new openfl.filters.GlowFilter(glowColor, 1, 5, 5, 210, BitmapFilterQuality.MEDIUM), + // new openfl.filters.BlurFilter(5, 5, BitmapFilterQuality.LOW) + ]; + } + // ???? none // 255, 27 normal // 220, 27 favourited @@ -99,7 +112,7 @@ class CapsuleText extends FlxSpriteGroup whiteText.text = value; checkClipWidth(); whiteText.textField.filters = [ - new openfl.filters.GlowFilter(0x00ccff, 1, 5, 5, 210, BitmapFilterQuality.MEDIUM), + new openfl.filters.GlowFilter(glowColor, 1, 5, 5, 210, BitmapFilterQuality.MEDIUM), // new openfl.filters.BlurFilter(5, 5, BitmapFilterQuality.LOW) ]; @@ -186,7 +199,7 @@ class CapsuleText extends FlxSpriteGroup } else { - blurredText.color = 0xFF00aadd; + blurredText.color = glowColor; whiteText.color = 0xFFDDDDDD; whiteText.textField.filters = [ new openfl.filters.GlowFilter(0xDDDDDD, 1, 5, 5, 210, BitmapFilterQuality.MEDIUM), diff --git a/source/funkin/ui/freeplay/FreeplayScore.hx b/source/funkin/ui/freeplay/FreeplayScore.hx index da4c9f5d47..fee55ce7c9 100644 --- a/source/funkin/ui/freeplay/FreeplayScore.hx +++ b/source/funkin/ui/freeplay/FreeplayScore.hx @@ -42,13 +42,20 @@ class FreeplayScore extends FlxTypedSpriteGroup return val; } - public function new(x:Float, y:Float, digitCount:Int, scoreShit:Int = 100) + public function new(x:Float, y:Float, digitCount:Int, scoreShit:Int = 100, ?styleData:FreeplayStyle) { super(x, y); for (i in 0...digitCount) { - add(new ScoreNum(x + (45 * i), y, 0)); + if (styleData == null) + { + add(new ScoreNum(x + (45 * i), y, 0)); + } + else + { + add(new ScoreNum(x + (45 * i), y, 0, styleData)); + } } this.scoreShit = scoreShit; @@ -76,16 +83,16 @@ class ScoreNum extends FlxSprite case 1: offset.x -= 15; case 5: - // set offsets - // offset.x += 0; - // offset.y += 10; + // set offsets + // offset.x += 0; + // offset.y += 10; case 7: - // offset.y += 6; + // offset.y += 6; case 4: - // offset.y += 5; + // offset.y += 5; case 9: - // offset.y += 5; + // offset.y += 5; default: centerOffsets(false); } @@ -99,14 +106,21 @@ class ScoreNum extends FlxSprite var numToString:Array = ["ZERO", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"]; - public function new(x:Float, y:Float, ?initDigit:Int = 0) + public function new(x:Float, y:Float, ?initDigit:Int = 0, ?styleData:FreeplayStyle) { super(x, y); baseY = y; baseX = x; - frames = Paths.getSparrowAtlas('digital_numbers'); + if (styleData == null) + { + frames = Paths.getSparrowAtlas('digital_numbers'); + } + else + { + frames = Paths.getSparrowAtlas(styleData.getNumbersAssetKey()); + } for (i in 0...10) { diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx index b4409d377b..11ca44d541 100644 --- a/source/funkin/ui/freeplay/SongMenuItem.hx +++ b/source/funkin/ui/freeplay/SongMenuItem.hx @@ -88,7 +88,7 @@ class SongMenuItem extends FlxSpriteGroup super(x, y); capsule = new FlxSprite(); - capsule.frames = Paths.getSparrowAtlas('freeplay/freeplayCapsule'); + capsule.frames = Paths.getSparrowAtlas('freeplay/freeplayCapsule/capsule/freeplayCapsule'); capsule.animation.addByPrefix('selected', 'mp3 capsule w backing0', 24); capsule.animation.addByPrefix('unselected', 'mp3 capsule w backing NOT SELECTED', 24); // capsule.animation @@ -500,12 +500,23 @@ class SongMenuItem extends FlxSpriteGroup updateSelected(); } - public function init(?x:Float, ?y:Float, songData:Null):Void + public function init(?x:Float, ?y:Float, songData:Null, ?styleData:FreeplayStyle = null):Void { if (x != null) this.x = x; if (y != null) this.y = y; this.songData = songData; + // im so mad i have to do this but im pretty sure with the capsules recycling i cant call the new function properly :/ + // if thats possible someone Please change the new function to be something like + // capsule.frames = Paths.getSparrowAtlas(styleData == null ? 'freeplay/freeplayCapsule/capsule/freeplayCapsule' : styleData.getCapsuleAssetKey()); thank u luv u + if (styleData != null) + { + capsule.frames = Paths.getSparrowAtlas(styleData.getCapsuleAssetKey()); + capsule.animation.addByPrefix('selected', 'mp3 capsule w backing0', 24); + capsule.animation.addByPrefix('unselected', 'mp3 capsule w backing NOT SELECTED', 24); + songText.applyStyle(styleData); + } + // Update capsule text. songText.text = songData?.songName ?? 'Random'; // Update capsule character. @@ -727,7 +738,7 @@ class FreeplayRank extends FlxSprite switch (val) { case SHIT: - // offset.x -= 1; + // offset.x -= 1; case GOOD: // offset.x -= 1; offset.y -= 8; @@ -735,11 +746,11 @@ class FreeplayRank extends FlxSprite // offset.x -= 1; offset.y -= 8; case EXCELLENT: - // offset.y += 5; + // offset.y += 5; case PERFECT: - // offset.y += 5; + // offset.y += 5; case PERFECT_GOLD: - // offset.y += 5; + // offset.y += 5; default: centerOffsets(false); this.visible = false; @@ -796,9 +807,9 @@ class CapsuleNumber extends FlxSprite case 6: case 4: - // offset.y += 5; + // offset.y += 5; case 9: - // offset.y += 5; + // offset.y += 5; default: centerOffsets(false); } From de03da7e6fef675c200554ed34903233c5440c8e Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 30 Aug 2024 01:02:32 +0100 Subject: [PATCH 331/469] backing card classes --- .../ui/freeplay/backcards/BackingCard.hx | 48 ++- .../ui/freeplay/backcards/BoyfriendCard.hx | 82 +++++- .../ui/freeplay/backcards/NewCharacterCard.hx | 278 ++++++++++++++++++ .../funkin/ui/freeplay/backcards/PicoCard.hx | 278 ++++++++++++++++-- 4 files changed, 653 insertions(+), 33 deletions(-) create mode 100644 source/funkin/ui/freeplay/backcards/NewCharacterCard.hx diff --git a/source/funkin/ui/freeplay/backcards/BackingCard.hx b/source/funkin/ui/freeplay/backcards/BackingCard.hx index 8a2a2c910b..bb662cc8dd 100644 --- a/source/funkin/ui/freeplay/backcards/BackingCard.hx +++ b/source/funkin/ui/freeplay/backcards/BackingCard.hx @@ -39,10 +39,14 @@ class BackingCard extends FlxSpriteGroup var _exitMovers:Null; var _exitMoversCharSel:Null; - public function new(currentCharacter:PlayableCharacter) + public var instance:FreeplayState; + + public function new(currentCharacter:PlayableCharacter, ?_instance:FreeplayState) { super(); + if (_instance != null) instance = _instance; + cardGlow = new FlxSprite(-30, -30).loadGraphic(Paths.image('freeplay/cardGlow')); confirmGlow = new FlxSprite(-30, 240).loadGraphic(Paths.image('freeplay/confirmGlow')); confirmTextGlow = new FlxSprite(-8, 115).loadGraphic(Paths.image('freeplay/glowingText')); @@ -115,6 +119,16 @@ class BackingCard extends FlxSpriteGroup }); } + /** + * Helper function to snap the back of the card to its final position. + * Used when returning from character select, as we dont want to play the full animation of everything sliding in. + */ + public function skipIntroTween():Void + { + FlxTween.cancelTweensOf(pinkBack); + pinkBack.x = 0; + } + /** * Called in create. Adds sprites and tweens. */ @@ -152,11 +166,6 @@ class BackingCard extends FlxSpriteGroup add(cardGlow); } - /** - * Override parts of Freeplay depending on the card class. - */ - public function applyStyle(_freeplayState:FreeplayState):Void {} - /** * Called after the dj finishes their start animation. */ @@ -181,10 +190,16 @@ class BackingCard extends FlxSpriteGroup confirmGlow.visible = true; confirmGlow2.visible = true; - backingTextYeah.playAnimation("BF back card confirm raw", false, false, false, 0); + backingTextYeah.anim.play(""); confirmGlow2.alpha = 0; confirmGlow.alpha = 0; + FlxTween.color(instance.bgDad, 0.5, 0xFFA8A8A8, 0xFF646464, + { + onUpdate: function(_) { + instance.angleMaskShader.extraColor = instance.bgDad.color; + } + }); FlxTween.tween(confirmGlow2, {alpha: 0.5}, 0.33, { ease: FlxEase.quadOut, @@ -195,12 +210,29 @@ class BackingCard extends FlxSpriteGroup confirmTextGlow.alpha = 1; FlxTween.tween(confirmTextGlow, {alpha: 0.4}, 0.5); FlxTween.tween(confirmGlow, {alpha: 0}, 0.5); + FlxTween.color(instance.bgDad, 2, 0xFFCDCDCD, 0xFF555555, + { + ease: FlxEase.expoOut, + onUpdate: function(_) { + instance.angleMaskShader.extraColor = instance.bgDad.color; + } + }); } }); } /** - * Called when exiting the freeplay menu + * Called when entering character select, does nothing by default. + */ + public function enterCharSel():Void {} + + /** + * Called on each beat in freeplay state. + */ + public function beatHit():Void {} + + /** + * Called when exiting the freeplay menu. */ public function disappear():Void { diff --git a/source/funkin/ui/freeplay/backcards/BoyfriendCard.hx b/source/funkin/ui/freeplay/backcards/BoyfriendCard.hx index 737a0a2dc1..597fd1a34b 100644 --- a/source/funkin/ui/freeplay/backcards/BoyfriendCard.hx +++ b/source/funkin/ui/freeplay/backcards/BoyfriendCard.hx @@ -22,9 +22,6 @@ import lime.utils.Assets; import openfl.display.BlendMode; import flixel.group.FlxSpriteGroup; -/** - * A class for the backing cards so they dont have to be part of freeplayState...... - */ class BoyfriendCard extends BackingCard { public var moreWays:BGScrollingText; @@ -34,6 +31,9 @@ class BoyfriendCard extends BackingCard public var moreWays2:BGScrollingText; public var funnyScroll3:BGScrollingText; + var glow:FlxSprite; + var glowDark:FlxSprite; + public override function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData, ?exitMoversCharSel:FreeplayState.ExitMoverData):Void { super.applyExitMovers(exitMovers, exitMoversCharSel); @@ -79,6 +79,16 @@ class BoyfriendCard extends BackingCard }); } + public override function enterCharSel():Void + { + FlxTween.tween(funnyScroll, {speed: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(funnyScroll2, {speed: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(moreWays, {speed: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(moreWays2, {speed: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(txtNuts, {speed: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(funnyScroll3, {speed: 0}, 0.8, {ease: FlxEase.sineIn}); + } + public override function new(currentCharacter:PlayableCharacter) { super(currentCharacter); @@ -93,10 +103,34 @@ class BoyfriendCard extends BackingCard public override function init():Void { - super.init(); + FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut}); + add(pinkBack); + + add(orangeBackShit); + + add(alsoOrangeLOL); + + FlxSpriteUtil.alphaMaskFlxSprite(orangeBackShit, pinkBack, orangeBackShit); + orangeBackShit.visible = false; + alsoOrangeLOL.visible = false; + + confirmTextGlow.blend = BlendMode.ADD; + confirmTextGlow.visible = false; + + confirmGlow.blend = BlendMode.ADD; + + confirmGlow.visible = false; + confirmGlow2.visible = false; + + add(confirmGlow2); + add(confirmGlow); - // var grpTxtScrolls:FlxGroup = new FlxGroup(); - // add(grpTxtScrolls); + add(confirmTextGlow); + + add(backingTextYeah); + + cardGlow.blend = BlendMode.ADD; + cardGlow.visible = false; moreWays.visible = false; funnyScroll.visible = false; @@ -127,6 +161,36 @@ class BoyfriendCard extends BackingCard funnyScroll3.funnyColor = 0xFFFEA400; funnyScroll3.speed = -3.8; add(funnyScroll3); + + glowDark = new FlxSprite(-300, 330).loadGraphic(Paths.image('freeplay/beatglow')); + glowDark.blend = BlendMode.MULTIPLY; + add(glowDark); + + glow = new FlxSprite(-300, 330).loadGraphic(Paths.image('freeplay/beatglow')); + glow.blend = BlendMode.ADD; + add(glow); + + glowDark.visible = false; + glow.visible = false; + + add(cardGlow); + } + + var beatFreq:Int = 1; + var beatFreqList:Array = [1,2,4,8]; + + public override function beatHit():Void { + // increases the amount of beats that need to go by to pulse the glow because itd flash like craazy at high bpms..... + beatFreq = beatFreqList[Math.floor(Conductor.instance.bpm/140)]; + + if(Conductor.instance.currentBeat % beatFreq != 0) return; + FlxTween.cancelTweensOf(glow); + FlxTween.cancelTweensOf(glowDark); + + glow.alpha = 0.8; + FlxTween.tween(glow, {alpha: 0}, 16/24, {ease: FlxEase.quartOut}); + glowDark.alpha = 0; + FlxTween.tween(glowDark, {alpha: 0.6}, 18/24, {ease: FlxEase.quartOut}); } public override function introDone():Void @@ -139,6 +203,8 @@ class BoyfriendCard extends BackingCard moreWays2.visible = true; funnyScroll3.visible = true; // grpTxtScrolls.visible = true; + glowDark.visible = true; + glow.visible = true; } public override function confirm():Void @@ -152,6 +218,8 @@ class BoyfriendCard extends BackingCard funnyScroll2.visible = false; moreWays2.visible = false; funnyScroll3.visible = false; + glowDark.visible = false; + glow.visible = false; } public override function disappear():Void @@ -164,5 +232,7 @@ class BoyfriendCard extends BackingCard funnyScroll2.visible = false; moreWays2.visible = false; funnyScroll3.visible = false; + glowDark.visible = false; + glow.visible = false; } } diff --git a/source/funkin/ui/freeplay/backcards/NewCharacterCard.hx b/source/funkin/ui/freeplay/backcards/NewCharacterCard.hx new file mode 100644 index 0000000000..a44ff88a67 --- /dev/null +++ b/source/funkin/ui/freeplay/backcards/NewCharacterCard.hx @@ -0,0 +1,278 @@ +package funkin.ui.freeplay.backcards; + +import funkin.ui.freeplay.FreeplayState; +import flash.display.BitmapData; +import flixel.FlxCamera; +import flixel.math.FlxMath; +import flixel.FlxSprite; +import flixel.group.FlxGroup; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; +import flixel.math.FlxAngle; +import flixel.math.FlxPoint; +import flixel.text.FlxText; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; +import flixel.util.FlxSpriteUtil; +import flixel.util.FlxTimer; +import funkin.graphics.adobeanimate.FlxAtlasSprite; +import funkin.graphics.FunkinSprite; +import funkin.ui.freeplay.charselect.PlayableCharacter; +import funkin.ui.MusicBeatSubState; +import lime.utils.Assets; +import openfl.display.BlendMode; +import flixel.group.FlxSpriteGroup; +import funkin.graphics.shaders.AdjustColorShader; +import flixel.addons.display.FlxTiledSprite; +import flixel.addons.display.FlxBackdrop; + +class NewCharacterCard extends BackingCard +{ + var confirmAtlas:FlxAtlasSprite; + + var darkBg:FlxSprite; + var lightLayer:FlxSprite; + var multiply1:FlxSprite; + var multiply2:FlxSprite; + var lightLayer2:FlxSprite; + var lightLayer3:FlxSprite; + var yellow:FlxSprite; + var multiplyBar:FlxSprite; + + var bruh:FlxSprite; + + public var friendFoe:BGScrollingText; + public var newUnlock1:BGScrollingText; + public var waiting:BGScrollingText; + public var newUnlock2:BGScrollingText; + public var friendFoe2:BGScrollingText; + public var newUnlock3:BGScrollingText; + + public override function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData, ?exitMoversCharSel:FreeplayState.ExitMoverData):Void + { + super.applyExitMovers(exitMovers, exitMoversCharSel); + if (exitMovers == null || exitMoversCharSel == null) return; + exitMovers.set([friendFoe], + { + x: FlxG.width * 2, + speed: 0.4, + }); + exitMovers.set([newUnlock1], + { + x: -newUnlock1.width * 2, + y: newUnlock1.y, + speed: 0.4, + wait: 0 + }); + exitMovers.set([waiting], + { + x: FlxG.width * 2, + speed: 0.4, + }); + exitMovers.set([newUnlock2], + { + x: -newUnlock2.width * 2, + speed: 0.5, + }); + exitMovers.set([friendFoe2], + { + x: FlxG.width * 2, + speed: 0.4 + }); + exitMovers.set([newUnlock3], + { + x: -newUnlock3.width * 2, + speed: 0.3 + }); + + exitMoversCharSel.set([friendFoe, newUnlock1, waiting, newUnlock2, friendFoe2, newUnlock3, multiplyBar], { + y: -60, + speed: 0.8, + wait: 0.1 + }); + } + + public override function introDone():Void + { + // pinkBack.color = 0xFFFFD863; + + darkBg.visible = true; + friendFoe.visible = true; + newUnlock1.visible = true; + waiting.visible = true; + newUnlock2.visible = true; + friendFoe2.visible = true; + newUnlock3.visible = true; + multiplyBar.visible = true; + lightLayer.visible = true; + multiply1.visible = true; + multiply2.visible = true; + lightLayer2.visible = true; + yellow.visible = true; + lightLayer3.visible = true; + + cardGlow.visible = true; + FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.45, {ease: FlxEase.sineOut}); + } + + public override function enterCharSel():Void + { + FlxTween.tween(friendFoe, {speed: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(newUnlock1, {speed: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(waiting, {speed: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(newUnlock2, {speed: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(friendFoe2, {speed: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(newUnlock3, {speed: 0}, 0.8, {ease: FlxEase.sineIn}); + } + + public override function init():Void + { + FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut}); + add(pinkBack); + + confirmTextGlow.blend = BlendMode.ADD; + confirmTextGlow.visible = false; + + confirmGlow.blend = BlendMode.ADD; + + confirmGlow.visible = false; + confirmGlow2.visible = false; + + friendFoe = new BGScrollingText(0, 163, "COULD IT BE A NEW FRIEND? OR FOE??", FlxG.width, true, 43); + newUnlock1 = new BGScrollingText(-440, 215, 'NEW UNLOCK!', FlxG.width / 2, true, 80); + waiting = new BGScrollingText(0, 286, "SOMEONE'S WAITING!", FlxG.width / 2, true, 43); + newUnlock2 = new BGScrollingText(-220, 331, 'NEW UNLOCK!', FlxG.width / 2, true, 80); + friendFoe2 = new BGScrollingText(0, 402, 'COULD IT BE A NEW FRIEND? OR FOE??', FlxG.width, true, 43); + newUnlock3 = new BGScrollingText(0, 458, 'NEW UNLOCK!', FlxG.width / 2, true, 80); + + darkBg = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/darkback')); + add(darkBg); + + friendFoe.funnyColor = 0xFF139376; + friendFoe.speed = -4; + add(friendFoe); + + newUnlock1.funnyColor = 0xFF99BDF2; + newUnlock1.speed = 2; + add(newUnlock1); + + waiting.funnyColor = 0xFF40EA84; + waiting.speed = -2; + add(waiting); + + newUnlock2.funnyColor = 0xFF99BDF2; + newUnlock2.speed = 2; + add(newUnlock2); + + friendFoe2.funnyColor = 0xFF139376; + friendFoe2.speed = -4; + add(friendFoe2); + + newUnlock3.funnyColor = 0xFF99BDF2; + newUnlock3.speed = 2; + add(newUnlock3); + + multiplyBar = new FlxSprite(-10, 440).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/multiplyBar')); + multiplyBar.blend = BlendMode.MULTIPLY; + add(multiplyBar); + + lightLayer = new FlxSprite(-360, 230).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/orange gradient')); + lightLayer.blend = BlendMode.ADD; + add(lightLayer); + + multiply1 = new FlxSprite(-15, -125).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/red')); + multiply1.blend = BlendMode.MULTIPLY; + add(multiply1); + + multiply2 = new FlxSprite(-15, -125).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/red')); + multiply2.blend = BlendMode.MULTIPLY; + add(multiply2); + + lightLayer2 = new FlxSprite(-360, 230).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/orange gradient')); + lightLayer2.blend = BlendMode.ADD; + add(lightLayer2); + + yellow = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/yellow bg piece')); + yellow.blend = BlendMode.MULTIPLY; + add(yellow); + + lightLayer3 = new FlxSprite(-360, 290).loadGraphic(Paths.image('freeplay/backingCards/newCharacter/red gradient')); + lightLayer3.blend = BlendMode.ADD; + add(lightLayer3); + + cardGlow.blend = BlendMode.ADD; + cardGlow.visible = false; + + add(cardGlow); + + darkBg.visible = false; + friendFoe.visible = false; + newUnlock1.visible = false; + waiting.visible = false; + newUnlock2.visible = false; + friendFoe2.visible = false; + newUnlock3.visible = false; + multiplyBar.visible = false; + lightLayer.visible = false; + multiply1.visible = false; + multiply2.visible = false; + lightLayer2.visible = false; + yellow.visible = false; + lightLayer3.visible = false; + } + + var _timer:Float = 0; + + override public function update(elapsed:Float):Void + { + super.update(elapsed); + + _timer += elapsed * 2; + var sinTest:Float = (Math.sin(_timer) + 1) / 2; + lightLayer.alpha = FlxMath.lerp(0.4, 1, sinTest); + lightLayer2.alpha = FlxMath.lerp(0.2, 0.5, sinTest); + lightLayer3.alpha = FlxMath.lerp(0.1, 0.7, sinTest); + + multiply1.alpha = FlxMath.lerp(1, 0.21, sinTest); + multiply2.alpha = FlxMath.lerp(1, 0.21, sinTest); + + yellow.alpha = FlxMath.lerp(0.2, 0.72, sinTest); + + if (instance != null) + { + instance.angleMaskShader.extraColor = FlxColor.interpolate(0xFF2E2E46, 0xFF60607B, sinTest); + } + } + + public override function disappear():Void + { + FlxTween.color(pinkBack, 0.25, 0xFF05020E, 0xFFFFD0D5, {ease: FlxEase.quadOut}); + + darkBg.visible = false; + friendFoe.visible = false; + newUnlock1.visible = false; + waiting.visible = false; + newUnlock2.visible = false; + friendFoe2.visible = false; + newUnlock3.visible = false; + multiplyBar.visible = false; + lightLayer.visible = false; + multiply1.visible = false; + multiply2.visible = false; + lightLayer2.visible = false; + yellow.visible = false; + lightLayer3.visible = false; + + cardGlow.visible = true; + cardGlow.alpha = 1; + cardGlow.scale.set(1, 1); + FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.25, {ease: FlxEase.sineOut}); + } + + override public function confirm():Void + { + // confirmAtlas.visible = true; + // confirmAtlas.anim.play(""); + } +} diff --git a/source/funkin/ui/freeplay/backcards/PicoCard.hx b/source/funkin/ui/freeplay/backcards/PicoCard.hx index a424a76efb..f5db1ccc36 100644 --- a/source/funkin/ui/freeplay/backcards/PicoCard.hx +++ b/source/funkin/ui/freeplay/backcards/PicoCard.hx @@ -1,7 +1,9 @@ package funkin.ui.freeplay.backcards; import funkin.ui.freeplay.FreeplayState; +import flash.display.BitmapData; import flixel.FlxCamera; +import flixel.math.FlxMath; import flixel.FlxSprite; import flixel.group.FlxGroup; import flixel.group.FlxGroup.FlxTypedGroup; @@ -21,24 +23,76 @@ import funkin.ui.MusicBeatSubState; import lime.utils.Assets; import openfl.display.BlendMode; import flixel.group.FlxSpriteGroup; +import funkin.graphics.shaders.AdjustColorShader; +import flixel.addons.display.FlxTiledSprite; +import flixel.addons.display.FlxBackdrop; -/** - * A class for the backing cards so they dont have to be part of freeplayState...... - */ class PicoCard extends BackingCard { - public override function init():Void + var scrollBack:FlxBackdrop; + var scrollLower:FlxBackdrop; + var scrollTop:FlxBackdrop; + var scrollMiddle:FlxBackdrop; + + var glow:FlxSprite; + var glowDark:FlxSprite; + var blueBar:FlxSprite; + + var confirmAtlas:FlxAtlasSprite; + + public override function enterCharSel():Void { - FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut}); - add(pinkBack); + FlxTween.tween(scrollBack.velocity, {x: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(scrollLower.velocity, {x: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(scrollTop.velocity, {x: 0}, 0.8, {ease: FlxEase.sineIn}); + FlxTween.tween(scrollMiddle.velocity, {x: 0}, 0.8, {ease: FlxEase.sineIn}); + } - // add(orangeBackShit); + public override function applyExitMovers(?exitMovers:FreeplayState.ExitMoverData, ?exitMoversCharSel:FreeplayState.ExitMoverData):Void + { + super.applyExitMovers(exitMovers, exitMoversCharSel); + if (exitMovers == null || exitMoversCharSel == null) return; + + exitMoversCharSel.set([scrollTop], + { + y: -90, + speed: 0.8, + wait: 0.1 + }); - // add(alsoOrangeLOL); + exitMoversCharSel.set([scrollMiddle], + { + y: -80, + speed: 0.8, + wait: 0.1 + }); - // FlxSpriteUtil.alphaMaskFlxSprite(orangeBackShit, pinkBack, orangeBackShit); - // orangeBackShit.visible = false; - // alsoOrangeLOL.visible = false; + exitMoversCharSel.set([blueBar], + { + y: -70, + speed: 0.8, + wait: 0.1 + }); + + exitMoversCharSel.set([scrollLower], + { + y: -60, + speed: 0.8, + wait: 0.1 + }); + + exitMoversCharSel.set([scrollBack], + { + y: -50, + speed: 0.8, + wait: 0.1 + }); + } + + public override function init():Void + { + FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut}); + add(pinkBack); confirmTextGlow.blend = BlendMode.ADD; confirmTextGlow.visible = false; @@ -48,27 +102,213 @@ class PicoCard extends BackingCard confirmGlow.visible = false; confirmGlow2.visible = false; - add(confirmGlow2); - add(confirmGlow); + scrollBack = new FlxBackdrop(Paths.image('freeplay/backingCards/pico/lowerLoop'), X, 20); + scrollBack.setPosition(0, 200); + scrollBack.flipX = true; + scrollBack.alpha = 0.39; + scrollBack.velocity.x = 110; + add(scrollBack); + + scrollLower = new FlxBackdrop(Paths.image('freeplay/backingCards/pico/lowerLoop'), X, 20); + scrollLower.setPosition(0, 406); + scrollLower.velocity.x = -110; + add(scrollLower); + + blueBar = new FlxSprite(0, 239).loadGraphic(Paths.image('freeplay/backingCards/pico/blueBar')); + blueBar.blend = BlendMode.MULTIPLY; + blueBar.alpha = 0.4; + add(blueBar); + + scrollTop = new FlxBackdrop(null, X, 20); + scrollTop.setPosition(0, 80); + scrollTop.velocity.x = -220; + + scrollTop.frames = Paths.getSparrowAtlas('freeplay/backingCards/pico/topLoop'); + scrollTop.animation.addByPrefix('uzi', 'uzi info', 24, false); + scrollTop.animation.addByPrefix('sniper', 'sniper info', 24, false); + scrollTop.animation.addByPrefix('rocket launcher', 'rocket launcher info', 24, false); + scrollTop.animation.addByPrefix('rifle', 'rifle info', 24, false); + scrollTop.animation.addByPrefix('base', 'base', 24, false); + scrollTop.animation.play('base'); - add(confirmTextGlow); + add(scrollTop); - add(backingTextYeah); + scrollMiddle = new FlxBackdrop(Paths.image('freeplay/backingCards/pico/middleLoop'), X, 15); + scrollMiddle.setPosition(0, 346); + add(scrollMiddle); + scrollMiddle.velocity.x = 220; + + glowDark = new FlxSprite(-300, 330).loadGraphic(Paths.image('freeplay/backingCards/pico/glow')); + glowDark.blend = BlendMode.MULTIPLY; + add(glowDark); + + glow = new FlxSprite(-300, 330).loadGraphic(Paths.image('freeplay/backingCards/pico/glow')); + glow.blend = BlendMode.ADD; + add(glow); + + blueBar.visible = false; + scrollBack.visible = false; + scrollLower.visible = false; + scrollTop.visible = false; + scrollMiddle.visible = false; + glow.visible = false; + glowDark.visible = false; + + confirmAtlas = new FlxAtlasSprite(5, 55, Paths.animateAtlas("freeplay/backingCards/pico/pico-confirm")); + confirmAtlas.visible = false; + add(confirmAtlas); cardGlow.blend = BlendMode.ADD; cardGlow.visible = false; - add(cardGlow); } - public override function applyStyle(_freeplayState:FreeplayState):Void {} + override public function confirm():Void + { + confirmAtlas.visible = true; + confirmAtlas.anim.play(""); + + FlxTween.color(instance.bgDad, 10 / 24, 0xFFFFFFFF, 0xFF8A8A8A, + { + ease: FlxEase.expoOut, + onUpdate: function(_) { + instance.angleMaskShader.extraColor = instance.bgDad.color; + } + }); + + new FlxTimer().start(10 / 24, function(_) { + // shoot + FlxTween.color(instance.bgDad, 3 / 24, 0xFF343036, 0xFF696366, + { + ease: FlxEase.expoOut, + onUpdate: function(_) { + instance.angleMaskShader.extraColor = instance.bgDad.color; + } + }); + }); + + new FlxTimer().start(14 / 24, function(_) { + // shoot + FlxTween.color(instance.bgDad, 3 / 24, 0xFF27292D, 0xFF686A6F, + { + ease: FlxEase.expoOut, + onUpdate: function(_) { + instance.angleMaskShader.extraColor = instance.bgDad.color; + } + }); + }); + + new FlxTimer().start(18 / 24, function(_) { + // shoot + FlxTween.color(instance.bgDad, 3 / 24, 0xFF2D282D, 0xFF676164, + { + ease: FlxEase.expoOut, + onUpdate: function(_) { + instance.angleMaskShader.extraColor = instance.bgDad.color; + } + }); + }); + + new FlxTimer().start(21 / 24, function(_) { + // shoot + FlxTween.color(instance.bgDad, 3 / 24, 0xFF29292F, 0xFF62626B, + { + ease: FlxEase.expoOut, + onUpdate: function(_) { + instance.angleMaskShader.extraColor = instance.bgDad.color; + } + }); + }); + + new FlxTimer().start(24 / 24, function(_) { + // shoot + FlxTween.color(instance.bgDad, 3 / 24, 0xFF29232C, 0xFF808080, + { + ease: FlxEase.expoOut, + onUpdate: function(_) { + instance.angleMaskShader.extraColor = instance.bgDad.color; + } + }); + }); + } + + var beatFreq:Int = 1; + var beatFreqList:Array = [1,2,4,8]; + + public override function beatHit():Void { + // increases the amount of beats that need to go by to pulse the glow because itd flash like craazy at high bpms..... + beatFreq = beatFreqList[Math.floor(Conductor.instance.bpm/140)]; + + if(Conductor.instance.currentBeat % beatFreq != 0) return; + FlxTween.cancelTweensOf(glow); + FlxTween.cancelTweensOf(glowDark); + + glow.alpha = 1; + FlxTween.tween(glow, {alpha: 0}, 16/24, {ease: FlxEase.quartOut}); + glowDark.alpha = 0; + FlxTween.tween(glowDark, {alpha: 1}, 18/24, {ease: FlxEase.quartOut}); + } public override function introDone():Void { pinkBack.color = 0xFF98A2F3; - // orangeBackShit.visible = true; - // alsoOrangeLOL.visible = true; + + blueBar.visible = true; + scrollBack.visible = true; + scrollLower.visible = true; + scrollTop.visible = true; + scrollMiddle.visible = true; + glowDark.visible = true; + glow.visible = true; + cardGlow.visible = true; FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.45, {ease: FlxEase.sineOut}); } + + public override function disappear():Void + { + FlxTween.color(pinkBack, 0.25, 0xFF98A2F3, 0xFFFFD0D5, {ease: FlxEase.quadOut}); + + blueBar.visible = false; + scrollBack.visible = false; + scrollLower.visible = false; + scrollTop.visible = false; + scrollMiddle.visible = false; + glowDark.visible = false; + glow.visible = false; + + cardGlow.visible = true; + cardGlow.alpha = 1; + cardGlow.scale.set(1, 1); + FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.25, {ease: FlxEase.sineOut}); + } + + override public function update(elapsed:Float):Void + { + super.update(elapsed); + var scrollProgress:Float = Math.abs(scrollTop.x % (scrollTop.frameWidth + 20)); + + if (scrollTop.animation.curAnim.finished == true) + { + if (FlxMath.inBounds(scrollProgress, 500, 700) && scrollTop.animation.curAnim.name != 'sniper') + { + scrollTop.animation.play('sniper', true, false); + } + + if (FlxMath.inBounds(scrollProgress, 700, 1300) && scrollTop.animation.curAnim.name != 'rifle') + { + scrollTop.animation.play('rifle', true, false); + } + + if (FlxMath.inBounds(scrollProgress, 1450, 2000) && scrollTop.animation.curAnim.name != 'rocket launcher') + { + scrollTop.animation.play('rocket launcher', true, false); + } + + if (FlxMath.inBounds(scrollProgress, 0, 300) && scrollTop.animation.curAnim.name != 'uzi') + { + scrollTop.animation.play('uzi', true, false); + } + } + } } From 0c809c828e369130e8429a1d5d68732c3acfb4cf Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 30 Aug 2024 01:03:11 +0100 Subject: [PATCH 332/469] color tint for angle mask shader --- source/funkin/graphics/shaders/AngleMask.hx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/source/funkin/graphics/shaders/AngleMask.hx b/source/funkin/graphics/shaders/AngleMask.hx index c5ef87b720..ce27311cdb 100644 --- a/source/funkin/graphics/shaders/AngleMask.hx +++ b/source/funkin/graphics/shaders/AngleMask.hx @@ -1,12 +1,25 @@ package funkin.graphics.shaders; import flixel.system.FlxAssets.FlxShader; +import flixel.util.FlxColor; class AngleMask extends FlxShader { + public var extraColor(default, set):FlxColor = 0xFFFFFFFF; + + function set_extraColor(value:FlxColor):FlxColor + { + extraTint.value = [value.redFloat, value.greenFloat, value.blueFloat]; + this.extraColor = value; + + return this.extraColor; + } + @:glFragmentSource(' #pragma header + uniform vec3 extraTint; + uniform vec2 endPosition; vec2 hash22(vec2 p) { vec3 p3 = fract(vec3(p.xyx) * vec3(.1031, .1030, .0973)); @@ -69,6 +82,7 @@ class AngleMask extends FlxShader void main() { vec4 col = antialias(openfl_TextureCoordv); + col.xyz = col.xyz * extraTint.xyz; // col.xyz = gamma(col.xyz); gl_FragColor = col; }') @@ -77,5 +91,6 @@ class AngleMask extends FlxShader super(); endPosition.value = [90, 100]; // 100 AS DEFAULT WORKS NICELY FOR FREEPLAY? + extraTint.value = [1, 1, 1]; } } From 60c10aae997a64c2817e27c49e2be2c6e9419550 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 30 Aug 2024 01:04:30 +0100 Subject: [PATCH 333/469] char select to freeplay transition, backing card implementation, beatHit for backing cards --- .../ui/charSelect/CharSelectSubState.hx | 13 +- source/funkin/ui/freeplay/FreeplayState.hx | 185 ++++++++++++++---- 2 files changed, 159 insertions(+), 39 deletions(-) diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 34ffbaf580..d0bafddf9a 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -391,9 +391,15 @@ class CharSelectSubState extends MusicBeatSubState } } - function testLol():Void + function goToFreeplay():Void { autoFollow = false; + + FlxTween.tween(cursor, {alpha: 0}, 0.8, {ease: FlxEase.expoOut}); + FlxTween.tween(cursorBlue, {alpha: 0}, 0.8, {ease: FlxEase.expoOut}); + FlxTween.tween(cursorDarkBlue, {alpha: 0}, 0.8, {ease: FlxEase.expoOut}); + FlxTween.tween(cursorConfirmed, {alpha: 0}, 0.8, {ease: FlxEase.expoOut}); + FlxTween.tween(barthing, {y: barthing.y + 80}, 0.8, {ease: FlxEase.backIn}); FlxTween.tween(dipshitBacking, {y: dipshitBacking.y + 210}, 0.8, {ease: FlxEase.backIn}); FlxTween.tween(chooseDipshit, {y: chooseDipshit.y + 200}, 0.8, {ease: FlxEase.backIn}); @@ -413,7 +419,8 @@ class CharSelectSubState extends MusicBeatSubState FlxG.switchState(FreeplayState.build( { { - character: curChar + character: curChar, + fromCharSelect: true } })); } @@ -542,7 +549,7 @@ class CharSelectSubState extends MusicBeatSubState // character: curChar // } // })); - testLol(); + goToFreeplay(); }); } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 614e8cc869..5d3a2beada 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1,8 +1,6 @@ package funkin.ui.freeplay; -import funkin.ui.freeplay.backcards.BackingCard; -import funkin.ui.freeplay.backcards.BoyfriendCard; -import funkin.ui.freeplay.backcards.PicoCard; +import funkin.ui.freeplay.backcards.*; import flixel.addons.transition.FlxTransitionableState; import flixel.addons.ui.FlxInputText; import flixel.FlxCamera; @@ -55,6 +53,8 @@ import funkin.util.MathUtil; import funkin.util.SortUtil; import lime.utils.Assets; import openfl.display.BlendMode; +import funkin.data.freeplay.style.FreeplayStyleRegistry; +import funkin.data.song.SongData.SongMusicData; /** * Parameters used to initialize the FreeplayState. @@ -63,6 +63,8 @@ typedef FreeplayStateParams = { ?character:String, + ?fromCharSelect:Bool, + ?fromResults:FromResultsParams, }; @@ -149,7 +151,8 @@ class FreeplayState extends MusicBeatSubState var curSelected:Int = 0; var currentDifficulty:String = Constants.DEFAULT_DIFFICULTY; - var fp:FreeplayScore; + public var fp:FreeplayScore; + var txtCompletion:AtlasText; var lerpCompletion:Float = 0; var intendedCompletion:Float = 0; @@ -201,9 +204,14 @@ class FreeplayState extends MusicBeatSubState var prepForNewRank:Bool = false; + var styleData:Null = null; + + var fromCharSelect:Null = null; + public function new(?params:FreeplayStateParams, ?stickers:StickerSubState) { currentCharacterId = params?.character ?? Constants.DEFAULT_CHARACTER; + styleData = FreeplayStyleRegistry.instance.fetchEntry(currentCharacterId); var fetchPlayableCharacter = function():PlayableCharacter { var result = PlayerRegistry.instance.fetchEntry(params?.character ?? Constants.DEFAULT_CHARACTER); if (result == null) throw 'No valid playable character with id ${params?.character}'; @@ -211,6 +219,8 @@ class FreeplayState extends MusicBeatSubState }; currentCharacter = fetchPlayableCharacter(); + fromCharSelect = params?.fromCharSelect; + fromResultsParams = params?.fromResults; if (fromResultsParams?.playRankAnim == true) @@ -228,7 +238,7 @@ class FreeplayState extends MusicBeatSubState switch (currentCharacterId) { case 'bf': - backingCard = new BoyfriendCard(currentCharacter); + backingCard = new NewCharacterCard(currentCharacter); case 'pico': backingCard = new PicoCard(currentCharacter); default: @@ -237,7 +247,7 @@ class FreeplayState extends MusicBeatSubState // We build a bunch of sprites BEFORE create() so we can guarantee they aren't null later on. albumRoll = new AlbumRoll(); - fp = new FreeplayScore(460, 60, 7, 100); + fp = new FreeplayScore(460, 60, 7, 100, styleData); rankCamera = new FunkinCamera('rankCamera', 0, 0, FlxG.width, FlxG.height); funnyCam = new FunkinCamera('freeplayFunny', 0, 0, FlxG.width, FlxG.height); grpCapsules = new FlxTypedGroup(); @@ -252,11 +262,13 @@ class FreeplayState extends MusicBeatSubState ostName = new FlxText(8, 8, FlxG.width - 8 - 8, 'OFFICIAL OST', 48); - bgDad = new FlxSprite(backingCard.pinkBack.width * 0.74, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad')); + bgDad = new FlxSprite(backingCard.pinkBack.width * 0.74, 0).loadGraphic(styleData == null ? 'freeplay/freeplayBGdad' : styleData.getBgAssetGraphic()); } var fadeShader:BlueFade = new BlueFade(); + public var angleMaskShader:AngleMask = new AngleMask(); + override function create():Void { super.create(); @@ -350,6 +362,7 @@ class FreeplayState extends MusicBeatSubState add(backingCard); backingCard.init(); backingCard.applyExitMovers(exitMovers, exitMoversCharSel); + backingCard.instance = this; } if (currentCharacter?.getFreeplayDJData() != null) @@ -369,7 +382,7 @@ class FreeplayState extends MusicBeatSubState }); } - bgDad.shader = new AngleMask(); + bgDad.shader = angleMaskShader; bgDad.visible = false; var blackOverlayBullshitLOLXD:FlxSprite = new FlxSprite(FlxG.width).makeGraphic(Std.int(bgDad.width), Std.int(bgDad.height), FlxColor.BLACK); @@ -398,7 +411,6 @@ class FreeplayState extends MusicBeatSubState add(bgDad); // backingCard.pinkBack.width * 0.74 - FlxTween.tween(blackOverlayBullshitLOLXD, {x: 387.76}, 0.7, {ease: FlxEase.quintOut}); blackOverlayBullshitLOLXD.shader = bgDad.shader; @@ -445,12 +457,18 @@ class FreeplayState extends MusicBeatSubState albumRoll.albumId = null; add(albumRoll); - albumRoll.applyExitMovers(exitMovers, exitMoversCharSel); - var overhangStuff:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 164, FlxColor.BLACK); overhangStuff.y -= overhangStuff.height; - FlxTween.tween(overhangStuff, {y: -100}, 0.3, {ease: FlxEase.quartOut}); + if(fromCharSelect == true){ + blackOverlayBullshitLOLXD.x = 387.76; + overhangStuff.y = -100; + backingCard?.skipIntroTween(); + }else{ + albumRoll.applyExitMovers(exitMovers, exitMoversCharSel); + FlxTween.tween(overhangStuff, {y: -100}, 0.3, {ease: FlxEase.quartOut}); + FlxTween.tween(blackOverlayBullshitLOLXD, {x: 387.76}, 0.7, {ease: FlxEase.quintOut}); + } var fnfFreeplay:FlxText = new FlxText(8, 8, 0, 'FREEPLAY', 48); fnfFreeplay.font = 'VCR OSD Mono'; @@ -554,8 +572,8 @@ class FreeplayState extends MusicBeatSubState wait: 0.1 }); - var diffSelLeft:DifficultySelector = new DifficultySelector(20, grpDifficulties.y - 10, false, controls); - var diffSelRight:DifficultySelector = new DifficultySelector(325, grpDifficulties.y - 10, true, controls); + var diffSelLeft:DifficultySelector = new DifficultySelector(20, grpDifficulties.y - 10, false, controls, styleData); + var diffSelRight:DifficultySelector = new DifficultySelector(325, grpDifficulties.y - 10, true, controls, styleData); diffSelLeft.visible = false; diffSelRight.visible = false; add(diffSelLeft); @@ -573,6 +591,19 @@ class FreeplayState extends MusicBeatSubState albumRoll.playIntro(); + if (fromCharSelect == null) { + // render optimisation + if (_parentState != null) _parentState.persistentDraw = false; + + FlxTween.color(bgDad, 0.6, 0xFF000000, 0xFFFFFFFF, + { + ease: FlxEase.expoOut, + onUpdate: function(_) { + angleMaskShader.extraColor = bgDad.color; + } + }); + } + FlxTween.tween(grpDifficulties, {x: 90}, 0.6, {ease: FlxEase.quartOut}); diffSelLeft.visible = true; @@ -613,9 +644,6 @@ class FreeplayState extends MusicBeatSubState bgDad.visible = true; backingCard?.introDone(); - // render optimisation - if (_parentState != null) _parentState.persistentDraw = false; - if (prepForNewRank && fromResultsParams != null) { rankAnimStart(fromResultsParams); @@ -657,6 +685,12 @@ class FreeplayState extends MusicBeatSubState { rankCamera.fade(0xFF000000, 0, false, null, true); } + + if (fromCharSelect == true) + { + enterFromCharSel(); + onDJIntroDone(); + } } var currentFilter:Null = null; @@ -715,7 +749,7 @@ class FreeplayState extends MusicBeatSubState var hsvShader:HSVShader = new HSVShader(); var randomCapsule:SongMenuItem = grpCapsules.recycle(SongMenuItem); - randomCapsule.init(FlxG.width, 0, null); + randomCapsule.init(FlxG.width, 0, null, styleData); randomCapsule.onConfirm = function() { capsuleOnConfirmRandom(randomCapsule); }; @@ -727,7 +761,14 @@ class FreeplayState extends MusicBeatSubState randomCapsule.favIconBlurred.visible = false; randomCapsule.ranking.visible = false; randomCapsule.blurredRanking.visible = false; - randomCapsule.initJumpIn(0, force); + if (fromCharSelect == false) + { + randomCapsule.initJumpIn(0, force); + } + else + { + randomCapsule.forcePosition(); + } randomCapsule.hsvShader = hsvShader; grpCapsules.add(randomCapsule); @@ -738,7 +779,7 @@ class FreeplayState extends MusicBeatSubState var funnyMenu:SongMenuItem = grpCapsules.recycle(SongMenuItem); - funnyMenu.init(FlxG.width, 0, tempSong); + funnyMenu.init(FlxG.width, 0, tempSong, styleData); funnyMenu.onConfirm = function() { capsuleOnConfirmDefault(funnyMenu); }; @@ -1106,6 +1147,7 @@ class FreeplayState extends MusicBeatSubState function goToCharSelect():Void { + busy = true; var transitionGradient = new FlxSprite(0, 720).loadGraphic(Paths.image('freeplay/transitionGradient')); transitionGradient.scale.set(1280, 1); transitionGradient.updateHitbox(); @@ -1117,7 +1159,6 @@ class FreeplayState extends MusicBeatSubState wait: 0.1 }); add(transitionGradient); - // busy = true; for (index => capsule in grpCapsules.members) { var distFromSelected:Float = Math.abs(index - curSelected) - 1; @@ -1134,7 +1175,6 @@ class FreeplayState extends MusicBeatSubState } fadeShader.fade(1.0, 0.0, 0.8, {ease: FlxEase.quadIn}); FlxG.sound.music.fadeOut(0.9, 0); - // FlxTween.tween(transitionGradient, {y: 0}, 1, {ease: FlxEase.expoOut, startDelay: 0.3}); new FlxTimer().start(0.9, _ -> { FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState()); }); @@ -1149,13 +1189,77 @@ class FreeplayState extends MusicBeatSubState var funnyMoveShit:MoveData = moveData; - var moveDataX = funnyMoveShit.x ?? spr.x; var moveDataY = funnyMoveShit.y ?? spr.y; var moveDataSpeed = funnyMoveShit.speed ?? 0.2; var moveDataWait = funnyMoveShit.wait ?? 0.0; - FlxTween.tween(spr, {x: moveDataX, y: moveDataY + spr.y}, moveDataSpeed, {ease: FlxEase.backIn}); - // longestTimer = Math.max(longestTimer, moveDataSpeed + moveDataWait); + FlxTween.tween(spr, {y: moveDataY + spr.y}, moveDataSpeed, {ease: FlxEase.backIn}); + } + } + backingCard?.enterCharSel(); + } + + function enterFromCharSel():Void + { + busy = true; + if (_parentState != null) _parentState.persistentDraw = false; + + var transitionGradient = new FlxSprite(0, 720).loadGraphic(Paths.image('freeplay/transitionGradient')); + transitionGradient.scale.set(1280, 1); + transitionGradient.updateHitbox(); + transitionGradient.cameras = [rankCamera]; + exitMoversCharSel.set([transitionGradient], + { + y: -720, + speed: 1.5, + wait: 0.1 + }); + add(transitionGradient); + // FlxTween.tween(transitionGradient, {alpha: 0}, 1, {ease: FlxEase.circIn}); + // for (index => capsule in grpCapsules.members) + // { + // var distFromSelected:Float = Math.abs(index - curSelected) - 1; + // if (distFromSelected < 5) + // { + // capsule.doLerp = false; + // exitMoversCharSel.set([capsule], + // { + // y: -250, + // speed: 0.8, + // wait: 0.1 + // }); + // } + // } + fadeShader.fade(0.0, 1.0, 0.8, {ease: FlxEase.quadIn}); + for (grpSpr in exitMoversCharSel.keys()) + { + var moveData:Null = exitMoversCharSel.get(grpSpr); + if (moveData == null) continue; + + for (spr in grpSpr) + { + if (spr == null) continue; + + var funnyMoveShit:MoveData = moveData; + + var moveDataY = funnyMoveShit.y ?? spr.y; + var moveDataSpeed = funnyMoveShit.speed ?? 0.2; + var moveDataWait = funnyMoveShit.wait ?? 0.0; + + spr.y += moveDataY; + FlxTween.tween(spr, {y: spr.y - moveDataY}, moveDataSpeed * 1.2, + { + ease: FlxEase.expoOut, + onComplete: function(_) { + for (index => capsule in grpCapsules.members) + { + capsule.doLerp = true; + fromCharSelect = false; + busy = false; + albumRoll.applyExitMovers(exitMovers, exitMoversCharSel); + } + } + }); } } } @@ -1185,10 +1289,7 @@ class FreeplayState extends MusicBeatSubState { super.update(elapsed); - if (FlxG.keys.justPressed.G) - { - goToCharSelect(); - } + Conductor.instance.update(); if (FlxG.keys.justPressed.P) { @@ -1225,7 +1326,7 @@ class FreeplayState extends MusicBeatSubState if (controls.FREEPLAY_CHAR_SELECT && !busy) { - FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState()); + goToCharSelect(); } if (controls.FREEPLAY_FAVORITE && !busy) @@ -1549,6 +1650,13 @@ class FreeplayState extends MusicBeatSubState } } + override function beatHit():Bool + { + backingCard?.beatHit(); + + return super.beatHit(); + } + public override function destroy():Void { super.destroy(); @@ -1764,7 +1872,7 @@ class FreeplayState extends MusicBeatSubState backingCard?.confirm(); - new FlxTimer().start(1, function(tmr:FlxTimer) { + new FlxTimer().start(styleData?.getStartDelay(), function(tmr:FlxTimer) { FunkinSound.emptyPartialQueue(); Paths.setCurrentLevel(cap?.songData?.levelId); @@ -1878,7 +1986,7 @@ class FreeplayState extends MusicBeatSubState if (previewSongId == null) return; var previewSong:Null = SongRegistry.instance.fetchEntry(previewSongId); - var songDifficulty = previewSong?.getDifficulty(currentDifficulty, + var songDifficulty:Null = previewSong?.getDifficulty(currentDifficulty, previewSong?.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST); var baseInstrumentalId:String = songDifficulty?.characters?.instrumental ?? ''; var altInstrumentalIds:Array = songDifficulty?.characters?.altInstrumentals ?? []; @@ -1908,13 +2016,18 @@ class FreeplayState extends MusicBeatSubState partialParams: { loadPartial: true, - start: 0.05, - end: 0.25 + start: 0, + end: 0.2 }, onLoad: function() { FlxG.sound.music.fadeIn(2, 0, 0.4); } }); + + if(songDifficulty != null) { + Conductor.instance.mapTimeChanges(songDifficulty.timeChanges); + Conductor.instance.update(FlxG.sound?.music?.time ?? 0.0); + } } } @@ -1944,13 +2057,13 @@ class DifficultySelector extends FlxSprite var controls:Controls; var whiteShader:PureColor; - public function new(x:Float, y:Float, flipped:Bool, controls:Controls) + public function new(x:Float, y:Float, flipped:Bool, controls:Controls, ?styleData:FreeplayStyle = null) { super(x, y); this.controls = controls; - frames = Paths.getSparrowAtlas('freeplay/freeplaySelector'); + frames = Paths.getSparrowAtlas(styleData == null ? 'freeplay/freeplaySelector' : styleData.getSelectorAssetKey()); animation.addByPrefix('shine', 'arrow pointer loop', 24); animation.play('shine'); From 27970e4c2dbf60003d67e27cfff6da165908cce9 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 30 Aug 2024 01:04:51 +0100 Subject: [PATCH 334/469] freeplay style changelog oops --- source/funkin/data/freeplay/style/CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 source/funkin/data/freeplay/style/CHANGELOG.md diff --git a/source/funkin/data/freeplay/style/CHANGELOG.md b/source/funkin/data/freeplay/style/CHANGELOG.md new file mode 100644 index 0000000000..8fe9f7eb88 --- /dev/null +++ b/source/funkin/data/freeplay/style/CHANGELOG.md @@ -0,0 +1,9 @@ +# Freeplay Style Data Schema Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] +Initial release. From 4f562e3d6c825aa82d3c211c9a5339afe74a07f1 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 30 Aug 2024 01:08:47 +0100 Subject: [PATCH 335/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 0d97c9b20a..2ef40f931e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0d97c9b20a989b4517fd6174fe56583ac600522c +Subproject commit 2ef40f931e1b514c2d0381078e0a4f46db83a3c6 From 71da847158273b12cd692a9503e741391790cebe Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 30 Aug 2024 01:12:26 +0100 Subject: [PATCH 336/469] forgot to change these back --- source/funkin/ui/freeplay/FreeplayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 5d3a2beada..bf24aa3b3e 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -238,7 +238,7 @@ class FreeplayState extends MusicBeatSubState switch (currentCharacterId) { case 'bf': - backingCard = new NewCharacterCard(currentCharacter); + backingCard = new BoyfriendCard(currentCharacter); case 'pico': backingCard = new PicoCard(currentCharacter); default: From 21d5bb6112403c017f7f0eb1f51114b1b3b6f807 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 29 Aug 2024 22:10:27 -0400 Subject: [PATCH 337/469] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index fda4eaaf3b..91877ca4d1 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit fda4eaaf3bab4e6ebcf66548517ee89de0438e3d +Subproject commit 91877ca4d17a319e8aa84234ab62c88269a8df39 From 7511de1e7a1856f325f07940d8800e9bf5e8fe5f Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sun, 1 Sep 2024 03:22:34 -0400 Subject: [PATCH 338/469] A bunch of Freeplay visual fixes --- assets | 2 +- source/funkin/Assets.hx | 33 +++ .../funkin/data/freeplay/player/PlayerData.hx | 20 ++ source/funkin/data/song/SongData.hx | 11 +- .../graphics/adobeanimate/FlxAtlasSprite.hx | 49 +++-- source/funkin/modding/IScriptedClass.hx | 28 ++- source/funkin/modding/PolymodHandler.hx | 9 +- .../modding/events/ScriptEventDispatcher.hx | 19 +- source/funkin/ui/charSelect/CharSelectGF.hx | 45 ++++- .../funkin/ui/charSelect/CharSelectPlayer.hx | 43 +++- .../ui/charSelect/CharSelectSubState.hx | 22 +- source/funkin/ui/freeplay/FreeplayDJ.hx | 6 +- source/funkin/ui/freeplay/FreeplayState.hx | 191 +++++++++++++++--- .../freeplay/charselect/PlayableCharacter.hx | 33 +-- source/funkin/ui/mainmenu/MainMenuState.hx | 12 +- source/funkin/util/Constants.hx | 5 + source/funkin/util/ReflectUtil.hx | 15 ++ 17 files changed, 435 insertions(+), 108 deletions(-) create mode 100644 source/funkin/Assets.hx diff --git a/assets b/assets index 5a0c8da321..dc22633365 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5a0c8da32111571648b8fd07deabe2f78ae99eaa +Subproject commit dc226333655b7ec841b213968ef5264278fbcd63 diff --git a/source/funkin/Assets.hx b/source/funkin/Assets.hx new file mode 100644 index 0000000000..67a6688393 --- /dev/null +++ b/source/funkin/Assets.hx @@ -0,0 +1,33 @@ +package funkin; + +/** + * A wrapper around `openfl.utils.Assets` which disallows access to the harmful functions. + * Later we'll add Funkin-specific caching to this. + */ +class Assets +{ + public static function getText(path:String):String + { + return openfl.utils.Assets.getText(path); + } + + public static function getMusic(path:String):openfl.media.Sound + { + return openfl.utils.Assets.getMusic(path); + } + + public static function getBitmapData(path:String):openfl.display.BitmapData + { + return openfl.utils.Assets.getBitmapData(path); + } + + public static function getBytes(path:String):haxe.io.Bytes + { + return openfl.utils.Assets.getBytes(path); + } + + public static function list(type:openfl.utils.AssetType):Array + { + return openfl.utils.Assets.list(type); + } +} diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx index 55657ba464..b2905ae985 100644 --- a/source/funkin/data/freeplay/player/PlayerData.hx +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -31,6 +31,13 @@ class PlayerData @:default(false) public var showUnownedChars:Bool = false; + /** + * Which freeplay style to use for this character. + */ + @:optional + @:default("bf") + public var freeplayStyle:String = Constants.DEFAULT_FREEPLAY_STYLE; + /** * Data for displaying this character in the Freeplay menu. * If null, display no DJ. @@ -105,6 +112,9 @@ class PlayerFreeplayDJData @:jignored var prefixToOffsetsMap:Map>; + @:optional + var charSelect:Null; + @:optional var cartoon:Null; @@ -237,6 +247,11 @@ class PlayerFreeplayDJData { return fistPump?.loopBadEndFrame ?? 0; } + + public function getCharSelectTransitionDelay():Float + { + return charSelect?.transitionDelay ?? 0.25; + } } class PlayerCharSelectData @@ -300,6 +315,11 @@ typedef PlayerResultsAnimationData = var loopFrameLabel:Null; }; +typedef PlayerFreeplayDJCharSelectData = +{ + var transitionDelay:Float; +} + typedef PlayerFreeplayDJCartoonData = { var soundClickFrame:Int; diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx index f487eb54dc..551bcc8a3c 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -264,11 +264,20 @@ class SongOffsets implements ICloneable @:default([]) public var vocals:Map; - public function new(instrumental:Float = 0.0, ?altInstrumentals:Map, ?vocals:Map) + /** + * The offset, in milliseconds, to apply to the songs vocals, relative to each alternate instrumental. + * This is useful for the circumstance where, for example, an alt instrumental has a few seconds of lead in before the song starts. + */ + @:optional + @:default([]) + public var altVocals:Map>; + + public function new(instrumental:Float = 0.0, ?altInstrumentals:Map, ?vocals:Map, ?altVocals:Map>) { this.instrumental = instrumental; this.altInstrumentals = altInstrumentals == null ? new Map() : altInstrumentals; this.vocals = vocals == null ? new Map() : vocals; + this.altVocals = altVocals == null ? new Map>() : altVocals; } public function getInstrumentalOffset(?instrumental:String):Float diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index b32914ca3a..c4eaaff50b 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -105,23 +105,6 @@ class FlxAtlasSprite extends FlxAnimate return this.currentAnimation; } - /** - * `anim.finished` always returns false on looping animations, - * but this function will return true if we are on the last frame of the looping animation. - */ - public function isLoopFinished():Bool - { - if (this.anim == null) return false; - if (!this.anim.isPlaying) return false; - - // Reverse animation finished. - if (this.anim.reversed && this.anim.curFrame == 0) return true; - // Forward animation finished. - if (!this.anim.reversed && this.anim.curFrame >= (this.anim.length - 1)) return true; - - return false; - } - var _completeAnim:Bool = false; var fr:FlxKeyFrame = null; @@ -142,6 +125,8 @@ class FlxAtlasSprite extends FlxAnimate // Skip if not allowed to play animations. if ((!canPlayOtherAnims && !ignoreOther)) return; + if (anim == null) return; + if (id == null || id == '') id = this.currentAnimation; if (this.currentAnimation == id && !restart) @@ -189,10 +174,16 @@ class FlxAtlasSprite extends FlxAnimate // Move to the first frame of the animation. // goToFrameLabel(id); trace('Playing animation $id'); - this.anim.play(id, restart, false, startFrame); - goToFrameLabel(id); - - fr = anim.getFrameLabel(id); + if (this.anim.symbolDictionary.exists(id) || (this.anim.getByName(id) != null)) + { + this.anim.play(id, restart, false, startFrame); + } + // Only call goToFrameLabel if there is a frame label with that name. This prevents annoying warnings! + if (getFrameLabelNames().indexOf(id) != -1) + { + goToFrameLabel(id); + fr = anim.getFrameLabel(id); + } anim.curFrame += startFrame; this.currentAnimation = id; @@ -218,6 +209,8 @@ class FlxAtlasSprite extends FlxAnimate */ public function isLoopComplete():Bool { + if (this.anim == null) return false; + if (!this.anim.isPlaying) return false; return (anim.reversed && anim.curFrame == 0 || !(anim.reversed) && (anim.curFrame) >= (anim.length - 1)); } @@ -244,6 +237,18 @@ class FlxAtlasSprite extends FlxAnimate this.anim.goToFrameLabel(label); } + function getFrameLabelNames(?layer:haxe.extern.EitherType = null) + { + var labels = this.anim.getFrameLabels(layer); + var array = []; + for (label in labels) + { + array.push(label.name); + } + + return array; + } + function getNextFrameLabel(label:String):String { return listAnimations()[(getLabelIndex(label) + 1) % listAnimations().length]; @@ -272,7 +277,7 @@ class FlxAtlasSprite extends FlxAnimate { onAnimationFrame.dispatch(currentAnimation, frame); - if (fr != null && frame > (fr.index + fr.duration - 1) || isLoopFinished()) + if (fr != null && frame > (fr.index + fr.duration - 1) || isLoopComplete()) { anim.pause(); _onAnimationComplete(); diff --git a/source/funkin/modding/IScriptedClass.hx b/source/funkin/modding/IScriptedClass.hx index 5f2ff2b9ee..14aa6b494c 100644 --- a/source/funkin/modding/IScriptedClass.hx +++ b/source/funkin/modding/IScriptedClass.hx @@ -73,6 +73,22 @@ interface INoteScriptedClass extends IScriptedClass public function onNoteMiss(event:NoteScriptEvent):Void; } +/** + * Defines a set of callbacks available to scripted classes which represent sprites synced with the BPM. + */ +interface IBPMSyncedScriptedClass extends IScriptedClass +{ + /** + * Called once every step of the song. + */ + public function onStepHit(event:SongTimeScriptEvent):Void; + + /** + * Called once every beat of the song. + */ + public function onBeatHit(event:SongTimeScriptEvent):Void; +} + /** * Developer note: * @@ -86,7 +102,7 @@ interface INoteScriptedClass extends IScriptedClass /** * Defines a set of callbacks available to scripted classes that involve the lifecycle of the Play State. */ -interface IPlayStateScriptedClass extends INoteScriptedClass +interface IPlayStateScriptedClass extends INoteScriptedClass extends IBPMSyncedScriptedClass { /** * Called when the game is paused. @@ -136,16 +152,6 @@ interface IPlayStateScriptedClass extends INoteScriptedClass */ public function onSongEvent(event:SongEventScriptEvent):Void; - /** - * Called once every step of the song. - */ - public function onStepHit(event:SongTimeScriptEvent):Void; - - /** - * Called once every beat of the song. - */ - public function onBeatHit(event:SongTimeScriptEvent):Void; - /** * Called when the countdown of the song starts. */ diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index 52d4624cbf..75c69e5065 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -235,6 +235,10 @@ class PolymodHandler Polymod.addImportAlias('funkin.data.event.SongEventSchema', funkin.data.event.SongEventSchema.SongEventSchemaRaw); + // `lime.utils.Assets` literally just has a private `resolveClass` function for some reason? so we replace it with our own. + Polymod.addImportAlias('lime.utils.Assets', funkin.Assets); + Polymod.addImportAlias('openfl.utils.Assets', funkin.Assets); + // Add blacklisting for prohibited classes and packages. // `Sys` @@ -269,11 +273,6 @@ class PolymodHandler // System.load() can load malicious DLLs Polymod.blacklistImport('lime.system.System'); - // `lime.utils.Assets` - // Literally just has a private `resolveClass` function for some reason? - Polymod.blacklistImport('lime.utils.Assets'); - Polymod.blacklistImport('openfl.utils.Assets'); - // `openfl.desktop.NativeProcess` // Can load native processes on the host operating system. Polymod.blacklistImport('openfl.desktop.NativeProcess'); diff --git a/source/funkin/modding/events/ScriptEventDispatcher.hx b/source/funkin/modding/events/ScriptEventDispatcher.hx index c262c311dc..7e19173c46 100644 --- a/source/funkin/modding/events/ScriptEventDispatcher.hx +++ b/source/funkin/modding/events/ScriptEventDispatcher.hx @@ -94,20 +94,29 @@ class ScriptEventDispatcher } } - if (Std.isOfType(target, IPlayStateScriptedClass)) + if (Std.isOfType(target, IBPMSyncedScriptedClass)) { - var t:IPlayStateScriptedClass = cast(target, IPlayStateScriptedClass); + var t:IBPMSyncedScriptedClass = cast(target, IBPMSyncedScriptedClass); switch (event.type) { - case NOTE_GHOST_MISS: - t.onNoteGhostMiss(cast event); - return; case SONG_BEAT_HIT: t.onBeatHit(cast event); return; case SONG_STEP_HIT: t.onStepHit(cast event); return; + default: // Continue; + } + } + + if (Std.isOfType(target, IPlayStateScriptedClass)) + { + var t:IPlayStateScriptedClass = cast(target, IPlayStateScriptedClass); + switch (event.type) + { + case NOTE_GHOST_MISS: + t.onNoteGhostMiss(cast event); + return; case SONG_START: t.onSongStart(event); return; diff --git a/source/funkin/ui/charSelect/CharSelectGF.hx b/source/funkin/ui/charSelect/CharSelectGF.hx index 2e0e3980c4..ea7d414487 100644 --- a/source/funkin/ui/charSelect/CharSelectGF.hx +++ b/source/funkin/ui/charSelect/CharSelectGF.hx @@ -7,10 +7,12 @@ import flixel.math.FlxMath; import funkin.util.FramesJSFLParser; import funkin.util.FramesJSFLParser.FramesJSFLInfo; import funkin.util.FramesJSFLParser.FramesJSFLFrame; +import funkin.modding.IScriptedClass.IBPMSyncedScriptedClass; import flixel.math.FlxMath; +import funkin.modding.events.ScriptEvent; import funkin.vis.dsp.SpectralAnalyzer; -class CharSelectGF extends FlxAtlasSprite +class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass { var fadeTimer:Float = 0; var fadingStatus:FadeStatus = OFF; @@ -54,6 +56,7 @@ class CharSelectGF extends FlxAtlasSprite default: } + #if FEATURE_DEBUG_FUNCTIONS if (FlxG.keys.justPressed.J) { alpha = 1; @@ -65,8 +68,27 @@ class CharSelectGF extends FlxAtlasSprite alpha = 0; fadingStatus = FADE_IN; } + #end } + public function onStepHit(event:SongTimeScriptEvent):Void {} + + var danceEvery:Int = 2; + + public function onBeatHit(event:SongTimeScriptEvent):Void + { + // TODO: There's a minor visual bug where there's a little stutter. + // This happens because the animation is getting restarted while it's already playing. + // I tried make this not interrupt an existing idle, + // but isAnimationFinished() and isLoopComplete() both don't work! What the hell? + // danceEvery isn't necessary if that gets fixed. + if (getCurrentAnimation() == "idle" && (event.beat % danceEvery == 0)) + { + trace('GF beat hit'); + playAnimation("idle", true, false, false); + } + }; + override public function draw() { if (analyzer != null) drawFFT(); @@ -160,18 +182,27 @@ class CharSelectGF extends FlxAtlasSprite } // We don't need to update any anims if we didn't change GF - if (prevGF == curGF) return; - - loadAtlas(Paths.animateAtlas("charSelect/" + curGF + "Chill")); + if (prevGF != curGF) + { + loadAtlas(Paths.animateAtlas("charSelect/" + curGF + "Chill")); - animInInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "In.txt")); - animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "Out.txt")); + animInInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "In.txt")); + animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "Out.txt")); + } - playAnimation("idle", true, false, true); + playAnimation("idle", true, false, false); // addFrameCallback(getNextFrameLabel("idle"), () -> playAnimation("idle", true, false, false)); updateHitbox(); } + + public function onScriptEvent(event:ScriptEvent):Void {}; + + public function onCreate(event:ScriptEvent):Void {}; + + public function onDestroy(event:ScriptEvent):Void {}; + + public function onUpdate(event:UpdateScriptEvent):Void {}; } enum FadeStatus diff --git a/source/funkin/ui/charSelect/CharSelectPlayer.hx b/source/funkin/ui/charSelect/CharSelectPlayer.hx index 07c82d038d..8e94e25fed 100644 --- a/source/funkin/ui/charSelect/CharSelectPlayer.hx +++ b/source/funkin/ui/charSelect/CharSelectPlayer.hx @@ -3,8 +3,10 @@ package funkin.ui.charSelect; import flixel.FlxSprite; import funkin.graphics.adobeanimate.FlxAtlasSprite; import flxanimate.animate.FlxKeyFrame; +import funkin.modding.IScriptedClass.IBPMSyncedScriptedClass; +import funkin.modding.events.ScriptEvent; -class CharSelectPlayer extends FlxAtlasSprite +class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass { var desLp:FlxKeyFrame = null; @@ -18,11 +20,20 @@ class CharSelectPlayer extends FlxAtlasSprite switch (animLabel) { case "slidein": - if (hasAnimation("slidein idle point")) playAnimation("slidein idle point", true, false, false); + if (hasAnimation("slidein idle point")) + { + playAnimation("slidein idle point", true, false, false); + } else - playAnimation("idle", true, false, true); + { + // Handled by onBeatHit now + playAnimation("idle", true, false, false); + } case "slidein idle point": - playAnimation("idle", true, false, true); + // Handled by onBeatHit now + playAnimation("idle", true, false, false); + case "idle": + trace('Waiting for onBeatHit'); } }); @@ -31,6 +42,22 @@ class CharSelectPlayer extends FlxAtlasSprite }); } + public function onStepHit(event:SongTimeScriptEvent):Void {} + + public function onBeatHit(event:SongTimeScriptEvent):Void + { + // TODO: There's a minor visual bug where there's a little stutter. + // This happens because the animation is getting restarted while it's already playing. + // I tried make this not interrupt an existing idle, + // but isAnimationFinished() and isLoopComplete() both don't work! What the hell? + // danceEvery isn't necessary if that gets fixed. + if (getCurrentAnimation() == "idle") + { + trace('Player beat hit'); + playAnimation("idle", true, false, false); + } + }; + public function updatePosition(str:String) { switch (str) @@ -61,4 +88,12 @@ class CharSelectPlayer extends FlxAtlasSprite updatePosition(str); } + + public function onScriptEvent(event:ScriptEvent):Void {}; + + public function onCreate(event:ScriptEvent):Void {}; + + public function onDestroy(event:ScriptEvent):Void {}; + + public function onUpdate(event:UpdateScriptEvent):Void {}; } diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index c2850916ac..ccfe17a819 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -148,7 +148,7 @@ class CharSelectSubState extends MusicBeatSubState var stageSpr:FlxSprite = new FlxSprite(-40, 391); stageSpr.frames = Paths.getSparrowAtlas("charSelect/charSelectStage"); - stageSpr.animation.addByPrefix("idle", "stage", 24, true); + stageSpr.animation.addByPrefix("idle", "stage full instance 1", 24, true); stageSpr.animation.play("idle"); add(stageSpr); @@ -195,14 +195,14 @@ class CharSelectSubState extends MusicBeatSubState var dipshitBlur:FlxSprite = new FlxSprite(419, -65); dipshitBlur.frames = Paths.getSparrowAtlas("charSelect/dipshitBlur"); - dipshitBlur.animation.addByPrefix('idle', "CHOOSE vertical", 24, true); + dipshitBlur.animation.addByPrefix('idle', "CHOOSE vertical offset instance 1", 24, true); dipshitBlur.blend = BlendMode.ADD; dipshitBlur.animation.play("idle"); add(dipshitBlur); var dipshitBacking:FlxSprite = new FlxSprite(423, -17); dipshitBacking.frames = Paths.getSparrowAtlas("charSelect/dipshitBacking"); - dipshitBacking.animation.addByPrefix('idle', "CHOOSE horizontal", 24, true); + dipshitBacking.animation.addByPrefix('idle', "CHOOSE horizontal offset instance 1", 24, true); dipshitBacking.blend = BlendMode.ADD; dipshitBacking.animation.play("idle"); add(dipshitBacking); @@ -261,14 +261,14 @@ class CharSelectSubState extends MusicBeatSubState cursorConfirmed = new FlxSprite(0, 0); cursorConfirmed.scrollFactor.set(); cursorConfirmed.frames = Paths.getSparrowAtlas("charSelect/charSelectorConfirm"); - cursorConfirmed.animation.addByPrefix("idle", "cursor ACCEPTED", 24, true); + cursorConfirmed.animation.addByPrefix("idle", "cursor ACCEPTED instance 1", 24, true); cursorConfirmed.visible = false; add(cursorConfirmed); cursorDenied = new FlxSprite(0, 0); cursorDenied.scrollFactor.set(); cursorDenied.frames = Paths.getSparrowAtlas("charSelect/charSelectorDenied"); - cursorDenied.animation.addByPrefix("idle", "cursor DENIED", 24, false); + cursorDenied.animation.addByPrefix("idle", "cursor DENIED instance 1", 24, false); cursorDenied.visible = false; add(cursorDenied); @@ -289,8 +289,6 @@ class CharSelectSubState extends MusicBeatSubState FlxG.debugger.addTrackerProfile(new TrackerProfile(CharSelectSubState, ["curChar", "grpXSpread", "grpYSpread"])); FlxG.debugger.track(this); - FlxG.sound.playMusic(Paths.music('charSelect/charSelectMusic')); - camFollow = new FlxObject(0, 0, 1, 1); add(camFollow); camFollow.screenCenter(); @@ -567,6 +565,16 @@ class CharSelectSubState extends MusicBeatSubState cursorDarkBlue.y = MathUtil.coolLerp(cursorDarkBlue.y, cursorLocIntended.y, lerpAmnt * 0.2); } + public override function dispatchEvent(event:ScriptEvent):Void + { + // super.dispatchEvent(event) dispatches event to module scripts. + super.dispatchEvent(event); + + // Dispatch events (like onBeatHit) to props + ScriptEventDispatcher.callEvent(playerChill, event); + ScriptEventDispatcher.callEvent(gfChill, event); + } + function spamOnStep():Void { if (spamUp || spamDown || spamLeft || spamRight) diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index b1528d906b..3ef00eb090 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -15,7 +15,9 @@ class FreeplayDJ extends FlxAtlasSprite { // Represents the sprite's current status. // Without state machines I would have driven myself crazy years ago. - public var currentState:FreeplayDJState = Intro; + // Made this PRIVATE so we can keep track of everything that can alter the state! + // Add a function to this class if you want to edit this value from outside. + private var currentState:FreeplayDJState = Intro; // A callback activated when the intro animation finishes. public var onIntroDone:FlxSignal = new FlxSignal(); @@ -378,7 +380,7 @@ class FreeplayDJ extends FlxAtlasSprite public function toCharSelect():Void { - if (hasAnimation('charSelect')) + if (hasAnimation(playableCharData.getAnimationPrefix('charSelect'))) { currentState = CharSelect; var animPrefix = playableCharData.getAnimationPrefix('charSelect'); diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index f0bad1718f..4d6d8dc9d3 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -224,12 +224,14 @@ class FreeplayState extends MusicBeatSubState { currentCharacterId = params?.character ?? rememberedCharacterId; var fetchPlayableCharacter = function():PlayableCharacter { - var result = PlayerRegistry.instance.fetchEntry(params?.character ?? rememberedCharacterId); - if (result == null) throw 'No valid playable character with id ${params?.character}'; + var targetCharId = params?.character ?? rememberedCharacterId; + var result = PlayerRegistry.instance.fetchEntry(targetCharId); + if (result == null) throw 'No valid playable character with id ${targetCharId}'; return result; }; currentCharacter = fetchPlayableCharacter(); + styleData = FreeplayStyleRegistry.instance.fetchEntry(currentCharacter.getFreeplayStyleID()); rememberedCharacterId = currentCharacter?.id ?? Constants.DEFAULT_CHARACTER; fromResultsParams = params?.fromResults; @@ -317,6 +319,9 @@ class FreeplayState extends MusicBeatSubState isDebug = true; #end + // Block input until the intro finishes. + busy = true; + // Add a null entry that represents the RANDOM option songs.push(null); @@ -645,13 +650,14 @@ class FreeplayState extends MusicBeatSubState // be careful not to "add()" things in here unless it's to a group that's already added to the state // otherwise it won't be properly attatched to funnyCamera (relavent code should be at the bottom of create()) var onDJIntroDone = function() { + busy = false; + // when boyfriend hits dat shiii albumRoll.playIntro(); var daSong = grpCapsules.members[curSelected].songData; albumRoll.albumId = daSong?.albumId; - FlxTween.tween(grpDifficulties, {x: 90}, 0.6, {ease: FlxEase.quartOut}); diffSelLeft.visible = true; @@ -1187,6 +1193,158 @@ class FreeplayState extends MusicBeatSubState }); } + function tryOpenCharSelect():Void + { + // Check if we have ACCESS to character select! + trace('Is Pico unlocked? ${PlayerRegistry.instance.fetchEntry('pico')?.isUnlocked()}'); + trace('Number of characters: ${PlayerRegistry.instance.countUnlockedCharacters()}'); + + if (PlayerRegistry.instance.countUnlockedCharacters() > 1) + { + trace('Opening character select!'); + } + else + { + trace('Not enough characters unlocked to open character select!'); + FunkinSound.playOnce(Paths.sound('cancelMenu')); + return; + } + + busy = true; + + FunkinSound.playOnce(Paths.sound('confirmMenu')); + + if (dj != null) + { + dj.toCharSelect(); + } + + // Get this character's transition delay, with a reasonable default. + var transitionDelay:Float = currentCharacter.getFreeplayDJData()?.getCharSelectTransitionDelay() ?? 0.25; + + new FlxTimer().start(transitionDelay, _ -> { + transitionToCharSelect(); + }); + } + + function transitionToCharSelect():Void + { + var transitionGradient = new FlxSprite(0, 720).loadGraphic(Paths.image('freeplay/transitionGradient')); + transitionGradient.scale.set(1280, 1); + transitionGradient.updateHitbox(); + transitionGradient.cameras = [rankCamera]; + exitMoversCharSel.set([transitionGradient], + { + y: -720, + speed: 0.8, + wait: 0.1 + }); + add(transitionGradient); + for (index => capsule in grpCapsules.members) + { + var distFromSelected:Float = Math.abs(index - curSelected) - 1; + if (distFromSelected < 5) + { + capsule.doLerp = false; + exitMoversCharSel.set([capsule], + { + y: -250, + speed: 0.8, + wait: 0.1 + }); + } + } + fadeShader.fade(1.0, 0.0, 0.8, {ease: FlxEase.quadIn}); + FlxG.sound.music.fadeOut(0.9, 0); + new FlxTimer().start(0.9, _ -> { + FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState()); + }); + for (grpSpr in exitMoversCharSel.keys()) + { + var moveData:Null = exitMoversCharSel.get(grpSpr); + if (moveData == null) continue; + + for (spr in grpSpr) + { + if (spr == null) continue; + + var funnyMoveShit:MoveData = moveData; + + var moveDataY = funnyMoveShit.y ?? spr.y; + var moveDataSpeed = funnyMoveShit.speed ?? 0.2; + var moveDataWait = funnyMoveShit.wait ?? 0.0; + + FlxTween.tween(spr, {y: moveDataY + spr.y}, moveDataSpeed, {ease: FlxEase.backIn}); + } + } + backingCard?.enterCharSel(); + } + + function enterFromCharSel():Void + { + busy = true; + if (_parentState != null) _parentState.persistentDraw = false; + + var transitionGradient = new FlxSprite(0, 720).loadGraphic(Paths.image('freeplay/transitionGradient')); + transitionGradient.scale.set(1280, 1); + transitionGradient.updateHitbox(); + transitionGradient.cameras = [rankCamera]; + exitMoversCharSel.set([transitionGradient], + { + y: -720, + speed: 1.5, + wait: 0.1 + }); + add(transitionGradient); + // FlxTween.tween(transitionGradient, {alpha: 0}, 1, {ease: FlxEase.circIn}); + // for (index => capsule in grpCapsules.members) + // { + // var distFromSelected:Float = Math.abs(index - curSelected) - 1; + // if (distFromSelected < 5) + // { + // capsule.doLerp = false; + // exitMoversCharSel.set([capsule], + // { + // y: -250, + // speed: 0.8, + // wait: 0.1 + // }); + // } + // } + fadeShader.fade(0.0, 1.0, 0.8, {ease: FlxEase.quadIn}); + for (grpSpr in exitMoversCharSel.keys()) + { + var moveData:Null = exitMoversCharSel.get(grpSpr); + if (moveData == null) continue; + + for (spr in grpSpr) + { + if (spr == null) continue; + + var funnyMoveShit:MoveData = moveData; + + var moveDataY = funnyMoveShit.y ?? spr.y; + var moveDataSpeed = funnyMoveShit.speed ?? 0.2; + var moveDataWait = funnyMoveShit.wait ?? 0.0; + + spr.y += moveDataY; + FlxTween.tween(spr, {y: spr.y - moveDataY}, moveDataSpeed * 1.2, + { + ease: FlxEase.expoOut, + onComplete: function(_) { + for (index => capsule in grpCapsules.members) + { + capsule.doLerp = true; + fromCharSelect = false; + busy = false; + albumRoll.applyExitMovers(exitMovers, exitMoversCharSel); + } + } + }); + } + } + } + var touchY:Float = 0; var touchX:Float = 0; var dxTouch:Float = 0; @@ -1247,32 +1405,7 @@ class FreeplayState extends MusicBeatSubState if (controls.FREEPLAY_CHAR_SELECT && !busy) { - // Check if we have ACCESS to character select! - trace('Is Pico unlocked? ${PlayerRegistry.instance.fetchEntry('pico')?.isUnlocked()}'); - trace('Number of characters: ${PlayerRegistry.instance.countUnlockedCharacters()}'); - - if (PlayerRegistry.instance.countUnlockedCharacters() > 1) - { - if (dj != null) - { - busy = true; - // Transition to character select after animation - dj.onCharSelectComplete = function() { - FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState()); - } - dj.toCharSelect(); - } - else - { - // Transition to character select immediately - FlxG.switchState(new funkin.ui.charSelect.CharSelectSubState()); - } - } - else - { - trace('Not enough characters unlocked to open character select!'); - FunkinSound.playOnce(Paths.sound('cancelMenu')); - } + tryOpenCharSelect(); } if (controls.FREEPLAY_FAVORITE && !busy) diff --git a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx index d4dd7aaa48..85ecb4247f 100644 --- a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx +++ b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx @@ -9,6 +9,7 @@ import funkin.play.scoring.Scoring.ScoringRank; * An object used to retrieve data about a playable character (also known as "weeks"). * Can be scripted to override each function, for custom behavior. */ +@:nullSafety class PlayableCharacter implements IRegistryEntry { /** @@ -19,7 +20,7 @@ class PlayableCharacter implements IRegistryEntry /** * Playable character data as parsed from the JSON file. */ - public final _data:PlayerData; + public final _data:Null; /** * @param id The ID of the JSON file to parse. @@ -41,7 +42,7 @@ class PlayableCharacter implements IRegistryEntry public function getName():String { // TODO: Maybe add localization support? - return _data.name; + return _data?.name ?? "Unknown"; } /** @@ -50,7 +51,7 @@ class PlayableCharacter implements IRegistryEntry */ public function getOwnedCharacterIds():Array { - return _data.ownedChars; + return _data?.ownedChars ?? []; } /** @@ -59,17 +60,17 @@ class PlayableCharacter implements IRegistryEntry */ public function shouldShowUnownedChars():Bool { - return _data.showUnownedChars; + return _data?.showUnownedChars ?? false; } public function shouldShowCharacter(id:String):Bool { - if (_data.ownedChars.contains(id)) + if (getOwnedCharacterIds().contains(id)) { return true; } - if (_data.showUnownedChars) + if (shouldShowUnownedChars()) { var result = !PlayerRegistry.instance.isCharacterOwned(id); return result; @@ -78,19 +79,25 @@ class PlayableCharacter implements IRegistryEntry return false; } - public function getFreeplayDJData():PlayerFreeplayDJData + public function getFreeplayStyleID():String { - return _data.freeplayDJ; + return _data?.freeplayStyle ?? Constants.DEFAULT_FREEPLAY_STYLE; + } + + public function getFreeplayDJData():Null + { + return _data?.freeplayDJ; } public function getFreeplayDJText(index:Int):String { - return _data.freeplayDJ.getFreeplayDJText(index); + // Silly little placeholder + return _data?.freeplayDJ?.getFreeplayDJText(index) ?? 'GET FREAKY ON A FRIDAY'; } - public function getCharSelectData():PlayerCharSelectData + public function getCharSelectData():Null { - return _data.charSelect; + return _data?.charSelect; } /** @@ -99,7 +106,7 @@ class PlayableCharacter implements IRegistryEntry */ public function getResultsAnimationDatas(rank:ScoringRank):Array { - if (_data.results == null) + if (_data == null || _data.results == null) { return []; } @@ -124,7 +131,7 @@ class PlayableCharacter implements IRegistryEntry */ public function isUnlocked():Bool { - return _data.unlocked; + return _data?.unlocked ?? true; } /** diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index c777e685b4..d2a9f046a8 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -110,7 +110,17 @@ class MainMenuState extends MusicBeatState FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - openSubState(new FreeplayState()); + #if FEATURE_DEBUG_FUNCTIONS + // Debug function: Hold SHIFT when selecting Freeplay to swap character without the char select menu + var targetCharacter:Null = (FlxG.keys.pressed.SHIFT) ? (FreeplayState.rememberedCharacterId == "pico" ? "bf" : "pico") : null; + #else + var targetCharacter:Null = null; + #end + + openSubState(new FreeplayState( + { + character: targetCharacter + })); }); #if CAN_OPEN_LINKS diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index fa03b229da..1653311df6 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -258,6 +258,11 @@ class Constants */ public static final DEFAULT_NOTE_STYLE:String = 'funkin'; + /** + * The default freeplay style for characters. + */ + public static final DEFAULT_FREEPLAY_STYLE:String = 'bf'; + /** * The default pixel note style for songs. */ diff --git a/source/funkin/util/ReflectUtil.hx b/source/funkin/util/ReflectUtil.hx index 830edd31d2..da98c820b5 100644 --- a/source/funkin/util/ReflectUtil.hx +++ b/source/funkin/util/ReflectUtil.hx @@ -33,4 +33,19 @@ class ReflectUtil { return Type.getClassName(Type.getClass(obj)); } + + public static function getAnonymousFieldsOf(obj:Dynamic):Array + { + return Reflect.fields(obj); + } + + public static function getAnonymousField(obj:Dynamic, name:String):Dynamic + { + return Reflect.field(obj, name); + } + + public static function hasAnonymousField(obj:Dynamic, name:String):Bool + { + return Reflect.hasField(obj, name); + } } From cdc3883173634469be23f90603b50a1fb219d8fe Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sun, 1 Sep 2024 17:19:39 -0400 Subject: [PATCH 339/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 91877ca4d1..0586aae556 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 91877ca4d17a319e8aa84234ab62c88269a8df39 +Subproject commit 0586aae5565e3707f232e05439f42c815e32aec8 From 2f59363432946d493cb580f9aa417f384a0e502c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 3 Sep 2024 00:21:25 -0400 Subject: [PATCH 340/469] Merge branch 'bugfix/assets-cleanup' into develop-2 --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 67c2375ef0..114dce37ba 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 67c2375ef0d6e8d48dea50bc17e8252219c05a97 +Subproject commit 114dce37bab3938eebf788910cde60140dd7562f From d049478c65eae793b61f2b8cb4ff1b998c6bcc26 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Tue, 3 Sep 2024 19:16:46 +0100 Subject: [PATCH 341/469] registry updates --- source/funkin/data/stage/StageRegistry.hx | 2 +- source/funkin/ui/debug/charting/ChartEditorState.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/data/stage/StageRegistry.hx b/source/funkin/data/stage/StageRegistry.hx index 7754c380e0..3d3779bcec 100644 --- a/source/funkin/data/stage/StageRegistry.hx +++ b/source/funkin/data/stage/StageRegistry.hx @@ -94,7 +94,7 @@ class StageRegistry extends BaseRegistry { return [ "mainStage", "mainStageErect", "spookyMansion", "phillyTrain", "phillyTrainErect", "limoRide", "limoRideErect", "mallXmas", "mallEvil", "school", - "schoolEvil", "tankmanBattlefield", "phillyStreets", "phillyBlazin", + "schoolEvil", "tankmanBattlefield", "phillyStreets", "phillyStreetsErect", "phillyBlazin", ]; } diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index a1d7aca6be..20d39183b7 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -5713,7 +5713,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState PlayStatePlaylist.campaignId = 'week6'; case 'tankmanBattlefield': PlayStatePlaylist.campaignId = 'week7'; - case 'phillyStreets' | 'phillyBlazin' | 'phillyBlazin2': + case 'phillyStreets' | 'phillyStreetsErect' | 'phillyBlazin' | 'phillyBlazin2': PlayStatePlaylist.campaignId = 'weekend1'; } Paths.setCurrentLevel(PlayStatePlaylist.campaignId); From b58bd07fd266474d8af2345b6318cf6ac93f1508 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Tue, 3 Sep 2024 19:16:58 +0100 Subject: [PATCH 342/469] rain shader recolor support --- source/funkin/graphics/shaders/RuntimeRainShader.hx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/funkin/graphics/shaders/RuntimeRainShader.hx b/source/funkin/graphics/shaders/RuntimeRainShader.hx index 68a2031792..d0c036623e 100644 --- a/source/funkin/graphics/shaders/RuntimeRainShader.hx +++ b/source/funkin/graphics/shaders/RuntimeRainShader.hx @@ -4,6 +4,7 @@ import flixel.system.FlxAssets.FlxShader; import openfl.display.BitmapData; import openfl.display.ShaderParameter; import openfl.display.ShaderParameterType; +import flixel.util.FlxColor; import openfl.utils.Assets; typedef Light = @@ -94,6 +95,14 @@ class RuntimeRainShader extends RuntimePostEffectShader return mask = value; } + public var rainColor(default, set):FlxColor; + + function set_rainColor(color:FlxColor):FlxColor + { + this.setFloatArray("uRainColor", [color.red / 255, color.green / 255, color.blue / 255]); + return rainColor = color; + } + public var lightMap(default, set):BitmapData; function set_lightMap(value:BitmapData):BitmapData @@ -113,6 +122,7 @@ class RuntimeRainShader extends RuntimePostEffectShader public function new() { super(Assets.getText(Paths.frag('rain'))); + this.rainColor = 0xFF6680cc; } public function update(elapsed:Float):Void From f16bffb3fcf5bf18b3d59de7dd6c7325772f7bf0 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Tue, 3 Sep 2024 19:19:25 +0100 Subject: [PATCH 343/469] assets submod..................... --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 5a0c8da321..e80d92ac48 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5a0c8da32111571648b8fd07deabe2f78ae99eaa +Subproject commit e80d92ac484fe432ab34d45b22158f5d565afb0f From 36505f75c1cdfb7597b0ce5b0d180aecb6440fa3 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 3 Sep 2024 21:14:57 -0400 Subject: [PATCH 344/469] The actual code for custom freeplay music LOL --- CHANGELOG.md | 18 ++++++++- source/funkin/Assets.hx | 38 +++++++++++++++++++ .../funkin/data/freeplay/player/PlayerData.hx | 23 +++++++++++ source/funkin/play/ResultState.hx | 13 +++++-- source/funkin/play/scoring/Scoring.hx | 34 ----------------- source/funkin/ui/freeplay/FreeplayDJ.hx | 1 + .../freeplay/charselect/PlayableCharacter.hx | 21 ++++++++++ 7 files changed, 108 insertions(+), 40 deletions(-) create mode 100644 source/funkin/Assets.hx diff --git a/CHANGELOG.md b/CHANGELOG.md index 53e981284e..22a36fa8ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,28 @@ All notable changes will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.5.0] - 2024-08-?? +## [0.5.0] - 2024-09-12 ### Added - Added a new Character Select screen to switch between playable characters in Freeplay - Modding isn't 100% there but we're working on it! - Added Pico as a playable character! Unlock him by completing Weekend 1 (if you haven't already done that) - The songs from Weekend 1 have moved; you must now switch to Pico in Freeplay to access them -- Added ## new Pico remixes! Access them by selecting Pico from in the Character Select screen +- Added 10 new Pico remixes! Access them by selecting Pico from in the Character Select screen + - Bopeebo (Pico Mix) + - Fresh (Pico Mix) + - DadBattle (Pico Mix) + - Spookeez (Pico Mix) + - South (Pico Mix) + - Philly Nice (Pico Mix) + - Blammed (Pico Mix) + - Eggnog (Pico Mix) + - Ugh (Pico Mix) + - Guns (Pico Mix) +- Added 1 new Boyfriend remix! Access it by selecting Pico from in the Character Select screen + - Darnell (BF Mix) - Added 2 new Erect remixes! Access them by switching difficulty on the song + - Cocoa Erect + - Ugh Erect - Implemented support for a new Instrumental Selector in Freeplay - Beating a Pico remix lets you use that instrumental when playing as Boyfriend - Added the first batch of Erect Stages! These graphical overhauls of the original stages will be used when playing Erect remixes and Pico remixes diff --git a/source/funkin/Assets.hx b/source/funkin/Assets.hx new file mode 100644 index 0000000000..5351676d4d --- /dev/null +++ b/source/funkin/Assets.hx @@ -0,0 +1,38 @@ +package funkin; + +/** + * A wrapper around `openfl.utils.Assets` which disallows access to the harmful functions. + * Later we'll add Funkin-specific caching to this. + */ +class Assets +{ + public static function getText(path:String):String + { + return openfl.utils.Assets.getText(path); + } + + public static function getMusic(path:String):openfl.media.Sound + { + return openfl.utils.Assets.getMusic(path); + } + + public static function getBitmapData(path:String):openfl.display.BitmapData + { + return openfl.utils.Assets.getBitmapData(path); + } + + public static function getBytes(path:String):haxe.io.Bytes + { + return openfl.utils.Assets.getBytes(path); + } + + public static function exists(path:String, ?type:openfl.utils.AssetType):Bool + { + return openfl.utils.Assets.exists(path, type); + } + + public static function list(type:openfl.utils.AssetType):Array + { + return openfl.utils.Assets.list(type); + } +} diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx index 55657ba464..fa72f497bc 100644 --- a/source/funkin/data/freeplay/player/PlayerData.hx +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -253,6 +253,8 @@ class PlayerCharSelectData typedef PlayerResultsData = { + var music:PlayerResultsMusicData; + var perfect:Array; var excellent:Array; var great:Array; @@ -260,6 +262,27 @@ typedef PlayerResultsData = var loss:Array; }; +typedef PlayerResultsMusicData = +{ + @:optional + var PERFECT_GOLD:String; + + @:optional + var PERFECT:String; + + @:optional + var EXCELLENT:String; + + @:optional + var GREAT:String; + + @:optional + var GOOD:String; + + @:optional + var SHIT:String; +} + typedef PlayerResultsAnimationData = { /** diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index b1ff69a3a9..ec6069ad4c 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -404,12 +404,12 @@ class ResultState extends MusicBeatSubState // } new FlxTimer().start(rank.getMusicDelay(), _ -> { - if (rank.hasMusicIntro()) + var introMusic:String = Paths.music(getMusicPath(playerCharacter, rank) + '/' + getMusicPath(playerCharacter, rank) + '-intro'); + if (Assets.exists(introMusic)) { // Play the intro music. - var introMusic:String = Paths.music(rank.getMusicPath() + '/' + rank.getMusicPath() + '-intro'); FunkinSound.load(introMusic, 1.0, false, true, true, () -> { - FunkinSound.playMusic(rank.getMusicPath(), + FunkinSound.playMusic(getMusicPath(playerCharacter, rank), { startingVolume: 1.0, overrideExisting: true, @@ -420,7 +420,7 @@ class ResultState extends MusicBeatSubState } else { - FunkinSound.playMusic(rank.getMusicPath(), + FunkinSound.playMusic(getMusicPath(playerCharacter, rank), { startingVolume: 1.0, overrideExisting: true, @@ -441,6 +441,11 @@ class ResultState extends MusicBeatSubState super.create(); } + function getMusicPath(playerCharacter:Null, rank:ScoringRank):String + { + return playerCharacter?.getResultsMusicPath(rank) ?? 'resultsNORMAL'; + } + var rankTallyTimer:Null = null; var clearPercentTarget:Int = 100; var clearPercentLerp:Int = 0; diff --git a/source/funkin/play/scoring/Scoring.hx b/source/funkin/play/scoring/Scoring.hx index 02e5750bcb..6c9f9bd979 100644 --- a/source/funkin/play/scoring/Scoring.hx +++ b/source/funkin/play/scoring/Scoring.hx @@ -556,40 +556,6 @@ enum abstract ScoringRank(String) } } - public function getMusicPath():String - { - switch (abstract) - { - case PERFECT_GOLD: - return 'resultsPERFECT'; - case PERFECT: - return 'resultsPERFECT'; - case EXCELLENT: - return 'resultsEXCELLENT'; - case GREAT: - return 'resultsNORMAL'; - case GOOD: - return 'resultsNORMAL'; - case SHIT: - return 'resultsSHIT'; - default: - return 'resultsNORMAL'; - } - } - - public function hasMusicIntro():Bool - { - switch (abstract) - { - case EXCELLENT: - return true; - case SHIT: - return true; - default: - return false; - } - } - public function getFreeplayRankIconAsset():String { switch (abstract) diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index b1528d906b..3ae0361ce0 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -386,6 +386,7 @@ class FreeplayDJ extends FlxAtlasSprite } else { + FlxG.log.warn("Freeplay character does not have 'charSelect' animation!"); currentState = Confirm; // Call this immediately; otherwise, we get locked out of Character Select. onCharSelectComplete(); diff --git a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx index d4dd7aaa48..09d9fd6647 100644 --- a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx +++ b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx @@ -119,6 +119,27 @@ class PlayableCharacter implements IRegistryEntry } } + public function getResultsMusicPath(rank:ScoringRank):String + { + switch (rank) + { + case PERFECT_GOLD: + return _data?.results?.music?.PERFECT_GOLD ?? "resultsPERFECT"; + case PERFECT: + return _data?.results?.music?.PERFECT ?? "resultsPERFECT"; + case EXCELLENT: + return _data?.results?.music?.EXCELLENT ?? "resultsEXCELLENT"; + case GREAT: + return _data?.results?.music?.GREAT ?? "resultsNORMAL"; + case GOOD: + return _data?.results?.music?.GOOD ?? "resultsNORMAL"; + case SHIT: + return _data?.results?.music?.SHIT ?? "resultsSHIT"; + default: + return _data?.results?.music?.GOOD ?? "resultsNORMAL"; + } + } + /** * Returns whether this character is unlocked. */ From b37ebf1aacfb6a655a40f81b9f5bda068fd52bb1 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 3 Sep 2024 23:57:01 -0400 Subject: [PATCH 345/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 6857c7c899..0250e67186 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 6857c7c8999f02b595098fc36a2e1026cc0a22dd +Subproject commit 0250e67186915094244615fe10db549ba7bc59d7 From 0a6350113e81ff935c991b2bd9b4703e348cc043 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 26 Aug 2024 21:24:46 -0400 Subject: [PATCH 346/469] Fix some issues with Blazin --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 0250e67186..8156c2d4b3 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0250e67186915094244615fe10db549ba7bc59d7 +Subproject commit 8156c2d4b356a9e4b12a0275a82e58c84b65c4c3 From 3a2ca6b52506d64df01a0e42e3cadc8801c4fa46 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 26 Aug 2024 21:25:04 -0400 Subject: [PATCH 347/469] Code changes to fix Blazin' --- .../funkin/graphics/adobeanimate/FlxAtlasSprite.hx | 6 ++++++ .../funkin/play/character/AnimateAtlasCharacter.hx | 6 +++++- source/funkin/play/components/PopUpStuff.hx | 14 ++++++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index c4eaaff50b..e187c78083 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -56,6 +56,12 @@ class FlxAtlasSprite extends FlxAnimate throw 'Null path specified for FlxAtlasSprite!'; } + // Validate asset path. + if (!Assets.exists('${path}/Animation.json')) + { + throw 'FlxAtlasSprite does not have an Animation.json file at the specified path (${path})'; + } + super(x, y, path, settings); if (this.anim.stageInstance == null) diff --git a/source/funkin/play/character/AnimateAtlasCharacter.hx b/source/funkin/play/character/AnimateAtlasCharacter.hx index 32a4e765ca..5cc603429c 100644 --- a/source/funkin/play/character/AnimateAtlasCharacter.hx +++ b/source/funkin/play/character/AnimateAtlasCharacter.hx @@ -129,7 +129,11 @@ class AnimateAtlasCharacter extends BaseCharacter { trace('[ATLASCHAR] Loading sprite atlas for ${characterId}.'); - var sprite:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas(_data.assetPath)); + var animLibrary:String = Paths.getLibrary(_data.assetPath); + var animPath:String = Paths.stripLibrary(_data.assetPath); + var assetPath:String = Paths.animateAtlas(animPath, animLibrary); + + var sprite:FlxAtlasSprite = new FlxAtlasSprite(0, 0, assetPath); // sprite.onAnimationComplete.removeAll(); sprite.onAnimationComplete.add(this.onAnimationFinished); diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index 911c3578c0..a02291e4e2 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -21,6 +21,12 @@ class PopUpStuff extends FlxTypedGroup */ var noteStyle:NoteStyle; + /** + * Offsets that are applied to all elements, independent of the note style. + * Used to allow scripts to reposition the elements. + */ + var offsets:Array = [0, 0]; + override public function new(noteStyle:NoteStyle) { super(); @@ -42,9 +48,11 @@ class PopUpStuff extends FlxTypedGroup rating.y = (FlxG.camera.height * 0.45 - 60); rating.y -= rating.height / 2; - var offsets = noteStyle.getJudgementSpriteOffsets(daRating); rating.x += offsets[0]; rating.y += offsets[1]; + var styleOffsets = noteStyle.getJudgementSpriteOffsets(daRating); + rating.x += styleOffsets[0]; + rating.y += styleOffsets[1]; rating.acceleration.y = 550; rating.velocity.y -= FlxG.random.int(140, 175); @@ -90,9 +98,11 @@ class PopUpStuff extends FlxTypedGroup trace('numScore($daLoop) = ${numScore.x}'); numScore.y = (FlxG.camera.height * 0.44); - var offsets = noteStyle.getComboNumSpriteOffsets(digit); numScore.x += offsets[0]; numScore.y += offsets[1]; + var styleOffsets = noteStyle.getComboNumSpriteOffsets(digit); + numScore.x += styleOffsets[0]; + numScore.y += styleOffsets[1]; numScore.acceleration.y = FlxG.random.int(250, 300); numScore.velocity.y -= FlxG.random.int(130, 150); From 24096bd319c10aa150c9a8fcd578fbb812e14045 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 4 Sep 2024 10:58:18 -0400 Subject: [PATCH 348/469] Fixed positioning in Blazin'. --- assets | 2 +- source/funkin/audio/visualize/ABotVis.hx | 4 +++- source/funkin/play/character/AnimateAtlasCharacter.hx | 2 +- source/funkin/ui/charSelect/CharSelectGF.hx | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/assets b/assets index 8156c2d4b3..bbf8c7d53b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8156c2d4b356a9e4b12a0275a82e58c84b65c4c3 +Subproject commit bbf8c7d53b7560c7440efd4fb8b1d5cfa17e56b9 diff --git a/source/funkin/audio/visualize/ABotVis.hx b/source/funkin/audio/visualize/ABotVis.hx index a6ad0570eb..4d2243b7c2 100644 --- a/source/funkin/audio/visualize/ABotVis.hx +++ b/source/funkin/audio/visualize/ABotVis.hx @@ -102,7 +102,9 @@ class ABotVis extends FlxTypedSpriteGroup var animFrame:Int = Math.round(levels[i].value * 5); #if desktop - animFrame = Math.round(animFrame * FlxG.sound.volume); + // Web version scales with the Flixel volume level. + // This line brings platform parity but looks worse. + // animFrame = Math.round(animFrame * FlxG.sound.volume); #end animFrame = Math.floor(Math.min(5, animFrame)); diff --git a/source/funkin/play/character/AnimateAtlasCharacter.hx b/source/funkin/play/character/AnimateAtlasCharacter.hx index 5cc603429c..f432d08a05 100644 --- a/source/funkin/play/character/AnimateAtlasCharacter.hx +++ b/source/funkin/play/character/AnimateAtlasCharacter.hx @@ -122,7 +122,7 @@ class AnimateAtlasCharacter extends BaseCharacter */ public override function isAnimationFinished():Bool { - return mainSprite.isAnimationFinished(); + return mainSprite?.isAnimationFinished() ?? false; } function loadAtlasSprite():FlxAtlasSprite diff --git a/source/funkin/ui/charSelect/CharSelectGF.hx b/source/funkin/ui/charSelect/CharSelectGF.hx index ea7d414487..a990db8178 100644 --- a/source/funkin/ui/charSelect/CharSelectGF.hx +++ b/source/funkin/ui/charSelect/CharSelectGF.hx @@ -109,7 +109,9 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass var animFrame:Int = Math.round(levels[i].value * 12); #if desktop - animFrame = Math.round(animFrame * FlxG.sound.volume); + // Web version scales with the Flixel volume level. + // This line brings platform parity but looks worse. + // animFrame = Math.round(animFrame * FlxG.sound.volume); #end animFrame = Math.floor(Math.min(12, animFrame)); From def2b64a417470322a8b26e22734258795270555 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 4 Sep 2024 20:08:38 +0200 Subject: [PATCH 349/469] Add lock bizz --- assets | 2 +- project/lib/harfbuzz | 1 + project/lib/hashlink | 1 + project/lib/jpeg | 1 + project/lib/lzma | 1 + project/lib/mbedtls | 1 + project/lib/mojoal | 1 + project/lib/neko | 1 + project/lib/ogg | 1 + project/lib/openal | 1 + project/lib/pixman | 1 + project/lib/png | 1 + project/lib/sdl | 1 + project/lib/tinyfiledialogs | 1 + project/lib/vorbis | 1 + project/lib/vpx | 1 + project/lib/webm | 1 + project/lib/zlib | 1 + .../ui/charSelect/CharSelectSubState.hx | 29 ++++++--------- source/funkin/ui/charSelect/Lock.hx | 35 +++++++++++++++++++ 20 files changed, 64 insertions(+), 19 deletions(-) create mode 160000 project/lib/harfbuzz create mode 160000 project/lib/hashlink create mode 160000 project/lib/jpeg create mode 160000 project/lib/lzma create mode 160000 project/lib/mbedtls create mode 160000 project/lib/mojoal create mode 160000 project/lib/neko create mode 160000 project/lib/ogg create mode 160000 project/lib/openal create mode 160000 project/lib/pixman create mode 160000 project/lib/png create mode 160000 project/lib/sdl create mode 160000 project/lib/tinyfiledialogs create mode 160000 project/lib/vorbis create mode 160000 project/lib/vpx create mode 160000 project/lib/webm create mode 160000 project/lib/zlib create mode 100644 source/funkin/ui/charSelect/Lock.hx diff --git a/assets b/assets index 5a0c8da321..74bd0131b7 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5a0c8da32111571648b8fd07deabe2f78ae99eaa +Subproject commit 74bd0131b705e87f9704c54a44d64579153ab4af diff --git a/project/lib/harfbuzz b/project/lib/harfbuzz new file mode 160000 index 0000000000..2ba0b9ee94 --- /dev/null +++ b/project/lib/harfbuzz @@ -0,0 +1 @@ +Subproject commit 2ba0b9ee94e4fdea5c3e9d20fa73eb893c0bb95e diff --git a/project/lib/hashlink b/project/lib/hashlink new file mode 160000 index 0000000000..9888913ed2 --- /dev/null +++ b/project/lib/hashlink @@ -0,0 +1 @@ +Subproject commit 9888913ed2e1eef9fc03085100c92bd44985c781 diff --git a/project/lib/jpeg b/project/lib/jpeg new file mode 160000 index 0000000000..5cf7960678 --- /dev/null +++ b/project/lib/jpeg @@ -0,0 +1 @@ +Subproject commit 5cf79606784f0616590e6a3a7fdf17ad32a6df9b diff --git a/project/lib/lzma b/project/lib/lzma new file mode 160000 index 0000000000..5748ce6ad6 --- /dev/null +++ b/project/lib/lzma @@ -0,0 +1 @@ +Subproject commit 5748ce6ad668bb10e1a1fe322b37245d0053e71a diff --git a/project/lib/mbedtls b/project/lib/mbedtls new file mode 160000 index 0000000000..ab0af45d11 --- /dev/null +++ b/project/lib/mbedtls @@ -0,0 +1 @@ +Subproject commit ab0af45d11db04bd6e42ccfabe1b8d8deaae3243 diff --git a/project/lib/mojoal b/project/lib/mojoal new file mode 160000 index 0000000000..a9e2f30b04 --- /dev/null +++ b/project/lib/mojoal @@ -0,0 +1 @@ +Subproject commit a9e2f30b04f10df1912d06ff5a610edda4677ba5 diff --git a/project/lib/neko b/project/lib/neko new file mode 160000 index 0000000000..d8a3b46386 --- /dev/null +++ b/project/lib/neko @@ -0,0 +1 @@ +Subproject commit d8a3b463861779656a78c1a5e31c013580642185 diff --git a/project/lib/ogg b/project/lib/ogg new file mode 160000 index 0000000000..db5c7a49ce --- /dev/null +++ b/project/lib/ogg @@ -0,0 +1 @@ +Subproject commit db5c7a49ce7ebda47b15b78471e78fb7f2483e22 diff --git a/project/lib/openal b/project/lib/openal new file mode 160000 index 0000000000..326133368c --- /dev/null +++ b/project/lib/openal @@ -0,0 +1 @@ +Subproject commit 326133368c3d7149d0ab4a98fc96f654553094a7 diff --git a/project/lib/pixman b/project/lib/pixman new file mode 160000 index 0000000000..0cb4fbe324 --- /dev/null +++ b/project/lib/pixman @@ -0,0 +1 @@ +Subproject commit 0cb4fbe3241988a5d6b51d3551998ec6837c75a1 diff --git a/project/lib/png b/project/lib/png new file mode 160000 index 0000000000..e4a31f024b --- /dev/null +++ b/project/lib/png @@ -0,0 +1 @@ +Subproject commit e4a31f024b6158aaaf55a43502f574d5f5d1c894 diff --git a/project/lib/sdl b/project/lib/sdl new file mode 160000 index 0000000000..8ea38ebedc --- /dev/null +++ b/project/lib/sdl @@ -0,0 +1 @@ +Subproject commit 8ea38ebedc846faa547693f60bcd213b21370982 diff --git a/project/lib/tinyfiledialogs b/project/lib/tinyfiledialogs new file mode 160000 index 0000000000..4927ea8f44 --- /dev/null +++ b/project/lib/tinyfiledialogs @@ -0,0 +1 @@ +Subproject commit 4927ea8f44fa0151dc8e0a2b79a13dbd7827cc48 diff --git a/project/lib/vorbis b/project/lib/vorbis new file mode 160000 index 0000000000..84c023699c --- /dev/null +++ b/project/lib/vorbis @@ -0,0 +1 @@ +Subproject commit 84c023699cdf023a32fa4ded32019f194afcdad0 diff --git a/project/lib/vpx b/project/lib/vpx new file mode 160000 index 0000000000..fbf63dff1f --- /dev/null +++ b/project/lib/vpx @@ -0,0 +1 @@ +Subproject commit fbf63dff1f528d44f24bd662abb89fd01a4a1c25 diff --git a/project/lib/webm b/project/lib/webm new file mode 160000 index 0000000000..6c1e511f48 --- /dev/null +++ b/project/lib/webm @@ -0,0 +1 @@ +Subproject commit 6c1e511f487eaea026d94d0c6494a617155b5cfb diff --git a/project/lib/zlib b/project/lib/zlib new file mode 160000 index 0000000000..545f194963 --- /dev/null +++ b/project/lib/zlib @@ -0,0 +1 @@ +Subproject commit 545f1949635949159fa6282e81712aec32b5d4f1 diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index c2850916ac..1a27d03927 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -331,21 +331,8 @@ class CharSelectSubState extends MusicBeatSubState } else { - var temp:FlxSprite = new FlxSprite(); + var temp:Lock = new Lock(0, 0, i); temp.ID = 1; - temp.frames = Paths.getSparrowAtlas("charSelect/locks"); - - var lockIndex:Int = i + 1; - - if (i == 3) lockIndex = 3; - - if (i >= 4) lockIndex = i - 2; - - temp.animation.addByIndices("idle", "LOCK FULL " + lockIndex + " instance 1", [0], "", 24); - temp.animation.addByIndices("selected", "LOCK FULL " + lockIndex + " instance 1", [3, 4, 5], "", 24, false); - temp.animation.addByIndices("clicked", "LOCK FULL " + lockIndex + " instance 1", [9, 10, 11, 12, 13, 14, 15], "", 24, false); - - temp.animation.play("idle"); grpIcons.add(temp); } @@ -569,6 +556,11 @@ class CharSelectSubState extends MusicBeatSubState function spamOnStep():Void { + if (FlxG.keys.justPressed.B) + { + cursorY = 3; + cursorX = 3; + } if (spamUp || spamDown || spamLeft || spamRight) { // selectSound.changePitchBySemitone(1); @@ -605,19 +597,20 @@ class CharSelectSubState extends MusicBeatSubState switch (member.ID) { case 1: + var lock:Lock = cast member; if (index == getCurrentSelected()) { - switch (member.animation.curAnim.name) + switch (lock.getCurrentAnimation()) { case "idle": - member.animation.play("selected"); + lock.playAnimation("selected"); case "selected" | "clicked": - if (controls.ACCEPT) member.animation.play("clicked", true); + if (controls.ACCEPT) lock.playAnimation((FlxG.keys.pressed.CONTROL) ? "unlock" : "clicked", true); } } else { - member.animation.play("idle"); + lock.playAnimation("idle"); } case 0: var memb:PixelatedIcon = cast member; diff --git a/source/funkin/ui/charSelect/Lock.hx b/source/funkin/ui/charSelect/Lock.hx new file mode 100644 index 0000000000..42e5d63f46 --- /dev/null +++ b/source/funkin/ui/charSelect/Lock.hx @@ -0,0 +1,35 @@ +package funkin.ui.charSelect; + +import flixel.util.FlxColor; +import flxanimate.effects.FlxTint; +import funkin.graphics.adobeanimate.FlxAtlasSprite; + +class Lock extends FlxAtlasSprite +{ + var colors:Array = [ + 0x31F2A5, 0x20ECCD, 0x24D9E8, + 0x20ECCD, 0x20C8D4, 0x209BDD, + 0x209BDD, 0x2362C9, 0x243FB9 + ]; // lock colors, in a nx3 matrix format + + public function new(x:Float = 0, y:Float = 0, index:Int) + { + super(x, y, Paths.animateAtlas("charSelect/lock")); + + var tint:FlxTint = new FlxTint(colors[index], 1); + + var arr:Array = ["lock", "lock top 1", "lock top 2", "lock top 3", "lock base fuck it"]; + + onAnimationComplete.add((_) -> if (_ == "unlock") playAnimation("idle")); + var func = function(name) { + var symbol = anim.symbolDictionary[name]; + if (symbol != null && symbol.timeline.get("color") != null) symbol.timeline.get("color").get(0).colorEffect = tint; + } + for (symbol in arr) + { + func(symbol); + } + + playAnimation("idle"); + } +} From 33aaabe190ce4469a221f2113d62cc1a77aac648 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 5 Sep 2024 01:22:45 -0400 Subject: [PATCH 350/469] lil console command helper for camera zoom --- source/funkin/play/PlayState.hx | 65 ++++++++++++++++----------- source/funkin/ui/MusicBeatSubState.hx | 4 ++ 2 files changed, 43 insertions(+), 26 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 414c833ff0..0b2b8846d3 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -503,6 +503,12 @@ class PlayState extends MusicBeatSubState */ public var camGame:FlxCamera; + /** + * Simple helper debug variable, to be able to move the camera around for debug purposes + * without worrying about the camera tweening back to the follow point. + */ + public var debugUnbindCameraZoom:Bool = false; + /** * The camera which contains, and controls visibility of, a video cutscene, dialogue, pause menu and sticker transition. */ @@ -992,7 +998,7 @@ class PlayState extends MusicBeatSubState { cameraBopMultiplier = FlxMath.lerp(1.0, cameraBopMultiplier, 0.95); // Lerp bop multiplier back to 1.0x var zoomPlusBop = currentCameraZoom * cameraBopMultiplier; // Apply camera bop multiplier. - FlxG.camera.zoom = zoomPlusBop; // Actually apply the zoom to the camera. + if (!debugUnbindCameraZoom) FlxG.camera.zoom = zoomPlusBop; // Actually apply the zoom to the camera. camHUD.zoom = FlxMath.lerp(defaultHUDCameraZoom, camHUD.zoom, 0.95); } @@ -1458,6 +1464,13 @@ class PlayState extends MusicBeatSubState super.destroy(); } + public override function initConsoleHelpers():Void + { + FlxG.console.registerFunction("debugUnbindCameraZoom", () -> { + debugUnbindCameraZoom = !debugUnbindCameraZoom; + }); + }; + /** * Initializes the game and HUD cameras. */ @@ -2224,10 +2237,10 @@ class PlayState extends MusicBeatSubState // Skip handling the miss in botplay! if (!isBotPlayMode) { - // Judge the miss. - // NOTE: This is what handles the scoring. - trace('Missed note! ${note.noteData}'); - onNoteMiss(note, event.playSound, event.healthChange); + // Judge the miss. + // NOTE: This is what handles the scoring. + trace('Missed note! ${note.noteData}'); + onNoteMiss(note, event.playSound, event.healthChange); } note.handledMiss = true; @@ -2344,31 +2357,31 @@ class PlayState extends MusicBeatSubState playerStrumline.playPress(input.noteDirection); trace('PENALTY Score: ${songScore}'); } - else if (notesInDirection.length == 0) - { - // Press a key with no penalty. + else if (notesInDirection.length == 0) + { + // Press a key with no penalty. - // Play the strumline animation. - playerStrumline.playPress(input.noteDirection); - trace('NO PENALTY Score: ${songScore}'); - } - else - { - // Choose the first note, deprioritizing low priority notes. - var targetNote:Null = notesInDirection.find((note) -> !note.lowPriority); - if (targetNote == null) targetNote = notesInDirection[0]; - if (targetNote == null) continue; + // Play the strumline animation. + playerStrumline.playPress(input.noteDirection); + trace('NO PENALTY Score: ${songScore}'); + } + else + { + // Choose the first note, deprioritizing low priority notes. + var targetNote:Null = notesInDirection.find((note) -> !note.lowPriority); + if (targetNote == null) targetNote = notesInDirection[0]; + if (targetNote == null) continue; - // Judge and hit the note. - trace('Hit note! ${targetNote.noteData}'); - goodNoteHit(targetNote, input); - trace('Score: ${songScore}'); + // Judge and hit the note. + trace('Hit note! ${targetNote.noteData}'); + goodNoteHit(targetNote, input); + trace('Score: ${songScore}'); - notesInDirection.remove(targetNote); + notesInDirection.remove(targetNote); - // Play the strumline animation. - playerStrumline.playConfirm(input.noteDirection); - } + // Play the strumline animation. + playerStrumline.playConfirm(input.noteDirection); + } } while (inputReleaseQueue.length > 0) diff --git a/source/funkin/ui/MusicBeatSubState.hx b/source/funkin/ui/MusicBeatSubState.hx index 5c40b37bcb..02cebeb450 100644 --- a/source/funkin/ui/MusicBeatSubState.hx +++ b/source/funkin/ui/MusicBeatSubState.hx @@ -56,6 +56,8 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler Conductor.beatHit.add(this.beatHit); Conductor.stepHit.add(this.stepHit); + + initConsoleHelpers(); } public override function destroy():Void @@ -79,6 +81,8 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler dispatchEvent(new UpdateScriptEvent(elapsed)); } + public function initConsoleHelpers():Void {} + function reloadAssets() { PolymodHandler.forceReloadAssets(); From 239b9b0419fdbb273c0571f0426308df6be58e9f Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 5 Sep 2024 02:10:51 -0400 Subject: [PATCH 351/469] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index f1c74ef595..a9f81e916f 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit f1c74ef595a29d11544492e0dc907ba9a21d7c4f +Subproject commit a9f81e916f673c5084851b56ee37a2952e9f9587 From 34d13c61de236682535bab4b04bfca5c6744bf1d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sun, 24 Sep 2023 00:53:53 -0400 Subject: [PATCH 352/469] Cleanup character unlock impl and queue it for after Story Mode is completed. --- source/funkin/InitState.hx | 2 +- .../data/freeplay/player/PlayerRegistry.hx | 24 +++- source/funkin/play/ResultState.hx | 74 +++++++--- source/funkin/play/components/HealthIcon.hx | 32 +++-- .../ui/charSelect/CharacterUnlockState.hx | 128 ++++++++++++++++++ source/funkin/ui/mainmenu/MainMenuState.hx | 14 +- 6 files changed, 240 insertions(+), 34 deletions(-) create mode 100644 source/funkin/ui/charSelect/CharacterUnlockState.hx diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index a203a8d777..f71de00f4a 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -225,7 +225,7 @@ class InitState extends FlxState // -DRESULTS FlxG.switchState(() -> new funkin.play.ResultState( { - storyMode: false, + storyMode: true, title: "Cum Song Erect by Kawai Sprite", songId: "cum", characterId: "pico-playable", diff --git a/source/funkin/data/freeplay/player/PlayerRegistry.hx b/source/funkin/data/freeplay/player/PlayerRegistry.hx index c0a15ed1c3..76b1c25c15 100644 --- a/source/funkin/data/freeplay/player/PlayerRegistry.hx +++ b/source/funkin/data/freeplay/player/PlayerRegistry.hx @@ -71,7 +71,7 @@ class PlayerRegistry extends BaseRegistry public function hasNewCharacter():Bool { - var characters = Save.instance.charactersSeen.clone(); + var charactersSeen = Save.instance.charactersSeen.clone(); for (charId in listEntryIds()) { @@ -79,7 +79,7 @@ class PlayerRegistry extends BaseRegistry if (player == null) continue; if (!player.isUnlocked()) continue; - if (characters.contains(charId)) continue; + if (charactersSeen.contains(charId)) continue; // This character is unlocked but we haven't seen them in Freeplay yet. return true; @@ -89,6 +89,26 @@ class PlayerRegistry extends BaseRegistry return false; } + public function listNewCharacters():Array + { + var charactersSeen = Save.instance.charactersSeen.clone(); + var result = []; + + for (charId in listEntryIds()) + { + var player = fetchEntry(charId); + if (player == null) continue; + + if (!player.isUnlocked()) continue; + if (charactersSeen.contains(charId)) continue; + + // This character is unlocked but we haven't seen them in Freeplay yet. + result.push(charId); + } + + return result; + } + /** * Get the playable character associated with a given stage character. * @param characterId The stage character ID. diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index ec6069ad4c..2878248e2a 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -731,42 +731,59 @@ class ResultState extends MusicBeatSubState } }); } + + // Determining the target state(s) to go to. + // Default to main menu because that's better than `null`. + var targetState:flixel.FlxState = new funkin.ui.mainmenu.MainMenuState(); + var shouldTween = false; + if (params.storyMode) { - openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new StoryMenuState(sticker))); + if (PlayerRegistry.instance.hasNewCharacter()) + { + targetState = new StoryMenuState(null); + + var newCharacters = PlayerRegistry.instance.listNewCharacters(); + + for (charId in newCharacters) + { + shouldTween = true; + // This works recursively, ehe! + targetState = new funkin.ui.charSelect.CharacterUnlockState(charId, targetState); + } + } + else + { + targetState = new funkin.ui.transition.StickerSubState(null, (sticker) -> new StoryMenuState(sticker)); + } } else { - var rigged:Bool = true; - if (rank > Scoring.calculateRank(params?.prevScoreData)) // if (rigged) + if (rank > Scoring.calculateRank(params?.prevScoreData)) { trace('THE RANK IS Higher.....'); - FlxTween.tween(rankBg, {alpha: 1}, 0.5, + shouldTween = true; + targetState = FreeplayState.build( { - ease: FlxEase.expoOut, - onComplete: function(_) { - FlxG.switchState(FreeplayState.build( + { + character: playerCharacterId ?? "bf", + fromResults: { - { - character: playerCharacterId ?? "bf", - fromResults: - { - oldRank: Scoring.calculateRank(params?.prevScoreData), - newRank: rank, - songId: params.songId, - difficultyId: params.difficultyId, - playRankAnim: true - } - } - })); + oldRank: Scoring.calculateRank(params?.prevScoreData), + newRank: rank, + songId: params.songId, + difficultyId: params.difficultyId, + playRankAnim: true + } } }); } else { trace('rank is lower...... and/or equal'); - openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> FreeplayState.build( + + targetState = new funkin.ui.transition.StickerSubState(null, (sticker) -> FreeplayState.build( { { fromResults: @@ -778,9 +795,24 @@ class ResultState extends MusicBeatSubState difficultyId: params.difficultyId } } - }, sticker))); + }, sticker)); } } + + if (shouldTween) + { + FlxTween.tween(rankBg, {alpha: 1}, 0.5, + { + ease: FlxEase.expoOut, + onComplete: function(_) { + FlxG.switchState(targetState); + } + }); + } + else + { + FlxG.switchState(targetState); + } } super.update(elapsed); diff --git a/source/funkin/play/components/HealthIcon.hx b/source/funkin/play/components/HealthIcon.hx index 0e24d73fb2..c11850b2a8 100644 --- a/source/funkin/play/components/HealthIcon.hx +++ b/source/funkin/play/components/HealthIcon.hx @@ -49,7 +49,7 @@ class HealthIcon extends FunkinSprite * this value allows you to set a relative scale for the icon. * @default 1x scale = 150px width and height. */ - public var size:FlxPoint = new FlxPoint(1, 1); + public var size:FlxPoint; /** * Apply the "bop" animation once every X steps. @@ -120,11 +120,15 @@ class HealthIcon extends FunkinSprite { super(0, 0); this.playerId = playerId; + this.size = new FlxCallbackPoint(onSetSize); this.scrollFactor.set(); - + size.set(1.0, 1.0); this.characterId = char; + } - initTargetSize(); + function onSetSize(value:FlxPoint):Void + { + snapToTargetSize(); } function set_characterId(value:Null):Null @@ -243,6 +247,22 @@ class HealthIcon extends FunkinSprite this.updateHitbox(); } + /* + * Immediately snap the health icon to its target size without lerping. + */ + public function snapToTargetSize():Void + { + if (this.width > this.height) + { + setGraphicSize(Std.int(HEALTH_ICON_SIZE * this.size.x), 0); + } + else + { + setGraphicSize(0, Std.int(HEALTH_ICON_SIZE * this.size.y)); + } + updateHitbox(); + } + /** * Update the position (and status) of the health icon. */ @@ -301,12 +321,6 @@ class HealthIcon extends FunkinSprite } } - inline function initTargetSize():Void - { - setGraphicSize(HEALTH_ICON_SIZE); - updateHitbox(); - } - function updateHealthIcon(health:Float):Void { // We want to efficiently handle animation playback diff --git a/source/funkin/ui/charSelect/CharacterUnlockState.hx b/source/funkin/ui/charSelect/CharacterUnlockState.hx new file mode 100644 index 0000000000..25cd1baf0b --- /dev/null +++ b/source/funkin/ui/charSelect/CharacterUnlockState.hx @@ -0,0 +1,128 @@ +package funkin.ui.charSelect; + +import flixel.FlxSprite; +import flixel.FlxState; +import flixel.group.FlxSpriteGroup; +import flixel.text.FlxText; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; +import funkin.play.character.CharacterData; +import funkin.play.character.CharacterData.CharacterDataParser; +import funkin.play.components.HealthIcon; +import funkin.ui.freeplay.charselect.PlayableCharacter; +import funkin.data.freeplay.player.PlayerData; +import funkin.data.freeplay.player.PlayerRegistry; +import funkin.ui.mainmenu.MainMenuState; + +using flixel.util.FlxSpriteUtil; + +/** + * When you want the player to unlock a character, call `CharacterUnlockState.unlock(characterName)`. + * It handles both the act of unlocking the character and displaying the dialog. + */ +class CharacterUnlockState extends MusicBeatState +{ + public var targetCharacterId:String = ""; + public var targetCharacterData:Null; + + var nextState:FlxState; + + static final DIALOG_BG_COLOR:FlxColor = 0xFF000000; // Iconic + static final DIALOG_COLOR:FlxColor = 0xFF4344F6; // Iconic + static final DIALOG_FONT_COLOR:FlxColor = 0xFFFFFFFF; // Iconic + + var busy:Bool = false; + + public function new(targetPlayableCharacter:String, ?nextState:FlxState) + { + super(); + + this.targetCharacterId = targetPlayableCharacter; + this.targetCharacterData = PlayerRegistry.instance.fetchEntry(targetCharacterId); + this.nextState = nextState == null ? new MainMenuState() : nextState; + } + + override function create():Void + { + super.create(); + + handleMusic(); + + bgColor = DIALOG_BG_COLOR; + + var dialogContainer:FlxSpriteGroup = new FlxSpriteGroup(); + add(dialogContainer); + + // Build the graphic for the text... + var charName:String = targetCharacterData != null ? targetCharacterData.getName() : targetCharacterId.toTitleCase(); + // var dialogText:FlxText = new FlxText(0, 0, 0, 'You can now play as $charName.\n\nCheck it out in Freeplay!'); + var dialogText:FlxText = new FlxText(0, 0, 0, 'You can now play as $charName.'); + dialogText.setFormat("VCR OSD Mono", 32, DIALOG_FONT_COLOR, LEFT); + + // THEN we can size the dialog to match... + var dialogBG:FlxSprite = new FlxSprite(0, 0); + dialogBG.makeGraphic(Std.int(dialogText.width + 32), Std.int(dialogText.height + 32), FlxColor.TRANSPARENT); + dialogBG.drawRoundRect(0, 0, dialogBG.width, dialogBG.height, 16, 16, DIALOG_COLOR); + dialogContainer.add(dialogBG); + + dialogBG.screenCenter(XY); + + // THEN we can position the text inside that. + dialogText.x = dialogBG.x + 16; + dialogText.y = dialogBG.y + 16; + dialogContainer.add(dialogText); + + // HealthIcon handles getting the right frames for us, + // but it has a bunch of overhead in it that makes it gross to work with outside the health bar. + var healthIconCharacterId = targetCharacterData.getOwnedCharacterIds()[0]; + var baseCharacter = CharacterDataParser.fetchCharacter(healthIconCharacterId); + var healthIcon:HealthIcon = new HealthIcon(healthIconCharacterId); + @:privateAccess + healthIcon.configure(baseCharacter._data.healthIcon); + healthIcon.autoUpdate = false; + healthIcon.bopEvery = 0; // You can increase this number later once the animation is done. + healthIcon.size.set(0.5, 0.5); + healthIcon.x = dialogBG.x + 390; + healthIcon.y = dialogBG.y + 6; + healthIcon.flipX = true; + healthIcon.snapToTargetSize(); + dialogContainer.add(healthIcon); + + dialogContainer.scale.set(0, 0); + FlxTween.num(0.0, 1.0, 0.75, + { + ease: FlxEase.elasticOut, + }, function(curScale) { + dialogContainer.scale.set(curScale, curScale); + healthIcon.size.set(0.5 * curScale, 0.5 * curScale); + }); + + // performUnlock(); + } + + function handleMusic():Void + { + FlxG.sound.music.stop(); + FlxG.sound.play(Paths.sound('confirmMenu')); + } + + override function update(elapsed:Float):Void + { + super.update(elapsed); + + if (controls.ACCEPT || controls.BACK && !busy) + { + busy = true; + startClose(); + } + } + + function startClose():Void + { + // Fade to black, then switch state. + FlxG.camera.fade(FlxColor.BLACK, 0.75, false, () -> { + FlxG.switchState(nextState); + }); + } +} diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index d2a9f046a8..19dc0d687f 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -344,8 +344,8 @@ class MainMenuState extends MusicBeatState } } - #if FEATURE_DEBUG_FUNCTIONS // Open the debug menu, defaults to ` / ~ + // This includes stuff like the Chart Editor, so it should be present on all builds. if (controls.DEBUG_MENU) { persistentUpdate = false; @@ -353,6 +353,18 @@ class MainMenuState extends MusicBeatState FlxG.state.openSubState(new DebugMenuSubState()); } + #if FEATURE_DEBUG_FUNCTIONS + // Ctrl+Alt+Shift+P = Character Unlock screen + // Ctrl+Alt+Shift+W = Meet requirements for Pico Unlock + // Ctrl+Alt+Shift+L = Revoke requirements for Pico Unlock + // Ctrl+Alt+Shift+R = Score/Rank conflict test + // Ctrl+Alt+Shift+E = Dump save data + + if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.P) + { + FlxG.switchState(() -> new funkin.ui.charSelect.CharacterUnlockState('pico')); + } + if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.W) { FunkinSound.playOnce(Paths.sound('confirmMenu')); From 677357ba37d1aaba3e3154ed70dd97e786353194 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Fri, 6 Sep 2024 02:55:47 +0200 Subject: [PATCH 353/469] wip sequence --- source/funkin/audio/FunkinSound.hx | 4 +- .../funkin/ui/charSelect/CharSelectPlayer.hx | 2 +- .../ui/charSelect/CharSelectSubState.hx | 160 ++++++++++++------ 3 files changed, 111 insertions(+), 55 deletions(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 9cb131c912..b33126998e 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -388,7 +388,7 @@ class FunkinSound extends FlxSound implements ICloneable } else { - var music = FunkinSound.load(pathToUse, params?.startingVolume ?? 1.0, params.loop ?? true, false, true); + var music = FunkinSound.load(pathToUse, params?.startingVolume ?? 1.0, params.loop ?? true, false, true, params.onComplete); if (music != null) { FlxG.sound.music = music; @@ -396,6 +396,8 @@ class FunkinSound extends FlxSound implements ICloneable // Prevent repeat update() and onFocus() calls. FlxG.sound.list.remove(FlxG.sound.music); + if (FlxG.sound.music != null && params.onLoad != null) params.onLoad(); + return true; } else diff --git a/source/funkin/ui/charSelect/CharSelectPlayer.hx b/source/funkin/ui/charSelect/CharSelectPlayer.hx index 07c82d038d..0ac74f98e5 100644 --- a/source/funkin/ui/charSelect/CharSelectPlayer.hx +++ b/source/funkin/ui/charSelect/CharSelectPlayer.hx @@ -21,7 +21,7 @@ class CharSelectPlayer extends FlxAtlasSprite if (hasAnimation("slidein idle point")) playAnimation("slidein idle point", true, false, false); else playAnimation("idle", true, false, true); - case "slidein idle point": + case "slidein idle point", "cannot select", "unlock": playAnimation("idle", true, false, true); } }); diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 1a27d03927..a2fe4a7dc0 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -26,6 +26,7 @@ import funkin.ui.PixelatedIcon; import funkin.util.MathUtil; import funkin.vis.dsp.SpectralAnalyzer; import openfl.display.BlendMode; +import funkin.save.Save; class CharSelectSubState extends MusicBeatSubState { @@ -69,8 +70,6 @@ class CharSelectSubState extends MusicBeatSubState var charSelectCam:FunkinCamera; - var introM:FunkinSound = null; - public function new() { super(); @@ -89,6 +88,8 @@ class CharSelectSubState extends MusicBeatSubState var playerData = player.getCharSelectData(); if (playerData == null) continue; + trace(player.isUnlocked()); + var targetPosition:Int = playerData.position ?? 0; while (availableChars.exists(targetPosition)) { @@ -104,38 +105,6 @@ class CharSelectSubState extends MusicBeatSubState { super.create(); - selectSound = new FunkinSound(); - selectSound.loadEmbedded(Paths.sound('CS_select')); - selectSound.pitch = 1; - selectSound.volume = 0.7; - FlxG.sound.defaultSoundGroup.add(selectSound); - - // playing it here to preload it. not doing this makes a super awkward pause at the end of the intro - // TODO: probably make an intro thing for funkinSound itself that preloads the next audio? - FunkinSound.playMusic('stayFunky', - { - startingVolume: 0, - overrideExisting: true, - restartTrack: true - }); - var introMusic:String = Paths.music('stayFunky/stayFunky-intro'); - introM = FunkinSound.load(introMusic, 1.0, false, true, true, () -> { - FunkinSound.playMusic('stayFunky', - { - startingVolume: 1, - overrideExisting: true, - restartTrack: true - }); - @:privateAccess - gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1); - #if desktop - // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 - // So we want to manually change it! - @:privateAccess - gfChill.analyzer.fftN = 512; - #end - }); - var bg:FlxSprite = new FlxSprite(-153, -140); bg.loadGraphic(Paths.image('charSelect/charSelectBG')); bg.scrollFactor.set(0.1, 0.1); @@ -174,15 +143,15 @@ class CharSelectSubState extends MusicBeatSubState gfChill = new CharSelectGF(); gfChill.switchGF("bf"); add(gfChill); - @:privateAccess - playerChill = new CharSelectPlayer(0, 0); - playerChill.switchChar("bf"); - add(playerChill); playerChillOut = new CharSelectPlayer(0, 0); playerChillOut.switchChar("bf"); add(playerChillOut); + playerChill = new CharSelectPlayer(0, 0); + playerChill.switchChar("bf"); + add(playerChill); + var speakers:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/charSelectSpeakers")); speakers.anim.play(""); speakers.scrollFactor.set(1.8, 1.8); @@ -289,7 +258,7 @@ class CharSelectSubState extends MusicBeatSubState FlxG.debugger.addTrackerProfile(new TrackerProfile(CharSelectSubState, ["curChar", "grpXSpread", "grpYSpread"])); FlxG.debugger.track(this); - FlxG.sound.playMusic(Paths.music('charSelect/charSelectMusic')); + // FlxG.sound.playMusic(Paths.music('charSelect/charSelectMusic')); camFollow = new FlxObject(0, 0, 1, 1); add(camFollow); @@ -301,6 +270,14 @@ class CharSelectSubState extends MusicBeatSubState temp.loadGraphic(Paths.image('charSelect/placement')); add(temp); temp.alpha = 0.0; + + selectSound = new FunkinSound(); + selectSound.loadEmbedded(Paths.sound('CS_select')); + selectSound.pitch = 1; + selectSound.volume = 0.7; + + FlxG.sound.defaultSoundGroup.add(selectSound); + Conductor.stepHit.add(spamOnStep); // FlxG.debugger.track(temp, "tempBG"); } @@ -309,6 +286,8 @@ class CharSelectSubState extends MusicBeatSubState var grpXSpread(default, set):Float = 107; var grpYSpread(default, set):Float = 127; + var nonLocks = []; + function initLocks():Void { grpIcons = new FlxSpriteGroup(); @@ -319,7 +298,7 @@ class CharSelectSubState extends MusicBeatSubState for (i in 0...9) { - if (availableChars.exists(i)) + if (availableChars.exists(i) && Save.instance.charactersSeen.contains(availableChars[i])) { var path:String = availableChars.get(i); var temp:PixelatedIcon = new PixelatedIcon(0, 0); @@ -331,9 +310,15 @@ class CharSelectSubState extends MusicBeatSubState } else { + if (availableChars.exists(i)) nonLocks.push(i); + var temp:Lock = new Lock(0, 0, i); temp.ID = 1; + temp.onAnimationComplete.add(function(anim) { + if (anim == "unlock") playerChill.playAnimation("unlock", true); + }); + grpIcons.add(temp); } } @@ -341,6 +326,78 @@ class CharSelectSubState extends MusicBeatSubState updateIconPositions(); grpIcons.scrollFactor.set(); + + // playing it here to preload it. not doing this makes a super awkward pause at the end of the intro + // TODO: probably make an intro thing for funkinSound itself that preloads the next audio? + FunkinSound.playMusic('stayFunky', + { + startingVolume: 0, + overrideExisting: true, + restartTrack: true, + }); + + unLock(); + } + + function unLock() + { + var index = nonLocks[0]; + + var copy = 3; + + var yThing = 0; + + while (index > copy) + { + yThing++; + copy += 3; + } + + var xThing = copy - index - 1; + + cursorY = 3 - yThing; + cursorX = 3 - xThing; + + selectTimer.start(1, function(_) { + var lock:Lock = cast grpIcons.group.members[index]; + + lock.playAnimation("unlock"); + + playerChill.visible = false; + playerChill.switchChar(availableChars[index]); + + playerChillOut.visible = true; + playerChillOut.playAnimation("death"); + playerChillOut.onAnimationComplete.addOnce((_) -> if (_ == "death") + { + playerChill.playAnimation("unlock"); + playerChill.onAnimationComplete.addOnce(function(_) { + nonLocks.shift(); + + if (nonLocks.length > 0) unLock(); + else + { + FunkinSound.playMusic('stayFunky', + { + startingVolume: 1, + overrideExisting: true, + restartTrack: true, + onLoad: function() { + @:privateAccess + gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1); + #if desktop + // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 + // So we want to manually change it! + @:privateAccess + gfChill.analyzer.fftN = 512; + #end + } + }); + } + }); + playerChill.visible = true; + }); + }); } function updateIconPositions() @@ -372,17 +429,6 @@ class CharSelectSubState extends MusicBeatSubState override public function update(elapsed:Float):Void { super.update(elapsed); - @:privateAccess - if (introM != null && !introM.paused && gfChill.analyzer == null) - { - gfChill.analyzer = new SpectralAnalyzer(introM._channel.__audioSource, 7, 0.1); - #if desktop - // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 - // So we want to manually change it! - @:privateAccess - gfChill.analyzer.fftN = 512; - #end - } Conductor.instance.update(); @@ -426,6 +472,8 @@ class CharSelectSubState extends MusicBeatSubState if (controls.UI_UP_P) { cursorY -= 1; + cursorDenied.visible = false; + holdTmrUp = 0; selectSound.play(true); @@ -433,18 +481,22 @@ class CharSelectSubState extends MusicBeatSubState if (controls.UI_DOWN_P) { cursorY += 1; + cursorDenied.visible = false; holdTmrDown = 0; selectSound.play(true); } if (controls.UI_LEFT_P) { cursorX -= 1; + cursorDenied.visible = false; + holdTmrLeft = 0; selectSound.play(true); } if (controls.UI_RIGHT_P) { cursorX += 1; + cursorDenied.visible = false; holdTmrRight = 0; selectSound.play(true); } @@ -466,7 +518,7 @@ class CharSelectSubState extends MusicBeatSubState cursorY = -1; } - if (availableChars.exists(getCurrentSelected())) + if (availableChars.exists(getCurrentSelected()) && Save.instance.charactersSeen.contains(availableChars[getCurrentSelected()])) { curChar = availableChars.get(getCurrentSelected()); @@ -567,6 +619,8 @@ class CharSelectSubState extends MusicBeatSubState if (selectSound.pitch > 5) selectSound.pitch = 5; selectSound.play(true); + cursorDenied.visible = false; + if (spamUp) { cursorY -= 1; @@ -621,7 +675,7 @@ class CharSelectSubState extends MusicBeatSubState memb.scale.set(2.6, 2.6); if (controls.ACCEPT) memb.animation.play("confirm"); - if (controls.BACK) + if (memb.animation.curAnim.name == "confirm" && controls.BACK) { memb.animation.play("confirm", false, true); member.animation.finishCallback = (_) -> { From 4cad56705ab5866d2b2a94224b08ebc7e63bc132 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 4 Sep 2024 20:08:38 +0200 Subject: [PATCH 354/469] Add lock bizz --- assets | 2 +- project/lib/harfbuzz | 1 + project/lib/hashlink | 1 + project/lib/jpeg | 1 + project/lib/lzma | 1 + project/lib/mbedtls | 1 + project/lib/mojoal | 1 + project/lib/neko | 1 + project/lib/ogg | 1 + project/lib/openal | 1 + project/lib/pixman | 1 + project/lib/png | 1 + project/lib/sdl | 1 + project/lib/tinyfiledialogs | 1 + project/lib/vorbis | 1 + project/lib/vpx | 1 + project/lib/webm | 1 + project/lib/zlib | 1 + .../ui/charSelect/CharSelectSubState.hx | 29 ++++++--------- source/funkin/ui/charSelect/Lock.hx | 35 +++++++++++++++++++ 20 files changed, 64 insertions(+), 19 deletions(-) create mode 160000 project/lib/harfbuzz create mode 160000 project/lib/hashlink create mode 160000 project/lib/jpeg create mode 160000 project/lib/lzma create mode 160000 project/lib/mbedtls create mode 160000 project/lib/mojoal create mode 160000 project/lib/neko create mode 160000 project/lib/ogg create mode 160000 project/lib/openal create mode 160000 project/lib/pixman create mode 160000 project/lib/png create mode 160000 project/lib/sdl create mode 160000 project/lib/tinyfiledialogs create mode 160000 project/lib/vorbis create mode 160000 project/lib/vpx create mode 160000 project/lib/webm create mode 160000 project/lib/zlib create mode 100644 source/funkin/ui/charSelect/Lock.hx diff --git a/assets b/assets index f1c74ef595..b0549bb2b4 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit f1c74ef595a29d11544492e0dc907ba9a21d7c4f +Subproject commit b0549bb2b4341e10502cc964df06d6148575c1e1 diff --git a/project/lib/harfbuzz b/project/lib/harfbuzz new file mode 160000 index 0000000000..2ba0b9ee94 --- /dev/null +++ b/project/lib/harfbuzz @@ -0,0 +1 @@ +Subproject commit 2ba0b9ee94e4fdea5c3e9d20fa73eb893c0bb95e diff --git a/project/lib/hashlink b/project/lib/hashlink new file mode 160000 index 0000000000..9888913ed2 --- /dev/null +++ b/project/lib/hashlink @@ -0,0 +1 @@ +Subproject commit 9888913ed2e1eef9fc03085100c92bd44985c781 diff --git a/project/lib/jpeg b/project/lib/jpeg new file mode 160000 index 0000000000..5cf7960678 --- /dev/null +++ b/project/lib/jpeg @@ -0,0 +1 @@ +Subproject commit 5cf79606784f0616590e6a3a7fdf17ad32a6df9b diff --git a/project/lib/lzma b/project/lib/lzma new file mode 160000 index 0000000000..5748ce6ad6 --- /dev/null +++ b/project/lib/lzma @@ -0,0 +1 @@ +Subproject commit 5748ce6ad668bb10e1a1fe322b37245d0053e71a diff --git a/project/lib/mbedtls b/project/lib/mbedtls new file mode 160000 index 0000000000..ab0af45d11 --- /dev/null +++ b/project/lib/mbedtls @@ -0,0 +1 @@ +Subproject commit ab0af45d11db04bd6e42ccfabe1b8d8deaae3243 diff --git a/project/lib/mojoal b/project/lib/mojoal new file mode 160000 index 0000000000..a9e2f30b04 --- /dev/null +++ b/project/lib/mojoal @@ -0,0 +1 @@ +Subproject commit a9e2f30b04f10df1912d06ff5a610edda4677ba5 diff --git a/project/lib/neko b/project/lib/neko new file mode 160000 index 0000000000..d8a3b46386 --- /dev/null +++ b/project/lib/neko @@ -0,0 +1 @@ +Subproject commit d8a3b463861779656a78c1a5e31c013580642185 diff --git a/project/lib/ogg b/project/lib/ogg new file mode 160000 index 0000000000..db5c7a49ce --- /dev/null +++ b/project/lib/ogg @@ -0,0 +1 @@ +Subproject commit db5c7a49ce7ebda47b15b78471e78fb7f2483e22 diff --git a/project/lib/openal b/project/lib/openal new file mode 160000 index 0000000000..326133368c --- /dev/null +++ b/project/lib/openal @@ -0,0 +1 @@ +Subproject commit 326133368c3d7149d0ab4a98fc96f654553094a7 diff --git a/project/lib/pixman b/project/lib/pixman new file mode 160000 index 0000000000..0cb4fbe324 --- /dev/null +++ b/project/lib/pixman @@ -0,0 +1 @@ +Subproject commit 0cb4fbe3241988a5d6b51d3551998ec6837c75a1 diff --git a/project/lib/png b/project/lib/png new file mode 160000 index 0000000000..e4a31f024b --- /dev/null +++ b/project/lib/png @@ -0,0 +1 @@ +Subproject commit e4a31f024b6158aaaf55a43502f574d5f5d1c894 diff --git a/project/lib/sdl b/project/lib/sdl new file mode 160000 index 0000000000..8ea38ebedc --- /dev/null +++ b/project/lib/sdl @@ -0,0 +1 @@ +Subproject commit 8ea38ebedc846faa547693f60bcd213b21370982 diff --git a/project/lib/tinyfiledialogs b/project/lib/tinyfiledialogs new file mode 160000 index 0000000000..4927ea8f44 --- /dev/null +++ b/project/lib/tinyfiledialogs @@ -0,0 +1 @@ +Subproject commit 4927ea8f44fa0151dc8e0a2b79a13dbd7827cc48 diff --git a/project/lib/vorbis b/project/lib/vorbis new file mode 160000 index 0000000000..84c023699c --- /dev/null +++ b/project/lib/vorbis @@ -0,0 +1 @@ +Subproject commit 84c023699cdf023a32fa4ded32019f194afcdad0 diff --git a/project/lib/vpx b/project/lib/vpx new file mode 160000 index 0000000000..fbf63dff1f --- /dev/null +++ b/project/lib/vpx @@ -0,0 +1 @@ +Subproject commit fbf63dff1f528d44f24bd662abb89fd01a4a1c25 diff --git a/project/lib/webm b/project/lib/webm new file mode 160000 index 0000000000..6c1e511f48 --- /dev/null +++ b/project/lib/webm @@ -0,0 +1 @@ +Subproject commit 6c1e511f487eaea026d94d0c6494a617155b5cfb diff --git a/project/lib/zlib b/project/lib/zlib new file mode 160000 index 0000000000..545f194963 --- /dev/null +++ b/project/lib/zlib @@ -0,0 +1 @@ +Subproject commit 545f1949635949159fa6282e81712aec32b5d4f1 diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 60b9314102..6c18c905fb 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -387,21 +387,8 @@ class CharSelectSubState extends MusicBeatSubState } else { - var temp:FlxSprite = new FlxSprite(); + var temp:Lock = new Lock(0, 0, i); temp.ID = 1; - temp.frames = Paths.getSparrowAtlas("charSelect/locks"); - - var lockIndex:Int = i + 1; - - if (i == 3) lockIndex = 3; - - if (i >= 4) lockIndex = i - 2; - - temp.animation.addByIndices("idle", "LOCK FULL " + lockIndex + " instance 1", [0], "", 24); - temp.animation.addByIndices("selected", "LOCK FULL " + lockIndex + " instance 1", [3, 4, 5], "", 24, false); - temp.animation.addByIndices("clicked", "LOCK FULL " + lockIndex + " instance 1", [9, 10, 11, 12, 13, 14, 15], "", 24, false); - - temp.animation.play("idle"); grpIcons.add(temp); } @@ -677,6 +664,11 @@ class CharSelectSubState extends MusicBeatSubState function spamOnStep():Void { + if (FlxG.keys.justPressed.B) + { + cursorY = 3; + cursorX = 3; + } if (spamUp || spamDown || spamLeft || spamRight) { // selectSound.changePitchBySemitone(1); @@ -713,19 +705,20 @@ class CharSelectSubState extends MusicBeatSubState switch (member.ID) { case 1: + var lock:Lock = cast member; if (index == getCurrentSelected()) { - switch (member.animation.curAnim.name) + switch (lock.getCurrentAnimation()) { case "idle": - member.animation.play("selected"); + lock.playAnimation("selected"); case "selected" | "clicked": - if (controls.ACCEPT) member.animation.play("clicked", true); + if (controls.ACCEPT) lock.playAnimation((FlxG.keys.pressed.CONTROL) ? "unlock" : "clicked", true); } } else { - member.animation.play("idle"); + lock.playAnimation("idle"); } case 0: var memb:PixelatedIcon = cast member; diff --git a/source/funkin/ui/charSelect/Lock.hx b/source/funkin/ui/charSelect/Lock.hx new file mode 100644 index 0000000000..42e5d63f46 --- /dev/null +++ b/source/funkin/ui/charSelect/Lock.hx @@ -0,0 +1,35 @@ +package funkin.ui.charSelect; + +import flixel.util.FlxColor; +import flxanimate.effects.FlxTint; +import funkin.graphics.adobeanimate.FlxAtlasSprite; + +class Lock extends FlxAtlasSprite +{ + var colors:Array = [ + 0x31F2A5, 0x20ECCD, 0x24D9E8, + 0x20ECCD, 0x20C8D4, 0x209BDD, + 0x209BDD, 0x2362C9, 0x243FB9 + ]; // lock colors, in a nx3 matrix format + + public function new(x:Float = 0, y:Float = 0, index:Int) + { + super(x, y, Paths.animateAtlas("charSelect/lock")); + + var tint:FlxTint = new FlxTint(colors[index], 1); + + var arr:Array = ["lock", "lock top 1", "lock top 2", "lock top 3", "lock base fuck it"]; + + onAnimationComplete.add((_) -> if (_ == "unlock") playAnimation("idle")); + var func = function(name) { + var symbol = anim.symbolDictionary[name]; + if (symbol != null && symbol.timeline.get("color") != null) symbol.timeline.get("color").get(0).colorEffect = tint; + } + for (symbol in arr) + { + func(symbol); + } + + playAnimation("idle"); + } +} From 1021f5c2eee9e01289021132d2cfb98f3d1539f4 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Fri, 6 Sep 2024 02:55:47 +0200 Subject: [PATCH 355/469] wip sequence --- source/funkin/audio/FunkinSound.hx | 4 +- .../funkin/ui/charSelect/CharSelectPlayer.hx | 8 +- .../ui/charSelect/CharSelectSubState.hx | 169 +++++++++++------- 3 files changed, 113 insertions(+), 68 deletions(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 9cb131c912..b33126998e 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -388,7 +388,7 @@ class FunkinSound extends FlxSound implements ICloneable } else { - var music = FunkinSound.load(pathToUse, params?.startingVolume ?? 1.0, params.loop ?? true, false, true); + var music = FunkinSound.load(pathToUse, params?.startingVolume ?? 1.0, params.loop ?? true, false, true, params.onComplete); if (music != null) { FlxG.sound.music = music; @@ -396,6 +396,8 @@ class FunkinSound extends FlxSound implements ICloneable // Prevent repeat update() and onFocus() calls. FlxG.sound.list.remove(FlxG.sound.music); + if (FlxG.sound.music != null && params.onLoad != null) params.onLoad(); + return true; } else diff --git a/source/funkin/ui/charSelect/CharSelectPlayer.hx b/source/funkin/ui/charSelect/CharSelectPlayer.hx index 8e94e25fed..eac2f459a4 100644 --- a/source/funkin/ui/charSelect/CharSelectPlayer.hx +++ b/source/funkin/ui/charSelect/CharSelectPlayer.hx @@ -26,12 +26,10 @@ class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass } else { - // Handled by onBeatHit now - playAnimation("idle", true, false, false); + playAnimation("idle", true, false, true); } - case "slidein idle point": - // Handled by onBeatHit now - playAnimation("idle", true, false, false); + case "slidein idle point", "cannot select", "unlock": + playAnimation("idle", true, false, true); case "idle": trace('Waiting for onBeatHit'); } diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 6c18c905fb..65fb48bb3b 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -17,20 +17,17 @@ import funkin.data.freeplay.player.PlayerData; import funkin.data.freeplay.player.PlayerRegistry; import funkin.graphics.adobeanimate.FlxAtlasSprite; import funkin.graphics.FunkinCamera; +import funkin.graphics.shaders.BlueFade; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEventDispatcher; import funkin.play.stage.Stage; +import funkin.save.Save; import funkin.ui.freeplay.charselect.PlayableCharacter; import funkin.ui.freeplay.FreeplayState; import funkin.ui.PixelatedIcon; import funkin.util.MathUtil; import funkin.vis.dsp.SpectralAnalyzer; import openfl.display.BlendMode; -import flixel.util.FlxTimer; -import flixel.tweens.FlxEase; -import flixel.sound.FlxSound; -import funkin.audio.FunkinSound; -import funkin.graphics.shaders.BlueFade; import openfl.filters.ShaderFilter; class CharSelectSubState extends MusicBeatSubState @@ -82,8 +79,6 @@ class CharSelectSubState extends MusicBeatSubState var charSelectCam:FunkinCamera; - var introM:FunkinSound = null; - public function new() { super(); @@ -102,6 +97,8 @@ class CharSelectSubState extends MusicBeatSubState var playerData = player.getCharSelectData(); if (playerData == null) continue; + trace(player.isUnlocked()); + var targetPosition:Int = playerData.position ?? 0; while (availableChars.exists(targetPosition)) { @@ -119,43 +116,6 @@ class CharSelectSubState extends MusicBeatSubState { super.create(); - autoFollow = false; - - var fadeShaderFilter:ShaderFilter = new ShaderFilter(fadeShader); - FlxG.camera.filters = [fadeShaderFilter]; - - selectSound = new FunkinSound(); - selectSound.loadEmbedded(Paths.sound('CS_select')); - selectSound.pitch = 1; - selectSound.volume = 0.7; - FlxG.sound.defaultSoundGroup.add(selectSound); - - // playing it here to preload it. not doing this makes a super awkward pause at the end of the intro - // TODO: probably make an intro thing for funkinSound itself that preloads the next audio? - FunkinSound.playMusic('stayFunky', - { - startingVolume: 0, - overrideExisting: true, - restartTrack: true - }); - var introMusic:String = Paths.music('stayFunky/stayFunky-intro'); - introM = FunkinSound.load(introMusic, 1.0, false, true, true, () -> { - FunkinSound.playMusic('stayFunky', - { - startingVolume: 1, - overrideExisting: true, - restartTrack: true - }); - @:privateAccess - gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1); - #if desktop - // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 - // So we want to manually change it! - @:privateAccess - gfChill.analyzer.fftN = 512; - #end - }); - var bg:FlxSprite = new FlxSprite(-153, -140); bg.loadGraphic(Paths.image('charSelect/charSelectBG')); bg.scrollFactor.set(0.1, 0.1); @@ -197,15 +157,15 @@ class CharSelectSubState extends MusicBeatSubState gfChill = new CharSelectGF(); gfChill.switchGF("bf"); add(gfChill); - @:privateAccess - playerChill = new CharSelectPlayer(0, 0); - playerChill.switchChar("bf"); - add(playerChill); playerChillOut = new CharSelectPlayer(0, 0); playerChillOut.switchChar("bf"); add(playerChillOut); + playerChill = new CharSelectPlayer(0, 0); + playerChill.switchChar("bf"); + add(playerChill); + var speakers:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/charSelectSpeakers")); speakers.anim.play(""); speakers.scrollFactor.set(1.8, 1.8); @@ -338,6 +298,14 @@ class CharSelectSubState extends MusicBeatSubState temp.loadGraphic(Paths.image('charSelect/placement')); add(temp); temp.alpha = 0.0; + + selectSound = new FunkinSound(); + selectSound.loadEmbedded(Paths.sound('CS_select')); + selectSound.pitch = 1; + selectSound.volume = 0.7; + + FlxG.sound.defaultSoundGroup.add(selectSound); + Conductor.stepHit.add(spamOnStep); // FlxG.debugger.track(temp, "tempBG"); @@ -365,6 +333,8 @@ class CharSelectSubState extends MusicBeatSubState var grpXSpread(default, set):Float = 107; var grpYSpread(default, set):Float = 127; + var nonLocks = []; + function initLocks():Void { grpIcons = new FlxSpriteGroup(); @@ -375,7 +345,7 @@ class CharSelectSubState extends MusicBeatSubState for (i in 0...9) { - if (availableChars.exists(i)) + if (availableChars.exists(i) && Save.instance.charactersSeen.contains(availableChars[i])) { var path:String = availableChars.get(i); var temp:PixelatedIcon = new PixelatedIcon(0, 0); @@ -387,9 +357,15 @@ class CharSelectSubState extends MusicBeatSubState } else { + if (availableChars.exists(i)) nonLocks.push(i); + var temp:Lock = new Lock(0, 0, i); temp.ID = 1; + temp.onAnimationComplete.add(function(anim) { + if (anim == "unlock") playerChill.playAnimation("unlock", true); + }); + grpIcons.add(temp); } } @@ -397,6 +373,78 @@ class CharSelectSubState extends MusicBeatSubState updateIconPositions(); grpIcons.scrollFactor.set(); + + // playing it here to preload it. not doing this makes a super awkward pause at the end of the intro + // TODO: probably make an intro thing for funkinSound itself that preloads the next audio? + FunkinSound.playMusic('stayFunky', + { + startingVolume: 0, + overrideExisting: true, + restartTrack: true, + }); + + unLock(); + } + + function unLock() + { + var index = nonLocks[0]; + + var copy = 3; + + var yThing = 0; + + while (index > copy) + { + yThing++; + copy += 3; + } + + var xThing = copy - index - 1; + + cursorY = 3 - yThing; + cursorX = 3 - xThing; + + selectTimer.start(1, function(_) { + var lock:Lock = cast grpIcons.group.members[index]; + + lock.playAnimation("unlock"); + + playerChill.visible = false; + playerChill.switchChar(availableChars[index]); + + playerChillOut.visible = true; + playerChillOut.playAnimation("death"); + playerChillOut.onAnimationComplete.addOnce((_) -> if (_ == "death") + { + playerChill.playAnimation("unlock"); + playerChill.onAnimationComplete.addOnce(function(_) { + nonLocks.shift(); + + if (nonLocks.length > 0) unLock(); + else + { + FunkinSound.playMusic('stayFunky', + { + startingVolume: 1, + overrideExisting: true, + restartTrack: true, + onLoad: function() { + @:privateAccess + gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1); + #if desktop + // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 + // So we want to manually change it! + @:privateAccess + gfChill.analyzer.fftN = 512; + #end + } + }); + } + }); + playerChill.visible = true; + }); + }); } function updateIconPositions() @@ -464,17 +512,6 @@ class CharSelectSubState extends MusicBeatSubState override public function update(elapsed:Float):Void { super.update(elapsed); - @:privateAccess - if (introM != null && !introM.paused && gfChill.analyzer == null) - { - gfChill.analyzer = new SpectralAnalyzer(introM._channel.__audioSource, 7, 0.1); - #if desktop - // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 - // So we want to manually change it! - @:privateAccess - gfChill.analyzer.fftN = 512; - #end - } Conductor.instance.update(); @@ -518,6 +555,8 @@ class CharSelectSubState extends MusicBeatSubState if (controls.UI_UP_P) { cursorY -= 1; + cursorDenied.visible = false; + holdTmrUp = 0; selectSound.play(true); @@ -525,18 +564,22 @@ class CharSelectSubState extends MusicBeatSubState if (controls.UI_DOWN_P) { cursorY += 1; + cursorDenied.visible = false; holdTmrDown = 0; selectSound.play(true); } if (controls.UI_LEFT_P) { cursorX -= 1; + cursorDenied.visible = false; + holdTmrLeft = 0; selectSound.play(true); } if (controls.UI_RIGHT_P) { cursorX += 1; + cursorDenied.visible = false; holdTmrRight = 0; selectSound.play(true); } @@ -558,7 +601,7 @@ class CharSelectSubState extends MusicBeatSubState cursorY = -1; } - if (availableChars.exists(getCurrentSelected())) + if (availableChars.exists(getCurrentSelected()) && Save.instance.charactersSeen.contains(availableChars[getCurrentSelected()])) { curChar = availableChars.get(getCurrentSelected()); @@ -675,6 +718,8 @@ class CharSelectSubState extends MusicBeatSubState if (selectSound.pitch > 5) selectSound.pitch = 5; selectSound.play(true); + cursorDenied.visible = false; + if (spamUp) { cursorY -= 1; @@ -729,7 +774,7 @@ class CharSelectSubState extends MusicBeatSubState memb.scale.set(2.6, 2.6); if (controls.ACCEPT) memb.animation.play("confirm"); - if (controls.BACK) + if (memb.animation.curAnim.name == "confirm" && controls.BACK) { memb.animation.play("confirm", false, true); member.animation.finishCallback = (_) -> { From 313dba6985b473c3d8ec8f7283fa41178c4793fd Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 6 Sep 2024 00:24:07 -0400 Subject: [PATCH 356/469] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index f1c74ef595..8e0d71c8f3 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit f1c74ef595a29d11544492e0dc907ba9a21d7c4f +Subproject commit 8e0d71c8f32ebd872f7f7411b3f7108083554de9 From a61f40571d31d792e0d4ea7322af4261ca574cf5 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Fri, 6 Sep 2024 06:28:08 +0200 Subject: [PATCH 357/469] add wip bizz --- assets | 2 +- source/funkin/graphics/FlxFilteredSprite.hx | 407 ++++++++++++++++++ source/funkin/ui/PixelatedIcon.hx | 3 +- .../ui/charSelect/CharSelectSubState.hx | 3 + 4 files changed, 413 insertions(+), 2 deletions(-) create mode 100644 source/funkin/graphics/FlxFilteredSprite.hx diff --git a/assets b/assets index 74bd0131b7..f73c8f4798 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 74bd0131b705e87f9704c54a44d64579153ab4af +Subproject commit f73c8f4798bfcd23c98db03bcde2e0afcbce4cfd diff --git a/source/funkin/graphics/FlxFilteredSprite.hx b/source/funkin/graphics/FlxFilteredSprite.hx new file mode 100644 index 0000000000..3563ea9764 --- /dev/null +++ b/source/funkin/graphics/FlxFilteredSprite.hx @@ -0,0 +1,407 @@ +package funkin.graphics; + +import flixel.FlxBasic; +import flixel.FlxCamera; +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.graphics.FlxGraphic; +import flixel.graphics.frames.FlxFrame; +import flixel.math.FlxMatrix; +import flixel.math.FlxPoint; +import flixel.math.FlxRect; +import flixel.util.FlxColor; +import lime.graphics.cairo.Cairo; +import openfl.display.BitmapData; +import openfl.display.BlendMode; +import openfl.display.DisplayObjectRenderer; +import openfl.display.Graphics; +import openfl.display.OpenGLRenderer; +import openfl.display._internal.Context3DGraphics; +import openfl.display3D.Context3D; +import openfl.display3D.Context3DClearMask; +import openfl.filters.BitmapFilter; +import openfl.filters.BlurFilter; +import openfl.geom.ColorTransform; +import openfl.geom.Matrix; +import openfl.geom.Point; +import openfl.geom.Rectangle; +#if (js && html5) +import lime._internal.graphics.ImageCanvasUtil; +import openfl.display.CanvasRenderer; +import openfl.display._internal.CanvasGraphics as GfxRenderer; +#else +import openfl.display.CairoRenderer; +import openfl.display._internal.CairoGraphics as GfxRenderer; +#end + +/** + * A modified `FlxSprite` that supports filters. + * The name's pretty much self-explanatory. + * @author CheemsAndFriends + */ +@:access(openfl.geom.Rectangle) +@:access(openfl.filters.BitmapFilter) +@:access(flixel.graphics.frames.FlxFrame) +class FlxFilteredSprite extends FlxSprite +{ + @:noCompletion var _renderer:FlxAnimateFilterRenderer = new FlxAnimateFilterRenderer(); + + @:noCompletion var _filterMatrix:FlxMatrix; + + /** + * An `Array` of shader filters (aka `BitmapFilter`). + */ + public var filters(default, set):Array; + + /** + * a flag to update the image with the filters. + * Useful when trying to render a shader at all times. + */ + public var filterDirty:Bool = false; + + @:noCompletion var filtered:Bool; + + @:noCompletion var _blankFrame:FlxFrame; + + var _filterBmp1:BitmapData; + var _filterBmp2:BitmapData; + + override public function update(elapsed:Float) + { + super.update(elapsed); + if (!filterDirty && filters != null) + { + for (filter in filters) + { + if (filter.__renderDirty) + { + filterDirty = true; + break; + } + } + } + } + + @:noCompletion + override function initVars():Void + { + super.initVars(); + _filterMatrix = new FlxMatrix(); + filters = null; + filtered = false; + } + + override public function draw():Void + { + checkEmptyFrame(); + + if (alpha == 0 || _frame.type == FlxFrameType.EMPTY) return; + + if (dirty) // rarely + calcFrame(useFramePixels); + + if (filterDirty) filterFrame(); + + for (camera in cameras) + { + if (!camera.visible || !camera.exists || !isOnScreen(camera)) continue; + + getScreenPosition(_point, camera).subtractPoint(offset); + + if (isSimpleRender(camera)) drawSimple(camera); + else + drawComplex(camera); + + #if FLX_DEBUG + FlxBasic.visibleCount++; + #end + } + + #if FLX_DEBUG + if (FlxG.debugger.drawDebug) drawDebug(); + #end + } + + @:noCompletion + override function drawComplex(camera:FlxCamera):Void + { + _frame.prepareMatrix(_matrix, FlxFrameAngle.ANGLE_0, checkFlipX(), checkFlipY()); + _matrix.concat(_filterMatrix); + _matrix.translate(-origin.x, -origin.y); + _matrix.scale(scale.x, scale.y); + + if (bakedRotationAngle <= 0) + { + updateTrig(); + + if (angle != 0) _matrix.rotateWithTrig(_cosAngle, _sinAngle); + } + + _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); + } + + camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shader); + } + + @:noCompletion + function filterFrame() + { + filterDirty = false; + _filterMatrix.identity(); + var filteredFrame = (filtered) ? _frame : null; + + if (filters != null && filters.length > 0) + { + _flashRect.setEmpty(); + + for (filter in filters) + { + _flashRect.__expand(-filter.__leftExtension, + -filter.__topExtension, filter.__leftExtension + + filter.__rightExtension, + filter.__topExtension + + filter.__bottomExtension); + } + _flashRect.width += frameWidth; + _flashRect.height += frameHeight; + if (_blankFrame == null) _blankFrame = new FlxFrame(null); + + if (_blankFrame.parent == null || _flashRect.width > _blankFrame.parent.width || _flashRect.height > _blankFrame.parent.height) + { + if (_blankFrame.parent != null) + { + _blankFrame.parent.destroy(); + _filterBmp1.dispose(); + _filterBmp2.dispose(); + } + + _blankFrame.parent = FlxGraphic.fromRectangle(Math.ceil(_flashRect.width * 1.25), Math.ceil(_flashRect.height * 1.25), 0, true); + _filterBmp1 = new BitmapData(_blankFrame.parent.width, _blankFrame.parent.height, 0); + _filterBmp2 = new BitmapData(_blankFrame.parent.width, _blankFrame.parent.height, 0); + } + _blankFrame.offset.copyFrom(_frame.offset); + _blankFrame.parent.bitmap = _renderer.applyFilter(_blankFrame.parent.bitmap, _filterBmp1, _filterBmp2, frame.parent.bitmap, filters, _flashRect, + frame.frame.copyToFlash()); + _blankFrame.frame = FlxRect.get(0, 0, _blankFrame.parent.bitmap.width, _blankFrame.parent.bitmap.height); + _filterMatrix.translate(_flashRect.x, _flashRect.y); + _frame = _blankFrame.copyTo(); + filtered = true; + } + else + { + resetFrame(); + filtered = false; + } + } + + @:noCompletion + function set_filters(value:Array) + { + if (filters != value) filterDirty = true; + + return filters = value; + } + + @:noCompletion + override function set_frame(value:FlxFrame) + { + if (value != frame) filterDirty = true; + + return super.set_frame(value); + } +} + +@:noCompletion +@:access(openfl.display.OpenGLRenderer) +@:access(openfl.filters.BitmapFilter) +@:access(openfl.geom.Rectangle) +@:access(openfl.display.Stage) +@:access(openfl.display.Graphics) +@:access(openfl.display.Shader) +@:access(openfl.display.BitmapData) +@:access(openfl.geom.ColorTransform) +@:access(openfl.display.DisplayObject) +@:access(openfl.display3D.Context3D) +@:access(openfl.display.CanvasRenderer) +@:access(openfl.display.CairoRenderer) +@:access(openfl.display3D.Context3D) +class FlxAnimateFilterRenderer +{ + var renderer:OpenGLRenderer; + var context:Context3D; + + public function new() + { + // context = new openfl.display3D.Context3D(null); + renderer = new OpenGLRenderer(FlxG.game.stage.context3D); + renderer.__worldTransform = new Matrix(); + renderer.__worldColorTransform = new ColorTransform(); + } + + @:noCompletion function setRenderer(renderer:DisplayObjectRenderer, rect:Rectangle) + { + @:privateAccess + if (true) + { + var displayObject = FlxG.game; + var pixelRatio = FlxG.game.stage.__renderer.__pixelRatio; + + var offsetX = rect.x > 0 ? Math.ceil(rect.x) : Math.floor(rect.x); + var offsetY = rect.y > 0 ? Math.ceil(rect.y) : Math.floor(rect.y); + if (renderer.__worldTransform == null) + { + renderer.__worldTransform = new Matrix(); + renderer.__worldColorTransform = new ColorTransform(); + } + if (displayObject.__cacheBitmapColorTransform == null) displayObject.__cacheBitmapColorTransform = new ColorTransform(); + + renderer.__stage = displayObject.stage; + + renderer.__allowSmoothing = true; + renderer.__setBlendMode(NORMAL); + renderer.__worldAlpha = 1 / displayObject.__worldAlpha; + + renderer.__worldTransform.identity(); + renderer.__worldTransform.invert(); + renderer.__worldTransform.concat(new Matrix()); + renderer.__worldTransform.tx -= offsetX; + renderer.__worldTransform.ty -= offsetY; + renderer.__worldTransform.scale(pixelRatio, pixelRatio); + + renderer.__pixelRatio = pixelRatio; + } + } + + public function applyFilter(target:BitmapData = null, target1:BitmapData = null, target2:BitmapData = null, bmp:BitmapData, filters:Array, + rect:Rectangle, bmpRect:Rectangle) + { + if (filters == null || filters.length == 0) return bmp; + + renderer.__setBlendMode(NORMAL); + renderer.__worldAlpha = 1; + + if (renderer.__worldTransform == null) + { + renderer.__worldTransform = new Matrix(); + renderer.__worldColorTransform = new ColorTransform(); + } + renderer.__worldTransform.identity(); + renderer.__worldColorTransform.__identity(); + + var bitmap:BitmapData = (target == null) ? new BitmapData(Math.ceil(rect.width * 1.25), Math.ceil(rect.height * 1.25), true, 0) : target; + + var bitmap2 = (target1 == null) ? new BitmapData(Math.ceil(rect.width * 1.25), Math.ceil(rect.height * 1.25), true, 0) : target1, + bitmap3 = (target2 == null) ? bitmap2.clone() : target2; + renderer.__setRenderTarget(bitmap); + + bmp.__renderTransform.translate(Math.abs(rect.x) - bmpRect.x, Math.abs(rect.y) - bmpRect.y); + bmpRect.x = Math.abs(rect.x); + bmpRect.y = Math.abs(rect.y); + + var bestResolution = renderer.__context3D.__backBufferWantsBestResolution; + renderer.__context3D.__backBufferWantsBestResolution = false; + renderer.__scissorRect(bmpRect); + renderer.__renderFilterPass(bmp, renderer.__defaultDisplayShader, true); + renderer.__scissorRect(); + + renderer.__context3D.__backBufferWantsBestResolution = bestResolution; + + bmp.__renderTransform.identity(); + + var shader, cacheBitmap = null; + for (filter in filters) + { + if (filter.__preserveObject) + { + renderer.__setRenderTarget(bitmap3); + renderer.__renderFilterPass(bitmap, renderer.__defaultDisplayShader, filter.__smooth); + } + + for (i in 0...filter.__numShaderPasses) + { + shader = filter.__initShader(renderer, i, (filter.__preserveObject) ? bitmap3 : null); + renderer.__setBlendMode(filter.__shaderBlendMode); + renderer.__setRenderTarget(bitmap2); + renderer.__renderFilterPass(bitmap, shader, filter.__smooth); + + cacheBitmap = bitmap; + bitmap = bitmap2; + bitmap2 = cacheBitmap; + } + filter.__renderDirty = false; + } + if (target1 == null) bitmap2.dispose(); + if (target2 == null) bitmap3.dispose(); + + return bitmap; + } + + public function applyBlend(blend:BlendMode, bitmap:BitmapData) + { + bitmap.__update(false, true); + var bmp = new BitmapData(bitmap.width, bitmap.height, 0); + + #if (js && html5) + ImageCanvasUtil.convertToCanvas(bmp.image); + @:privateAccess + var renderer = new CanvasRenderer(bmp.image.buffer.__srcContext); + #else + var renderer = new CairoRenderer(new Cairo(bmp.getSurface())); + #end + + // setRenderer(renderer, bmp.rect); + + var m = new Matrix(); + var c = new ColorTransform(); + renderer.__allowSmoothing = true; + renderer.__overrideBlendMode = blend; + renderer.__worldTransform = m; + renderer.__worldAlpha = 1; + renderer.__worldColorTransform = c; + + renderer.__setBlendMode(blend); + #if (js && html5) + bmp.__drawCanvas(bitmap, renderer); + #else + bmp.__drawCairo(bitmap, renderer); + #end + + return bitmap; + } + + public function graphicstoBitmapData(gfx:Graphics) + { + if (gfx.__bounds == null) return null; + // var cacheRTT = renderer.__context3D.__state.renderToTexture; + // var cacheRTTDepthStencil = renderer.__context3D.__state.renderToTextureDepthStencil; + // var cacheRTTAntiAlias = renderer.__context3D.__state.renderToTextureAntiAlias; + // var cacheRTTSurfaceSelector = renderer.__context3D.__state.renderToTextureSurfaceSelector; + + // var bmp = new BitmapData(Math.ceil(gfx.__width), Math.ceil(gfx.__height), 0); + // renderer.__context3D.setRenderToTexture(bmp.getTexture(renderer.__context3D)); + // gfx.__owner.__renderTransform.identity(); + // gfx.__renderTransform.identity(); + // Context3DGraphics.render(gfx, renderer); + GfxRenderer.render(gfx, cast renderer.__softwareRenderer); + var bmp = gfx.__bitmap; + + gfx.__bitmap = null; + + // if (cacheRTT != null) + // { + // renderer.__context3D.setRenderToTexture(cacheRTT, cacheRTTDepthStencil, cacheRTTAntiAlias, cacheRTTSurfaceSelector); + // } + // else + // { + // renderer.__context3D.setRenderToBackBuffer(); + // } + + return bmp; + } +} diff --git a/source/funkin/ui/PixelatedIcon.hx b/source/funkin/ui/PixelatedIcon.hx index d1ea652c3b..4252c96958 100644 --- a/source/funkin/ui/PixelatedIcon.hx +++ b/source/funkin/ui/PixelatedIcon.hx @@ -1,12 +1,13 @@ package funkin.ui; import flixel.FlxSprite; +import funkin.graphics.FlxFilteredSprite; /** * The icon that gets used for Freeplay capsules and char select * NOT to be confused with the CharIcon class, which is for the in-game icons */ -class PixelatedIcon extends FlxSprite +class PixelatedIcon extends FlxFilteredSprite { public function new(x:Float, y:Float) { diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index a2fe4a7dc0..e2b662c142 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -16,6 +16,7 @@ import funkin.audio.FunkinSound; import funkin.data.freeplay.player.PlayerData; import funkin.data.freeplay.player.PlayerRegistry; import funkin.graphics.adobeanimate.FlxAtlasSprite; +import openfl.filters.DropShadowFilter; import funkin.graphics.FunkinCamera; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEventDispatcher; @@ -672,6 +673,7 @@ class CharSelectSubState extends MusicBeatSubState if (index == getCurrentSelected()) { // memb.pixels = memb.withDropShadow.clone(); + if (memb.scale.x != 2.6) memb.filters = [new DropShadowFilter()]; memb.scale.set(2.6, 2.6); if (controls.ACCEPT) memb.animation.play("confirm"); @@ -687,6 +689,7 @@ class CharSelectSubState extends MusicBeatSubState else { // memb.pixels = memb.noDropShadow.clone(); + if (memb.scale.x == 2) memb.filters = []; memb.scale.set(2, 2); } } From c45fa93786453077da19b9940e8f94622351bf68 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 6 Sep 2024 16:46:11 -0400 Subject: [PATCH 358/469] update lime (ndlls) --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index 3cd8d5d3d4..35c40798b8 100644 --- a/hmm.json +++ b/hmm.json @@ -145,7 +145,7 @@ "name": "lime", "type": "git", "dir": null, - "ref": "e0b2339e02fff91168789dbd1a0dd019ea3dda39", + "ref": "fe3368f611a84a19afc03011353945ae4da8fffd", "url": "https://github.com/FunkinCrew/lime" }, { From 4f666e48559f823e670b2f69538c6301d3410144 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 6 Sep 2024 19:12:36 -0400 Subject: [PATCH 359/469] creds merge --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index a9f81e916f..0ffaac38e3 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit a9f81e916f673c5084851b56ee37a2952e9f9587 +Subproject commit 0ffaac38e32afe8cb95e896f7fa4db43bb3d884d From 89e1cb82588f80a0f6f9af6ab820365d68a35938 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 6 Sep 2024 19:44:33 -0400 Subject: [PATCH 360/469] small null fix for if we're not playing music --- source/funkin/ui/charSelect/CharacterUnlockState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/charSelect/CharacterUnlockState.hx b/source/funkin/ui/charSelect/CharacterUnlockState.hx index 25cd1baf0b..b32a35145c 100644 --- a/source/funkin/ui/charSelect/CharacterUnlockState.hx +++ b/source/funkin/ui/charSelect/CharacterUnlockState.hx @@ -103,7 +103,7 @@ class CharacterUnlockState extends MusicBeatState function handleMusic():Void { - FlxG.sound.music.stop(); + FlxG.sound.music?.stop(); FlxG.sound.play(Paths.sound('confirmMenu')); } From e0a4818e5a781b4bcf1d58484cc7ee9a93e5ea8c Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Sat, 7 Sep 2024 00:52:38 +0100 Subject: [PATCH 361/469] registry updates --- source/funkin/data/stage/StageRegistry.hx | 4 ++-- source/funkin/ui/debug/charting/ChartEditorState.hx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/funkin/data/stage/StageRegistry.hx b/source/funkin/data/stage/StageRegistry.hx index 3d3779bcec..87113ef051 100644 --- a/source/funkin/data/stage/StageRegistry.hx +++ b/source/funkin/data/stage/StageRegistry.hx @@ -93,8 +93,8 @@ class StageRegistry extends BaseRegistry public function listBaseGameStageIds():Array { return [ - "mainStage", "mainStageErect", "spookyMansion", "phillyTrain", "phillyTrainErect", "limoRide", "limoRideErect", "mallXmas", "mallEvil", "school", - "schoolEvil", "tankmanBattlefield", "phillyStreets", "phillyStreetsErect", "phillyBlazin", + "mainStage", "mainStageErect", "spookyMansion", "phillyTrain", "phillyTrainErect", "limoRide", "limoRideErect", "mallXmas", "mallXmasErect", "mallEvil", + "school", "schoolEvil", "tankmanBattlefield", "phillyStreets", "phillyStreetsErect", "phillyBlazin", ]; } diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 20d39183b7..811e08e5d8 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -5707,7 +5707,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState PlayStatePlaylist.campaignId = 'week3'; case 'limoRide' | 'limoRideErect': PlayStatePlaylist.campaignId = 'week4'; - case 'mallXmas' | 'mallEvil': + case 'mallXmas' | 'mallXmasErect' | 'mallEvil': PlayStatePlaylist.campaignId = 'week5'; case 'school' | 'schoolEvil': PlayStatePlaylist.campaignId = 'week6'; From 40b5ad7c2a7d32c13af8a5c7a83b7c58ce2e1dfe Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Sat, 7 Sep 2024 00:54:21 +0100 Subject: [PATCH 362/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index f1c74ef595..c430b1c7b2 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit f1c74ef595a29d11544492e0dc907ba9a21d7c4f +Subproject commit c430b1c7b20efbd5062df17e31b5de0add6a62ff From f3722e5da2643660805ce6c3b466013c50c0d5f0 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Sat, 7 Sep 2024 02:07:00 +0200 Subject: [PATCH 363/469] Add hmm flxanimate, new conditional for Cheems' PC sake and more wip char select animation --- assets | 2 +- hmm.json | 4 +- source/Main.hx | 2 +- .../ui/charSelect/CharSelectSubState.hx | 100 ++++++++++-------- source/funkin/ui/charSelect/Lock.hx | 1 - 5 files changed, 62 insertions(+), 47 deletions(-) diff --git a/assets b/assets index f73c8f4798..d2ea072f32 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit f73c8f4798bfcd23c98db03bcde2e0afcbce4cfd +Subproject commit d2ea072f32d0ba74cf8c62576f28e7b03d591ddb diff --git a/hmm.json b/hmm.json index 3cd8d5d3d4..eefaf86d5e 100644 --- a/hmm.json +++ b/hmm.json @@ -46,7 +46,7 @@ "name": "flxanimate", "type": "git", "dir": null, - "ref": "27a0a1f0afc1074f619991472fb3ee209aad28df", + "ref": "280d1a46ac60021d08bb18181631cbd6d061782c", "url": "https://github.com/Dot-Stuff/flxanimate" }, { @@ -205,4 +205,4 @@ "url": "https://github.com/fponticelli/thx.semver" } ] -} +} \ No newline at end of file diff --git a/source/Main.hx b/source/Main.hx index 2426fa0d99..dc462ee5ce 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -22,7 +22,7 @@ class Main extends Sprite var gameHeight:Int = 720; // Height of the game in pixels (might be less / more in actual pixels depending on your zoom). var initialState:Class = funkin.InitState; // The FlxState the game starts with. var zoom:Float = -1; // If -1, zoom is automatically calculated to fit the window dimensions. - #if web + #if (web || CHEEMS) var framerate:Int = 60; // How many frames per second the game should run at. #else // TODO: This should probably be in the options menu? diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 3c53fe9164..80ad594396 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -78,6 +78,7 @@ class CharSelectSubState extends MusicBeatSubState var selectTimer:FlxTimer = new FlxTimer(); var selectSound:FunkinSound; + var unlockSound:FunkinSound; var charSelectCam:FunkinCamera; @@ -270,6 +271,20 @@ class CharSelectSubState extends MusicBeatSubState grpCursors.add(cursorBlue); grpCursors.add(cursor); + selectSound = new FunkinSound(); + selectSound.loadEmbedded(Paths.sound('CS_select')); + selectSound.pitch = 1; + selectSound.volume = 0.7; + + FlxG.sound.defaultSoundGroup.add(selectSound); + + unlockSound = new FunkinSound(); + unlockSound.loadEmbedded(Paths.sound('CS_unlock')); + unlockSound.pitch = 1; + unlockSound.volume = 0.7; + + FlxG.sound.defaultSoundGroup.add(unlockSound); + initLocks(); for (index => member in grpIcons.members) @@ -301,13 +316,6 @@ class CharSelectSubState extends MusicBeatSubState add(temp); temp.alpha = 0.0; - selectSound = new FunkinSound(); - selectSound.loadEmbedded(Paths.sound('CS_select')); - selectSound.pitch = 1; - selectSound.volume = 0.7; - - FlxG.sound.defaultSoundGroup.add(selectSound); - Conductor.stepHit.add(spamOnStep); // FlxG.debugger.track(temp, "tempBG"); @@ -394,58 +402,66 @@ class CharSelectSubState extends MusicBeatSubState var copy = 3; - var yThing = 0; + var yThing = -1; - while (index > copy) + while ((index + 1) > copy) { yThing++; copy += 3; } - var xThing = copy - index - 1; + var xThing = (copy - index - 2) * -1; + // Look, I'd write better code but I had better aneurysms, my bad - Cheems + cursorY = yThing; + cursorX = xThing; + + selectSound.play(true); - cursorY = 3 - yThing; - cursorX = 3 - xThing; + nonLocks.shift(); selectTimer.start(1, function(_) { var lock:Lock = cast grpIcons.group.members[index]; + lock.anim.getFrameLabel("unlockAnim").add(function() { + playerChillOut.playAnimation("death"); + }); + lock.playAnimation("unlock"); - playerChill.visible = false; - playerChill.switchChar(availableChars[index]); + unlockSound.play(true); - playerChillOut.visible = true; - playerChillOut.playAnimation("death"); - playerChillOut.onAnimationComplete.addOnce((_) -> if (_ == "death") - { + lock.onAnimationComplete.addOnce(function(_) { + camera.flash(0xFFFFFFFF, 0.1); playerChill.playAnimation("unlock"); - playerChill.onAnimationComplete.addOnce(function(_) { - nonLocks.shift(); - - if (nonLocks.length > 0) unLock(); - else - { - FunkinSound.playMusic('stayFunky', - { - startingVolume: 1, - overrideExisting: true, - restartTrack: true, - onLoad: function() { - @:privateAccess - gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1); - #if desktop - // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 - // So we want to manually change it! - @:privateAccess - gfChill.analyzer.fftN = 512; - #end - } - }); - } - }); playerChill.visible = true; + + if (nonLocks.length == 0) + { + FunkinSound.playMusic('stayFunky', + { + startingVolume: 1, + overrideExisting: true, + restartTrack: true, + onLoad: function() { + @:privateAccess + gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1); + #if desktop + // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 + // So we want to manually change it! + @:privateAccess + gfChill.analyzer.fftN = 512; + #end + } + }); + } + else + playerChill.onAnimationComplete.addOnce((_) -> unLock()); }); + + playerChill.visible = false; + playerChill.switchChar(availableChars[index]); + + playerChillOut.visible = true; }); } diff --git a/source/funkin/ui/charSelect/Lock.hx b/source/funkin/ui/charSelect/Lock.hx index 42e5d63f46..982145d5c4 100644 --- a/source/funkin/ui/charSelect/Lock.hx +++ b/source/funkin/ui/charSelect/Lock.hx @@ -20,7 +20,6 @@ class Lock extends FlxAtlasSprite var arr:Array = ["lock", "lock top 1", "lock top 2", "lock top 3", "lock base fuck it"]; - onAnimationComplete.add((_) -> if (_ == "unlock") playAnimation("idle")); var func = function(name) { var symbol = anim.symbolDictionary[name]; if (symbol != null && symbol.timeline.get("color") != null) symbol.timeline.get("color").get(0).colorEffect = tint; From 51e5eb929ce70ad314d827bcf30f39b8d7449854 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sun, 8 Sep 2024 11:22:24 -0400 Subject: [PATCH 364/469] add hxjsonast to hmm.json to fix builds? --- hmm.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index 35c40798b8..ea32399f80 100644 --- a/hmm.json +++ b/hmm.json @@ -115,6 +115,13 @@ "ref": "147294123f983e35f50a966741474438069a7a8f", "url": "https://github.com/FunkinCrew/hxcpp-debugger" }, + { + "name": "hxjsonast", + "type": "git", + "dir": null, + "ref": "20e72cc68c823496359775ac1f06500e67f189d5", + "url": "https://github.com/nadako/hxjsonast/" + }, { "name": "hxp", "type": "haxelib", @@ -205,4 +212,4 @@ "url": "https://github.com/fponticelli/thx.semver" } ] -} +} \ No newline at end of file From 4f1423870cb6248cc8bdb0d2e9696b278e24a982 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sun, 8 Sep 2024 14:44:22 -0400 Subject: [PATCH 365/469] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 5af650e1ac..2b48f3d9e2 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5af650e1ac562f95e03f6484d7fa33d7d83f5c8d +Subproject commit 2b48f3d9e2a83c6eb9d4dd1a9dada5f35b87d49b From 16905c8210b0ada422d2552d3908f70cf9c6a2d3 Mon Sep 17 00:00:00 2001 From: Abnormal <86753001+AbnormalPoof@users.noreply.github.com> Date: Sun, 8 Sep 2024 21:48:48 +0000 Subject: [PATCH 366/469] Update ScriptedFunkinSprite.hx --- source/funkin/modding/base/ScriptedFunkinSprite.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/modding/base/ScriptedFunkinSprite.hx b/source/funkin/modding/base/ScriptedFunkinSprite.hx index dd8d150070..2ce84db3e1 100644 --- a/source/funkin/modding/base/ScriptedFunkinSprite.hx +++ b/source/funkin/modding/base/ScriptedFunkinSprite.hx @@ -1,8 +1,8 @@ package funkin.modding.base; /** - * A script that can be tied to an FlxSprite. - * Create a scripted class that extends FlxSprite to use this. + * A script that can be tied to a FunkinSprite. + * Create a scripted class that extends FunkinSprite to use this. */ @:hscriptClass class ScriptedFunkinSprite extends funkin.graphics.FunkinSprite implements HScriptedClass {} From 6961e6cc63f9e9fc63cf8007eba8a82c82c86389 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Mon, 9 Sep 2024 08:23:46 +0200 Subject: [PATCH 367/469] Add a buncho Gizmos --- art | 2 +- assets | 2 +- source/funkin/graphics/FlxFilteredSprite.hx | 19 ++- .../graphics/adobeanimate/FlxAtlasSprite.hx | 5 +- source/funkin/save/Save.hx | 2 +- source/funkin/ui/charSelect/CharSelectGF.hx | 2 +- .../funkin/ui/charSelect/CharSelectPlayer.hx | 11 +- .../ui/charSelect/CharSelectSubState.hx | 143 +++++++++++++++--- source/funkin/ui/title/TitleState.hx | 3 + source/funkin/util/FramesJSFLParser.hx | 21 ++- source/funkin/util/logging/AnsiTrace.hx | 3 + 11 files changed, 175 insertions(+), 38 deletions(-) diff --git a/art b/art index faeba700c5..e2663c1cbe 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit faeba700c5526bd4fd57ccc927d875c82b9d3553 +Subproject commit e2663c1cbe029f04a98500735943f0b9465548bf diff --git a/assets b/assets index d2ea072f32..c6a2a13d75 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit d2ea072f32d0ba74cf8c62576f28e7b03d591ddb +Subproject commit c6a2a13d754e3566d25e762acec3a74e7116f23e diff --git a/source/funkin/graphics/FlxFilteredSprite.hx b/source/funkin/graphics/FlxFilteredSprite.hx index 3563ea9764..af4418d60e 100644 --- a/source/funkin/graphics/FlxFilteredSprite.hx +++ b/source/funkin/graphics/FlxFilteredSprite.hx @@ -37,7 +37,6 @@ import openfl.display._internal.CairoGraphics as GfxRenderer; /** * A modified `FlxSprite` that supports filters. * The name's pretty much self-explanatory. - * @author CheemsAndFriends */ @:access(openfl.geom.Rectangle) @:access(openfl.filters.BitmapFilter) @@ -146,7 +145,7 @@ class FlxFilteredSprite extends FlxSprite _matrix.ty = Math.floor(_matrix.ty); } - camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shader); + camera.drawPixels((filtered) ? _blankFrame : _frame, framePixels, _matrix, colorTransform, blend, antialiasing, shader); } @:noCompletion @@ -154,7 +153,6 @@ class FlxFilteredSprite extends FlxSprite { filterDirty = false; _filterMatrix.identity(); - var filteredFrame = (filtered) ? _frame : null; if (filters != null && filters.length > 0) { @@ -195,6 +193,7 @@ class FlxFilteredSprite extends FlxSprite } else { + trace("GAGAGA"); resetFrame(); filtered = false; } @@ -215,6 +214,11 @@ class FlxFilteredSprite extends FlxSprite return super.set_frame(value); } + + override public function destroy() + { + super.destroy(); + } } @:noCompletion @@ -339,6 +343,15 @@ class FlxAnimateFilterRenderer if (target1 == null) bitmap2.dispose(); if (target2 == null) bitmap3.dispose(); + // var gl = renderer.__gl; + + // var renderBuffer = bitmap.getTexture(renderer.__context3D); + // @:privateAccess + // gl.readPixels(0, 0, bitmap.width, bitmap.height, renderBuffer.__format, gl.UNSIGNED_BYTE, bitmap.image.data); + // bitmap.image.version = 0; + // @:privateAccess + // bitmap.__textureVersion = -1; + return bitmap; } diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index e187c78083..d4df2a4f7a 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -217,6 +217,9 @@ class FlxAtlasSprite extends FlxAnimate { if (this.anim == null) return false; if (!this.anim.isPlaying) return false; + + if (fr != null) return (anim.reversed && anim.curFrame < fr.index || !anim.reversed && anim.curFrame >= (fr.index + fr.duration)); + return (anim.reversed && anim.curFrame == 0 || !(anim.reversed) && (anim.curFrame) >= (anim.length - 1)); } @@ -283,7 +286,7 @@ class FlxAtlasSprite extends FlxAnimate { onAnimationFrame.dispatch(currentAnimation, frame); - if (fr != null && frame > (fr.index + fr.duration - 1) || isLoopComplete()) + if (isLoopComplete()) { anim.pause(); _onAnimationComplete(); diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index a3d9455944..80b05e5ac2 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -412,7 +412,7 @@ class Save */ public function addCharacterSeen(character:String):Void { - data.unlocks.charactersSeen.push(character); + if (!data.unlocks.charactersSeen.contains(character)) data.unlocks.charactersSeen.push(character); } /** diff --git a/source/funkin/ui/charSelect/CharSelectGF.hx b/source/funkin/ui/charSelect/CharSelectGF.hx index a990db8178..e8eeded40f 100644 --- a/source/funkin/ui/charSelect/CharSelectGF.hx +++ b/source/funkin/ui/charSelect/CharSelectGF.hx @@ -133,7 +133,7 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass fadeTimer += FlxG.elapsed; if (fadeTimer >= 1 / 24) { - fadeTimer = 0; + fadeTimer -= FlxG.elapsed; // only inc the index for the first frame, used for reference of where to "start" if (fadeAnimIndex == 0) { diff --git a/source/funkin/ui/charSelect/CharSelectPlayer.hx b/source/funkin/ui/charSelect/CharSelectPlayer.hx index 8809ad29b8..9767c2d3bf 100644 --- a/source/funkin/ui/charSelect/CharSelectPlayer.hx +++ b/source/funkin/ui/charSelect/CharSelectPlayer.hx @@ -26,19 +26,17 @@ class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass } else { - playAnimation("idle", true, false, true); + playAnimation("idle", true, false, false); } + case "deselect": + playAnimation("deselect loop start", true, false, true); case "slidein idle point", "cannot select", "unlock": - playAnimation("idle", true, false, true); + playAnimation("idle", true, false, false); case "idle": trace('Waiting for onBeatHit'); } }); - - onAnimationFrame.add(function(animLabel:String, frame:Int) { - if (animLabel == "deselect" && desLp != null && frame >= desLp.index) playAnimation("deselect loop start", true, false, true); - }); } public function onStepHit(event:SongTimeScriptEvent):Void {} @@ -50,6 +48,7 @@ class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass // I tried make this not interrupt an existing idle, // but isAnimationFinished() and isLoopComplete() both don't work! What the hell? // danceEvery isn't necessary if that gets fixed. + // if (getCurrentAnimation() == "idle") { trace('Player beat hit'); diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 80ad594396..818b68c1cf 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -1,5 +1,6 @@ package funkin.ui.charSelect; +import openfl.filters.BitmapFilter; import flixel.FlxObject; import flixel.FlxSprite; import flixel.group.FlxGroup; @@ -31,6 +32,9 @@ import funkin.vis.dsp.SpectralAnalyzer; import openfl.display.BlendMode; import funkin.save.Save; import openfl.filters.ShaderFilter; +import funkin.util.FramesJSFLParser; +import funkin.util.FramesJSFLParser.FramesJSFLInfo; +import funkin.util.FramesJSFLParser.FramesJSFLFrame; class CharSelectSubState extends MusicBeatSubState { @@ -82,10 +86,16 @@ class CharSelectSubState extends MusicBeatSubState var charSelectCam:FunkinCamera; + var selectedBizz:Array = [ + new DropShadowFilter(0, 0, 0xFFFFFF, 1, 2, 2, 21, 1, false, false, false), + new DropShadowFilter(5, 45, 0x000000, 1, 2, 2, 1, 1, false, false, false) + ]; + + var bopInfo:FramesJSFLInfo; + public function new() { super(); - loadAvailableCharacters(); } @@ -119,6 +129,8 @@ class CharSelectSubState extends MusicBeatSubState { super.create(); + bopInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/iconBopInfo/iconBopInfo.txt")); + var bg:FlxSprite = new FlxSprite(-153, -140); bg.loadGraphic(Paths.image('charSelect/charSelectBG')); bg.scrollFactor.set(0.1, 0.1); @@ -281,10 +293,21 @@ class CharSelectSubState extends MusicBeatSubState unlockSound = new FunkinSound(); unlockSound.loadEmbedded(Paths.sound('CS_unlock')); unlockSound.pitch = 1; - unlockSound.volume = 0.7; + + unlockSound.volume = 0; + unlockSound.play(true); FlxG.sound.defaultSoundGroup.add(unlockSound); + // playing it here to preload it. not doing this makes a super awkward pause at the end of the intro + // TODO: probably make an intro thing for funkinSound itself that preloads the next audio? + FunkinSound.playMusic('stayFunky', + { + startingVolume: 0, + overrideExisting: true, + restartTrack: true, + }); + initLocks(); for (index => member in grpIcons.members) @@ -384,16 +407,24 @@ class CharSelectSubState extends MusicBeatSubState grpIcons.scrollFactor.set(); - // playing it here to preload it. not doing this makes a super awkward pause at the end of the intro - // TODO: probably make an intro thing for funkinSound itself that preloads the next audio? - FunkinSound.playMusic('stayFunky', - { - startingVolume: 0, - overrideExisting: true, - restartTrack: true, - }); - - unLock(); + if (nonLocks.length > 0) unLock(); + else + FunkinSound.playMusic('stayFunky', + { + startingVolume: 1, + overrideExisting: true, + restartTrack: true, + onLoad: function() { + @:privateAccess + gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1); + #if desktop + // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 + // So we want to manually change it! + @:privateAccess + gfChill.analyzer.fftN = 512; + #end + } + }); } function unLock() @@ -428,15 +459,37 @@ class CharSelectSubState extends MusicBeatSubState lock.playAnimation("unlock"); + unlockSound.volume = 0.7; unlockSound.play(true); lock.onAnimationComplete.addOnce(function(_) { + var char = availableChars.get(index); camera.flash(0xFFFFFFFF, 0.1); playerChill.playAnimation("unlock"); playerChill.visible = true; + var id = grpIcons.members.indexOf(lock); + + nametag.switchChar(char); + gfChill.switchGF(char); + + var icon = new PixelatedIcon(0, 0); + icon.setCharacter(char); + icon.setGraphicSize(128, 128); + icon.updateHitbox(); + grpIcons.insert(id, icon); + grpIcons.remove(lock, true); + icon.ID = 0; + + bopPlay = true; + + updateIconPositions(); + if (nonLocks.length == 0) { + playerChillOut.onAnimationComplete.addOnce((_) -> playerChillOut.switchChar(char)); + @:bypassAccessor curChar = char; + Save.instance.addCharacterSeen(char); FunkinSound.playMusic('stayFunky', { startingVolume: 1, @@ -713,6 +766,48 @@ class CharSelectSubState extends MusicBeatSubState cursorDarkBlue.y = MathUtil.coolLerp(cursorDarkBlue.y, cursorLocIntended.y, lerpAmnt * 0.2); } + var bopTimer:Float = 0; + + var delay = 1 / 24; + + var bopFr = 0; + + var bopPlay:Bool = false; + + var bopRefX:Float = 0; + var bopRefY:Float = 0; + + function doBop(icon:PixelatedIcon, elapsed:Float):Void + { + if (bopFr >= bopInfo.frames.length) + { + bopRefX = 0; + bopRefY = 0; + bopPlay = false; + bopFr = 0; + return; + } + bopTimer += elapsed; + + if (bopTimer >= delay) + { + bopTimer -= bopTimer; + + var refFrame = bopInfo.frames[bopInfo.frames.length - 1]; + var curFrame = bopInfo.frames[bopFr]; + + var xDiff:Float = curFrame.x - refFrame.x; + var yDiff:Float = curFrame.y - refFrame.y; + var scaleXDiff:Float = curFrame.scaleX - refFrame.scaleX; + var scaleYDiff:Float = curFrame.scaleY - refFrame.scaleY; + + icon.scale.set(2.6, 2.6); + icon.scale.add(scaleXDiff, scaleYDiff); + + bopFr++; + } + } + public override function dispatchEvent(event:ScriptEvent):Void { // super.dispatchEvent(event) dispatches event to module scripts. @@ -725,11 +820,6 @@ class CharSelectSubState extends MusicBeatSubState function spamOnStep():Void { - if (FlxG.keys.justPressed.B) - { - cursorY = 3; - cursorX = 3; - } if (spamUp || spamDown || spamLeft || spamRight) { // selectSound.changePitchBySemitone(1); @@ -776,7 +866,7 @@ class CharSelectSubState extends MusicBeatSubState case "idle": lock.playAnimation("selected"); case "selected" | "clicked": - if (controls.ACCEPT) lock.playAnimation((FlxG.keys.pressed.CONTROL) ? "unlock" : "clicked", true); + if (controls.ACCEPT) lock.playAnimation("clicked", true); } } else @@ -789,8 +879,19 @@ class CharSelectSubState extends MusicBeatSubState if (index == getCurrentSelected()) { // memb.pixels = memb.withDropShadow.clone(); - if (memb.scale.x != 2.6) memb.filters = [new DropShadowFilter()]; - memb.scale.set(2.6, 2.6); + memb.filters = selectedBizz; + + if (bopPlay) + { + if (bopRefX == 0) + { + bopRefX = memb.x; + bopRefY = memb.y; + } + doBop(memb, FlxG.elapsed); + } + else + memb.scale.set(2.6, 2.6); if (controls.ACCEPT) memb.animation.play("confirm"); if (memb.animation.curAnim.name == "confirm" && controls.BACK) @@ -805,7 +906,7 @@ class CharSelectSubState extends MusicBeatSubState else { // memb.pixels = memb.noDropShadow.clone(); - if (memb.scale.x == 2) memb.filters = []; + memb.filters = null; memb.scale.set(2, 2); } } diff --git a/source/funkin/ui/title/TitleState.hx b/source/funkin/ui/title/TitleState.hx index 8087916cbd..79b30ff4d8 100644 --- a/source/funkin/ui/title/TitleState.hx +++ b/source/funkin/ui/title/TitleState.hx @@ -32,6 +32,7 @@ import openfl.media.Video; import openfl.net.NetStream; import funkin.api.newgrounds.NGio; import openfl.display.BlendMode; +import funkin.save.Save; #if desktop #end @@ -272,6 +273,8 @@ class TitleState extends MusicBeatState } #end + if (Save.instance.charactersSeen.contains("pico")) Save.instance.charactersSeen.remove("pico"); + Conductor.instance.update(); /* if (FlxG.onMobile) diff --git a/source/funkin/util/FramesJSFLParser.hx b/source/funkin/util/FramesJSFLParser.hx index 33bcf9d9d9..fa01a2c875 100644 --- a/source/funkin/util/FramesJSFLParser.hx +++ b/source/funkin/util/FramesJSFLParser.hx @@ -25,9 +25,22 @@ class FramesJSFLParser var x:Float = Std.parseFloat(frameInfo[0]); var y:Float = Std.parseFloat(frameInfo[1]); - var alpha:Float = Std.parseFloat(frameInfo[2]); - - var shit:FramesJSFLFrame = {x: x, y: y, alpha: alpha}; + var alpha:Float = (frameInfo[2] != "undefined") ? Std.parseFloat(frameInfo[2]) : 100; + + var scaleX:Float = 1; + var scaleY:Float = 1; + + if (frameInfo[3] != null) scaleX = Std.parseFloat(frameInfo[4]); + if (frameInfo[4] != null) scaleX = Std.parseFloat(frameInfo[4]); + + var shit:FramesJSFLFrame = + { + x: x, + y: y, + alpha: alpha, + scaleX: scaleX, + scaleY: scaleY + }; output.frames.push(shit); } @@ -45,4 +58,6 @@ typedef FramesJSFLFrame = var x:Float; var y:Float; var alpha:Float; + var scaleX:Float; + var scaleY:Float; } diff --git a/source/funkin/util/logging/AnsiTrace.hx b/source/funkin/util/logging/AnsiTrace.hx index 2c18d494d2..322a66820f 100644 --- a/source/funkin/util/logging/AnsiTrace.hx +++ b/source/funkin/util/logging/AnsiTrace.hx @@ -6,6 +6,9 @@ class AnsiTrace // but adds nice cute ANSI things public static function trace(v:Dynamic, ?info:haxe.PosInfos) { + #if TREMOVE + return; + #end var str = formatOutput(v, info); #if js if (js.Syntax.typeof(untyped console) != "undefined" && (untyped console).log != null) (untyped console).log(str); From f8a2709482ec00b03c9bc77cd5128d6918ba63aa Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Mon, 9 Sep 2024 08:03:06 +0100 Subject: [PATCH 368/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 5af650e1ac..08b78bdb4a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5af650e1ac562f95e03f6484d7fa33d7d83f5c8d +Subproject commit 08b78bdb4a3e14c487a5cb51dffff29f20c240df From 50d2a8fd9d7e1475be54906d455243ed1108846d Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Mon, 9 Sep 2024 08:06:11 +0100 Subject: [PATCH 369/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 5af650e1ac..3879a4905a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5af650e1ac562f95e03f6484d7fa33d7d83f5c8d +Subproject commit 3879a4905a96c3bbe0756fe49f07902041e893d3 From b9b849024334102b6d7a17d323d9460b96b57444 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Mon, 9 Sep 2024 08:09:40 +0100 Subject: [PATCH 370/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 08b78bdb4a..8791959f46 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 08b78bdb4a3e14c487a5cb51dffff29f20c240df +Subproject commit 8791959f4643d838cccb5b582b5a20df964f673a From d61357b80db4eaa1b929009726de0e3555539015 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 9 Sep 2024 11:40:17 -0400 Subject: [PATCH 371/469] hmm flxanimate bump --- hmm.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hmm.json b/hmm.json index ea32399f80..5205603c92 100644 --- a/hmm.json +++ b/hmm.json @@ -46,7 +46,7 @@ "name": "flxanimate", "type": "git", "dir": null, - "ref": "27a0a1f0afc1074f619991472fb3ee209aad28df", + "ref": "280d1a46ac60021d08bb18181631cbd6d061782c", "url": "https://github.com/Dot-Stuff/flxanimate" }, { @@ -212,4 +212,4 @@ "url": "https://github.com/fponticelli/thx.semver" } ] -} \ No newline at end of file +} From 35af057f88ef52da2488d556d092e26f6f950f98 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Mon, 9 Sep 2024 17:41:25 +0200 Subject: [PATCH 372/469] Fix scaleY not parsing --- source/funkin/ui/charSelect/CharSelectSubState.hx | 9 ++++----- source/funkin/util/FramesJSFLParser.hx | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index 818b68c1cf..db88442887 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -110,8 +110,6 @@ class CharSelectSubState extends MusicBeatSubState var playerData = player.getCharSelectData(); if (playerData == null) continue; - trace(player.isUnlocked()); - var targetPosition:Int = playerData.position ?? 0; while (availableChars.exists(targetPosition)) { @@ -487,7 +485,10 @@ class CharSelectSubState extends MusicBeatSubState if (nonLocks.length == 0) { - playerChillOut.onAnimationComplete.addOnce((_) -> playerChillOut.switchChar(char)); + playerChillOut.onAnimationComplete.addOnce((_) -> { + playerChillOut.visible = false; + playerChillOut.switchChar(char); + }); @:bypassAccessor curChar = char; Save.instance.addCharacterSeen(char); FunkinSound.playMusic('stayFunky', @@ -796,8 +797,6 @@ class CharSelectSubState extends MusicBeatSubState var refFrame = bopInfo.frames[bopInfo.frames.length - 1]; var curFrame = bopInfo.frames[bopFr]; - var xDiff:Float = curFrame.x - refFrame.x; - var yDiff:Float = curFrame.y - refFrame.y; var scaleXDiff:Float = curFrame.scaleX - refFrame.scaleX; var scaleYDiff:Float = curFrame.scaleY - refFrame.scaleY; diff --git a/source/funkin/util/FramesJSFLParser.hx b/source/funkin/util/FramesJSFLParser.hx index fa01a2c875..3a9ff8d0a0 100644 --- a/source/funkin/util/FramesJSFLParser.hx +++ b/source/funkin/util/FramesJSFLParser.hx @@ -31,7 +31,7 @@ class FramesJSFLParser var scaleY:Float = 1; if (frameInfo[3] != null) scaleX = Std.parseFloat(frameInfo[4]); - if (frameInfo[4] != null) scaleX = Std.parseFloat(frameInfo[4]); + if (frameInfo[4] != null) scaleY = Std.parseFloat(frameInfo[4]); var shit:FramesJSFLFrame = { From 5a4795eea28319cb5f614137b67e2ce3761e7fef Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 9 Sep 2024 11:44:27 -0400 Subject: [PATCH 373/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 3879a4905a..f0c46cf53c 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 3879a4905a96c3bbe0756fe49f07902041e893d3 +Subproject commit f0c46cf53cebc3aed5773c945b8633f7b31fe25d From 7220fd7e95a0a0f8d26f2550ca6bbf62f625b85a Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 9 Sep 2024 11:45:37 -0400 Subject: [PATCH 374/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 8791959f46..80cfbbdeba 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8791959f4643d838cccb5b582b5a20df964f673a +Subproject commit 80cfbbdebad184f2f5429677a0ac05f95cef6706 From 591d4a29f42e26f6decf1c064c341cc533b245b1 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 9 Sep 2024 12:34:55 -0400 Subject: [PATCH 375/469] gameover positioning fixes --- source/funkin/play/GameOverSubState.hx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index 6eb954c0fa..f6a7148f81 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -17,6 +17,7 @@ import funkin.ui.story.StoryMenuState; import funkin.util.MathUtil; import openfl.utils.Assets; import funkin.effects.RetroCameraFade; +import flixel.math.FlxPoint; /** * A substate which renders over the PlayState when the player dies. @@ -168,8 +169,8 @@ class GameOverSubState extends MusicBeatSubState // Assign a camera follow point to the boyfriend's position. cameraFollowPoint = new FlxObject(PlayState.instance.cameraFollowPoint.x, PlayState.instance.cameraFollowPoint.y, 1, 1); - cameraFollowPoint.x = boyfriend.getGraphicMidpoint().x; - cameraFollowPoint.y = boyfriend.getGraphicMidpoint().y; + cameraFollowPoint.x = getMidPointOld(boyfriend).x; + cameraFollowPoint.y = getMidPointOld(boyfriend).y; var offsets:Array = boyfriend.getDeathCameraOffsets(); cameraFollowPoint.x += offsets[0]; cameraFollowPoint.y += offsets[1]; @@ -180,6 +181,21 @@ class GameOverSubState extends MusicBeatSubState targetCameraZoom = (PlayState?.instance?.currentStage?.camZoom ?? 1.0) * boyfriend.getDeathCameraZoom(); } + /** + * FlxSprite.getMidpoint(); calculations changed in this git commit + * https://github.com/HaxeFlixel/flixel/commit/1553b5af0871462fcefedc091b7885437d6c36d2 + * https://github.com/HaxeFlixel/flixel/pull/3125 + * + * So we use this to do the old math that gets the midpoint of our graphics + * Luckily, we don't use getGraphicMidpoint() much in the code, so it's fine being in GameoverSubState here. + * @return FlxPoint + */ + function getMidPointOld(spr:FlxSprite, ?point:FlxPoint):FlxPoint + { + if (point == null) point = FlxPoint.get(); + return point.set(spr.x + spr.frameWidth * 0.5 * spr.scale.x, spr.y + spr.frameHeight * 0.5 * spr.scale.y); + } + /** * Forcibly reset the camera zoom level to that of the current stage. * This prevents camera zoom events from adversely affecting the game over state. From e2c287e112a3b1b4369be464caa11b20016b7083 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 9 Sep 2024 13:01:19 -0400 Subject: [PATCH 376/469] logarithmic volume control --- hmm.json | 2 +- source/funkin/ui/options/FunkinSoundTray.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hmm.json b/hmm.json index 5205603c92..cf41fdcd9f 100644 --- a/hmm.json +++ b/hmm.json @@ -18,7 +18,7 @@ "name": "flixel", "type": "git", "dir": null, - "ref": "599f38eeb502a8ba6439784036c2cfdc7b485260", + "ref": "f2b090d6c608471e730b051c8ee22b8b378964b1", "url": "https://github.com/FunkinCrew/flixel" }, { diff --git a/source/funkin/ui/options/FunkinSoundTray.hx b/source/funkin/ui/options/FunkinSoundTray.hx index 792e38fc43..170ad84972 100644 --- a/source/funkin/ui/options/FunkinSoundTray.hx +++ b/source/funkin/ui/options/FunkinSoundTray.hx @@ -120,7 +120,7 @@ class FunkinSoundTray extends FlxSoundTray lerpYPos = 10; visible = true; active = true; - var globalVolume:Int = Math.round(FlxG.sound.volume * 10); + var globalVolume:Int = Math.round(FlxG.sound.logToLinear(FlxG.sound.volume) * 10); if (FlxG.sound.muted) { From 8d27a45d17635e526abb91c7b01186e22f300aca Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Mon, 9 Sep 2024 21:39:38 +0200 Subject: [PATCH 377/469] change lock a bit --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index c6a2a13d75..37b30c8d39 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit c6a2a13d754e3566d25e762acec3a74e7116f23e +Subproject commit 37b30c8d3934617f2a4630e69a51887555a605eb From af8fe0c0d2445bb2ebf01b0bc669f179916e7020 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Tue, 10 Sep 2024 01:11:41 +0200 Subject: [PATCH 378/469] Remove onAnimationLoopComplete --- source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index d4df2a4f7a..fefb224eba 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -38,11 +38,6 @@ class FlxAtlasSprite extends FlxAnimate */ public var onAnimationComplete:FlxTypedSignalVoid> = new FlxTypedSignal(); - /** - * Signal dispatched when a looping animation finishes playing - */ - public var onAnimationLoopComplete:FlxTypedSignalVoid> = new FlxTypedSignal(); - var currentAnimation:String; var canPlayOtherAnims:Bool = true; @@ -162,7 +157,6 @@ class FlxAtlasSprite extends FlxAnimate anim.onComplete.add(function() { if (loop) { - onAnimationLoopComplete.dispatch(id); this.anim.play(id, restart, false, startFrame); this.currentAnimation = id; } @@ -307,9 +301,7 @@ class FlxAtlasSprite extends FlxAnimate { if (currentAnimation != null) { - if (looping) onAnimationLoopComplete.dispatch(currentAnimation); - else - onAnimationComplete.dispatch(currentAnimation); + onAnimationComplete.dispatch(currentAnimation); } } From 877d64c32bf877ad501883efc638386bc20cc46f Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 9 Sep 2024 21:21:50 -0400 Subject: [PATCH 379/469] Fixed a bug where the instrumental selector would be ignored sometimes --- source/funkin/ui/freeplay/FreeplayState.hx | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index e421efd8ac..6e961c2875 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1985,22 +1985,6 @@ class FreeplayState extends MusicBeatSubState return; } - var baseInstrumentalId:String = targetSong?.getBaseInstrumentalId(targetDifficultyId, targetDifficulty.variation ?? Constants.DEFAULT_VARIATION) ?? ''; - var altInstrumentalIds:Array = targetSong?.listAltInstrumentalIds(targetDifficultyId, - targetDifficulty.variation ?? Constants.DEFAULT_VARIATION) ?? []; - - var targetInstId:String = baseInstrumentalId; - - // TODO: Make this a UI element. - #if FEATURE_DEBUG_FUNCTIONS - if (altInstrumentalIds.length > 0 && FlxG.keys.pressed.CONTROL) - { - targetInstId = altInstrumentalIds[0]; - } - - if (targetInstId == null) targetInstId = baseInstrumentalId; - #end - // Visual and audio effects. FunkinSound.playOnce(Paths.sound('confirmMenu')); if (dj != null) dj.confirm(); From 4c1b4859e77ac0634b1500002624fc053e188de8 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 9 Sep 2024 22:35:02 -0400 Subject: [PATCH 380/469] Fix a bug where the random event in Week 3 would always trigger --- assets | 2 +- source/funkin/play/character/BaseCharacter.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets b/assets index 80cfbbdeba..5a4dea4251 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 80cfbbdebad184f2f5429677a0ac05f95cef6706 +Subproject commit 5a4dea425190d192f4f242a189a8daf5779efb8b diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index 28b2dbee20..bce7f0d983 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -376,7 +376,7 @@ class BaseCharacter extends Bopper { if (isAnimationFinished()) { - trace('Not playing hold (${getCurrentAnimation()}) (${isAnimationFinished()}, ${getCurrentAnimation().endsWith(Constants.ANIMATION_HOLD_SUFFIX)}, ${hasAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX)})'); + // trace('Not playing hold (${getCurrentAnimation()}) (${isAnimationFinished()}, ${getCurrentAnimation().endsWith(Constants.ANIMATION_HOLD_SUFFIX)}, ${hasAnimation(getCurrentAnimation() + Constants.ANIMATION_HOLD_SUFFIX)})'); } } From adb4bc96ca58f7ea9c595516f270455841820709 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 9 Sep 2024 23:47:17 -0400 Subject: [PATCH 381/469] flxanimate version bump --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index cf41fdcd9f..50b81bb855 100644 --- a/hmm.json +++ b/hmm.json @@ -46,7 +46,7 @@ "name": "flxanimate", "type": "git", "dir": null, - "ref": "280d1a46ac60021d08bb18181631cbd6d061782c", + "ref": "0654797e5eb7cd7de0c1b2dbaa1efe5a1e1d9412", "url": "https://github.com/Dot-Stuff/flxanimate" }, { From f6bd20378669a3fef445ef3f0a3580e90a1e86df Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 10 Sep 2024 01:33:15 -0400 Subject: [PATCH 382/469] Fix an issue where base inst wouldn't be chosen properly on remixes --- source/funkin/ui/freeplay/FreeplayState.hx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 6e961c2875..d3c72e924e 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1985,6 +1985,12 @@ class FreeplayState extends MusicBeatSubState return; } + if (targetInstId == null) + { + var baseInstrumentalId:String = targetSong?.getBaseInstrumentalId(targetDifficultyId, targetDifficulty.variation ?? Constants.DEFAULT_VARIATION) ?? ''; + targetInstId = baseInstrumentalId; + } + // Visual and audio effects. FunkinSound.playOnce(Paths.sound('confirmMenu')); if (dj != null) dj.confirm(); @@ -2409,6 +2415,7 @@ class DifficultySprite extends FlxSprite if (assetDiffIdParts.length == 0) break; assetDiffId = assetDiffIdParts.join('-'); } + trace('Found difficulty asset: freeplay/freeplay${assetDiffId}'); // Check for an XML to use an animation instead of an image. if (Assets.exists(Paths.file('images/freeplay/freeplay${assetDiffId}.xml'))) From 34e93fa95a464656b5e7742a74d9f76fd5a7e4d1 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Tue, 10 Sep 2024 07:25:11 +0100 Subject: [PATCH 383/469] color util (with just one function lol) --- source/funkin/util/FlxColorUtil.hx | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 source/funkin/util/FlxColorUtil.hx diff --git a/source/funkin/util/FlxColorUtil.hx b/source/funkin/util/FlxColorUtil.hx new file mode 100644 index 0000000000..429d536d82 --- /dev/null +++ b/source/funkin/util/FlxColorUtil.hx @@ -0,0 +1,22 @@ +package funkin.util; + +import flixel.util.FlxColor; + +/** + * Non inline FlxColor functions for use in hscript files + */ +class FlxColorUtil +{ + /** + * Get an interpolated color based on two different colors. + * + * @param Color1 The first color + * @param Color2 The second color + * @param Factor Value from 0 to 1 representing how much to shift Color1 toward Color2 + * @return The interpolated color + */ + public static function interpolate(Color1:FlxColor, Color2:FlxColor, Factor:Float = 0.5):FlxColor + { + return FlxColor.interpolate(Color1, Color2, Factor); + } +} From 03d9416ab16ab07f6a3552dd3938555a5f8edd34 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Tue, 10 Sep 2024 07:26:15 +0100 Subject: [PATCH 384/469] texture swap shader --- source/funkin/graphics/shaders/TextureSwap.hx | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 source/funkin/graphics/shaders/TextureSwap.hx diff --git a/source/funkin/graphics/shaders/TextureSwap.hx b/source/funkin/graphics/shaders/TextureSwap.hx new file mode 100644 index 0000000000..65de87ea39 --- /dev/null +++ b/source/funkin/graphics/shaders/TextureSwap.hx @@ -0,0 +1,48 @@ +package funkin.graphics.shaders; + +import flixel.system.FlxAssets.FlxShader; +import flixel.util.FlxColor; +import openfl.display.BitmapData; + +class TextureSwap extends FlxShader +{ + public var swappedImage(default, set):BitmapData; + public var amount(default, set):Float; + + function set_swappedImage(_bitmapData:BitmapData):BitmapData + { + image.input = _bitmapData; + + return _bitmapData; + } + + function set_amount(val:Float):Float + { + fadeAmount.value = [val]; + + return val; + } + + @:glFragmentSource(' + #pragma header + + uniform sampler2D image; + uniform float fadeAmount; + + void main() + { + vec4 tex = flixel_texture2D(bitmap, openfl_TextureCoordv); + vec4 tex2 = flixel_texture2D(image, openfl_TextureCoordv); + + vec4 finalColor = mix(tex, vec4(tex2.rgb, tex.a), fadeAmount); + + gl_FragColor = finalColor; + } + ') + public function new() + { + super(); + + this.amount = 1; + } +} From ee78178b20bb90b04f83504b26f533f9be03d6ee Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Tue, 10 Sep 2024 07:30:24 +0100 Subject: [PATCH 385/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 5a4dea4251..aaa449a55f 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5a4dea425190d192f4f242a189a8daf5779efb8b +Subproject commit aaa449a55f9ab46463328074b9e3afc322aeb1ed From 7063fafd33de3d2d2b256cda9f08ce4462db2c9a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 10 Sep 2024 17:26:33 -0400 Subject: [PATCH 386/469] Fix web builds using disgusting amounts of bandwidth --- project.hxp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project.hxp b/project.hxp index 0e090a4c95..1193a9cd42 100644 --- a/project.hxp +++ b/project.hxp @@ -469,7 +469,7 @@ class Project extends HXProject { // Should be true only on web builds. // Enabling embedding and preloading is required to preload assets properly. EMBED_ASSETS.apply(this, isWeb()); - PRELOAD_ALL.apply(this, true); + PRELOAD_ALL.apply(this, !isWeb()); // Should be true except on MacOS. // File drop doesn't work there. From 5021b499159a4b330283d8c931178ec2c56e0cfb Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 10 Sep 2024 17:27:04 -0400 Subject: [PATCH 387/469] Enable the New Character backing card --- source/funkin/ui/freeplay/FreeplayState.hx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index d3c72e924e..ec07c9d153 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -253,6 +253,8 @@ class FreeplayState extends MusicBeatSubState switch (currentCharacterId) { + case(PlayerRegistry.instance.hasNewCharacter()) => true: + backingCard = new NewCharacterCard(currentCharacter); case 'bf': backingCard = new BoyfriendCard(currentCharacter); case 'pico': From c678cabbf2fd00b4d8d8a42b7d625061e4637f68 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 10 Sep 2024 17:30:33 -0400 Subject: [PATCH 388/469] Fix issue where difficulty names wouldn't show up properly sometimes. --- source/funkin/ui/freeplay/FreeplayState.hx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index ec07c9d153..14c5829af8 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -349,6 +349,7 @@ class FreeplayState extends MusicBeatSubState var displayedVariations = song.getVariationsByCharacter(currentCharacter); trace('Displayed Variations (${songId}): $displayedVariations'); var availableDifficultiesForSong:Array = song.listSuffixedDifficulties(displayedVariations, false, false); + var unsuffixedDifficulties = song.listDifficulties(displayedVariations, false, false); trace('Available Difficulties: $availableDifficultiesForSong'); if (availableDifficultiesForSong.length == 0) continue; @@ -357,6 +358,10 @@ class FreeplayState extends MusicBeatSubState { diffIdsTotal.pushUnique(difficulty); } + for (difficulty in unsuffixedDifficulties) + { + diffIdsTotal.pushUnique(difficulty); + } } } @@ -2414,10 +2419,13 @@ class DifficultySprite extends FlxSprite // Remove the last suffix of the difficulty id until we find an asset or there are no more suffixes. var assetDiffIdParts:Array = assetDiffId.split('-'); assetDiffIdParts.pop(); - if (assetDiffIdParts.length == 0) break; + if (assetDiffIdParts.length == 0) + { + trace('Could not find difficulty asset: freeplay/freeplay${diffId} (from ${diffId})'); + return; + }; assetDiffId = assetDiffIdParts.join('-'); } - trace('Found difficulty asset: freeplay/freeplay${assetDiffId}'); // Check for an XML to use an animation instead of an image. if (Assets.exists(Paths.file('images/freeplay/freeplay${assetDiffId}.xml'))) @@ -2429,6 +2437,7 @@ class DifficultySprite extends FlxSprite else { this.loadGraphic(Paths.image('freeplay/freeplay' + assetDiffId)); + trace('Loaded difficulty asset: freeplay/freeplay${assetDiffId} (from ${diffId})'); } } } From 7aa20223b98a9c99fbd236675e4ba8af7674137f Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 10 Sep 2024 17:35:52 -0400 Subject: [PATCH 389/469] Update art submodule --- art | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/art b/art index faeba700c5..0bb988c497 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit faeba700c5526bd4fd57ccc927d875c82b9d3553 +Subproject commit 0bb988c49788fd25a230b56dd9e4448838bc79c9 From 0ec22bcc85a2abb357da8dc20d96fe0eddd8cfa5 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 10 Sep 2024 17:40:27 -0400 Subject: [PATCH 390/469] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 5a4dea4251..ba6621b9e8 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5a4dea425190d192f4f242a189a8daf5779efb8b +Subproject commit ba6621b9e8126b3e84ae9b79f22999b2b2402c65 From f74365f2309f64c9cd6173a4b2728ed3a7c1b093 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 00:55:14 +0200 Subject: [PATCH 391/469] Add Changes --- assets | 2 +- source/funkin/graphics/FlxFilteredSprite.hx | 1 - .../funkin/ui/charSelect/CharSelectPlayer.hx | 6 - .../ui/charSelect/CharSelectSubState.hx | 237 +++++++++++------- source/funkin/ui/charSelect/IntroSubState.hx | 141 +++++++++++ 5 files changed, 293 insertions(+), 94 deletions(-) create mode 100644 source/funkin/ui/charSelect/IntroSubState.hx diff --git a/assets b/assets index 37b30c8d39..49e375616e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 37b30c8d3934617f2a4630e69a51887555a605eb +Subproject commit 49e375616e0c4f3fc66874ebe4262ac389d8c746 diff --git a/source/funkin/graphics/FlxFilteredSprite.hx b/source/funkin/graphics/FlxFilteredSprite.hx index af4418d60e..ea0376c3d0 100644 --- a/source/funkin/graphics/FlxFilteredSprite.hx +++ b/source/funkin/graphics/FlxFilteredSprite.hx @@ -193,7 +193,6 @@ class FlxFilteredSprite extends FlxSprite } else { - trace("GAGAGA"); resetFrame(); filtered = false; } diff --git a/source/funkin/ui/charSelect/CharSelectPlayer.hx b/source/funkin/ui/charSelect/CharSelectPlayer.hx index 9767c2d3bf..710bdd45ab 100644 --- a/source/funkin/ui/charSelect/CharSelectPlayer.hx +++ b/source/funkin/ui/charSelect/CharSelectPlayer.hx @@ -8,14 +8,10 @@ import funkin.modding.events.ScriptEvent; class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass { - var desLp:FlxKeyFrame = null; - public function new(x:Float, y:Float) { super(x, y, Paths.animateAtlas("charSelect/bfChill")); - desLp = anim.getFrameLabel("deselect loop start"); - onAnimationComplete.add(function(animLabel:String) { switch (animLabel) { @@ -80,8 +76,6 @@ class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass playAnimation("slidein", true, false, false); - desLp = anim.getFrameLabel("deselect loop start"); - updateHitbox(); updatePosition(str); diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index db88442887..aa207c865c 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -125,6 +125,11 @@ class CharSelectSubState extends MusicBeatSubState override public function create():Void { + openSubState(new IntroSubState()); + subStateClosed.addOnce((_) -> { + camera.flash(); + checkNewChar(); + }); super.create(); bopInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/iconBopInfo/iconBopInfo.txt")); @@ -287,6 +292,7 @@ class CharSelectSubState extends MusicBeatSubState selectSound.volume = 0.7; FlxG.sound.defaultSoundGroup.add(selectSound); + FlxG.sound.list.add(selectSound); unlockSound = new FunkinSound(); unlockSound.loadEmbedded(Paths.sound('CS_unlock')); @@ -296,6 +302,7 @@ class CharSelectSubState extends MusicBeatSubState unlockSound.play(true); FlxG.sound.defaultSoundGroup.add(unlockSound); + FlxG.sound.list.add(unlockSound); // playing it here to preload it. not doing this makes a super awkward pause at the end of the intro // TODO: probably make an intro thing for funkinSound itself that preloads the next audio? @@ -360,6 +367,32 @@ class CharSelectSubState extends MusicBeatSubState }); } + function checkNewChar():Void + { + if (nonLocks.length > 0) selectTimer.start(0.5, (_) -> { + unLock(); + }); + else + { + FunkinSound.playMusic('stayFunky', + { + startingVolume: 1, + overrideExisting: true, + restartTrack: true, + onLoad: function() { + @:privateAccess + gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1); + #if desktop + // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 + // So we want to manually change it! + @:privateAccess + gfChill.analyzer.fftN = 512; + #end + } + }); + } + } + var grpIcons:FlxSpriteGroup; var grpXSpread(default, set):Float = 107; var grpYSpread(default, set):Float = 127; @@ -404,31 +437,14 @@ class CharSelectSubState extends MusicBeatSubState updateIconPositions(); grpIcons.scrollFactor.set(); - - if (nonLocks.length > 0) unLock(); - else - FunkinSound.playMusic('stayFunky', - { - startingVolume: 1, - overrideExisting: true, - restartTrack: true, - onLoad: function() { - @:privateAccess - gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1); - #if desktop - // On desktop it uses FFT stuff that isn't as optimized as the direct browser stuff we use on HTML5 - // So we want to manually change it! - @:privateAccess - gfChill.analyzer.fftN = 512; - #end - } - }); } function unLock() { var index = nonLocks[0]; + // pressedSelect = true; + var copy = 3; var yThing = -1; @@ -448,7 +464,7 @@ class CharSelectSubState extends MusicBeatSubState nonLocks.shift(); - selectTimer.start(1, function(_) { + selectTimer.start(0.5, function(_) { var lock:Lock = cast grpIcons.group.members[index]; lock.anim.getFrameLabel("unlockAnim").add(function() { @@ -460,7 +476,12 @@ class CharSelectSubState extends MusicBeatSubState unlockSound.volume = 0.7; unlockSound.play(true); + syncLock = lock; + + // sync = true; + lock.onAnimationComplete.addOnce(function(_) { + syncLock = null; var char = availableChars.get(index); camera.flash(0xFFFFFFFF, 0.1); playerChill.playAnimation("unlock"); @@ -482,15 +503,18 @@ class CharSelectSubState extends MusicBeatSubState bopPlay = true; updateIconPositions(); + playerChillOut.onAnimationComplete.addOnce((_) -> if (_ == "death") + { + sync = false; + playerChillOut.visible = false; + playerChillOut.switchChar(char); + }); + Save.instance.addCharacterSeen(char); if (nonLocks.length == 0) { - playerChillOut.onAnimationComplete.addOnce((_) -> { - playerChillOut.visible = false; - playerChillOut.switchChar(char); - }); + pressedSelect = false; @:bypassAccessor curChar = char; - Save.instance.addCharacterSeen(char); FunkinSound.playMusic('stayFunky', { startingVolume: 1, @@ -536,6 +560,38 @@ class CharSelectSubState extends MusicBeatSubState } } + var sync:Bool = false; + + var syncLock:Lock = null; + + var audioBizz:Float = 0; + + function syncAudio(elapsed:Float):Void + { + @:privateAccess + if (sync && !unlockSound.paused) + { + // if (playerChillOut.anim.framerate > 0) + // { + // if (syncLock != null) syncLock.anim.framerate = 0; + // playerChillOut.anim.framerate = 0; + // } + + playerChillOut.anim._tick = 0; + if (syncLock != null) syncLock.anim._tick = 0; + + trace(unlockSound.time); + + if ((unlockSound.time - audioBizz) >= (delay * 1000)) + { + if (syncLock != null) syncLock.anim._tick = delay; + + playerChillOut.anim._tick = delay; + audioBizz += delay * 1000; + } + } + } + function goToFreeplay():Void { autoFollow = false; @@ -589,71 +645,76 @@ class CharSelectSubState extends MusicBeatSubState if (controls.UI_UP_R || controls.UI_DOWN_R || controls.UI_LEFT_R || controls.UI_RIGHT_R) selectSound.pitch = 1; - if (controls.UI_UP) holdTmrUp += elapsed; - if (controls.UI_UP_R) - { - holdTmrUp = 0; - spamUp = false; - } + syncAudio(elapsed); - if (controls.UI_DOWN) holdTmrDown += elapsed; - if (controls.UI_DOWN_R) + if (!pressedSelect) { - holdTmrDown = 0; - spamDown = false; - } + if (controls.UI_UP) holdTmrUp += elapsed; + if (controls.UI_UP_R) + { + holdTmrUp = 0; + spamUp = false; + } - if (controls.UI_LEFT) holdTmrLeft += elapsed; - if (controls.UI_LEFT_R) - { - holdTmrLeft = 0; - spamLeft = false; - } + if (controls.UI_DOWN) holdTmrDown += elapsed; + if (controls.UI_DOWN_R) + { + holdTmrDown = 0; + spamDown = false; + } - if (controls.UI_RIGHT) holdTmrRight += elapsed; - if (controls.UI_RIGHT_R) - { - holdTmrRight = 0; - spamRight = false; - } + if (controls.UI_LEFT) holdTmrLeft += elapsed; + if (controls.UI_LEFT_R) + { + holdTmrLeft = 0; + spamLeft = false; + } - var initSpam = 0.5; + if (controls.UI_RIGHT) holdTmrRight += elapsed; + if (controls.UI_RIGHT_R) + { + holdTmrRight = 0; + spamRight = false; + } - if (holdTmrUp >= initSpam) spamUp = true; - if (holdTmrDown >= initSpam) spamDown = true; - if (holdTmrLeft >= initSpam) spamLeft = true; - if (holdTmrRight >= initSpam) spamRight = true; + var initSpam = 0.5; - if (controls.UI_UP_P) - { - cursorY -= 1; - cursorDenied.visible = false; + if (holdTmrUp >= initSpam) spamUp = true; + if (holdTmrDown >= initSpam) spamDown = true; + if (holdTmrLeft >= initSpam) spamLeft = true; + if (holdTmrRight >= initSpam) spamRight = true; - holdTmrUp = 0; + if (controls.UI_UP_P) + { + cursorY -= 1; + cursorDenied.visible = false; - selectSound.play(true); - } - if (controls.UI_DOWN_P) - { - cursorY += 1; - cursorDenied.visible = false; - holdTmrDown = 0; - selectSound.play(true); - } - if (controls.UI_LEFT_P) - { - cursorX -= 1; - cursorDenied.visible = false; + holdTmrUp = 0; - holdTmrLeft = 0; - selectSound.play(true); - } - if (controls.UI_RIGHT_P) - { - cursorX += 1; - cursorDenied.visible = false; - holdTmrRight = 0; - selectSound.play(true); + selectSound.play(true); + } + if (controls.UI_DOWN_P) + { + cursorY += 1; + cursorDenied.visible = false; + holdTmrDown = 0; + selectSound.play(true); + } + if (controls.UI_LEFT_P) + { + cursorX -= 1; + cursorDenied.visible = false; + + holdTmrLeft = 0; + selectSound.play(true); + } + if (controls.UI_RIGHT_P) + { + cursorX += 1; + cursorDenied.visible = false; + holdTmrRight = 0; + selectSound.play(true); + } } if (cursorX < -1) @@ -694,7 +755,7 @@ class CharSelectSubState extends MusicBeatSubState gfChill.playAnimation("confirm"); pressedSelect = true; selectTimer.start(1.5, (_) -> { - pressedSelect = false; + // pressedSelect = false; // FlxG.switchState(FreeplayState.build( // { // { @@ -735,6 +796,8 @@ class CharSelectSubState extends MusicBeatSubState cursorDenied.visible = true; cursorDenied.x = cursor.x - 2; cursorDenied.y = cursor.y - 4; + + playerChill.playAnimation("cannot select", true); cursorDenied.animation.play("idle", true); cursorDenied.animation.finishCallback = (_) -> { cursorDenied.visible = false; @@ -796,6 +859,7 @@ class CharSelectSubState extends MusicBeatSubState var refFrame = bopInfo.frames[bopInfo.frames.length - 1]; var curFrame = bopInfo.frames[bopFr]; + if (bopFr >= 13) icon.filters = selectedBizz; var scaleXDiff:Float = curFrame.scaleX - refFrame.scaleX; var scaleYDiff:Float = curFrame.scaleY - refFrame.scaleY; @@ -878,7 +942,6 @@ class CharSelectSubState extends MusicBeatSubState if (index == getCurrentSelected()) { // memb.pixels = memb.withDropShadow.clone(); - memb.filters = selectedBizz; if (bopPlay) { @@ -890,12 +953,14 @@ class CharSelectSubState extends MusicBeatSubState doBop(memb, FlxG.elapsed); } else + { + memb.filters = selectedBizz; memb.scale.set(2.6, 2.6); - - if (controls.ACCEPT) memb.animation.play("confirm"); - if (memb.animation.curAnim.name == "confirm" && controls.BACK) + } + if (controls.ACCEPT && memb.animation.curAnim.name == "confirm") memb.animation.play("confirm"); + if (pressedSelect && controls.BACK) { - memb.animation.play("confirm", false, true); + memb.animation.play("confirm", true, true); member.animation.finishCallback = (_) -> { member.animation.play("idle"); member.animation.finishCallback = null; diff --git a/source/funkin/ui/charSelect/IntroSubState.hx b/source/funkin/ui/charSelect/IntroSubState.hx new file mode 100644 index 0000000000..04503cbb9e --- /dev/null +++ b/source/funkin/ui/charSelect/IntroSubState.hx @@ -0,0 +1,141 @@ +package funkin.ui.charSelect; + +#if html5 +import funkin.graphics.video.FlxVideo; +#end +#if hxCodec +import hxcodec.flixel.FlxVideoSprite; +#end +import funkin.ui.MusicBeatSubState; +import funkin.audio.FunkinSound; + +/** + * After about 2 minutes of inactivity on the title screen, + * the game will enter the Attract state, as a reference to physical arcade machines. + * + * In the current version, this just plays the ~~Kickstarter trailer~~ Erect teaser, but this can be changed to + * gameplay footage, a generic game trailer, or something more elaborate. + */ +class IntroSubState extends MusicBeatSubState +{ + static final ATTRACT_VIDEO_PATH:String = Paths.stripLibrary(Paths.videos('introSelect')); + + var introSound:FunkinSound = null; + + public override function create():Void + { + // Pause existing music. + if (FlxG.sound.music != null) + { + FlxG.sound.music.destroy(); + FlxG.sound.music = null; + } + + #if html5 + trace('Playing web video ${ATTRACT_VIDEO_PATH}'); + playVideoHTML5(ATTRACT_VIDEO_PATH); + #end + + #if hxCodec + trace('Playing native video ${ATTRACT_VIDEO_PATH}'); + playVideoNative(ATTRACT_VIDEO_PATH); + #end + + introSound = new FunkinSound(); + introSound.loadEmbedded(Paths.sound('CS_Lights')); + introSound.pitch = 1; + + FlxG.sound.defaultSoundGroup.add(introSound); + FlxG.sound.list.add(introSound); + + introSound.play(true); + } + + #if html5 + var vid:FlxVideo; + + function playVideoHTML5(filePath:String):Void + { + // Video displays OVER the FlxState. + vid = new FlxVideo(filePath); + + vid.scrollFactor.set(); + if (vid != null) + { + vid.zIndex = 0; + + vid.finishCallback = onAttractEnd; + + add(vid); + } + else + { + trace('ALERT: Video is null! Could not play cutscene!'); + } + } + #end + + #if hxCodec + var vid:FlxVideoSprite; + + function playVideoNative(filePath:String):Void + { + // Video displays OVER the FlxState. + vid = new FlxVideoSprite(0, 0); + + vid.scrollFactor.set(); + + if (vid != null) + { + vid.zIndex = 0; + vid.bitmap.onEndReached.add(onAttractEnd); + + add(vid); + vid.play(filePath, false); + } + else + { + trace('ALERT: Video is null! Could not play cutscene!'); + } + } + #end + + public override function update(elapsed:Float):Void + { + super.update(elapsed); + + if (controls.ACCEPT) + { + onAttractEnd(); + } + } + + /** + * When the attraction state ends (after the video ends or the user presses any button), + * switch immediately to the title screen. + */ + function onAttractEnd():Void + { + #if html5 + if (vid != null) + { + remove(vid); + } + #end + + #if hxCodec + if (vid != null) + { + vid.stop(); + remove(vid); + } + #end + + #if (html5 || hxCodec) + vid.destroy(); + vid = null; + #end + + close(); + } +} From a530d91a767c3028b16294e903e0993fa68cee02 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 10 Sep 2024 19:16:48 -0400 Subject: [PATCH 392/469] Fix a bug where StickerSubState was being loaded as a state and not a substate, breaking some transitions --- source/funkin/play/ResultState.hx | 43 +++++++++++++++++++------------ 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index 2878248e2a..e7087e340b 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -4,6 +4,8 @@ import funkin.util.MathUtil; import funkin.ui.story.StoryMenuState; import funkin.graphics.adobeanimate.FlxAtlasSprite; import flixel.FlxSprite; +import flixel.FlxState; +import flixel.FlxSubState; import funkin.graphics.FunkinSprite; import flixel.effects.FlxFlicker; import flixel.graphics.frames.FlxBitmapFont; @@ -736,11 +738,13 @@ class ResultState extends MusicBeatSubState // Default to main menu because that's better than `null`. var targetState:flixel.FlxState = new funkin.ui.mainmenu.MainMenuState(); var shouldTween = false; + var shouldUseSubstate = false; if (params.storyMode) { if (PlayerRegistry.instance.hasNewCharacter()) { + // New character, display the notif. targetState = new StoryMenuState(null); var newCharacters = PlayerRegistry.instance.listNewCharacters(); @@ -754,6 +758,9 @@ class ResultState extends MusicBeatSubState } else { + // No new characters. + shouldTween = false; + shouldUseSubstate = true; targetState = new funkin.ui.transition.StickerSubState(null, (sticker) -> new StoryMenuState(sticker)); } } @@ -781,21 +788,9 @@ class ResultState extends MusicBeatSubState } else { - trace('rank is lower...... and/or equal'); - - targetState = new funkin.ui.transition.StickerSubState(null, (sticker) -> FreeplayState.build( - { - { - fromResults: - { - oldRank: null, - playRankAnim: false, - newRank: rank, - songId: params.songId, - difficultyId: params.difficultyId - } - } - }, sticker)); + shouldTween = false; + shouldUseSubstate = true; + targetState = new funkin.ui.transition.StickerSubState(null, (sticker) -> FreeplayState.build(null, sticker)); } } @@ -805,13 +800,27 @@ class ResultState extends MusicBeatSubState { ease: FlxEase.expoOut, onComplete: function(_) { - FlxG.switchState(targetState); + if (shouldUseSubstate && targetState is FlxSubState) + { + openSubState(cast targetState); + } + else + { + FlxG.switchState(targetState); + } } }); } else { - FlxG.switchState(targetState); + if (shouldUseSubstate && targetState is FlxSubState) + { + openSubState(cast targetState); + } + else + { + FlxG.switchState(targetState); + } } } From 9a242c5d002b77ab9de4fefae4812ea6bd144d4a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 10 Sep 2024 19:32:10 -0400 Subject: [PATCH 393/469] Fix an issue where New labels wouldn't show on character mixes --- source/funkin/ui/freeplay/FreeplayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 14c5829af8..a908e54c6f 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -2377,7 +2377,7 @@ class FreeplaySongData this.scoringRank = Save.instance.getSongRank(songId, suffixedDifficulty); - this.isNew = song.isSongNew(currentDifficulty); + this.isNew = song.isSongNew(suffixedDifficulty); } } From 59fac5a809ad5dddf565ea4d6fc7275bbe1abc30 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 10 Sep 2024 19:32:19 -0400 Subject: [PATCH 394/469] Enable New labels for character mixes --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index ba6621b9e8..ebbe6a0d9a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit ba6621b9e8126b3e84ae9b79f22999b2b2402c65 +Subproject commit ebbe6a0d9a4f47786f858c2505ca27bf0760dd2e From e5a788de7ddae5e533147c0c17bd54a9bc5d6987 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 10 Sep 2024 20:22:44 -0400 Subject: [PATCH 395/469] change xargs input --- .github/actions/setup-haxe/action.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/setup-haxe/action.yml b/.github/actions/setup-haxe/action.yml index 438f330a24..a6c21f6a96 100644 --- a/.github/actions/setup-haxe/action.yml +++ b/.github/actions/setup-haxe/action.yml @@ -82,7 +82,8 @@ runs: with: run: | git config --global --name-only --get-regexp 'url\.https\:\/\/x-access-token:.+@github\.com\/\.insteadOf' \ - | xargs git config --global --unset + | xargs -I {} git config --global --unset {} + git config -l --show-scope --show-origin git config --global 'url.https://x-access-token:${{ inputs.gh-token }}@github.com/.insteadOf' https://github.com/ post: git config --global --unset 'url.https://x-access-token:${{ inputs.gh-token }}@github.com/.insteadOf' From 4cbea7d4863327b8975a91cdb876253b6f203d3b Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 11 Sep 2024 00:07:52 -0400 Subject: [PATCH 396/469] get rid of submodules that appeared for some reason --- .gitignore | 6 ------ project/lib/harfbuzz | 1 - project/lib/hashlink | 1 - project/lib/jpeg | 1 - project/lib/lzma | 1 - project/lib/mbedtls | 1 - project/lib/mojoal | 1 - project/lib/neko | 1 - project/lib/ogg | 1 - project/lib/openal | 1 - project/lib/pixman | 1 - project/lib/png | 1 - project/lib/sdl | 1 - project/lib/tinyfiledialogs | 1 - project/lib/vorbis | 1 - project/lib/vpx | 1 - project/lib/webm | 1 - project/lib/zlib | 1 - 18 files changed, 23 deletions(-) delete mode 160000 project/lib/harfbuzz delete mode 160000 project/lib/hashlink delete mode 160000 project/lib/jpeg delete mode 160000 project/lib/lzma delete mode 160000 project/lib/mbedtls delete mode 160000 project/lib/mojoal delete mode 160000 project/lib/neko delete mode 160000 project/lib/ogg delete mode 160000 project/lib/openal delete mode 160000 project/lib/pixman delete mode 160000 project/lib/png delete mode 160000 project/lib/sdl delete mode 160000 project/lib/tinyfiledialogs delete mode 160000 project/lib/vorbis delete mode 160000 project/lib/vpx delete mode 160000 project/lib/webm delete mode 160000 project/lib/zlib diff --git a/.gitignore b/.gitignore index 068780a258..ae402bdee4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,10 +13,4 @@ shitAudio/ node_modules/ package.json package-lock.json -<<<<<<< HEAD .aider* -||||||| bcaeae27 -======= -.aider.* -.aider* ->>>>>>> rewrite/master diff --git a/project/lib/harfbuzz b/project/lib/harfbuzz deleted file mode 160000 index 2ba0b9ee94..0000000000 --- a/project/lib/harfbuzz +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2ba0b9ee94e4fdea5c3e9d20fa73eb893c0bb95e diff --git a/project/lib/hashlink b/project/lib/hashlink deleted file mode 160000 index 9888913ed2..0000000000 --- a/project/lib/hashlink +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9888913ed2e1eef9fc03085100c92bd44985c781 diff --git a/project/lib/jpeg b/project/lib/jpeg deleted file mode 160000 index 5cf7960678..0000000000 --- a/project/lib/jpeg +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5cf79606784f0616590e6a3a7fdf17ad32a6df9b diff --git a/project/lib/lzma b/project/lib/lzma deleted file mode 160000 index 5748ce6ad6..0000000000 --- a/project/lib/lzma +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5748ce6ad668bb10e1a1fe322b37245d0053e71a diff --git a/project/lib/mbedtls b/project/lib/mbedtls deleted file mode 160000 index ab0af45d11..0000000000 --- a/project/lib/mbedtls +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ab0af45d11db04bd6e42ccfabe1b8d8deaae3243 diff --git a/project/lib/mojoal b/project/lib/mojoal deleted file mode 160000 index a9e2f30b04..0000000000 --- a/project/lib/mojoal +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a9e2f30b04f10df1912d06ff5a610edda4677ba5 diff --git a/project/lib/neko b/project/lib/neko deleted file mode 160000 index d8a3b46386..0000000000 --- a/project/lib/neko +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d8a3b463861779656a78c1a5e31c013580642185 diff --git a/project/lib/ogg b/project/lib/ogg deleted file mode 160000 index db5c7a49ce..0000000000 --- a/project/lib/ogg +++ /dev/null @@ -1 +0,0 @@ -Subproject commit db5c7a49ce7ebda47b15b78471e78fb7f2483e22 diff --git a/project/lib/openal b/project/lib/openal deleted file mode 160000 index 326133368c..0000000000 --- a/project/lib/openal +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 326133368c3d7149d0ab4a98fc96f654553094a7 diff --git a/project/lib/pixman b/project/lib/pixman deleted file mode 160000 index 0cb4fbe324..0000000000 --- a/project/lib/pixman +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0cb4fbe3241988a5d6b51d3551998ec6837c75a1 diff --git a/project/lib/png b/project/lib/png deleted file mode 160000 index e4a31f024b..0000000000 --- a/project/lib/png +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e4a31f024b6158aaaf55a43502f574d5f5d1c894 diff --git a/project/lib/sdl b/project/lib/sdl deleted file mode 160000 index 8ea38ebedc..0000000000 --- a/project/lib/sdl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8ea38ebedc846faa547693f60bcd213b21370982 diff --git a/project/lib/tinyfiledialogs b/project/lib/tinyfiledialogs deleted file mode 160000 index 4927ea8f44..0000000000 --- a/project/lib/tinyfiledialogs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4927ea8f44fa0151dc8e0a2b79a13dbd7827cc48 diff --git a/project/lib/vorbis b/project/lib/vorbis deleted file mode 160000 index 84c023699c..0000000000 --- a/project/lib/vorbis +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 84c023699cdf023a32fa4ded32019f194afcdad0 diff --git a/project/lib/vpx b/project/lib/vpx deleted file mode 160000 index fbf63dff1f..0000000000 --- a/project/lib/vpx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fbf63dff1f528d44f24bd662abb89fd01a4a1c25 diff --git a/project/lib/webm b/project/lib/webm deleted file mode 160000 index 6c1e511f48..0000000000 --- a/project/lib/webm +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6c1e511f487eaea026d94d0c6494a617155b5cfb diff --git a/project/lib/zlib b/project/lib/zlib deleted file mode 160000 index 545f194963..0000000000 --- a/project/lib/zlib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 545f1949635949159fa6282e81712aec32b5d4f1 From d1622040b6fd4b03f54087439839789d31b46980 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 11 Sep 2024 04:22:33 -0400 Subject: [PATCH 397/469] Fix a bug where vocals would restart after a song ends --- source/funkin/audio/FunkinSound.hx | 8 ++++++++ source/funkin/audio/SoundGroup.hx | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index b33126998e..2f891a1e76 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -567,6 +567,14 @@ class FunkinSound extends FlxSound implements ICloneable return sound; } + + /** + * Produces a string representation suitable for debugging. + */ + public function toString():String + { + return 'FunkinSound(${this._label})'; + } } /** diff --git a/source/funkin/audio/SoundGroup.hx b/source/funkin/audio/SoundGroup.hx index 5fc2abe0ed..5d53fedd64 100644 --- a/source/funkin/audio/SoundGroup.hx +++ b/source/funkin/audio/SoundGroup.hx @@ -113,6 +113,11 @@ class SoundGroup extends FlxTypedGroup public function play(forceRestart:Bool = false, startTime:Float = 0.0, ?endTime:Float) { forEachAlive(function(sound:FunkinSound) { + if (sound.length < startTime) + { + // trace('Queuing sound (${sound.toString()} past its length! Skipping...)'); + return; + } sound.play(forceRestart, startTime, endTime); }); } From c63feaafbd07c376359492d4dabbbc01f0d2c9a6 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 11 Sep 2024 06:00:06 -0400 Subject: [PATCH 398/469] BF force plays the unlock animation even when he would fist pump --- source/funkin/ui/freeplay/FreeplayDJ.hx | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index 51829d44da..3d9ffd4d69 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -375,6 +375,13 @@ class FreeplayDJ extends FlxAtlasSprite public function confirm():Void { + // We really don't want to play anything but the new character animation here. + if (PlayerRegistry.instance.hasNewCharacter()) + { + currentState = NewUnlock; + return; + } + currentState = Confirm; } @@ -397,6 +404,13 @@ class FreeplayDJ extends FlxAtlasSprite public function fistPumpIntro():Void { + // We really don't want to play anything but the new character animation here. + if (PlayerRegistry.instance.hasNewCharacter()) + { + currentState = NewUnlock; + return; + } + currentState = FistPumpIntro; var animPrefix = playableCharData.getAnimationPrefix('fistPump'); playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpIntroStartFrame()); @@ -404,6 +418,13 @@ class FreeplayDJ extends FlxAtlasSprite public function fistPump():Void { + // We really don't want to play anything but the new character animation here. + if (PlayerRegistry.instance.hasNewCharacter()) + { + currentState = NewUnlock; + return; + } + currentState = FistPump; var animPrefix = playableCharData.getAnimationPrefix('fistPump'); playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpLoopStartFrame()); @@ -411,6 +432,13 @@ class FreeplayDJ extends FlxAtlasSprite public function fistPumpLossIntro():Void { + // We really don't want to play anything but the new character animation here. + if (PlayerRegistry.instance.hasNewCharacter()) + { + currentState = NewUnlock; + return; + } + currentState = FistPumpIntro; var animPrefix = playableCharData.getAnimationPrefix('loss'); playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpIntroBadStartFrame()); @@ -418,6 +446,13 @@ class FreeplayDJ extends FlxAtlasSprite public function fistPumpLoss():Void { + // We really don't want to play anything but the new character animation here. + if (PlayerRegistry.instance.hasNewCharacter()) + { + currentState = NewUnlock; + return; + } + currentState = FistPump; var animPrefix = playableCharData.getAnimationPrefix('loss'); playFlashAnimation(animPrefix, true, false, false, playableCharData.getFistPumpLoopBadStartFrame()); From 0956b692d7d61eb76e97c2a0c69cc9c4a9cb5164 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 11 Sep 2024 06:18:16 -0400 Subject: [PATCH 399/469] Add a debug hotkey to set characters as not seen. --- source/funkin/audio/FunkinSound.hx | 2 +- source/funkin/ui/mainmenu/MainMenuState.hx | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 2f891a1e76..18a0b4da2e 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -571,7 +571,7 @@ class FunkinSound extends FlxSound implements ICloneable /** * Produces a string representation suitable for debugging. */ - public function toString():String + public override function toString():String { return 'FunkinSound(${this._label})'; } diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 19dc0d687f..4e416abeba 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -358,6 +358,7 @@ class MainMenuState extends MusicBeatState // Ctrl+Alt+Shift+W = Meet requirements for Pico Unlock // Ctrl+Alt+Shift+L = Revoke requirements for Pico Unlock // Ctrl+Alt+Shift+R = Score/Rank conflict test + // Ctrl+Alt+Shift+N = Mark all characters as not seen // Ctrl+Alt+Shift+E = Dump save data if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.P) @@ -433,6 +434,12 @@ class MainMenuState extends MusicBeatState }); } + if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.N) + { + @:privateAccess + funkin.save.Save.instance.data.unlocks.charactersSeen = ["bf"]; + } + if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.E) { funkin.save.Save.instance.debug_dumpSave(); From 94856a6c32667e89eb2c840f204258396ce11949 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 11 Sep 2024 06:18:31 -0400 Subject: [PATCH 400/469] Fix a bug where you could select Pico inst on erect difficulty --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 638a44fdd7..5822338a50 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 638a44fdd7635814db89e96bdfdc9a9be54b39c9 +Subproject commit 5822338a50cafbb277a91d949681baea2627fa80 From c3fcc707f1db976d34e1f2bb4a0e0c05b6c4865e Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 11 Sep 2024 06:52:46 -0400 Subject: [PATCH 401/469] Update compilation guide --- docs/COMPILING.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/COMPILING.md b/docs/COMPILING.md index 628eb13cc8..01bcaf78ae 100644 --- a/docs/COMPILING.md +++ b/docs/COMPILING.md @@ -14,7 +14,7 @@ 5. Run `haxelib --global install hmm` and then `haxelib --global run hmm setup` to install hmm.json 6. Run `hmm install` to install all haxelibs of the current branch 7. Run `haxelib run lime setup` to set up lime -8. Platform setup +8. Perform additional platform setup - For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe) - When prompted, select "Individual Components" and make sure to download the following: - MSVC v143 VS 2022 C++ x64/x86 build tools @@ -22,8 +22,24 @@ - Mac: [`lime setup mac` Documentation](https://lime.openfl.org/docs/advanced-setup/macos/) - Linux: [`lime setup linux` Documentation](https://lime.openfl.org/docs/advanced-setup/linux/) - HTML5: Compiles without any extra setup -9. If you are targeting for native, you may need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug` -10. `lime test PLATFORM` ! Add `-debug` to enable several debug features such as time travel (`PgUp`/`PgDn` in Play State). +9. If you are targeting for native, you may need to run `lime rebuild ` and `lime rebuild -debug` +10. `lime test ` to build and launch the game for your platform (for example, `lime test windows`) + +## Build Flags + +There are several useful build flags you can add to a build to affect how it works. A full list can be found in `project.hxp`, but here's information on some of them: + +- `-debug` to build the game in debug mode. This automatically enables several useful debug features. + - This includes enabling in-game debug functions, disables compile-time optimizations, enabling asset redirection (see below), and enabling the VSCode debug server (which can slow the game on some machines but allows for powerful debugging through breakpoints). + - `-DGITHUB_BUILD` will enable in-game debug functions (such as the ability to time travel in a song by pressing `PgUp`/`PgDn`), without enabling the other stuff +- `-DFEATURE_POLYMOD_MODS` or `-DNO_FEATURE_POLYMOD_MODS` to forcibly enable or disable modding support. +- `-DREDIRECT_ASSETS_FOLDER` or `-DNO_REDIRECT_ASSETS_FOLDER` to forcibly enable or disable asset redirection. + - This feature causes the game to load exported assets from the project's assets folder rather than the exported one. Great for fast iteration, but the game +- `-DFEATURE_DISCORD_RPC` or `-DNO_FEATURE_DISCORD_RPC` to forcibly enable or disable support for Discord Rich Presence. +- `-DFEATURE_VIDEO_PLAYBACK` or `-DNO_FEATURE_VIDEO_PLAYBACK` to forcibly enable or disable video cutscene support. +- `-DFEATURE_CHART_EDITOR` or `-DNO_FEATURE_CHART_EDITOR` to forcibly enable or disable the chart editor in the Debug menu. +- `-DFEATURE_STAGE_EDITOR` to forcibly enable the experimental stage editor. +- `-DFEATURE_GHOST_TAPPING` to forcibly enable an experimental gameplay change to the anti-mash system. # Troubleshooting - GO THROUGH THESE STEPS BEFORE OPENING ISSUES ON GITHUB! From 07d9ce2d5a524fd1c890a3ceebd3940c1f82f65f Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 17:15:31 +0200 Subject: [PATCH 402/469] Submodule thing --- art | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/art b/art index e2663c1cbe..1206249aa6 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit e2663c1cbe029f04a98500735943f0b9465548bf +Subproject commit 1206249aa65e13df2d6a196c35222f768ad4a3f4 From 2aa9e58f67a3176735d0b5edff01236be6b85c61 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 18:29:00 +0200 Subject: [PATCH 403/469] Fix titlestate reentering crash --- source/funkin/ui/title/TitleState.hx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/funkin/ui/title/TitleState.hx b/source/funkin/ui/title/TitleState.hx index 79b30ff4d8..df628fa87f 100644 --- a/source/funkin/ui/title/TitleState.hx +++ b/source/funkin/ui/title/TitleState.hx @@ -523,7 +523,8 @@ class TitleState extends MusicBeatState remove(ngSpr); FlxG.camera.flash(FlxColor.WHITE, initialized ? 1 : 4); - remove(credGroup); + + if (credGroup != null) remove(credGroup); skippedIntro = true; } } From 0dc286e1d37f222a4935ce5ea9e630ba74d7abc3 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 18:29:31 +0200 Subject: [PATCH 404/469] Fix Eggnog santa not playing plus Ugh fix --- .../graphics/adobeanimate/FlxAtlasSprite.hx | 43 +++++++++++++------ .../play/character/AnimateAtlasCharacter.hx | 2 + 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index fefb224eba..30ee200682 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -95,7 +95,7 @@ class FlxAtlasSprite extends FlxAnimate */ public function hasAnimation(id:String):Bool { - return getLabelIndex(id) != -1; + return getLabelIndex(id) != -1 || anim.symbolDictionary.exists(id); } /** @@ -112,6 +112,8 @@ class FlxAtlasSprite extends FlxAnimate var looping:Bool = false; + public var ignoreExclusionPref:Array = []; + /** * Plays an animation. * @param id A string ID of the animation to play. @@ -124,7 +126,25 @@ class FlxAtlasSprite extends FlxAnimate public function playAnimation(id:String, restart:Bool = false, ignoreOther:Bool = false, loop:Bool = false, startFrame:Int = 0):Void { // Skip if not allowed to play animations. - if ((!canPlayOtherAnims && !ignoreOther)) return; + if ((!canPlayOtherAnims)) + { + if (this.currentAnimation == id && restart) {} + else if (ignoreExclusionPref != null && ignoreExclusionPref.length > 0) + { + var detected:Bool = false; + for (entry in ignoreExclusionPref) + { + if (StringTools.startsWith(id, entry)) + { + detected = true; + break; + } + } + if (!detected) return; + } + else + return; + } if (anim == null) return; @@ -132,16 +152,12 @@ class FlxAtlasSprite extends FlxAnimate if (this.currentAnimation == id && !restart) { - if (anim.isPlaying) - { - // Skip if animation is already playing. - return; - } - else + if (!anim.isPlaying) { // Resume animation if it's paused. anim.play('', restart, false, startFrame); } + return; } else { @@ -174,18 +190,19 @@ class FlxAtlasSprite extends FlxAnimate // Move to the first frame of the animation. // goToFrameLabel(id); trace('Playing animation $id'); - if (this.anim.symbolDictionary.exists(id) || (this.anim.getByName(id) != null)) - { - this.anim.play(id, restart, false, startFrame); - } // Only call goToFrameLabel if there is a frame label with that name. This prevents annoying warnings! if (getFrameLabelNames().indexOf(id) != -1) { goToFrameLabel(id); fr = anim.getFrameLabel(id); + anim.curFrame += startFrame; + } + else + { + this.anim.play(id, restart, false, startFrame); + fr = null; } - anim.curFrame += startFrame; this.currentAnimation = id; } diff --git a/source/funkin/play/character/AnimateAtlasCharacter.hx b/source/funkin/play/character/AnimateAtlasCharacter.hx index f432d08a05..b78aed983e 100644 --- a/source/funkin/play/character/AnimateAtlasCharacter.hx +++ b/source/funkin/play/character/AnimateAtlasCharacter.hx @@ -166,6 +166,8 @@ class AnimateAtlasCharacter extends BaseCharacter this.mainSprite = sprite; + mainSprite.ignoreExclusionPref = ["sing"]; + // This forces the atlas to recalcuate its width and height this.mainSprite.alpha = 0.0001; this.mainSprite.draw(); From 5c1766a00f78fc1af65b03bc1a0c9ac446aafe9d Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 18:29:47 +0200 Subject: [PATCH 405/469] Fix char select substate crash --- source/funkin/ui/charSelect/CharSelectSubState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index aa207c865c..e075abca3d 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -239,7 +239,7 @@ class CharSelectSubState extends MusicBeatSubState // FlxG.debugger.track(bfChill, "bf chill"); // FlxG.debugger.track(playerChill, "player"); // FlxG.debugger.track(nametag, "nametag"); - FlxG.debugger.track(selectSound, "selectSound"); + // FlxG.debugger.track(selectSound, "selectSound"); // FlxG.debugger.track(chooseDipshit, "choose dipshit"); // FlxG.debugger.track(barthing, "barthing"); // FlxG.debugger.track(fgBlur, "fgBlur"); From 6349ba3a5ac5a0ddec7dabf03b7cc900f6fdf4b2 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 18:30:11 +0200 Subject: [PATCH 406/469] Fix GF not appearing on screen --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 638a44fdd7..26ab9a1dc8 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 638a44fdd7635814db89e96bdfdc9a9be54b39c9 +Subproject commit 26ab9a1dc8eec563717711b290c21d0e9e56a005 From 68d87a08372facd15678268fa2b4cc518dc6c9cb Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 19:24:21 +0200 Subject: [PATCH 407/469] Add one more conditional in atlas + add ignoreExclusions in Bopper/BaseCharacter --- .../graphics/adobeanimate/FlxAtlasSprite.hx | 2 +- source/funkin/play/character/BaseCharacter.hx | 2 ++ source/funkin/play/stage/Bopper.hx | 23 ++++++++++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index 30ee200682..037b87faaa 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -95,7 +95,7 @@ class FlxAtlasSprite extends FlxAnimate */ public function hasAnimation(id:String):Bool { - return getLabelIndex(id) != -1 || anim.symbolDictionary.exists(id); + return getLabelIndex(id) != -1 || anim.symbolDictionary.exists(id) || anim.getByName(id); } /** diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index bce7f0d983..365c8d112b 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -151,6 +151,8 @@ class BaseCharacter extends Bopper super(CharacterDataParser.DEFAULT_DANCEEVERY); this.characterId = id; + ignoreExclusionPref = ["sing"]; + _data = CharacterDataParser.fetchCharacterData(this.characterId); if (_data == null) { diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index 96a217d31d..721a60517c 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -260,6 +260,8 @@ class Bopper extends StageProp implements IPlayStateScriptedClass public var canPlayOtherAnims:Bool = true; + public var ignoreExclusionPref:Array = []; + /** * @param name The name of the animation to play. * @param restart Whether to restart the animation if it is already playing. @@ -268,7 +270,26 @@ class Bopper extends StageProp implements IPlayStateScriptedClass */ public function playAnimation(name:String, restart:Bool = false, ignoreOther:Bool = false, reversed:Bool = false):Void { - if (!canPlayOtherAnims && !ignoreOther) return; + if ((!canPlayOtherAnims)) + { + var id = name; + if (getCurrentAnimation() == id && restart) {} + else if (ignoreExclusionPref != null && ignoreExclusionPref.length > 0) + { + var detected:Bool = false; + for (entry in ignoreExclusionPref) + { + if (StringTools.startsWith(id, entry)) + { + detected = true; + break; + } + } + if (!detected) return; + } + else + return; + } var correctName = correctAnimationName(name); if (correctName == null) return; From 17a48bfc2b6b7bc1e5a16b11f0c3112fca4a0b2a Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 19:26:38 +0200 Subject: [PATCH 408/469] D'oh! --- source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index 037b87faaa..eeb4eccef0 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -95,7 +95,7 @@ class FlxAtlasSprite extends FlxAnimate */ public function hasAnimation(id:String):Bool { - return getLabelIndex(id) != -1 || anim.symbolDictionary.exists(id) || anim.getByName(id); + return getLabelIndex(id) != -1 || anim.symbolDictionary.exists(id) || anim.getByName(id) != null; } /** From c81f0416959370ad41b25f72be7eb6d936c2dc9a Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 20:05:39 +0200 Subject: [PATCH 409/469] Tinkering with atlassprite --- .../graphics/adobeanimate/FlxAtlasSprite.hx | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index eeb4eccef0..eef8bee2b7 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -169,19 +169,6 @@ class FlxAtlasSprite extends FlxAnimate } } - anim.onComplete.removeAll(); - anim.onComplete.add(function() { - if (loop) - { - this.anim.play(id, restart, false, startFrame); - this.currentAnimation = id; - } - else - { - onAnimationComplete.dispatch(id); - } - }); - looping = loop; // Prevent other animations from playing if `ignoreOther` is true. @@ -191,7 +178,7 @@ class FlxAtlasSprite extends FlxAnimate // goToFrameLabel(id); trace('Playing animation $id'); // Only call goToFrameLabel if there is a frame label with that name. This prevents annoying warnings! - if (getFrameLabelNames().indexOf(id) != -1) + if (getLabelIndex(id) != -1) { goToFrameLabel(id); fr = anim.getFrameLabel(id); From bea23c37a81bdc1f2a2b537a3c519c95b6b48ca0 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 20:47:45 +0200 Subject: [PATCH 410/469] readd FlxAtlasSprite from char unlock branch --- .../graphics/adobeanimate/FlxAtlasSprite.hx | 37 ++++++++++--------- source/funkin/ui/charSelect/IntroSubState.hx | 19 +++------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index eef8bee2b7..6f91dbac5e 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -95,7 +95,7 @@ class FlxAtlasSprite extends FlxAnimate */ public function hasAnimation(id:String):Bool { - return getLabelIndex(id) != -1 || anim.symbolDictionary.exists(id) || anim.getByName(id) != null; + return getLabelIndex(id) != -1 || anim.symbolDictionary.exists(id); } /** @@ -155,20 +155,24 @@ class FlxAtlasSprite extends FlxAnimate if (!anim.isPlaying) { // Resume animation if it's paused. - anim.play('', restart, false, startFrame); + anim.resume(); } + return; } - else + else if (!hasAnimation(id)) { // Skip if the animation doesn't exist - if (!hasAnimation(id)) - { - trace('Animation ' + id + ' not found'); - return; - } + trace('Animation ' + id + ' not found'); + return; } + this.currentAnimation = id; + anim.onComplete.removeAll(); + anim.onComplete.add(function() { + _onAnimationComplete(); + }); + looping = loop; // Prevent other animations from playing if `ignoreOther` is true. @@ -177,20 +181,18 @@ class FlxAtlasSprite extends FlxAnimate // Move to the first frame of the animation. // goToFrameLabel(id); trace('Playing animation $id'); + if (this.anim.symbolDictionary.exists(id) || (this.anim.getByName(id) != null)) + { + this.anim.play(id, true, false, startFrame); + fr = null; + } // Only call goToFrameLabel if there is a frame label with that name. This prevents annoying warnings! - if (getLabelIndex(id) != -1) + if (getFrameLabelNames().indexOf(id) != -1) { goToFrameLabel(id); fr = anim.getFrameLabel(id); anim.curFrame += startFrame; } - else - { - this.anim.play(id, restart, false, startFrame); - fr = null; - } - - this.currentAnimation = id; } override public function update(elapsed:Float) @@ -288,12 +290,13 @@ class FlxAtlasSprite extends FlxAnimate { anim.pause(); _onAnimationComplete(); + if (looping) { anim.curFrame = (fr != null) ? fr.index : 0; anim.resume(); } - else + else if (fr != null) { anim.curFrame--; } diff --git a/source/funkin/ui/charSelect/IntroSubState.hx b/source/funkin/ui/charSelect/IntroSubState.hx index 04503cbb9e..16271337bf 100644 --- a/source/funkin/ui/charSelect/IntroSubState.hx +++ b/source/funkin/ui/charSelect/IntroSubState.hx @@ -116,24 +116,17 @@ class IntroSubState extends MusicBeatSubState */ function onAttractEnd():Void { - #if html5 - if (vid != null) - { - remove(vid); - } - #end - - #if hxCodec + #if (html5 || hxCodec) if (vid != null) { + #if hxCodec vid.stop(); + #end remove(vid); - } - #end - #if (html5 || hxCodec) - vid.destroy(); - vid = null; + vid.destroy(); + vid = null; + } #end close(); From 9f5c154b1d29ac195023fd8a12856d27856d8777 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 20:49:55 +0200 Subject: [PATCH 411/469] readd IntroSubState from char unlock --- source/funkin/ui/charSelect/IntroSubState.hx | 54 ++++++++++++-------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/source/funkin/ui/charSelect/IntroSubState.hx b/source/funkin/ui/charSelect/IntroSubState.hx index 16271337bf..a0c44c2258 100644 --- a/source/funkin/ui/charSelect/IntroSubState.hx +++ b/source/funkin/ui/charSelect/IntroSubState.hx @@ -8,22 +8,24 @@ import hxcodec.flixel.FlxVideoSprite; #end import funkin.ui.MusicBeatSubState; import funkin.audio.FunkinSound; +import funkin.save.Save; /** - * After about 2 minutes of inactivity on the title screen, - * the game will enter the Attract state, as a reference to physical arcade machines. - * - * In the current version, this just plays the ~~Kickstarter trailer~~ Erect teaser, but this can be changed to - * gameplay footage, a generic game trailer, or something more elaborate. + * When you first enter the character select state, it will play an introductory video opening up the lights */ class IntroSubState extends MusicBeatSubState { - static final ATTRACT_VIDEO_PATH:String = Paths.stripLibrary(Paths.videos('introSelect')); + static final LIGHTS_VIDEO_PATH:String = Paths.stripLibrary(Paths.videos('introSelect')); var introSound:FunkinSound = null; public override function create():Void { + if (Save.instance.oldChar) + { + onLightsEnd(); + return; + } // Pause existing music. if (FlxG.sound.music != null) { @@ -32,15 +34,20 @@ class IntroSubState extends MusicBeatSubState } #if html5 - trace('Playing web video ${ATTRACT_VIDEO_PATH}'); - playVideoHTML5(ATTRACT_VIDEO_PATH); + trace('Playing web video ${LIGHTS_VIDEO_PATH}'); + playVideoHTML5(LIGHTS_VIDEO_PATH); #end #if hxCodec - trace('Playing native video ${ATTRACT_VIDEO_PATH}'); - playVideoNative(ATTRACT_VIDEO_PATH); + trace('Playing native video ${LIGHTS_VIDEO_PATH}'); + playVideoNative(LIGHTS_VIDEO_PATH); #end + // Im TOO lazy to even care, so uh, yep + FlxG.camera.zoom = 0.66666666666666666666666666666667; + vid.x = -(FlxG.width - (FlxG.width * FlxG.camera.zoom)); + vid.y = -((FlxG.height - (FlxG.height * FlxG.camera.zoom)) * 0.75); + introSound = new FunkinSound(); introSound.loadEmbedded(Paths.sound('CS_Lights')); introSound.pitch = 1; @@ -64,7 +71,7 @@ class IntroSubState extends MusicBeatSubState { vid.zIndex = 0; - vid.finishCallback = onAttractEnd; + vid.finishCallback = onLightsEnd; add(vid); } @@ -88,7 +95,7 @@ class IntroSubState extends MusicBeatSubState if (vid != null) { vid.zIndex = 0; - vid.bitmap.onEndReached.add(onAttractEnd); + vid.bitmap.onEndReached.add(onLightsEnd); add(vid); vid.play(filePath, false); @@ -104,30 +111,33 @@ class IntroSubState extends MusicBeatSubState { super.update(elapsed); - if (controls.ACCEPT) - { - onAttractEnd(); - } + // if (!introSound.paused) + // { + // #if html5 + // @:privateAccess + // vid.netStream.seek(introSound.time); + // #elseif hxCodec + // vid.bitmap.time = Std.int(introSound.time); + // #end + // } } /** - * When the attraction state ends (after the video ends or the user presses any button), - * switch immediately to the title screen. + * When the lights video finishes, it will close the substate */ - function onAttractEnd():Void + function onLightsEnd():Void { - #if (html5 || hxCodec) if (vid != null) { #if hxCodec vid.stop(); #end remove(vid); - vid.destroy(); vid = null; } - #end + + FlxG.camera.zoom = 1; close(); } From cfcfc773daa7ffc26dffe9574a443f5807fa4a94 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 20:53:05 +0200 Subject: [PATCH 412/469] readd Save from char unlock --- source/funkin/save/Save.hx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index 80b05e5ac2..1fa283b264 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -125,6 +125,7 @@ class Save { // Default to having seen the default character. charactersSeen: ["bf"], + oldChar: false }, optionsChartEditor: @@ -406,6 +407,18 @@ class Save return data.unlocks.charactersSeen; } + public var oldChar(get, set):Bool; + + function get_oldChar():Bool + { + return data.unlocks.oldChar; + } + + function set_oldChar(value:Bool):Bool + { + return data.unlocks.oldChar = value; + } + /** * When we've seen a character unlock, add it to the list of characters seen. * @param character @@ -1027,6 +1040,12 @@ typedef SaveDataUnlocks = * add it to this list so that we don't show it again. */ var charactersSeen:Array; + + /** + * This is a conditional when the player enters the character state + * For the first time ever + */ + var oldChar:Bool; } /** From 70ab000be42f62a25fb33440a3d6eee34ce163ac Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Wed, 11 Sep 2024 21:16:02 +0200 Subject: [PATCH 413/469] Readd CharSelectSubState from char unlock --- assets | 2 +- .../ui/charSelect/CharSelectSubState.hx | 68 ++++++++----------- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/assets b/assets index 26ab9a1dc8..f1813c9e4f 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 26ab9a1dc8eec563717711b290c21d0e9e56a005 +Subproject commit f1813c9e4f88426554293b68d8a46f5baa5b6228 diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index e075abca3d..bd55bfdd94 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -35,55 +35,44 @@ import openfl.filters.ShaderFilter; import funkin.util.FramesJSFLParser; import funkin.util.FramesJSFLParser.FramesJSFLInfo; import funkin.util.FramesJSFLParser.FramesJSFLFrame; +import funkin.graphics.FunkinSprite; class CharSelectSubState extends MusicBeatSubState { var cursor:FlxSprite; + var cursorBlue:FlxSprite; var cursorDarkBlue:FlxSprite; - var grpCursors:FlxTypedGroup; - var cursorConfirmed:FlxSprite; var cursorDenied:FlxSprite; - var cursorX:Int = 0; var cursorY:Int = 0; - var cursorFactor:Float = 110; var cursorOffsetX:Float = -16; var cursorOffsetY:Float = -48; - var cursorLocIntended:FlxPoint = new FlxPoint(0, 0); var lerpAmnt:Float = 0.95; - var tmrFrames:Int = 60; - var currentStage:Stage; - var playerChill:CharSelectPlayer; var playerChillOut:CharSelectPlayer; var gfChill:CharSelectGF; var gfChillOut:CharSelectGF; - var barthing:FlxAtlasSprite; var dipshitBacking:FlxSprite; var chooseDipshit:FlxSprite; var dipshitBlur:FlxSprite; var transitionGradient:FlxSprite; - var curChar(default, set):String = "pico"; var nametag:Nametag; var camFollow:FlxObject; var autoFollow:Bool = false; - var availableChars:Map = new Map(); var pressedSelect:Bool = false; - var selectTimer:FlxTimer = new FlxTimer(); var selectSound:FunkinSound; var unlockSound:FunkinSound; - var charSelectCam:FunkinCamera; var selectedBizz:Array = [ @@ -92,6 +81,7 @@ class CharSelectSubState extends MusicBeatSubState ]; var bopInfo:FramesJSFLInfo; + var blackScreen:FunkinSprite; public function new() { @@ -125,11 +115,6 @@ class CharSelectSubState extends MusicBeatSubState override public function create():Void { - openSubState(new IntroSubState()); - subStateClosed.addOnce((_) -> { - camera.flash(); - checkNewChar(); - }); super.create(); bopInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/iconBopInfo/iconBopInfo.txt")); @@ -239,7 +224,7 @@ class CharSelectSubState extends MusicBeatSubState // FlxG.debugger.track(bfChill, "bf chill"); // FlxG.debugger.track(playerChill, "player"); // FlxG.debugger.track(nametag, "nametag"); - // FlxG.debugger.track(selectSound, "selectSound"); + FlxG.debugger.track(selectSound, "selectSound"); // FlxG.debugger.track(chooseDipshit, "choose dipshit"); // FlxG.debugger.track(barthing, "barthing"); // FlxG.debugger.track(fgBlur, "fgBlur"); @@ -365,6 +350,20 @@ class CharSelectSubState extends MusicBeatSubState FlxG.camera.follow(camFollow, LOCKON, 0.01); } }); + + var blackScreen = new FunkinSprite().makeSolidColor(FlxG.width * 2, FlxG.height * 2, 0xFF000000); + blackScreen.x = -(FlxG.width * 0.5); + blackScreen.y = -(FlxG.height * 0.5); + add(blackScreen); + + openSubState(new IntroSubState()); + subStateClosed.addOnce((_) -> { + remove(blackScreen); + if (!Save.instance.oldChar) camera.flash(); + checkNewChar(); + + Save.instance.oldChar = true; + }); } function checkNewChar():Void @@ -396,7 +395,6 @@ class CharSelectSubState extends MusicBeatSubState var grpIcons:FlxSpriteGroup; var grpXSpread(default, set):Float = 107; var grpYSpread(default, set):Float = 127; - var nonLocks = []; function initLocks():Void @@ -426,9 +424,9 @@ class CharSelectSubState extends MusicBeatSubState var temp:Lock = new Lock(0, 0, i); temp.ID = 1; - temp.onAnimationComplete.add(function(anim) { - if (anim == "unlock") playerChill.playAnimation("unlock", true); - }); + // temp.onAnimationComplete.add(function(anim) { + // if (anim == "unlock") playerChill.playAnimation("unlock", true); + // }); grpIcons.add(temp); } @@ -443,7 +441,7 @@ class CharSelectSubState extends MusicBeatSubState { var index = nonLocks[0]; - // pressedSelect = true; + pressedSelect = true; var copy = 3; @@ -478,7 +476,7 @@ class CharSelectSubState extends MusicBeatSubState syncLock = lock; - // sync = true; + sync = true; lock.onAnimationComplete.addOnce(function(_) { syncLock = null; @@ -561,9 +559,7 @@ class CharSelectSubState extends MusicBeatSubState } var sync:Bool = false; - var syncLock:Lock = null; - var audioBizz:Float = 0; function syncAudio(elapsed:Float):Void @@ -580,8 +576,6 @@ class CharSelectSubState extends MusicBeatSubState playerChillOut.anim._tick = 0; if (syncLock != null) syncLock.anim._tick = 0; - trace(unlockSound.time); - if ((unlockSound.time - audioBizz) >= (delay * 1000)) { if (syncLock != null) syncLock.anim._tick = delay; @@ -738,7 +732,7 @@ class CharSelectSubState extends MusicBeatSubState { curChar = availableChars.get(getCurrentSelected()); - if (controls.ACCEPT) + if (!pressedSelect && controls.ACCEPT) { cursorConfirmed.visible = true; cursorConfirmed.x = cursor.x - 2; @@ -775,6 +769,7 @@ class CharSelectSubState extends MusicBeatSubState FlxTween.tween(FlxG.sound.music, {pitch: 1.0, volume: 1.0}, 1, {ease: FlxEase.quartInOut}); playerChill.playAnimation("deselect"); gfChill.playAnimation("deselect"); + pressedSelect = false; FlxTween.tween(FlxG.sound.music, {pitch: 1.0}, 1, { ease: FlxEase.quartInOut, @@ -783,7 +778,6 @@ class CharSelectSubState extends MusicBeatSubState gfChill.playAnimation("idle", true, false, true); } }); - pressedSelect = false; selectTimer.cancel(); } } @@ -797,7 +791,7 @@ class CharSelectSubState extends MusicBeatSubState cursorDenied.x = cursor.x - 2; cursorDenied.y = cursor.y - 4; - playerChill.playAnimation("cannot select", true); + playerChill.playAnimation("cannot select Label", true); cursorDenied.animation.play("idle", true); cursorDenied.animation.finishCallback = (_) -> { cursorDenied.visible = false; @@ -831,13 +825,9 @@ class CharSelectSubState extends MusicBeatSubState } var bopTimer:Float = 0; - var delay = 1 / 24; - var bopFr = 0; - var bopPlay:Bool = false; - var bopRefX:Float = 0; var bopRefY:Float = 0; @@ -957,10 +947,10 @@ class CharSelectSubState extends MusicBeatSubState memb.filters = selectedBizz; memb.scale.set(2.6, 2.6); } - if (controls.ACCEPT && memb.animation.curAnim.name == "confirm") memb.animation.play("confirm"); - if (pressedSelect && controls.BACK) + if (pressedSelect && memb.animation.curAnim.name == "idle") memb.animation.play("confirm"); + if (!pressedSelect && memb.animation.curAnim.name != "idle") { - memb.animation.play("confirm", true, true); + memb.animation.play("confirm", false, true); member.animation.finishCallback = (_) -> { member.animation.play("idle"); member.animation.finishCallback = null; From fded5892c5bde073118fa91cf3a1bd5d841db98f Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 11 Sep 2024 15:55:38 -0400 Subject: [PATCH 414/469] Update audio compression (via Saru) --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 638a44fdd7..acf6f9ddfd 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 638a44fdd7635814db89e96bdfdc9a9be54b39c9 +Subproject commit acf6f9ddfdb5867550ae90e5871cce3d8d3808f0 From e32dd0e5048fe77326f38a227f5336ffc85c7afc Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 11 Sep 2024 17:09:45 -0400 Subject: [PATCH 415/469] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 638a44fdd7..abe1deb437 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 638a44fdd7635814db89e96bdfdc9a9be54b39c9 +Subproject commit abe1deb4372b9bc5b97d7d30ed8eb4e70d721eb9 From 3553d008cf29147e5f878e1088236de72796efca Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Thu, 12 Sep 2024 01:37:32 +0200 Subject: [PATCH 416/469] tinkering shit --- assets | 2 +- source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx | 10 ++++++++-- source/funkin/play/ResultState.hx | 5 ++++- source/funkin/ui/charSelect/CharSelectPlayer.hx | 2 +- source/funkin/ui/freeplay/FreeplayDJ.hx | 2 ++ source/funkin/ui/title/TitleState.hx | 7 +++++-- 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/assets b/assets index f1813c9e4f..6b84e5b483 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit f1813c9e4f88426554293b68d8a46f5baa5b6228 +Subproject commit 6b84e5b4830cbdb21f0e92bd8def16b1446f2d84 diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index 6f91dbac5e..f245afec74 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -154,6 +154,10 @@ class FlxAtlasSprite extends FlxAnimate { if (!anim.isPlaying) { + if (fr != null) anim.curFrame = fr.index + startFrame; + else + anim.curFrame = startFrame; + // Resume animation if it's paused. anim.resume(); } @@ -183,7 +187,9 @@ class FlxAtlasSprite extends FlxAnimate trace('Playing animation $id'); if (this.anim.symbolDictionary.exists(id) || (this.anim.getByName(id) != null)) { - this.anim.play(id, true, false, startFrame); + this.anim.play(id, restart, false, startFrame); + + if (id == "Boyfriend DJ fist pump" || startFrame == 4) trace("PUMP COMMAND: " + anim.curFrame); fr = null; } // Only call goToFrameLabel if there is a frame label with that name. This prevents annoying warnings! @@ -296,7 +302,7 @@ class FlxAtlasSprite extends FlxAnimate anim.curFrame = (fr != null) ? fr.index : 0; anim.resume(); } - else if (fr != null) + else if (fr != null && anim.curFrame != anim.length - 1) { anim.curFrame--; } diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index e7087e340b..e0ac3579d7 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -194,6 +194,7 @@ class ResultState extends MusicBeatSubState { // Animation is not looped. animation.onAnimationComplete.add((_name:String) -> { + trace("AHAHAH 2"); if (animation != null) { animation.anim.pause(); @@ -203,9 +204,10 @@ class ResultState extends MusicBeatSubState else if (animData.loopFrameLabel != null) { animation.onAnimationComplete.add((_name:String) -> { + trace("AHAHAH 2"); if (animation != null) { - animation.playAnimation(animData.loopFrameLabel ?? ''); // unpauses this anim, since it's on PlayOnce! + animation.playAnimation(animData.loopFrameLabel ?? '', true, false, true); // unpauses this anim, since it's on PlayOnce! } }); } @@ -214,6 +216,7 @@ class ResultState extends MusicBeatSubState animation.onAnimationComplete.add((_name:String) -> { if (animation != null) { + trace("AHAHAH"); animation.anim.curFrame = animData.loopFrame ?? 0; animation.anim.play(); // unpauses this anim, since it's on PlayOnce! } diff --git a/source/funkin/ui/charSelect/CharSelectPlayer.hx b/source/funkin/ui/charSelect/CharSelectPlayer.hx index 710bdd45ab..b6319f16d8 100644 --- a/source/funkin/ui/charSelect/CharSelectPlayer.hx +++ b/source/funkin/ui/charSelect/CharSelectPlayer.hx @@ -27,7 +27,7 @@ class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass case "deselect": playAnimation("deselect loop start", true, false, true); - case "slidein idle point", "cannot select", "unlock": + case "slidein idle point", "cannot select Label", "unlock": playAnimation("idle", true, false, false); case "idle": trace('Waiting for onBeatHit'); diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index 51829d44da..a6cbab4404 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -141,6 +141,7 @@ class FreeplayDJ extends FlxAtlasSprite } else if (getCurrentAnimation() == animPrefixB) { + trace("Loss Intro"); var endFrame = playableCharData.getFistPumpIntroBadEndFrame(); if (endFrame > -1 && anim.curFrame >= endFrame) { @@ -166,6 +167,7 @@ class FreeplayDJ extends FlxAtlasSprite } else if (getCurrentAnimation() == animPrefixB) { + trace("Loss GYATT"); var endFrame = playableCharData.getFistPumpLoopBadEndFrame(); if (endFrame > -1 && anim.curFrame >= endFrame) { diff --git a/source/funkin/ui/title/TitleState.hx b/source/funkin/ui/title/TitleState.hx index df628fa87f..f5c641d0c4 100644 --- a/source/funkin/ui/title/TitleState.hx +++ b/source/funkin/ui/title/TitleState.hx @@ -273,8 +273,11 @@ class TitleState extends MusicBeatState } #end - if (Save.instance.charactersSeen.contains("pico")) Save.instance.charactersSeen.remove("pico"); - + if (Save.instance.charactersSeen.contains("pico")) + { + Save.instance.charactersSeen.remove("pico"); + Save.instance.oldChar = false; + } Conductor.instance.update(); /* if (FlxG.onMobile) From bfcbc54ced6cc4e186547ffc49f1a2530bb58b6a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 11 Sep 2024 20:39:31 -0400 Subject: [PATCH 417/469] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index abe1deb437..cbf695dd62 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit abe1deb4372b9bc5b97d7d30ed8eb4e70d721eb9 +Subproject commit cbf695dd622625d11abbed594832aa41fe6a20de From e12ed47162f709ea53e7bd6bcb056726c219c9f6 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 12 Sep 2024 09:25:42 +0100 Subject: [PATCH 418/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 638a44fdd7..d7e9f6e153 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 638a44fdd7635814db89e96bdfdc9a9be54b39c9 +Subproject commit d7e9f6e15331f6d2dfee3d380e804e8274d6661d From b7fb6105a773053162b11596865c538079e503dd Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 12 Sep 2024 17:50:46 +0100 Subject: [PATCH 419/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 638a44fdd7..3306aab5b7 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 638a44fdd7635814db89e96bdfdc9a9be54b39c9 +Subproject commit 3306aab5b747aa5b19508d88ffb801403218b2ca From 2c80701855cae07efd3c13185090e0fe1c5b5a32 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 14:26:11 -0400 Subject: [PATCH 420/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index acf6f9ddfd..bd1bdcf5e1 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit acf6f9ddfdb5867550ae90e5871cce3d8d3808f0 +Subproject commit bd1bdcf5e1349e6199d9d32e617cbefa2cea093b From d3e8938888f7c4feb27c806ca222f6a54e69b087 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 14:28:51 -0400 Subject: [PATCH 421/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 5822338a50..3954e95646 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5822338a50cafbb277a91d949681baea2627fa80 +Subproject commit 3954e95646643dfc729607d0da57ba0bbf273521 From 19f332ea7e0858c2998392b98ee5c09a9588718d Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 14:30:14 -0400 Subject: [PATCH 422/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index cbf695dd62..a00554b4c7 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit cbf695dd622625d11abbed594832aa41fe6a20de +Subproject commit a00554b4c78d3d1da18436bc285ccbe1f4ea98f2 From 96f306471cca18550cdfd6cb3d50d241d97ff07e Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 14:31:56 -0400 Subject: [PATCH 423/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 6b84e5b483..335fac6af6 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 6b84e5b4830cbdb21f0e92bd8def16b1446f2d84 +Subproject commit 335fac6af6b3362b1de5f233865486eaf9fbae11 From 0b13e0fdd5f3f66a9efc81b76e8a26067109f983 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 14:33:31 -0400 Subject: [PATCH 424/469] art submod (char select stuff) --- art | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/art b/art index 1206249aa6..0bb988c497 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit 1206249aa65e13df2d6a196c35222f768ad4a3f4 +Subproject commit 0bb988c49788fd25a230b56dd9e4448838bc79c9 From 260946f44f1303762cd18de0ce096926c3c66f79 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 14:35:19 -0400 Subject: [PATCH 425/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index d7e9f6e153..f1fe21240b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit d7e9f6e15331f6d2dfee3d380e804e8274d6661d +Subproject commit f1fe21240bac58a7ab729b351cb36f3f9ed07e7a From 07c5c5d5a7affa04102357060e29e305fd7c8d03 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 14:38:29 -0400 Subject: [PATCH 426/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 3306aab5b7..bd4e2421c8 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 3306aab5b747aa5b19508d88ffb801403218b2ca +Subproject commit bd4e2421c87c31e244bc9c162850b842582c5de4 From bfb19cc67ebd82fea3133301637fd96404a38389 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 14:46:29 -0400 Subject: [PATCH 427/469] lastTime error fix --- source/Main.hx | 1 - 1 file changed, 1 deletion(-) diff --git a/source/Main.hx b/source/Main.hx index 63e2b58780..7f7d25235e 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -73,7 +73,6 @@ class Main extends Sprite var timeToCall = 0; var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); - lastTime = currTime + timeToCall; return id; }"); #end From cdd78818a7f9ca47b95fd9d24e7df21231c118e9 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 12 Sep 2024 15:20:12 -0400 Subject: [PATCH 428/469] Update assets submodule and changelog --- CHANGELOG.md | 9 ++++++++- art | 2 +- assets | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22a36fa8ee..a2031ba24c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,8 +29,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Implemented support for a new Instrumental Selector in Freeplay - Beating a Pico remix lets you use that instrumental when playing as Boyfriend - Added the first batch of Erect Stages! These graphical overhauls of the original stages will be used when playing Erect remixes and Pico remixes + - Week 1 Erect Stage + - Week 2 Erect Stage + - Week 3 Erect Stage + - Week 4 Erect Stage + - Week 5 Erect Stage + - Weekend 1 Erect Stage +- Implemented alternate animations and music for Pico in the results screen. + - These display on Pico remixes, as well as when playing Weekend 1. - Implemented support for scripted Note Kinds. You can use HScript define a different note style to display for these notes as well as custom behavior. (community feature by lemz1) -- Implemented a new Strumline Background option, to display a darkened background behind the strumline with your choice of opacity. - Implemented support for Numeric and Selector options in the Options menu. (community feature by FlooferLand) ## Changed - Girlfriend and Nene now perform previously unused animations when you achieve a large combo, or drop a large combo. diff --git a/art b/art index 0bb988c497..42ac06e512 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit 0bb988c49788fd25a230b56dd9e4448838bc79c9 +Subproject commit 42ac06e51207bda89144fd20a407ed3c3e0c6673 diff --git a/assets b/assets index bd4e2421c8..90865213be 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit bd4e2421c87c31e244bc9c162850b842582c5de4 +Subproject commit 90865213be704f88df8398f28b8880c13f468546 From b2b16ff2d310169c8390329c2546af6556346789 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 12 Sep 2024 15:31:58 -0400 Subject: [PATCH 429/469] Update vocal delay again --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 90865213be..934468c047 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 90865213be704f88df8398f28b8880c13f468546 +Subproject commit 934468c0472504c282b12c63b473e9f74b5b4517 From 386dc22d7b24464a63a4f95c6fa2aa8cc854da86 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 12 Sep 2024 15:34:16 -0400 Subject: [PATCH 430/469] Debug hotkey properly resets char select cutscene --- source/funkin/ui/mainmenu/MainMenuState.hx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 4e416abeba..13d68da6d9 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -437,7 +437,10 @@ class MainMenuState extends MusicBeatState if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.N) { @:privateAccess - funkin.save.Save.instance.data.unlocks.charactersSeen = ["bf"]; + { + funkin.save.Save.instance.data.unlocks.charactersSeen = ["bf"]; + funkin.save.Save.instance.data.unlocks.oldChar = false; + } } if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.E) From e5673549cf7c17c58dfc340279c4ac0ed4377dd7 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 12 Sep 2024 15:38:20 -0400 Subject: [PATCH 431/469] Fix issues with the difficulty selector showing non-existant difficulties in Freeplay --- source/funkin/ui/freeplay/FreeplayState.hx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index a908e54c6f..ec80a1ff6a 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -354,10 +354,6 @@ class FreeplayState extends MusicBeatSubState if (availableDifficultiesForSong.length == 0) continue; songs.push(new FreeplaySongData(levelId, songId, song, displayedVariations)); - for (difficulty in availableDifficultiesForSong) - { - diffIdsTotal.pushUnique(difficulty); - } for (difficulty in unsuffixedDifficulties) { diffIdsTotal.pushUnique(difficulty); @@ -1764,12 +1760,13 @@ class FreeplayState extends MusicBeatSubState var songScore:Null = Save.instance.getSongScore(daSong.songId, suffixedDifficulty); intendedScore = songScore?.score ?? 0; intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes); - rememberedDifficulty = currentDifficulty; + rememberedDifficulty = suffixedDifficulty; } else { intendedScore = 0; intendedCompletion = 0.0; + rememberedDifficulty = currentDifficulty; } if (intendedCompletion == Math.POSITIVE_INFINITY || intendedCompletion == Math.NEGATIVE_INFINITY || Math.isNaN(intendedCompletion)) From 721cecd271fb8b40fe1ed26ee7908d94bd1f522e Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 15:38:44 -0400 Subject: [PATCH 432/469] add preference option for framerate capping --- source/Main.hx | 11 ++--- source/funkin/Preferences.hx | 53 +++++++++++++++++++++ source/funkin/save/Save.hx | 7 +++ source/funkin/ui/options/PreferencesMenu.hx | 6 +++ 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/source/Main.hx b/source/Main.hx index 7f7d25235e..724b118f81 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -67,14 +67,9 @@ class Main extends Sprite function init(?event:Event):Void { #if web - untyped js.Syntax.code(" - window.requestAnimationFrame = function(callback, element) { - var currTime = new Date().getTime(); - var timeToCall = 0; - var id = window.setTimeout(function() { callback(currTime + timeToCall); }, - timeToCall); - return id; - }"); + // set this variable (which is a function) from the lime version at lime/_internal/backend/html5/HTML5Application.hx + // The framerate cap will more thoroughly initialize via Preferences in InitState.hx + funkin.Preferences.lockedFramerateFunction = untyped js.Syntax.code("window.requestAnimationFrame"); #end if (hasEventListener(Event.ADDED_TO_STAGE)) diff --git a/source/funkin/Preferences.hx b/source/funkin/Preferences.hx index b2050c6a24..daeded897d 100644 --- a/source/funkin/Preferences.hx +++ b/source/funkin/Preferences.hx @@ -128,6 +128,48 @@ class Preferences return value; } + public static var unlockedFramerate(get, set):Bool; + + static function get_unlockedFramerate():Bool + { + return Save?.instance?.options?.unlockedFramerate; + } + + static function set_unlockedFramerate(value:Bool):Bool + { + if (value != Save.instance.options.unlockedFramerate) + { + #if web + toggleFramerateCap(value); + #end + } + + var save:Save = Save.instance; + save.options.unlockedFramerate = value; + save.flush(); + return value; + } + + #if web + // We create a haxe version of this just for readability. + // We use these to override `window.requestAnimationFrame` in Javascript to uncap the framerate / "animation" request rate + // Javascript is crazy since u can just do stuff like that lol + + public static function unlockedFramerateFunction(callback, element) + { + var currTime = Date.now().getTime(); + var timeToCall = 0; + var id = js.Browser.window.setTimeout(function() { + callback(currTime + timeToCall); + }, timeToCall); + return id; + } + + // Lime already implements their own little framerate cap, so we can just use that + // This also gets set in the init function in Main.hx, since we need to definitely override it + public static var lockedFramerateFunction = untyped js.Syntax.code("window.requestAnimationFrame"); + #end + /** * Loads the user's preferences from the save data and apply them. */ @@ -137,6 +179,17 @@ class Preferences FlxG.autoPause = Preferences.autoPause; // Apply the debugDisplay setting (enables the FPS and RAM display). toggleDebugDisplay(Preferences.debugDisplay); + #if web + toggleFramerateCap(Preferences.unlockedFramerate); + #end + } + + static function toggleFramerateCap(unlocked:Bool):Void + { + #if web + var framerateFunction = unlocked ? unlockedFramerateFunction : lockedFramerateFunction; + untyped js.Syntax.code("window.requestAnimationFrame = framerateFunction;"); + #end } static function toggleDebugDisplay(show:Bool):Void diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index 1fa283b264..4b1649c5b5 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -97,6 +97,7 @@ class Save autoPause: true, inputOffset: 0, audioVisualOffset: 0, + unlockedFramerate: false, controls: { @@ -1171,6 +1172,12 @@ typedef SaveDataOptions = */ var audioVisualOffset:Int; + /** + * If we want the framerate to be unlocked on HTML5. + * @default `false + */ + var unlockedFramerate:Bool; + var controls: { var p1: diff --git a/source/funkin/ui/options/PreferencesMenu.hx b/source/funkin/ui/options/PreferencesMenu.hx index 5fbefceed6..eb7b887927 100644 --- a/source/funkin/ui/options/PreferencesMenu.hx +++ b/source/funkin/ui/options/PreferencesMenu.hx @@ -72,6 +72,12 @@ class PreferencesMenu extends Page createPrefItemCheckbox('Auto Pause', 'Automatically pause the game when it loses focus', function(value:Bool):Void { Preferences.autoPause = value; }, Preferences.autoPause); + + #if web + createPrefItemCheckbox('Unlocked Framerate', 'Enable to unlock the framerate', function(value:Bool):Void { + Preferences.unlockedFramerate = value; + }, Preferences.unlockedFramerate); + #end } override function update(elapsed:Float):Void From 3970189fe92dbbdac9256687174f1a9953ee83cf Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 12 Sep 2024 15:43:40 -0400 Subject: [PATCH 433/469] Ensure save data gets flushed when you see a character --- source/funkin/save/Save.hx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index 1fa283b264..43b7667da3 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -407,6 +407,9 @@ class Save return data.unlocks.charactersSeen; } + /** + * Marks whether the player has seen the spotlight animation, which should only display once per save file ever. + */ public var oldChar(get, set):Bool; function get_oldChar():Bool @@ -416,7 +419,9 @@ class Save function set_oldChar(value:Bool):Bool { - return data.unlocks.oldChar = value; + data.unlocks.oldChar = value; + flush(); + return data.unlocks.oldChar; } /** @@ -425,7 +430,11 @@ class Save */ public function addCharacterSeen(character:String):Void { - if (!data.unlocks.charactersSeen.contains(character)) data.unlocks.charactersSeen.push(character); + if (!data.unlocks.charactersSeen.contains(character)) + { + data.unlocks.charactersSeen.push(character); + flush(); + } } /** From a9667c54f377a1207b70ba163ceebd8f239a5822 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 12 Sep 2024 15:48:41 -0400 Subject: [PATCH 434/469] Fix FlxAtlasSprite completion handling and music looping --- source/funkin/audio/FunkinSound.hx | 2 ++ .../funkin/graphics/adobeanimate/FlxAtlasSprite.hx | 9 +++++++-- source/funkin/play/ResultState.hx | 6 ++---- source/funkin/play/scoring/Scoring.hx | 13 ------------- 4 files changed, 11 insertions(+), 19 deletions(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 18a0b4da2e..dae31cd07e 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -340,6 +340,8 @@ class FunkinSound extends FlxSound implements ICloneable if (songMusicData != null) { Conductor.instance.mapTimeChanges(songMusicData.timeChanges); + + if (songMusicData.looped != null && params.loop == null) params.loop = songMusicData.looped; } else { diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index f245afec74..952fa8b717 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -185,11 +185,12 @@ class FlxAtlasSprite extends FlxAnimate // Move to the first frame of the animation. // goToFrameLabel(id); trace('Playing animation $id'); - if (this.anim.symbolDictionary.exists(id) || (this.anim.getByName(id) != null)) + if ((id == null || id == "") || this.anim.symbolDictionary.exists(id) || (this.anim.getByName(id) != null)) { this.anim.play(id, restart, false, startFrame); - if (id == "Boyfriend DJ fist pump" || startFrame == 4) trace("PUMP COMMAND: " + anim.curFrame); + this.currentAnimation = anim.curSymbol.name; + fr = null; } // Only call goToFrameLabel if there is a frame label with that name. This prevents annoying warnings! @@ -316,6 +317,10 @@ class FlxAtlasSprite extends FlxAnimate { onAnimationComplete.dispatch(currentAnimation); } + else + { + onAnimationComplete.dispatch(''); + } } var prevFrames:Map = []; diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index e0ac3579d7..739df167d8 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -418,8 +418,7 @@ class ResultState extends MusicBeatSubState { startingVolume: 1.0, overrideExisting: true, - restartTrack: true, - loop: rank.shouldMusicLoop() + restartTrack: true }); }); } @@ -429,8 +428,7 @@ class ResultState extends MusicBeatSubState { startingVolume: 1.0, overrideExisting: true, - restartTrack: true, - loop: rank.shouldMusicLoop() + restartTrack: true }); } }); diff --git a/source/funkin/play/scoring/Scoring.hx b/source/funkin/play/scoring/Scoring.hx index 6c9f9bd979..dae0986388 100644 --- a/source/funkin/play/scoring/Scoring.hx +++ b/source/funkin/play/scoring/Scoring.hx @@ -577,19 +577,6 @@ enum abstract ScoringRank(String) } } - public function shouldMusicLoop():Bool - { - switch (abstract) - { - case PERFECT_GOLD | PERFECT | EXCELLENT | GREAT | GOOD: - return true; - case SHIT: - return false; - default: - return false; - } - } - public function getHorTextAsset() { switch (abstract) From f7f311d6f2dc356b363efc8c2f6d7cdf50fd4e35 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 12 Sep 2024 21:07:40 +0100 Subject: [PATCH 435/469] assets submod....... --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index bd4e2421c8..e9cb3eb692 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit bd4e2421c87c31e244bc9c162850b842582c5de4 +Subproject commit e9cb3eb6922b60f43ab25b4695acfd15893ae96e From 33df8936b10e5ee216f6c3817550945993d9466c Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 16:41:14 -0400 Subject: [PATCH 436/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 934468c047..7b5fc30552 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 934468c0472504c282b12c63b473e9f74b5b4517 +Subproject commit 7b5fc3055235c401dd711da9617b3719df1dcf27 From 1ee351c83d13b1c616ac3751675389943a715f83 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 16:42:57 -0400 Subject: [PATCH 437/469] art submod --- art | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/art b/art index 42ac06e512..0dee03f11a 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit 42ac06e51207bda89144fd20a407ed3c3e0c6673 +Subproject commit 0dee03f11afc01c2883da223fa10405f7011dd33 From a4e977f60f531b90699473640be2ff0027c26b78 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 16:45:20 -0400 Subject: [PATCH 438/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index e9cb3eb692..8762fd51c2 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit e9cb3eb6922b60f43ab25b4695acfd15893ae96e +Subproject commit 8762fd51c26abdc9a45657eab9018aa48e972cd6 From 395de4ee9571daab95fc8f32bf7fb1c309d7d0b0 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Thu, 12 Sep 2024 22:53:01 +0200 Subject: [PATCH 439/469] Char select subs stuff --- assets | 2 +- .../ui/charSelect/CharSelectSubState.hx | 142 ++++++++++++------ 2 files changed, 94 insertions(+), 50 deletions(-) diff --git a/assets b/assets index 26ab9a1dc8..ff425d983e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 26ab9a1dc8eec563717711b290c21d0e9e56a005 +Subproject commit ff425d983e3b017b3a7dc241f03ae51f1fae9116 diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index e075abca3d..b83b3a6af2 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -35,63 +35,58 @@ import openfl.filters.ShaderFilter; import funkin.util.FramesJSFLParser; import funkin.util.FramesJSFLParser.FramesJSFLInfo; import funkin.util.FramesJSFLParser.FramesJSFLFrame; +import funkin.graphics.FunkinSprite; class CharSelectSubState extends MusicBeatSubState { var cursor:FlxSprite; + var cursorBlue:FlxSprite; var cursorDarkBlue:FlxSprite; - var grpCursors:FlxTypedGroup; - var cursorConfirmed:FlxSprite; var cursorDenied:FlxSprite; - var cursorX:Int = 0; var cursorY:Int = 0; - var cursorFactor:Float = 110; var cursorOffsetX:Float = -16; var cursorOffsetY:Float = -48; - var cursorLocIntended:FlxPoint = new FlxPoint(0, 0); var lerpAmnt:Float = 0.95; - var tmrFrames:Int = 60; - var currentStage:Stage; - var playerChill:CharSelectPlayer; var playerChillOut:CharSelectPlayer; var gfChill:CharSelectGF; var gfChillOut:CharSelectGF; - var barthing:FlxAtlasSprite; var dipshitBacking:FlxSprite; var chooseDipshit:FlxSprite; var dipshitBlur:FlxSprite; var transitionGradient:FlxSprite; - var curChar(default, set):String = "pico"; var nametag:Nametag; var camFollow:FlxObject; var autoFollow:Bool = false; - var availableChars:Map = new Map(); var pressedSelect:Bool = false; - var selectTimer:FlxTimer = new FlxTimer(); + var selectSound:FunkinSound; var unlockSound:FunkinSound; + var lockedSound:FunkinSound; + var introSound:FunkinSound; + var staticSound:FunkinSound; var charSelectCam:FunkinCamera; var selectedBizz:Array = [ - new DropShadowFilter(0, 0, 0xFFFFFF, 1, 2, 2, 21, 1, false, false, false), + new DropShadowFilter(0, 0, 0xFFFFFF, 1, 2, 2, 19, 1, false, false, false), new DropShadowFilter(5, 45, 0x000000, 1, 2, 2, 1, 1, false, false, false) ]; var bopInfo:FramesJSFLInfo; + var blackScreen:FunkinSprite; public function new() { @@ -125,11 +120,6 @@ class CharSelectSubState extends MusicBeatSubState override public function create():Void { - openSubState(new IntroSubState()); - subStateClosed.addOnce((_) -> { - camera.flash(); - checkNewChar(); - }); super.create(); bopInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/iconBopInfo/iconBopInfo.txt")); @@ -140,7 +130,7 @@ class CharSelectSubState extends MusicBeatSubState add(bg); var crowd:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/crowd")); - crowd.anim.play(""); + crowd.anim.play(); crowd.scrollFactor.set(0.3, 0.3); add(crowd); @@ -301,8 +291,25 @@ class CharSelectSubState extends MusicBeatSubState unlockSound.volume = 0; unlockSound.play(true); - FlxG.sound.defaultSoundGroup.add(unlockSound); - FlxG.sound.list.add(unlockSound); + lockedSound = new FunkinSound(); + lockedSound.loadEmbedded(Paths.sound('CS_locked')); + lockedSound.pitch = 1; + + lockedSound.volume = 1.; + + FlxG.sound.defaultSoundGroup.add(lockedSound); + FlxG.sound.list.add(lockedSound); + + staticSound = new FunkinSound(); + staticSound.loadEmbedded(Paths.sound('static loop')); + staticSound.pitch = 1; + + staticSound.looped = true; + + staticSound.volume = 0.6; + + FlxG.sound.defaultSoundGroup.add(staticSound); + FlxG.sound.list.add(staticSound); // playing it here to preload it. not doing this makes a super awkward pause at the end of the intro // TODO: probably make an intro thing for funkinSound itself that preloads the next audio? @@ -365,6 +372,34 @@ class CharSelectSubState extends MusicBeatSubState FlxG.camera.follow(camFollow, LOCKON, 0.01); } }); + + var blackScreen = new FunkinSprite().makeSolidColor(FlxG.width * 2, FlxG.height * 2, 0xFF000000); + blackScreen.x = -(FlxG.width * 0.5); + blackScreen.y = -(FlxG.height * 0.5); + add(blackScreen); + + introSound = new FunkinSound(); + introSound.loadEmbedded(Paths.sound('CS_Lights')); + introSound.pitch = 1; + introSound.volume = 0; + + FlxG.sound.defaultSoundGroup.add(introSound); + FlxG.sound.list.add(introSound); + + openSubState(new IntroSubState()); + subStateClosed.addOnce((_) -> { + remove(blackScreen); + if (!Save.instance.oldChar) + { + camera.flash(); + + introSound.volume = 1; + introSound.play(true); + } + checkNewChar(); + + Save.instance.oldChar = true; + }); } function checkNewChar():Void @@ -396,7 +431,6 @@ class CharSelectSubState extends MusicBeatSubState var grpIcons:FlxSpriteGroup; var grpXSpread(default, set):Float = 107; var grpYSpread(default, set):Float = 127; - var nonLocks = []; function initLocks():Void @@ -426,9 +460,9 @@ class CharSelectSubState extends MusicBeatSubState var temp:Lock = new Lock(0, 0, i); temp.ID = 1; - temp.onAnimationComplete.add(function(anim) { - if (anim == "unlock") playerChill.playAnimation("unlock", true); - }); + // temp.onAnimationComplete.add(function(anim) { + // if (anim == "unlock") playerChill.playAnimation("unlock", true); + // }); grpIcons.add(temp); } @@ -443,7 +477,7 @@ class CharSelectSubState extends MusicBeatSubState { var index = nonLocks[0]; - // pressedSelect = true; + pressedSelect = true; var copy = 3; @@ -505,7 +539,7 @@ class CharSelectSubState extends MusicBeatSubState updateIconPositions(); playerChillOut.onAnimationComplete.addOnce((_) -> if (_ == "death") { - sync = false; + // sync = false; playerChillOut.visible = false; playerChillOut.switchChar(char); }); @@ -515,6 +549,9 @@ class CharSelectSubState extends MusicBeatSubState { pressedSelect = false; @:bypassAccessor curChar = char; + + staticSound.stop(); + FunkinSound.playMusic('stayFunky', { startingVolume: 1, @@ -561,15 +598,13 @@ class CharSelectSubState extends MusicBeatSubState } var sync:Bool = false; - var syncLock:Lock = null; - var audioBizz:Float = 0; function syncAudio(elapsed:Float):Void { @:privateAccess - if (sync && !unlockSound.paused) + if (sync && unlockSound.time > 0) { // if (playerChillOut.anim.framerate > 0) // { @@ -580,14 +615,12 @@ class CharSelectSubState extends MusicBeatSubState playerChillOut.anim._tick = 0; if (syncLock != null) syncLock.anim._tick = 0; - trace(unlockSound.time); - - if ((unlockSound.time - audioBizz) >= (delay * 1000)) + if ((unlockSound.time - audioBizz) >= ((delay) * 100)) { if (syncLock != null) syncLock.anim._tick = delay; playerChillOut.anim._tick = delay; - audioBizz += delay * 1000; + audioBizz += delay * 100; } } } @@ -734,11 +767,14 @@ class CharSelectSubState extends MusicBeatSubState cursorY = -1; } - if (availableChars.exists(getCurrentSelected()) && Save.instance.charactersSeen.contains(availableChars[getCurrentSelected()])) + if (autoFollow + && availableChars.exists(getCurrentSelected()) + && Save.instance.charactersSeen.contains(availableChars[getCurrentSelected()])) { + gfChill.visible = true; curChar = availableChars.get(getCurrentSelected()); - if (controls.ACCEPT) + if (!pressedSelect && controls.ACCEPT) { cursorConfirmed.visible = true; cursorConfirmed.x = cursor.x - 2; @@ -752,7 +788,7 @@ class CharSelectSubState extends MusicBeatSubState FlxTween.tween(FlxG.sound.music, {pitch: 0.1}, 1, {ease: FlxEase.quadInOut}); FlxTween.tween(FlxG.sound.music, {volume: 0.0}, 1.5, {ease: FlxEase.quadInOut}); playerChill.playAnimation("select"); - gfChill.playAnimation("confirm"); + gfChill.playAnimation("confirm", true, false, true); pressedSelect = true; selectTimer.start(1.5, (_) -> { // pressedSelect = false; @@ -775,29 +811,37 @@ class CharSelectSubState extends MusicBeatSubState FlxTween.tween(FlxG.sound.music, {pitch: 1.0, volume: 1.0}, 1, {ease: FlxEase.quartInOut}); playerChill.playAnimation("deselect"); gfChill.playAnimation("deselect"); + pressedSelect = false; FlxTween.tween(FlxG.sound.music, {pitch: 1.0}, 1, { ease: FlxEase.quartInOut, onComplete: (_) -> { - playerChill.playAnimation("idle", true, false, true); - gfChill.playAnimation("idle", true, false, true); + if (playerChill.getCurrentAnimation() == "deselect loop start" || playerChill.getCurrentAnimation() == "deselect") + { + playerChill.playAnimation("idle", true, false, true); + gfChill.playAnimation("idle", true, false, true); + } } }); - pressedSelect = false; selectTimer.cancel(); } } - else + else if (autoFollow) { curChar = "locked"; + gfChill.visible = false; + if (controls.ACCEPT) { cursorDenied.visible = true; cursorDenied.x = cursor.x - 2; cursorDenied.y = cursor.y - 4; - playerChill.playAnimation("cannot select", true); + playerChill.playAnimation("cannot select Label", true); + + lockedSound.play(true); + cursorDenied.animation.play("idle", true); cursorDenied.animation.finishCallback = (_) -> { cursorDenied.visible = false; @@ -831,13 +875,9 @@ class CharSelectSubState extends MusicBeatSubState } var bopTimer:Float = 0; - var delay = 1 / 24; - var bopFr = 0; - var bopPlay:Bool = false; - var bopRefX:Float = 0; var bopRefY:Float = 0; @@ -957,10 +997,10 @@ class CharSelectSubState extends MusicBeatSubState memb.filters = selectedBizz; memb.scale.set(2.6, 2.6); } - if (controls.ACCEPT && memb.animation.curAnim.name == "confirm") memb.animation.play("confirm"); - if (pressedSelect && controls.BACK) + if (pressedSelect && memb.animation.curAnim.name == "idle") memb.animation.play("confirm"); + if (autoFollow && !pressedSelect && memb.animation.curAnim.name != "idle") { - memb.animation.play("confirm", true, true); + memb.animation.play("confirm", false, true); member.animation.finishCallback = (_) -> { member.animation.play("idle"); member.animation.finishCallback = null; @@ -991,6 +1031,10 @@ class CharSelectSubState extends MusicBeatSubState curChar = value; + if (value == "locked") staticSound.play(); + else + staticSound.stop(); + nametag.switchChar(value); playerChill.visible = false; playerChillOut.visible = true; From f0ac09f949f7f56789e08ceaacbbbdb456a8ad03 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Thu, 12 Sep 2024 22:53:11 +0200 Subject: [PATCH 440/469] Art master bizz --- art | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/art b/art index 1206249aa6..0bb988c497 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit 1206249aa65e13df2d6a196c35222f768ad4a3f4 +Subproject commit 0bb988c49788fd25a230b56dd9e4448838bc79c9 From 2a9814e03061ee6b18dc61a93d17b5616eae46d1 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Thu, 12 Sep 2024 22:58:31 +0200 Subject: [PATCH 441/469] port SAVE from develop --- source/funkin/save/Save.hx | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index 80b05e5ac2..43b7667da3 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -125,6 +125,7 @@ class Save { // Default to having seen the default character. charactersSeen: ["bf"], + oldChar: false }, optionsChartEditor: @@ -406,13 +407,34 @@ class Save return data.unlocks.charactersSeen; } + /** + * Marks whether the player has seen the spotlight animation, which should only display once per save file ever. + */ + public var oldChar(get, set):Bool; + + function get_oldChar():Bool + { + return data.unlocks.oldChar; + } + + function set_oldChar(value:Bool):Bool + { + data.unlocks.oldChar = value; + flush(); + return data.unlocks.oldChar; + } + /** * When we've seen a character unlock, add it to the list of characters seen. * @param character */ public function addCharacterSeen(character:String):Void { - if (!data.unlocks.charactersSeen.contains(character)) data.unlocks.charactersSeen.push(character); + if (!data.unlocks.charactersSeen.contains(character)) + { + data.unlocks.charactersSeen.push(character); + flush(); + } } /** @@ -1027,6 +1049,12 @@ typedef SaveDataUnlocks = * add it to this list so that we don't show it again. */ var charactersSeen:Array; + + /** + * This is a conditional when the player enters the character state + * For the first time ever + */ + var oldChar:Bool; } /** From 6dca858fd7370d23626d76b64c50eedafc36519c Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Thu, 12 Sep 2024 23:13:30 +0200 Subject: [PATCH 442/469] Introbizz --- source/funkin/ui/charSelect/IntroSubState.hx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/source/funkin/ui/charSelect/IntroSubState.hx b/source/funkin/ui/charSelect/IntroSubState.hx index 04503cbb9e..8706013b01 100644 --- a/source/funkin/ui/charSelect/IntroSubState.hx +++ b/source/funkin/ui/charSelect/IntroSubState.hx @@ -20,8 +20,6 @@ class IntroSubState extends MusicBeatSubState { static final ATTRACT_VIDEO_PATH:String = Paths.stripLibrary(Paths.videos('introSelect')); - var introSound:FunkinSound = null; - public override function create():Void { // Pause existing music. @@ -40,15 +38,6 @@ class IntroSubState extends MusicBeatSubState trace('Playing native video ${ATTRACT_VIDEO_PATH}'); playVideoNative(ATTRACT_VIDEO_PATH); #end - - introSound = new FunkinSound(); - introSound.loadEmbedded(Paths.sound('CS_Lights')); - introSound.pitch = 1; - - FlxG.sound.defaultSoundGroup.add(introSound); - FlxG.sound.list.add(introSound); - - introSound.play(true); } #if html5 From a4bdeeaf5f9b3a7aa57a87c6d28b190e860e7749 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 12 Sep 2024 17:13:40 -0400 Subject: [PATCH 443/469] Update polymod to fix a crash on missing imports --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index 50b81bb855..d967a69b31 100644 --- a/hmm.json +++ b/hmm.json @@ -194,7 +194,7 @@ "name": "polymod", "type": "git", "dir": null, - "ref": "96cfc5fa693b017e47f7cb13b765cc68698fa6b6", + "ref": "0fbdf27fe124549730accd540cec8a183f8652c0", "url": "https://github.com/larsiusprime/polymod" }, { From ae6f431b6a9f7a05bf819c908794a16b2fb073f2 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 12 Sep 2024 17:13:46 -0400 Subject: [PATCH 444/469] Update credits --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 8762fd51c2..dd2aab796d 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8762fd51c26abdc9a45657eab9018aa48e972cd6 +Subproject commit dd2aab796d2a54cf01c534402aa922da1f820080 From 34d1c568ea949740837bd7c1c4e9a685e4187d94 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Thu, 12 Sep 2024 23:22:20 +0200 Subject: [PATCH 445/469] locked fixin --- assets | 2 +- source/funkin/ui/charSelect/CharSelectPlayer.hx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets b/assets index ff425d983e..6b97a5ce8b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit ff425d983e3b017b3a7dc241f03ae51f1fae9116 +Subproject commit 6b97a5ce8bb09944e466bc6c342139462820f81f diff --git a/source/funkin/ui/charSelect/CharSelectPlayer.hx b/source/funkin/ui/charSelect/CharSelectPlayer.hx index 710bdd45ab..b6319f16d8 100644 --- a/source/funkin/ui/charSelect/CharSelectPlayer.hx +++ b/source/funkin/ui/charSelect/CharSelectPlayer.hx @@ -27,7 +27,7 @@ class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass case "deselect": playAnimation("deselect loop start", true, false, true); - case "slidein idle point", "cannot select", "unlock": + case "slidein idle point", "cannot select Label", "unlock": playAnimation("idle", true, false, false); case "idle": trace('Waiting for onBeatHit'); From 703e8992882b8b3e70a74e74c31aa5e52137d6c2 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 12 Sep 2024 22:43:12 +0100 Subject: [PATCH 446/469] apply shader filter --- source/funkin/ui/charSelect/CharSelectSubState.hx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index bd55bfdd94..00e15a2992 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -324,6 +324,9 @@ class CharSelectSubState extends MusicBeatSubState // FlxG.camera.follow(camFollow, LOCKON, 0.01); FlxG.camera.follow(camFollow, LOCKON); + var fadeShaderFilter:ShaderFilter = new ShaderFilter(fadeShader); + FlxG.camera.filters = [fadeShaderFilter]; + var temp:FlxSprite = new FlxSprite(); temp.loadGraphic(Paths.image('charSelect/placement')); add(temp); From 5f60be6d7569862486c9eb062da60ee23305f008 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 12 Sep 2024 22:44:22 +0100 Subject: [PATCH 447/469] allow controls to get dialouge name from control + ignore brackets --- source/funkin/input/Controls.hx | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/source/funkin/input/Controls.hx b/source/funkin/input/Controls.hx index b5aefd08d7..da5aaac58f 100644 --- a/source/funkin/input/Controls.hx +++ b/source/funkin/input/Controls.hx @@ -395,20 +395,37 @@ class Controls extends FlxActionSet return result; } - public function getDialogueName(action:FlxActionDigital):String + public function getDialogueName(action:FlxActionDigital, ?ignoreSurrounding:Bool = false):String { var input = action.inputs[0]; - return switch (input.device) + if (ignoreSurrounding == false) { - case KEYBOARD: return '[${(input.inputID : FlxKey)}]'; - case GAMEPAD: return '(${(input.inputID : FlxGamepadInputID)})'; - case device: throw 'unhandled device: $device'; + return switch (input.device) + { + case KEYBOARD: return '[${(input.inputID : FlxKey)}]'; + case GAMEPAD: return '(${(input.inputID : FlxGamepadInputID)})'; + case device: throw 'unhandled device: $device'; + } + } + else + { + return switch (input.device) + { + case KEYBOARD: return '${(input.inputID : FlxKey)}'; + case GAMEPAD: return '${(input.inputID : FlxGamepadInputID)}'; + case device: throw 'unhandled device: $device'; + } } } - public function getDialogueNameFromToken(token:String):String + public function getDialogueNameFromToken(token:String, ?ignoreSurrounding:Bool = false):String + { + return getDialogueName(getActionFromControl(Control.createByName(token.toUpperCase())), ignoreSurrounding); + } + + public function getDialogueNameFromControl(control:Control, ?ignoreSurrounding:Bool = false):String { - return getDialogueName(getActionFromControl(Control.createByName(token.toUpperCase()))); + return getDialogueName(getActionFromControl(control), ignoreSurrounding); } function getActionFromControl(control:Control):FlxActionDigital From bc86f1f459605450b092121ea295b8a3ef23541e Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 12 Sep 2024 22:44:40 +0100 Subject: [PATCH 448/469] character select hint text --- source/funkin/ui/freeplay/FreeplayState.hx | 29 ++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index a908e54c6f..23b9dd0bdb 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -9,6 +9,7 @@ import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; import flixel.input.touch.FlxTouch; import flixel.math.FlxAngle; +import flixel.math.FlxMath; import flixel.math.FlxPoint; import flixel.system.debug.watch.Tracker.TrackerProfile; import flixel.text.FlxText; @@ -180,6 +181,8 @@ class FreeplayState extends MusicBeatSubState var ostName:FlxText; var albumRoll:AlbumRoll; + var charSelectHint:FlxText; + var letterSort:LetterSort; var exitMovers:ExitMoverData = new Map(); @@ -279,6 +282,7 @@ class FreeplayState extends MusicBeatSubState txtCompletion = new AtlasText(1185, 87, '69', AtlasFont.FREEPLAY_CLEAR); ostName = new FlxText(8, 8, FlxG.width - 8 - 8, 'OFFICIAL OST', 48); + charSelectHint = new FlxText(-40, 18, FlxG.width - 8 - 8, 'Press [ LOL ] to change characters', 32); bgDad = new FlxSprite(backingCard.pinkBack.width * 0.74, 0).loadGraphic(styleData == null ? 'freeplay/freeplayBGdad' : styleData.getBgAssetGraphic()); } @@ -498,7 +502,14 @@ class FreeplayState extends MusicBeatSubState ostName.alignment = RIGHT; ostName.visible = false; - exitMovers.set([overhangStuff, fnfFreeplay, ostName], + charSelectHint.alignment = CENTER; + charSelectHint.font = "5by7"; + charSelectHint.color = 0xFF5F5F5F; + charSelectHint.text = 'Press [ ${controls.getDialogueNameFromControl(FREEPLAY_CHAR_SELECT, true)} ] to change characters'; + charSelectHint.y -= 100; + FlxTween.tween(charSelectHint, {y: charSelectHint.y + 100}, 0.8, {ease: FlxEase.quartOut}); + + exitMovers.set([overhangStuff, fnfFreeplay, ostName, charSelectHint], { y: -overhangStuff.height, x: 0, @@ -506,7 +517,7 @@ class FreeplayState extends MusicBeatSubState wait: 0 }); - exitMoversCharSel.set([overhangStuff, fnfFreeplay, ostName], + exitMoversCharSel.set([overhangStuff, fnfFreeplay, ostName, charSelectHint], { y: -300, speed: 0.8, @@ -604,6 +615,11 @@ class FreeplayState extends MusicBeatSubState add(fnfFreeplay); add(ostName); + if (PlayerRegistry.instance.hasNewCharacter() == true) + { + add(charSelectHint); + } + // be careful not to "add()" things in here unless it's to a group that's already added to the state // otherwise it won't be properly attatched to funnyCamera (relavent code should be at the bottom of create()) var onDJIntroDone = function() { @@ -1348,10 +1364,19 @@ class FreeplayState extends MusicBeatSubState var originalPos:FlxPoint = new FlxPoint(); + var hintTimer:Float = 0; + override function update(elapsed:Float):Void { super.update(elapsed); + if (charSelectHint != null) + { + hintTimer += elapsed * 2; + var targetAmt:Float = (Math.sin(hintTimer) + 1) / 2; + charSelectHint.alpha = FlxMath.lerp(0.3, 0.9, targetAmt); + } + #if FEATURE_DEBUG_FUNCTIONS if (FlxG.keys.justPressed.P) { From f96532f104dfc126b89824c6d0a570b6817d4ace Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Thu, 12 Sep 2024 22:47:58 +0100 Subject: [PATCH 449/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index bd4e2421c8..350910d809 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit bd4e2421c87c31e244bc9c162850b842582c5de4 +Subproject commit 350910d8094853d11deb2ee27cacbe3ada27af32 From 1e9c6f0c4cabaaf439e493c619dc65f564e0a7c7 Mon Sep 17 00:00:00 2001 From: CheemsAndFriends Date: Fri, 13 Sep 2024 00:31:52 +0200 Subject: [PATCH 450/469] Add Char Select stuff --- assets | 2 +- source/funkin/ui/charSelect/CharSelectSubState.hx | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/assets b/assets index 6b97a5ce8b..f4cf1d3721 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 6b97a5ce8bb09944e466bc6c342139462820f81f +Subproject commit f4cf1d3721155e16ab4e547b6baabc1238b66dae diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index b83b3a6af2..6839d5a21e 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -291,6 +291,9 @@ class CharSelectSubState extends MusicBeatSubState unlockSound.volume = 0; unlockSound.play(true); + FlxG.sound.defaultSoundGroup.add(unlockSound); + FlxG.sound.list.add(unlockSound); + lockedSound = new FunkinSound(); lockedSound.loadEmbedded(Paths.sound('CS_locked')); lockedSound.pitch = 1; @@ -404,7 +407,7 @@ class CharSelectSubState extends MusicBeatSubState function checkNewChar():Void { - if (nonLocks.length > 0) selectTimer.start(0.5, (_) -> { + if (nonLocks.length > 0) selectTimer.start(2, (_) -> { unLock(); }); else From 2dc4e13b001de00318884dbbf635e26240586622 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 19:08:13 -0400 Subject: [PATCH 451/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index dd2aab796d..41d20d45d7 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit dd2aab796d2a54cf01c534402aa922da1f820080 +Subproject commit 41d20d45d7503aaf1dfac825239620cffb512166 From fdc5bc6b79880390851559278df94de71b551f98 Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Fri, 13 Sep 2024 00:37:47 +0100 Subject: [PATCH 452/469] assets submod... --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 8762fd51c2..51d0537239 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8762fd51c26abdc9a45657eab9018aa48e972cd6 +Subproject commit 51d0537239debf699efa90591b10995c5b765095 From 47f3c6f00a18b50a614ffd09f9200ee18c89f499 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 19:56:04 -0400 Subject: [PATCH 453/469] vid sizing --- source/funkin/ui/charSelect/IntroSubState.hx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/funkin/ui/charSelect/IntroSubState.hx b/source/funkin/ui/charSelect/IntroSubState.hx index 261b7b35cf..2c29084732 100644 --- a/source/funkin/ui/charSelect/IntroSubState.hx +++ b/source/funkin/ui/charSelect/IntroSubState.hx @@ -41,10 +41,10 @@ class IntroSubState extends MusicBeatSubState playVideoNative(LIGHTS_VIDEO_PATH); #end - // Im TOO lazy to even care, so uh, yep - FlxG.camera.zoom = 0.66666666666666666666666666666667; - vid.x = -(FlxG.width - (FlxG.width * FlxG.camera.zoom)); - vid.y = -((FlxG.height - (FlxG.height * FlxG.camera.zoom)) * 0.75); + // // Im TOO lazy to even care, so uh, yep + // FlxG.camera.zoom = 0.66666666666666666666666666666667; + // vid.x = -(FlxG.width - (FlxG.width * FlxG.camera.zoom)); + // vid.y = -((FlxG.height - (FlxG.height * FlxG.camera.zoom)) * 0.75); } #if html5 From 0aa2ccd1cd6ddfb0c002a31d9bf408017cc24154 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 20:05:14 -0400 Subject: [PATCH 454/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 51d0537239..a3a8c4a153 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 51d0537239debf699efa90591b10995c5b765095 +Subproject commit a3a8c4a1535140a1e8127c862c28c86000fe8403 From 172c90b1bd6d6baedf06b6170bf64528b92f4adb Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 20:12:20 -0400 Subject: [PATCH 455/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index a3a8c4a153..3d096c3b89 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit a3a8c4a1535140a1e8127c862c28c86000fe8403 +Subproject commit 3d096c3b89e1b4c92c0a3f058e80f19585126e1e From af31be5436423422379c824d0c421dd66c6f7252 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 20:14:48 -0400 Subject: [PATCH 456/469] makeship linky --- source/funkin/util/Constants.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index 1653311df6..57fc484b81 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -63,7 +63,7 @@ class Constants /** * Link to buy merch for the game. */ - public static final URL_MERCH:String = 'https://needlejuicerecords.com/pages/friday-night-funkin'; + public static final URL_MERCH:String = 'https://www.makeship.com/shop/creator/friday-night-funkin'; /** * Preloader sitelock. From c6bdd36959cb0f85b429d6d66e5842a006a3d8e2 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 12 Sep 2024 20:32:19 -0400 Subject: [PATCH 457/469] Fix blazin offsets --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index a3a8c4a153..96e5401f7e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit a3a8c4a1535140a1e8127c862c28c86000fe8403 +Subproject commit 96e5401f7e1facbf9a59a52a2d8f59523cb83b3c From c9d6c194201da048becb9d6c6577e95d0d08d18e Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 12 Sep 2024 20:35:13 -0400 Subject: [PATCH 458/469] Update fresh audio --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 96e5401f7e..4e1827f7a5 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 96e5401f7e1facbf9a59a52a2d8f59523cb83b3c +Subproject commit 4e1827f7a53e07a70e1b489f2aaf91c93407be7d From 0e225c9bae809b59b303e0dff540b0a29fcc33f6 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 12 Sep 2024 20:51:02 -0400 Subject: [PATCH 459/469] Update eggnog vocals again --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 4e1827f7a5..9306f9b410 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 4e1827f7a53e07a70e1b489f2aaf91c93407be7d +Subproject commit 9306f9b4104cccb0988405c9d07fb2465f4ac8f2 From 80868ccbba3058b8292c5ee0156596982f113d73 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 21:53:38 -0400 Subject: [PATCH 460/469] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 9306f9b410..35395f8f9c 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 9306f9b4104cccb0988405c9d07fb2465f4ac8f2 +Subproject commit 35395f8f9c4ef1603cb33ea5f8e9fce58e01158b From d3d998d9947ebced6b245f1ee33ec103db6892d0 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 12 Sep 2024 21:57:48 -0400 Subject: [PATCH 461/469] saru fixies --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 35395f8f9c..bc7009b424 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 35395f8f9c4ef1603cb33ea5f8e9fce58e01158b +Subproject commit bc7009b4242691faa5c4552f7ca8a2f28e8cb1d2 From d59594ee453b54dc48a3e4b969e6a5940c5d9b7a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 12 Sep 2024 21:55:12 -0400 Subject: [PATCH 462/469] Fix animations not looping on Char Select --- source/funkin/ui/charSelect/CharSelectSubState.hx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index dd5b2a3a24..3109dc8f1b 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -131,6 +131,9 @@ class CharSelectSubState extends MusicBeatSubState var crowd:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/crowd")); crowd.anim.play(); + crowd.anim.onComplete.add(function() { + crowd.anim.play(); + }); crowd.scrollFactor.set(0.3, 0.3); add(crowd); @@ -147,6 +150,9 @@ class CharSelectSubState extends MusicBeatSubState barthing = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/barThing")); barthing.anim.play(""); + barthing.anim.onComplete.add(function() { + barthing.anim.play(""); + }); barthing.blend = BlendMode.MULTIPLY; barthing.scrollFactor.set(0, 0); add(barthing); @@ -176,6 +182,9 @@ class CharSelectSubState extends MusicBeatSubState var speakers:FlxAtlasSprite = new FlxAtlasSprite(0, 0, Paths.animateAtlas("charSelect/charSelectSpeakers")); speakers.anim.play(""); + speakers.anim.onComplete.add(function() { + speakers.anim.play(""); + }); speakers.scrollFactor.set(1.8, 1.8); add(speakers); From 0a8719088da94f295481d17a167e1875646a4d92 Mon Sep 17 00:00:00 2001 From: Til Tjardes Date: Sat, 14 Sep 2024 15:06:03 -0600 Subject: [PATCH 463/469] techniktil coming back with another typo lets go --- source/funkin/modding/events/ScriptEvent.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/modding/events/ScriptEvent.hx b/source/funkin/modding/events/ScriptEvent.hx index dd55de23b0..cc1d00b581 100644 --- a/source/funkin/modding/events/ScriptEvent.hx +++ b/source/funkin/modding/events/ScriptEvent.hx @@ -103,7 +103,7 @@ class NoteScriptEvent extends ScriptEvent public var comboCount(default, null):Int; /** - * Whether to play the record scratch sound (if this eventn type is `NOTE_MISS`). + * Whether to play the record scratch sound (if this event type is `NOTE_MISS`). */ public var playSound(default, default):Bool; From 1f21e1114b6e58487bedc3261046079da46938a0 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 16 Sep 2024 17:12:11 -0400 Subject: [PATCH 464/469] pub modules --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 452c0089b7..df142df35a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "assets"] path = assets - url = https://github.com/FunkinCrew/Funkin-Assets-secret + url = https://github.com/FunkinCrew/funkin.assets [submodule "art"] path = art - url = https://github.com/FunkinCrew/Funkin-Art-secret + url = https://github.com/FunkinCrew/funkin.assets From a27c4ae24f99d7c1eaa0bf48726dd94a420ec012 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 16 Sep 2024 17:14:45 -0400 Subject: [PATCH 465/469] pub modules art --- .gitmodules | 2 +- art | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index df142df35a..be5e0aaa81 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/FunkinCrew/funkin.assets [submodule "art"] path = art - url = https://github.com/FunkinCrew/funkin.assets + url = https://github.com/FunkinCrew/funkin.art diff --git a/art b/art index 0dee03f11a..bfca2ea98d 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit 0dee03f11afc01c2883da223fa10405f7011dd33 +Subproject commit bfca2ea98d11a0f4dee4a27b9390951fbc5701ea From f8d0ddef3456b31f729d9110f106b0594baaaf4a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 16 Sep 2024 18:17:07 -0400 Subject: [PATCH 466/469] Update troubleshooting guide --- docs/troubleshooting.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 3b93bab646..54ba396c49 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,4 +1,4 @@ -# Troubleshooting Common Issues +# Troubleshooting Common Compilation Issues - Weird macro error with a very tall call stack: Restart Visual Studio Code - NOTE: This is caused by Polymod somewhere, and seems to only occur when there is another compile error somewhere in the program. There is a bounty up for it. @@ -13,3 +13,11 @@ - `LINK : fatal error LNK1201: error writing to program database ''; check for insufficient disk space, invalid path, or insufficient privilege` - This error occurs if the PDB file located in your `export` folder is in use or exceeds 4 GB. Try deleting the `export` folder and building again from scratch. + +- `error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)` + - This error can happen during cloning as a result of poor network connectivity. A common fix is to run ` git config --global http.postBuffer 4096M` in your terminal. + +- Repository is missing an `assets` folder, or `assets` folder is empty. + - You did not clone the repository correctly! Copy the path to your `funkin` folder and run `cd the\path\you\copied`. Then follow the compilation guide starting from **Step 4**. + +- Other compilation issues may be caused by installing bad library versions. Try deleting the `.haxelib` folder and following the guide starting from **Step 5**. From 298bc9496b7bda261a6b37b24b7277b0a7a78ea3 Mon Sep 17 00:00:00 2001 From: Hyper_ Date: Sun, 22 Sep 2024 18:05:25 -0300 Subject: [PATCH 467/469] Upgrade API versions --- example_mods/introMod/_polymod_meta.json | 2 +- example_mods/testing123/_polymod_meta.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example_mods/introMod/_polymod_meta.json b/example_mods/introMod/_polymod_meta.json index 4dc0cd8044..74c4a25040 100644 --- a/example_mods/introMod/_polymod_meta.json +++ b/example_mods/introMod/_polymod_meta.json @@ -6,7 +6,7 @@ "name": "EliteMasterEric" } ], - "api_version": "0.1.0", + "api_version": "0.5.0", "mod_version": "1.0.0", "license": "Apache-2.0" } diff --git a/example_mods/testing123/_polymod_meta.json b/example_mods/testing123/_polymod_meta.json index 0a2ed042c6..b8fc60b112 100644 --- a/example_mods/testing123/_polymod_meta.json +++ b/example_mods/testing123/_polymod_meta.json @@ -6,7 +6,7 @@ "name": "EliteMasterEric" } ], - "api_version": "0.1.0", + "api_version": "0.5.0", "mod_version": "1.0.0", "license": "Apache-2.0" } From cca997b7243dcc20e0602253284a61de4ddcbfd0 Mon Sep 17 00:00:00 2001 From: Til Tjardes Date: Sun, 22 Sep 2024 17:59:18 -0600 Subject: [PATCH 468/469] fix develop branch cuz git is on crack --- source/funkin/play/PlayState.hx | 1 - source/funkin/play/components/PopUpStuff.hx | 9 --------- source/funkin/ui/debug/anim/DebugBoundingState.hx | 2 +- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index a25d3b2318..1c82eb6b15 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -3046,7 +3046,6 @@ class PlayState extends MusicBeatSubState GameOverSubState.reset(); PauseSubState.reset(); Countdown.reset(); - PopUpStuff.reset(); // Clear the static reference to this state. instance = null; diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index e5e9b46816..a02291e4e2 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -125,13 +125,4 @@ class PopUpStuff extends FlxTypedGroup daLoop++; } } - - /** - * Reset the popup configuration to the default. - */ - public static function reset() - { - noteStyle = NoteStyleRegistry.instance.fetchDefault(); - isPixel = false; - } } diff --git a/source/funkin/ui/debug/anim/DebugBoundingState.hx b/source/funkin/ui/debug/anim/DebugBoundingState.hx index 19391f8d95..d2a27999f1 100644 --- a/source/funkin/ui/debug/anim/DebugBoundingState.hx +++ b/source/funkin/ui/debug/anim/DebugBoundingState.hx @@ -77,7 +77,7 @@ class DebugBoundingState extends FlxState { // get the screen position, according to the HUD camera, temp default to FlxG.camera juuust in case? var hudMousePos:FlxPoint = FlxG.mouse.getScreenPosition(hudCam ?? FlxG.camera); - return Screen.instance.hasSolidComponentUnderPoint(hudMousePos.x, hudMousePos.y) || FlxG.mouse.overlaps(animDropDownMenu, hudCam); + return Screen.instance.hasSolidComponentUnderPoint(hudMousePos.x, hudMousePos.y); } override function create() From ffd76963bd736195ce23f2526acdbbbea6513da1 Mon Sep 17 00:00:00 2001 From: Abnormal <86753001+AbnormalPoof@users.noreply.github.com> Date: Thu, 26 Sep 2024 21:04:23 +0000 Subject: [PATCH 469/469] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b1e16f6de6..34ec930f62 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Please check out our [Contributor's guide](./CONTRIBUTORS.md) on how you can act # Credits and Special Thanks -Full credits can be found in-game, or wherever the credits.json file is. +Full credits can be found in-game, or in the `credits.json` file which is located [here](https://github.com/FunkinCrew/funkin.assets/blob/main/exclude/data/credits.json). ## Programming - [ninjamuffin99](https://twitter.com/ninja_muffin99) - Lead Programmer