From 858d8edf7aba344539317754e0e3e7dbefacb4e0 Mon Sep 17 00:00:00 2001 From: Jenny Crowe Date: Tue, 27 Feb 2024 23:29:40 -0700 Subject: [PATCH 01/75] Cam tweening working! UI buggy af. To fix. --- source/funkin/play/PlayState.hx | 46 ++++++++++++- .../funkin/play/event/FocusCameraSongEvent.hx | 67 +++++++++++++++++++ 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 5bbf83e17..a745be6cd 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -219,6 +219,12 @@ class PlayState extends MusicBeatSubState */ public var cameraFollowPoint:FlxObject; + /** + * An FlxTween that tweens the camera to the follow point. + * Only used when tweening the camera manually, rather than tweening via follow. + */ + public var cameraFollowTween:FlxTween; + /** * The camera follow point from the last stage. * Used to persist the position of the `cameraFollowPosition` between levels. @@ -2847,15 +2853,51 @@ class PlayState extends MusicBeatSubState /** * Resets the camera's zoom level and focus point. */ - public function resetCamera():Void + public function resetCamera(?resetZoom:Bool = true, ?cancelFollowTween:Bool = true):Void { + // Cancel the follow tween if it's active. + if (cancelFollowTween && cameraFollowTween != null) + { + cameraFollowTween.cancel(); + } + FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.04); FlxG.camera.targetOffset.set(); - FlxG.camera.zoom = defaultCameraZoom; + + if (resetZoom) + { + FlxG.camera.zoom = defaultCameraZoom; + } + // Snap the camera to the follow point immediately. FlxG.camera.focusOn(cameraFollowPoint.getPosition()); } + /** + * Disables camera following and tweens the camera to the follow point manually. + */ + public function tweenCamera(?duration:Float, ?ease:NullFloat>):Void + { + // Cancel the current tween if it's active. + if (cameraFollowTween != null) + { + cameraFollowTween.cancel(); + } + + // Disable camera following for the duration of the tween. + FlxG.camera.target = null; + + // Follow tween! Caching it so we can cancel it later if needed. + var followPos:FlxPoint = cameraFollowPoint.getPosition() - FlxPoint.weak(FlxG.camera.width * 0.5, FlxG.camera.height * 0.5); + cameraFollowTween = FlxTween.tween(FlxG.camera.scroll, {x: followPos.x, y: followPos.y}, duration, + { + ease: ease, + onComplete: function(_) { + resetCamera(false, false); // Re-enable camera following when the tween is complete. + } + }); + } + #if (debug || FORCE_DEBUG_VERSION) /** * Jumps forward or backward a number of sections in the song. diff --git a/source/funkin/play/event/FocusCameraSongEvent.hx b/source/funkin/play/event/FocusCameraSongEvent.hx index 847df4a60..28a629f1a 100644 --- a/source/funkin/play/event/FocusCameraSongEvent.hx +++ b/source/funkin/play/event/FocusCameraSongEvent.hx @@ -1,5 +1,6 @@ package funkin.play.event; +import flixel.tweens.FlxEase; // Data from the chart import funkin.data.song.SongData; import funkin.data.song.SongData.SongEventData; @@ -66,6 +67,13 @@ class FocusCameraSongEvent extends SongEvent if (char == null) char = cast data.value; + var useTween:Null = data.getBool('useTween'); + if (useTween == null) useTween = false; + var duration:Null = data.getFloat('duration'); + if (duration == null) duration = 4.0; + var ease:Null = data.getString('ease'); + if (ease == null) ease = 'linear'; + switch (char) { case -1: // Position @@ -114,6 +122,20 @@ class FocusCameraSongEvent extends SongEvent default: trace('Unknown camera focus: ' + data); } + + if (useTween) // always ends up false?? + { + var durSeconds = Conductor.instance.stepLengthMs * duration / 1000; + + var easeFunction:NullFloat> = Reflect.field(FlxEase, ease); + if (easeFunction == null) + { + trace('Invalid ease function: $ease'); + return; + } + + PlayState.instance.tweenCamera(durSeconds, easeFunction); + } } public override function getTitle():String @@ -155,6 +177,51 @@ class FocusCameraSongEvent extends SongEvent step: 10.0, type: SongEventFieldType.FLOAT, units: "px" + }, + { + name: 'useTween', + title: 'Use Tween', + type: SongEventFieldType.BOOL, + defaultValue: false + }, + { + name: 'duration', + title: 'Duration', + defaultValue: 4.0, + step: 0.5, + type: SongEventFieldType.FLOAT, + units: 'steps' + }, + { + name: 'ease', + title: 'Easing Type', + defaultValue: 'linear', + type: SongEventFieldType.ENUM, + keys: [ + 'Linear' => 'linear', + 'Instant' => 'INSTANT', + 'Quad In' => 'quadIn', + 'Quad Out' => 'quadOut', + 'Quad In/Out' => 'quadInOut', + 'Cube In' => 'cubeIn', + 'Cube Out' => 'cubeOut', + 'Cube In/Out' => 'cubeInOut', + 'Quart In' => 'quartIn', + 'Quart Out' => 'quartOut', + 'Quart In/Out' => 'quartInOut', + 'Quint In' => 'quintIn', + 'Quint Out' => 'quintOut', + 'Quint In/Out' => 'quintInOut', + 'Smooth Step In' => 'smoothStepIn', + 'Smooth Step Out' => 'smoothStepOut', + 'Smooth Step In/Out' => 'smoothStepInOut', + 'Sine In' => 'sineIn', + 'Sine Out' => 'sineOut', + 'Sine In/Out' => 'sineInOut', + 'Elastic In' => 'elasticIn', + 'Elastic Out' => 'elasticOut', + 'Elastic In/Out' => 'elasticInOut', + ] } ]); } From d9cf097e460de497a7a6754c85eade23b39b9aa3 Mon Sep 17 00:00:00 2001 From: Jenny Crowe Date: Mon, 4 Mar 2024 20:57:21 -0700 Subject: [PATCH 02/75] Fixed bools and associated checkboxes not updating properly. --- source/funkin/play/event/FocusCameraSongEvent.hx | 2 +- .../debug/charting/toolboxes/ChartEditorEventDataToolbox.hx | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/source/funkin/play/event/FocusCameraSongEvent.hx b/source/funkin/play/event/FocusCameraSongEvent.hx index 28a629f1a..979b1ad7b 100644 --- a/source/funkin/play/event/FocusCameraSongEvent.hx +++ b/source/funkin/play/event/FocusCameraSongEvent.hx @@ -123,7 +123,7 @@ class FocusCameraSongEvent extends SongEvent trace('Unknown camera focus: ' + data); } - if (useTween) // always ends up false?? + if (useTween) { var durSeconds = Conductor.instance.stepLengthMs * duration / 1000; diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx index ec46e1f85..50b341272 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx @@ -237,6 +237,11 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox { value = event.target.value.value; } + else if (field.type == BOOL) + { + var chk:CheckBox = cast event.target; + value = cast(chk.selected, Null); // Need to cast to nullable bool or the compiler will get mad. + } trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${value}'); From 21b895ab9735ca1c121026326ab5ab2a0c4c2e24 Mon Sep 17 00:00:00 2001 From: Jenny Crowe Date: Fri, 8 Mar 2024 14:50:26 -0700 Subject: [PATCH 03/75] Assets submod update --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 55c602f2a..49c409b4c 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 55c602f2adbbd84de541ea86e5e646c4d2a1df0b +Subproject commit 49c409b4c8321d8cd317787a78c479aaa64cb517 From d495420938ab4a4efad2c2c3d265352c165fe488 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sun, 10 Mar 2024 15:24:43 -0400 Subject: [PATCH 04/75] fixes capitalization in checkstyle settings --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 87ed06aed..ea93651ed 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -85,7 +85,7 @@ }, "projectManager.git.baseFolders": ["./"], - "haxecheckstyle.sourceFolders": ["src", "Source"], + "haxecheckstyle.sourceFolders": ["src", "source"], "haxecheckstyle.externalSourceRoots": [], "haxecheckstyle.configurationFile": "checkstyle.json", "haxecheckstyle.codeSimilarityBufferSize": 100, From 6b8fb7dc77a9c11ecc5afa541e05d004c64aaca2 Mon Sep 17 00:00:00 2001 From: Jenny Crowe Date: Sun, 10 Mar 2024 16:35:41 -0700 Subject: [PATCH 05/75] Standardized camera zoom tweening to match camera follow tweening. Implemented methods to cancel tweens in necessary places. Start of pausing tweens when pausing the game (WIP). (CHANGES NOT TESTED EXPECT SOMETHING TO BREAK) --- source/funkin/play/GameOverSubState.hx | 9 +- source/funkin/play/PlayState.hx | 124 +++++++++++++++--- .../funkin/play/event/FocusCameraSongEvent.hx | 22 ++-- .../funkin/play/event/ZoomCameraSongEvent.hx | 8 +- .../ui/debug/stage/StageOffsetSubState.hx | 11 +- 5 files changed, 136 insertions(+), 38 deletions(-) diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index 95304d762..b3e815a41 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -119,6 +119,8 @@ class GameOverSubState extends MusicBeatSubState // Set up the visuals // + var playState = PlayState.instance; + // Add a black background to the screen. var bg = new FunkinSprite().makeSolidColor(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK); // We make this transparent so that we can see the stage underneath during debugging, @@ -130,13 +132,16 @@ class GameOverSubState extends MusicBeatSubState // Pluck Boyfriend from the PlayState and place him (in the same position) in the GameOverSubState. // We can then play the character's `firstDeath` animation. - boyfriend = PlayState.instance.currentStage.getBoyfriend(true); + boyfriend = playState.currentStage.getBoyfriend(true); boyfriend.isDead = true; add(boyfriend); boyfriend.resetCharacter(); + // Cancel camera tweening if it's currently active. + playState.cancelAllCameraTweens(); + // Assign a camera follow point to the boyfriend's position. - cameraFollowPoint = new FlxObject(PlayState.instance.cameraFollowPoint.x, PlayState.instance.cameraFollowPoint.y, 1, 1); + cameraFollowPoint = new FlxObject(playState.cameraFollowPoint.x, playState.cameraFollowPoint.y, 1, 1); cameraFollowPoint.x = boyfriend.getGraphicMidpoint().x; cameraFollowPoint.y = boyfriend.getGraphicMidpoint().y; var offsets:Array = boyfriend.getDeathCameraOffsets(); diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 3ce1f4948..0bd731bc6 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -242,6 +242,11 @@ class PlayState extends MusicBeatSubState */ public var cameraFollowTween:FlxTween; + /** + * An FlxTween that zooms the camera to the desired amount. + */ + public var cameraZoomTween:FlxTween; + /** * The camera follow point from the last stage. * Used to persist the position of the `cameraFollowPosition` between levels. @@ -402,10 +407,12 @@ class PlayState extends MusicBeatSubState var startingSong:Bool = false; /** - * False if `FlxG.sound.music` + * Track if we currently have the music paused for a Pause substate, so we can unpause it when we return. */ var musicPausedBySubState:Bool = false; + var cameraFollowTweenPausedBySubState:Bool = false; // Idea FOR LATER: Store paused tweens in an array, so we know which ones to unpause when leaving the Pause substate. + /** * False until `create()` has completed. */ @@ -1126,14 +1133,24 @@ class PlayState extends MusicBeatSubState // Pause the music. if (FlxG.sound.music != null) { - musicPausedBySubState = FlxG.sound.music.playing; - if (musicPausedBySubState) + if (FlxG.sound.music.playing) { FlxG.sound.music.pause(); + musicPausedBySubState = true; } + + // Pause vocals. + // Not tracking that we've done this via a bool because vocal re-syncing involves pausing the vocals anyway. if (vocals != null) vocals.pause(); } + // Pause camera tweening. + if (cameraFollowTween != null && cameraFollowTween.active) + { + cameraFollowTween.active = false; + cameraFollowTweenPausedBySubState = true; + } + // Pause the countdown. Countdown.pauseCountdown(); } @@ -1155,10 +1172,18 @@ class PlayState extends MusicBeatSubState if (event.eventCanceled) return; - // Resume + // Resume music if we paused it. if (musicPausedBySubState) { FlxG.sound.music.play(); + musicPausedBySubState = false; + } + + // Resume camera tweening if we paused it. + if (cameraFollowTweenPausedBySubState) + { + cameraFollowTween.active = true; + cameraFollowTweenPausedBySubState = false; } if (currentConversation != null) @@ -1166,6 +1191,7 @@ class PlayState extends MusicBeatSubState currentConversation.resumeMusic(); } + // Re-sync vocals. if (FlxG.sound.music != null && !startingSong && !isInCutscene) resyncVocals(); // Resume the countdown. @@ -2852,6 +2878,9 @@ class PlayState extends MusicBeatSubState */ function performCleanup():Void { + // If the camera is being tweened, stop it. + cancelAllCameraTweens(); + if (currentConversation != null) { remove(currentConversation); @@ -2910,6 +2939,9 @@ class PlayState extends MusicBeatSubState // Stop camera zooming on beat. cameraZoomRate = 0; + // Cancel camera tweening if it's active. + cancelAllCameraTweens(); + // If the opponent is GF, zoom in on the opponent. // Else, if there is no GF, zoom in on BF. // Else, zoom in on GF. @@ -2996,12 +3028,12 @@ class PlayState extends MusicBeatSubState /** * Resets the camera's zoom level and focus point. */ - public function resetCamera(?resetZoom:Bool = true, ?cancelFollowTween:Bool = true):Void + public function resetCamera(?resetZoom:Bool = true, ?cancelTweens:Bool = true):Void { - // Cancel the follow tween if it's active. - if (cancelFollowTween && cameraFollowTween != null) + // Cancel camera tweens if any are active. + if (cancelTweens) { - cameraFollowTween.cancel(); + cancelAllCameraTweens(); } FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.04); @@ -3019,26 +3051,78 @@ class PlayState extends MusicBeatSubState /** * Disables camera following and tweens the camera to the follow point manually. */ - public function tweenCamera(?duration:Float, ?ease:NullFloat>):Void + public function tweenCameraToFollowPoint(?duration:Float, ?ease:NullFloat>):Void { // Cancel the current tween if it's active. + cancelCameraFollowTween(); + + if (duration == 0) + { + // Instant movement. Just reset the camera to force it to the follow point. + resetCamera(false, false); + } + else + { + // Disable camera following for the duration of the tween. + FlxG.camera.target = null; + + // Follow tween! Caching it so we can cancel/pause it later if needed. + var followPos:FlxPoint = cameraFollowPoint.getPosition() - FlxPoint.weak(FlxG.camera.width * 0.5, FlxG.camera.height * 0.5); + cameraFollowTween = FlxTween.tween(FlxG.camera.scroll, {x: followPos.x, y: followPos.y}, duration, + { + ease: ease, + onComplete: function(_) { + resetCamera(false, false); // Re-enable camera following when the tween is complete. + } + }); + } + } + + public function cancelCameraFollowTween() + { if (cameraFollowTween != null) { cameraFollowTween.cancel(); } + } - // Disable camera following for the duration of the tween. - FlxG.camera.target = null; + /** + * Tweens the camera zoom to the desired amount. Tweens defaultCameraZoom to avoid breaking camera bops. + */ + public function tweenCameraZoom(?zoom:Float, ?duration:Float, ?ease:NullFloat>):Void + { + // Cancel the current tween if it's active. + cancelCameraZoomTween(); - // Follow tween! Caching it so we can cancel it later if needed. - var followPos:FlxPoint = cameraFollowPoint.getPosition() - FlxPoint.weak(FlxG.camera.width * 0.5, FlxG.camera.height * 0.5); - cameraFollowTween = FlxTween.tween(FlxG.camera.scroll, {x: followPos.x, y: followPos.y}, duration, - { - ease: ease, - onComplete: function(_) { - resetCamera(false, false); // Re-enable camera following when the tween is complete. - } - }); + var targetZoom = zoom * FlxCamera.defaultZoom; + + if (duration == 0) + { + // Instant zoom. No tween needed. + defaultCameraZoom = targetZoom; + } + else + { + // Zoom tween! Caching it so we can cancel/pause it later if needed. + cameraZoomTween = FlxTween.tween(this, {defaultCameraZoom: targetZoom}, duration, {ease: ease}); + } + } + + public function cancelCameraZoomTween() + { + if (cameraZoomTween != null) + { + cameraZoomTween.cancel(); + } + } + + /** + * Cancel all active camera tweens simultaneously. + */ + public function cancelAllCameraTweens() + { + cancelCameraFollowTween(); + cancelCameraZoomTween(); } #if (debug || FORCE_DEBUG_VERSION) diff --git a/source/funkin/play/event/FocusCameraSongEvent.hx b/source/funkin/play/event/FocusCameraSongEvent.hx index 979b1ad7b..cd4366dd2 100644 --- a/source/funkin/play/event/FocusCameraSongEvent.hx +++ b/source/funkin/play/event/FocusCameraSongEvent.hx @@ -125,16 +125,22 @@ class FocusCameraSongEvent extends SongEvent if (useTween) { - var durSeconds = Conductor.instance.stepLengthMs * duration / 1000; - - var easeFunction:NullFloat> = Reflect.field(FlxEase, ease); - if (easeFunction == null) + switch (ease) { - trace('Invalid ease function: $ease'); - return; - } + case 'INSTANT': + PlayState.instance.tweenCameraToFollowPoint(0); + default: + var durSeconds = Conductor.instance.stepLengthMs * duration / 1000; - PlayState.instance.tweenCamera(durSeconds, easeFunction); + var easeFunction:NullFloat> = Reflect.field(FlxEase, ease); + if (easeFunction == null) + { + trace('Invalid ease function: $ease'); + return; + } + + PlayState.instance.tweenCameraToFollowPoint(durSeconds, easeFunction); + } } } diff --git a/source/funkin/play/event/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx index 809130499..187664e97 100644 --- a/source/funkin/play/event/ZoomCameraSongEvent.hx +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -69,9 +69,10 @@ class ZoomCameraSongEvent extends SongEvent switch (ease) { case 'INSTANT': - // Set the zoom. Use defaultCameraZoom to prevent breaking camera bops. - PlayState.instance.defaultCameraZoom = zoom * FlxCamera.defaultZoom; + PlayState.instance.tweenCameraZoom(zoom, 0); default: + var durSeconds = Conductor.instance.stepLengthMs * duration / 1000; + var easeFunction:NullFloat> = Reflect.field(FlxEase, ease); if (easeFunction == null) { @@ -79,8 +80,7 @@ class ZoomCameraSongEvent extends SongEvent return; } - FlxTween.tween(PlayState.instance, {defaultCameraZoom: zoom * FlxCamera.defaultZoom}, (Conductor.instance.stepLengthMs * duration / 1000), - {ease: easeFunction}); + PlayState.instance.tweenCameraZoom(zoom, durSeconds, easeFunction); } } diff --git a/source/funkin/ui/debug/stage/StageOffsetSubState.hx b/source/funkin/ui/debug/stage/StageOffsetSubState.hx index e8a5d0a23..fa5056220 100644 --- a/source/funkin/ui/debug/stage/StageOffsetSubState.hx +++ b/source/funkin/ui/debug/stage/StageOffsetSubState.hx @@ -49,8 +49,11 @@ class StageOffsetSubState extends HaxeUISubState { super.create(); + var playState = PlayState.instance; + FlxG.mouse.visible = true; - PlayState.instance.pauseMusic(); + playState.pauseMusic(); + playState.cancelAllCameraTweens(); FlxG.camera.target = null; setupUIListeners(); @@ -63,8 +66,8 @@ class StageOffsetSubState extends HaxeUISubState // add(uiStuff); - PlayState.instance.persistentUpdate = true; - component.cameras = [PlayState.instance.camHUD]; + playState.persistentUpdate = true; + component.cameras = [playState.camHUD]; // uiStuff.cameras = [PlayState.instance.camHUD]; // btn.cameras = [PlayState.instance.camHUD]; @@ -72,7 +75,7 @@ class StageOffsetSubState extends HaxeUISubState var layerList:ListView = findComponent("prop-layers"); - for (thing in PlayState.instance.currentStage) + for (thing in playState.currentStage) { var prop:StageProp = cast thing; if (prop != null && prop.name != null) From 3aada18a5998f18c745034910db16eb8c4aa816d Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 14 Mar 2024 04:03:33 -0700 Subject: [PATCH 06/75] title screen volume fix --- source/funkin/ui/title/TitleState.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/funkin/ui/title/TitleState.hx b/source/funkin/ui/title/TitleState.hx index 1c194d80d..9dca759be 100644 --- a/source/funkin/ui/title/TitleState.hx +++ b/source/funkin/ui/title/TitleState.hx @@ -223,6 +223,7 @@ class TitleState extends MusicBeatState var shouldFadeIn = (FlxG.sound.music == null); // Load music. Includes logic to handle BPM changes. FunkinSound.playMusic('freakyMenu', false, true); + FlxG.sound.music.volume = 0; // Fade from 0.0 to 0.7 over 4 seconds if (shouldFadeIn) FlxG.sound.music.fadeIn(4, 0, 0.7); } From e3b09bc8396f23754a8f6fa3e191825a6b77e755 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 14 Mar 2024 20:41:31 -0400 Subject: [PATCH 07/75] Clean up the classic MILF script --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 0e2c5bf21..fff87b632 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0e2c5bf2134c7e517b70cf74afd58abe5c7b5e50 +Subproject commit fff87b6320a1fe2b46538bb77abe06b5b20376e0 From 4866667e1c463bda8f6c5849985182c1b27b524d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 15 Mar 2024 02:25:34 -0400 Subject: [PATCH 08/75] ANNIHILATE M.I.L.F. custom gameplay --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index fff87b632..6fef3c78e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit fff87b6320a1fe2b46538bb77abe06b5b20376e0 +Subproject commit 6fef3c78e7c2ec16746337081c91af4095602634 From 1541f0aa68478d0651c8ba2fccc181e75883e964 Mon Sep 17 00:00:00 2001 From: Jenny Crowe Date: Fri, 15 Mar 2024 01:52:22 -0700 Subject: [PATCH 09/75] Camera tween pausing/unpausing additions --- assets | 2 +- source/funkin/play/PlayState.hx | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/assets b/assets index 0e2c5bf21..223722892 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0e2c5bf2134c7e517b70cf74afd58abe5c7b5e50 +Subproject commit 2237228923c6bd35df1e68e3b2a13dfffd1c243d diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 7c0ed64b3..d6cb5bd47 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -412,7 +412,10 @@ class PlayState extends MusicBeatSubState */ var musicPausedBySubState:Bool = false; - var cameraFollowTweenPausedBySubState:Bool = false; // Idea FOR LATER: Store paused tweens in an array, so we know which ones to unpause when leaving the Pause substate. + /** + * Track any camera tweens we've paused for a Pause substate, so we can unpause them when we return. + */ + var cameraTweensPausedBySubState:List = new List(); /** * False until `create()` has completed. @@ -1145,11 +1148,17 @@ class PlayState extends MusicBeatSubState if (vocals != null) vocals.pause(); } - // Pause camera tweening. + // Pause camera tweening, and keep track of which tweens we pause. if (cameraFollowTween != null && cameraFollowTween.active) { cameraFollowTween.active = false; - cameraFollowTweenPausedBySubState = true; + cameraTweensPausedBySubState.add(cameraFollowTween); + } + + if (cameraZoomTween != null && cameraZoomTween.active) + { + cameraZoomTween.active = false; + cameraTweensPausedBySubState.add(cameraZoomTween); } // Pause the countdown. @@ -1180,12 +1189,12 @@ class PlayState extends MusicBeatSubState musicPausedBySubState = false; } - // Resume camera tweening if we paused it. - if (cameraFollowTweenPausedBySubState) + // Resume camera tweens if we paused any. + for (camTween in cameraTweensPausedBySubState) { - cameraFollowTween.active = true; - cameraFollowTweenPausedBySubState = false; + camTween.active = true; } + cameraTweensPausedBySubState.clear(); if (currentConversation != null) { From 494a3c9e86dbc6ddb71a193488c3023c26558d9a Mon Sep 17 00:00:00 2001 From: Jenny Crowe Date: Sat, 16 Mar 2024 08:38:10 -0700 Subject: [PATCH 10/75] Bugfixes. New additive zoom mode for camera tweening. --- source/funkin/play/PlayState.hx | 64 ++++++++++++++----- .../funkin/play/event/ZoomCameraSongEvent.hx | 22 +++++-- 2 files changed, 66 insertions(+), 20 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index d6cb5bd47..a5152e727 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -255,14 +255,23 @@ class PlayState extends MusicBeatSubState public var previousCameraFollowPoint:FlxPoint = null; /** - * The current camera zoom level. - * - * The camera zoom is increased every beat, and lerped back to this value every frame, creating a smooth 'zoom-in' effect. - * Defaults to 1.05 but may be larger or smaller depending on the current stage, - * and may be changed by the `ZoomCamera` song event. + * The current camera zoom level without any modifiers applied. + */ + public var currentCameraZoom:Float = FlxCamera.defaultZoom * 1.05; + + /** + * currentCameraZoom is increased every beat, and lerped back to this value every frame, creating a smooth 'zoom-in' effect. + * Defaults to 1.05, but may be larger or smaller depending on the current stage. + * Tweened via the `ZoomCamera` song event in direct mode. */ public var defaultCameraZoom:Float = FlxCamera.defaultZoom * 1.05; + /** + * Camera zoom applied on top of currentCameraZoom. + * Tweened via the `ZoomCamera` song event in additive mode. + */ + public var additiveCameraZoom:Float = 0; + /** * The current HUD camera zoom level. * @@ -959,7 +968,9 @@ class PlayState extends MusicBeatSubState // Lerp the camera zoom towards the target level. if (subState == null) { - FlxG.camera.zoom = FlxMath.lerp(defaultCameraZoom, FlxG.camera.zoom, 0.95); + currentCameraZoom = FlxMath.lerp(defaultCameraZoom, currentCameraZoom, 0.95); + FlxG.camera.zoom = currentCameraZoom + additiveCameraZoom; + camHUD.zoom = FlxMath.lerp(defaultHUDCameraZoom, camHUD.zoom, 0.95); } @@ -1349,7 +1360,7 @@ class PlayState extends MusicBeatSubState if (FlxG.camera.zoom < (1.35 * defaultCameraZoom) && cameraZoomRate > 0 && Conductor.instance.currentBeat % cameraZoomRate == 0) { // Zoom camera in (1.5%) - FlxG.camera.zoom += cameraZoomIntensity * defaultCameraZoom; + currentCameraZoom += cameraZoomIntensity * defaultCameraZoom; // Hud zooms double (3%) camHUD.zoom += hudCameraZoomIntensity * defaultHUDCameraZoom; } @@ -1541,6 +1552,11 @@ class PlayState extends MusicBeatSubState { // Apply camera zoom level from stage data. defaultCameraZoom = currentStage.camZoom; + currentCameraZoom = defaultCameraZoom; + FlxG.camera.zoom = currentCameraZoom; + + // Reset additive zoom. + additiveCameraZoom = 0; } /** @@ -3051,7 +3067,7 @@ class PlayState extends MusicBeatSubState if (resetZoom) { - FlxG.camera.zoom = defaultCameraZoom; + resetCameraZoom(); } // Snap the camera to the follow point immediately. @@ -3097,24 +3113,40 @@ class PlayState extends MusicBeatSubState } /** - * Tweens the camera zoom to the desired amount. Tweens defaultCameraZoom to avoid breaking camera bops. + * Tweens the camera zoom to the desired amount. */ - public function tweenCameraZoom(?zoom:Float, ?duration:Float, ?ease:NullFloat>):Void + public function tweenCameraZoom(?zoom:Float, ?duration:Float, ?directMode:Bool, ?ease:NullFloat>):Void { // Cancel the current tween if it's active. cancelCameraZoomTween(); var targetZoom = zoom * FlxCamera.defaultZoom; - if (duration == 0) + if (directMode) // Direct mode: Tween defaultCameraZoom for basic "smooth" zooms. { - // Instant zoom. No tween needed. - defaultCameraZoom = targetZoom; + if (duration == 0) + { + // Instant zoom. No tween needed. + defaultCameraZoom = targetZoom; + } + else + { + // Zoom tween! Caching it so we can cancel/pause it later if needed. + cameraZoomTween = FlxTween.tween(this, {defaultCameraZoom: targetZoom}, duration, {ease: ease}); + } } - else + else // Additive mode: Tween additiveCameraZoom for ease-based zooms. { - // Zoom tween! Caching it so we can cancel/pause it later if needed. - cameraZoomTween = FlxTween.tween(this, {defaultCameraZoom: targetZoom}, duration, {ease: ease}); + if (duration == 0) + { + // Instant zoom. No tween needed. + additiveCameraZoom = targetZoom; + } + else + { + // Zoom tween! Caching it so we can cancel/pause it later if needed. + cameraZoomTween = FlxTween.tween(this, {additiveCameraZoom: targetZoom}, duration, {ease: ease}); + } } } diff --git a/source/funkin/play/event/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx index b9b634ffe..d1741a463 100644 --- a/source/funkin/play/event/ZoomCameraSongEvent.hx +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -62,17 +62,23 @@ class ZoomCameraSongEvent extends SongEvent var zoom:Null = data.getFloat('zoom'); if (zoom == null) zoom = 1.0; + var duration:Null = data.getFloat('duration'); if (duration == null) duration = 4.0; + var mode:Null = data.getString('mode'); + if (mode == null) mode = 'additive'; + var ease:Null = data.getString('ease'); if (ease == null) ease = 'linear'; + var directMode:Bool = mode == 'direct'; + // If it's a string, check the value. switch (ease) { case 'INSTANT': - PlayState.instance.tweenCameraZoom(zoom, 0); + PlayState.instance.tweenCameraZoom(zoom, 0, directMode); default: var durSeconds = Conductor.instance.stepLengthMs * duration / 1000; @@ -83,7 +89,7 @@ class ZoomCameraSongEvent extends SongEvent return; } - PlayState.instance.tweenCameraZoom(zoom, durSeconds, easeFunction); + PlayState.instance.tweenCameraZoom(zoom, durSeconds, directMode, easeFunction); } } @@ -96,8 +102,9 @@ class ZoomCameraSongEvent extends SongEvent * ``` * { * 'zoom': FLOAT, // Target zoom level. - * 'duration': FLOAT, // Optional duration in steps - * 'ease': ENUM, // Optional easing function + * 'duration': FLOAT, // Optional duration in steps. + * 'mode': ENUM, // Whether to set additive zoom or direct zoom. + * 'ease': ENUM, // Optional easing function. * } * @return SongEventSchema */ @@ -120,6 +127,13 @@ class ZoomCameraSongEvent extends SongEvent type: SongEventFieldType.FLOAT, units: 'steps' }, + { + name: 'mode', + title: 'Mode', + defaultValue: 'additive', + type: SongEventFieldType.ENUM, + keys: ['Additive' => 'additive', 'Direct' => 'direct'] + }, { name: 'ease', title: 'Easing Type', From 874a2d9e86adec6d606ad13a8e29ecbab63880a5 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 16 Mar 2024 19:24:05 -0400 Subject: [PATCH 11/75] Formatting cleanup in assets folder --- .prettierignore | 1 + assets | 2 +- tests/unit/assets/shared/images/arrows.xml | 53 ++++++++++------------ 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/.prettierignore b/.prettierignore index 657ff3812..2e132c06f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,6 +4,7 @@ export # Ignore all JSONS in the images folder (including FlxAnimate JSONs) assets/preload/images assets/shared/images +assets/weekend1/images # Don't ignore data files # TODO: These don't work. diff --git a/assets b/assets index 0e2c5bf21..8d393de26 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0e2c5bf2134c7e517b70cf74afd58abe5c7b5e50 +Subproject commit 8d393de26ecbbabcc8096258458df9eea8cb387f diff --git a/tests/unit/assets/shared/images/arrows.xml b/tests/unit/assets/shared/images/arrows.xml index 8f3355462..96a73a388 100644 --- a/tests/unit/assets/shared/images/arrows.xml +++ b/tests/unit/assets/shared/images/arrows.xml @@ -1,32 +1,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + From 3a35be916b0b544ef0c3f6fdcdc56685c653a05d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 16 Mar 2024 22:20:22 -0400 Subject: [PATCH 12/75] A bunch of checkstyle fixes --- checkstyle.json | 2 +- source/Main.hx | 27 +- source/Postbuild.hx | 19 +- source/Prebuild.hx | 13 +- .../transition/FlxTransitionableSubState.hx | 234 ------------------ source/funkin/Conductor.hx | 64 +++-- source/funkin/InitState.hx | 37 +-- source/funkin/Paths.hx | 60 ++--- source/funkin/PlayerSettings.hx | 45 +++- source/funkin/Preferences.hx | 15 +- source/funkin/Preloader.hx | 4 +- source/funkin/play/PlayState.hx | 20 +- source/funkin/ui/MusicBeatSubState.hx | 2 +- source/funkin/ui/mainmenu/MainMenuState.hx | 6 +- source/funkin/util/MathUtil.hx | 27 +- source/funkin/util/MemoryUtil.hx | 14 +- source/funkin/util/MouseUtil.hx | 3 + source/funkin/util/PlatformUtil.hx | 8 +- source/funkin/util/SortUtil.hx | 50 +++- source/funkin/util/TimerUtil.hx | 21 +- source/funkin/util/TrackerUtil.hx | 2 +- source/funkin/util/VersionUtil.hx | 13 +- source/funkin/util/WindowUtil.hx | 12 +- source/haxe/ui/backend/flixel/UIStateBase.hx | 3 + .../haxe/ui/backend/flixel/UISubStateBase.hx | 3 + 25 files changed, 310 insertions(+), 394 deletions(-) delete mode 100644 source/flixel/addons/transition/FlxTransitionableSubState.hx diff --git a/checkstyle.json b/checkstyle.json index d41a6d617..5300d94ad 100644 --- a/checkstyle.json +++ b/checkstyle.json @@ -79,7 +79,7 @@ { "props": { "ignoreExtern": true, - "format": "^[A-Z][A-Z0-9]*(_[A-Z0-9_]+)*$", + "format": "^[a-z][A-Z][A-Z0-9]*(_[A-Z0-9_]+)*$", "tokens": ["INLINE", "NOTINLINE"] }, "type": "ConstantName" diff --git a/source/Main.hx b/source/Main.hx index a40fda29d..ee29eaf70 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -11,9 +11,11 @@ import openfl.display.Sprite; import openfl.events.Event; import openfl.Lib; import openfl.media.Video; -import funkin.util.CLIUtil; import openfl.net.NetStream; +/** + * The main class which initializes HaxeFlixel and starts the game in its initial state. + */ class Main extends Sprite { var gameWidth:Int = 1280; // Width of the game in pixels (might be less / more in actual pixels depending on your zoom). @@ -76,26 +78,27 @@ class Main extends Sprite var netStream:NetStream; var overlay:Sprite; + /** + * A frame counter displayed at the top left. + */ public static var fpsCounter:FPS; + + /** + * A RAM counter displayed at the top left. + */ public static var memoryCounter:MemoryCounter; function setupGame():Void { - /** - * The `zoom` argument of FlxGame was removed in the dev branch of Flixel, - * since it was considered confusing and unintuitive. - * If you want to change how the game scales when you resize the window, - * you can use `FlxG.scaleMode`. - * -Eric - */ - initHaxeUI(); + // addChild gets called by the user settings code. fpsCounter = new FPS(10, 3, 0xFFFFFF); - // addChild(fpsCounter); // Handled by Preferences.init + #if !html5 + // addChild gets called by the user settings code. + // TODO: disabled on HTML5 (todo: find another method that works?) memoryCounter = new MemoryCounter(10, 13, 0xFFFFFF); - // addChild(memoryCounter); #end // George recommends binding the save before FlxGame is created. @@ -105,6 +108,8 @@ class Main extends Sprite #if hxcpp_debug_server trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.'); + #else + trace('hxcpp_debug_server is disabled! This build does not support debugging.'); #end } diff --git a/source/Postbuild.hx b/source/Postbuild.hx index f1827c4ab..405d0400b 100644 --- a/source/Postbuild.hx +++ b/source/Postbuild.hx @@ -3,34 +3,37 @@ package source; // Yeah, I know... import sys.FileSystem; import sys.io.File; +/** + * A script which executes after the game is built. + */ class Postbuild { - static inline final buildTimeFile = '.build_time'; + static inline final BUILD_TIME_FILE:String = '.build_time'; - static function main() + static function main():Void { printBuildTime(); } - static function printBuildTime() + static function printBuildTime():Void { // get buildEnd before fs operations since they are blocking var end:Float = Sys.time(); - if (FileSystem.exists(buildTimeFile)) + if (FileSystem.exists(BUILD_TIME_FILE)) { - var fi = File.read(buildTimeFile); + var fi:sys.io.FileInput = File.read(BUILD_TIME_FILE); var start:Float = fi.readDouble(); fi.close(); - sys.FileSystem.deleteFile(buildTimeFile); + sys.FileSystem.deleteFile(BUILD_TIME_FILE); - var buildTime = roundToTwoDecimals(end - start); + var buildTime:Float = roundToTwoDecimals(end - start); trace('Build took: ${buildTime} seconds'); } } - private static function roundToTwoDecimals(value:Float):Float + static function roundToTwoDecimals(value:Float):Float { return Math.round(value * 100) / 100; } diff --git a/source/Prebuild.hx b/source/Prebuild.hx index 18a5e2076..6856c414c 100644 --- a/source/Prebuild.hx +++ b/source/Prebuild.hx @@ -2,20 +2,23 @@ package source; // Yeah, I know... import sys.io.File; +/** + * A script which executes before the game is built. + */ class Prebuild { - static inline final buildTimeFile = '.build_time'; + static inline final BUILD_TIME_FILE:String = '.build_time'; - static function main() + static function main():Void { saveBuildTime(); trace('Building...'); } - static function saveBuildTime() + static function saveBuildTime():Void { - var fo = File.write(buildTimeFile); - var now = Sys.time(); + var fo:sys.io.FileOutput = File.write(BUILD_TIME_FILE); + var now:Float = Sys.time(); fo.writeDouble(now); fo.close(); } diff --git a/source/flixel/addons/transition/FlxTransitionableSubState.hx b/source/flixel/addons/transition/FlxTransitionableSubState.hx deleted file mode 100644 index ab416adbc..000000000 --- a/source/flixel/addons/transition/FlxTransitionableSubState.hx +++ /dev/null @@ -1,234 +0,0 @@ -package flixel.addons.transition; - -import flixel.FlxSubState; -import flixel.addons.transition.FlxTransitionableState; - -/** - * A `FlxSubState` which can perform visual transitions - * - * Usage: - * - * First, extend `FlxTransitionableSubState` as ie, `FooState`. - * - * Method 1: - * - * ```haxe - * var in:TransitionData = new TransitionData(...); // add your data where "..." is - * var out:TransitionData = new TransitionData(...); - * - * FlxG.switchState(() -> new FooState(in,out)); - * ``` - * - * Method 2: - * - * ```haxe - * FlxTransitionableSubState.defaultTransIn = new TransitionData(...); - * FlxTransitionableSubState.defaultTransOut = new TransitionData(...); - * - * FlxG.switchState(() -> new FooState()); - * ``` - */ -class FlxTransitionableSubState extends FlxSubState -{ - // global default transitions for ALL states, used if transIn/transOut are null - public static var defaultTransIn(get, set):TransitionData; - - static function get_defaultTransIn():TransitionData - { - return FlxTransitionableState.defaultTransIn; - } - - static function set_defaultTransIn(value:TransitionData):TransitionData - { - return FlxTransitionableState.defaultTransIn = value; - } - - public static var defaultTransOut(get, set):TransitionData; - - static function get_defaultTransOut():TransitionData - { - return FlxTransitionableState.defaultTransOut; - } - - static function set_defaultTransOut(value:TransitionData):TransitionData - { - return FlxTransitionableState.defaultTransOut = value; - } - - public static var skipNextTransIn(get, set):Bool; - - static function get_skipNextTransIn():Bool - { - return FlxTransitionableState.skipNextTransIn; - } - - static function set_skipNextTransIn(value:Bool):Bool - { - return FlxTransitionableState.skipNextTransIn = value; - } - - public static var skipNextTransOut(get, set):Bool; - - static function get_skipNextTransOut():Bool - { - return FlxTransitionableState.skipNextTransOut; - } - - static function set_skipNextTransOut(value:Bool):Bool - { - return FlxTransitionableState.skipNextTransOut = value; - } - - // beginning & ending transitions for THIS state: - public var transIn:TransitionData; - public var transOut:TransitionData; - - public var hasTransIn(get, never):Bool; - public var hasTransOut(get, never):Bool; - - /** - * Create a state with the ability to do visual transitions - * @param TransIn Plays when the state begins - * @param TransOut Plays when the state ends - */ - public function new(?TransIn:TransitionData, ?TransOut:TransitionData) - { - transIn = TransIn; - transOut = TransOut; - - if (transIn == null && defaultTransIn != null) - { - transIn = defaultTransIn; - } - if (transOut == null && defaultTransOut != null) - { - transOut = defaultTransOut; - } - super(); - } - - override function destroy():Void - { - super.destroy(); - transIn = null; - transOut = null; - _onExit = null; - } - - override function create():Void - { - super.create(); - transitionIn(); - } - - override function startOutro(onOutroComplete:() -> Void) - { - if (!hasTransOut) onOutroComplete(); - else if (!_exiting) - { - // play the exit transition, and when it's done call FlxG.switchState - _exiting = true; - transitionOut(onOutroComplete); - - if (skipNextTransOut) - { - skipNextTransOut = false; - finishTransOut(); - } - } - } - - /** - * Starts the in-transition. Can be called manually at any time. - */ - public function transitionIn():Void - { - if (transIn != null && transIn.type != NONE) - { - if (skipNextTransIn) - { - skipNextTransIn = false; - if (finishTransIn != null) - { - finishTransIn(); - } - return; - } - - var _trans = createTransition(transIn); - - _trans.setStatus(FULL); - openSubState(_trans); - - _trans.finishCallback = finishTransIn; - _trans.start(OUT); - } - } - - /** - * Starts the out-transition. Can be called manually at any time. - */ - public function transitionOut(?OnExit:Void->Void):Void - { - _onExit = OnExit; - if (hasTransOut) - { - var _trans = createTransition(transOut); - - _trans.setStatus(EMPTY); - openSubState(_trans); - - _trans.finishCallback = finishTransOut; - _trans.start(IN); - } - else - { - _onExit(); - } - } - - var transOutFinished:Bool = false; - - var _exiting:Bool = false; - var _onExit:Void->Void; - - function get_hasTransIn():Bool - { - return transIn != null && transIn.type != NONE; - } - - function get_hasTransOut():Bool - { - return transOut != null && transOut.type != NONE; - } - - function createTransition(data:TransitionData):Transition - { - return switch (data.type) - { - case TILES: new Transition(data); - case FADE: new Transition(data); - default: null; - } - } - - function finishTransIn() - { - closeSubState(); - } - - function finishTransOut() - { - transOutFinished = true; - - if (!_exiting) - { - closeSubState(); - } - - if (_onExit != null) - { - _onExit(); - } - } -} diff --git a/source/funkin/Conductor.hx b/source/funkin/Conductor.hx index 05c23108f..01147ce31 100644 --- a/source/funkin/Conductor.hx +++ b/source/funkin/Conductor.hx @@ -3,7 +3,6 @@ package funkin; import funkin.util.Constants; import flixel.util.FlxSignal; import flixel.math.FlxMath; -import funkin.play.song.Song.SongDifficulty; import funkin.data.song.SongData.SongTimeChange; import funkin.data.song.SongDataUtils; @@ -136,6 +135,9 @@ class Conductor return beatLengthMs / timeSignatureNumerator; } + /** + * The numerator for the current time signature (the `3` in `3/4`). + */ public var timeSignatureNumerator(get, never):Int; function get_timeSignatureNumerator():Int @@ -145,6 +147,9 @@ class Conductor return currentTimeChange.timeSignatureNum; } + /** + * The denominator for the current time signature (the `4` in `3/4`). + */ public var timeSignatureDenominator(get, never):Int; function get_timeSignatureDenominator():Int @@ -245,7 +250,7 @@ class Conductor * WARNING: Avoid this for things like setting the BPM of the title screen music, * you should have a metadata file for it instead. */ - public function forceBPM(?bpm:Float = null) + public function forceBPM(?bpm:Float):Void { if (bpm != null) { @@ -253,7 +258,7 @@ class Conductor } else { - // trace('[CONDUCTOR] Resetting BPM to default'); + trace('[CONDUCTOR] Resetting BPM to default'); } this.bpmOverride = bpm; @@ -266,7 +271,7 @@ class Conductor * @param songPosition The current position in the song in milliseconds. * Leave blank to use the FlxG.sound.music position. */ - public function update(?songPos:Float) + public function update(?songPos:Float):Void { if (songPos == null) { @@ -274,9 +279,9 @@ class Conductor songPos = (FlxG.sound.music != null) ? (FlxG.sound.music.time + instrumentalOffset + formatOffset) : 0.0; } - var oldMeasure = this.currentMeasure; - var oldBeat = this.currentBeat; - var oldStep = this.currentStep; + var oldMeasure:Float = this.currentMeasure; + var oldBeat:Float = this.currentBeat; + var oldStep:Float = this.currentStep; // Set the song position we are at (for purposes of calculating note positions, etc). this.songPosition = songPos; @@ -338,39 +343,43 @@ class Conductor } } - public function mapTimeChanges(songTimeChanges:Array) + /** + * Apply the `SongTimeChange` data from the song metadata to this Conductor. + * @param songTimeChanges The SongTimeChanges. + */ + public function mapTimeChanges(songTimeChanges:Array):Void { timeChanges = []; // Sort in place just in case it's out of order. SongDataUtils.sortTimeChanges(songTimeChanges); - for (currentTimeChange in songTimeChanges) + for (songTimeChange in songTimeChanges) { // TODO: Maybe handle this different? // Do we care about BPM at negative timestamps? // Without any custom handling, `currentStepTime` becomes non-zero at `songPosition = 0`. - if (currentTimeChange.timeStamp < 0.0) currentTimeChange.timeStamp = 0.0; + if (songTimeChange.timeStamp < 0.0) songTimeChange.timeStamp = 0.0; - if (currentTimeChange.timeStamp <= 0.0) + if (songTimeChange.timeStamp <= 0.0) { - currentTimeChange.beatTime = 0.0; + songTimeChange.beatTime = 0.0; } else { // Calculate the beat time of this timestamp. - currentTimeChange.beatTime = 0.0; + songTimeChange.beatTime = 0.0; - if (currentTimeChange.timeStamp > 0.0 && timeChanges.length > 0) + if (songTimeChange.timeStamp > 0.0 && timeChanges.length > 0) { var prevTimeChange:SongTimeChange = timeChanges[timeChanges.length - 1]; - currentTimeChange.beatTime = FlxMath.roundDecimal(prevTimeChange.beatTime - + ((currentTimeChange.timeStamp - prevTimeChange.timeStamp) * prevTimeChange.bpm / Constants.SECS_PER_MIN / Constants.MS_PER_SEC), + songTimeChange.beatTime = FlxMath.roundDecimal(prevTimeChange.beatTime + + ((songTimeChange.timeStamp - prevTimeChange.timeStamp) * prevTimeChange.bpm / Constants.SECS_PER_MIN / Constants.MS_PER_SEC), 4); } } - timeChanges.push(currentTimeChange); + timeChanges.push(songTimeChange); } if (timeChanges.length > 0) @@ -384,6 +393,8 @@ class Conductor /** * Given a time in milliseconds, return a time in steps. + * @param ms The time in milliseconds. + * @return The time in steps. */ public function getTimeInSteps(ms:Float):Float { @@ -413,7 +424,7 @@ class Conductor var lastStepLengthMs:Float = ((Constants.SECS_PER_MIN / lastTimeChange.bpm) * Constants.MS_PER_SEC) / timeSignatureNumerator; var resultFractionalStep:Float = (ms - lastTimeChange.timeStamp) / lastStepLengthMs; - resultStep += resultFractionalStep; // Math.floor(); + resultStep += resultFractionalStep; return resultStep; } @@ -421,6 +432,8 @@ class Conductor /** * Given a time in steps and fractional steps, return a time in milliseconds. + * @param stepTime The time in steps. + * @return The time in milliseconds. */ public function getStepTimeInMs(stepTime:Float):Float { @@ -457,6 +470,8 @@ class Conductor /** * Given a time in beats and fractional beats, return a time in milliseconds. + * @param beatTime The time in beats. + * @return The time in milliseconds. */ public function getBeatTimeInMs(beatTime:Float):Float { @@ -491,13 +506,16 @@ class Conductor } } + /** + * Add variables of the current Conductor instance to the Flixel debugger. + */ public static function watchQuick():Void { - FlxG.watch.addQuick("songPosition", Conductor.instance.songPosition); - FlxG.watch.addQuick("bpm", Conductor.instance.bpm); - FlxG.watch.addQuick("currentMeasureTime", Conductor.instance.currentMeasureTime); - FlxG.watch.addQuick("currentBeatTime", Conductor.instance.currentBeatTime); - FlxG.watch.addQuick("currentStepTime", Conductor.instance.currentStepTime); + FlxG.watch.addQuick('songPosition', Conductor.instance.songPosition); + FlxG.watch.addQuick('bpm', Conductor.instance.bpm); + FlxG.watch.addQuick('currentMeasureTime', Conductor.instance.currentMeasureTime); + FlxG.watch.addQuick('currentBeatTime', Conductor.instance.currentBeatTime); + FlxG.watch.addQuick('currentStepTime', Conductor.instance.currentStepTime); } /** diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index a9e8dbffa..401147ee4 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -12,7 +12,6 @@ import flixel.math.FlxRect; import flixel.FlxSprite; import flixel.system.debug.log.LogStyle; import flixel.util.FlxColor; -import funkin.ui.options.PreferencesMenu; import funkin.util.macro.MacroUtil; import funkin.util.WindowUtil; import funkin.play.PlayStatePlaylist; @@ -31,7 +30,6 @@ import funkin.ui.title.TitleState; import funkin.util.CLIUtil; import funkin.util.CLIUtil.CLIParams; import funkin.util.TimerUtil; -import funkin.ui.transition.LoadingState; import funkin.util.TrackerUtil; #if discord_rpc import Discord.DiscordClient; @@ -169,8 +167,9 @@ class InitState extends FlxState SpeakerRegistry.instance.loadEntries(); StageRegistry.instance.loadEntries(); - // TODO: CharacterDataParser doesn't use json2object, so it's way slower than the other parsers. - CharacterDataParser.loadCharacterCache(); // TODO: Migrate characters to BaseRegistry. + // TODO: CharacterDataParser doesn't use json2object, so it's way slower than the other parsers and more prone to syntax errors. + // Move it to use a BaseRegistry. + CharacterDataParser.loadCharacterCache(); ModuleHandler.buildModuleCallbacks(); ModuleHandler.loadModuleCache(); @@ -188,25 +187,35 @@ class InitState extends FlxState */ function startGame():Void { - #if SONG // -DSONG=bopeebo + #if SONG + // -DSONG=bopeebo startSong(defineSong(), defineDifficulty()); - #elseif LEVEL // -DLEVEL=week1 -DDIFFICULTY=hard + #elseif LEVEL + // -DLEVEL=week1 -DDIFFICULTY=hard startLevel(defineLevel(), defineDifficulty()); - #elseif FREEPLAY // -DFREEPLAY + #elseif FREEPLAY + // -DFREEPLAY FlxG.switchState(() -> new funkin.ui.freeplay.FreeplayState()); - #elseif DIALOGUE // -DDIALOGUE + #elseif DIALOGUE + // -DDIALOGUE FlxG.switchState(() -> new funkin.ui.debug.dialogue.ConversationDebugState()); - #elseif ANIMATE // -DANIMATE + #elseif ANIMATE + // -DANIMATE FlxG.switchState(() -> new funkin.ui.debug.anim.FlxAnimateTest()); - #elseif WAVEFORM // -DWAVEFORM + #elseif WAVEFORM + // -DWAVEFORM FlxG.switchState(() -> new funkin.ui.debug.WaveformTestState()); - #elseif CHARTING // -DCHARTING + #elseif CHARTING + // -DCHARTING FlxG.switchState(() -> new funkin.ui.debug.charting.ChartEditorState()); - #elseif STAGEBUILD // -DSTAGEBUILD + #elseif STAGEBUILD + // -DSTAGEBUILD FlxG.switchState(() -> new funkin.ui.debug.stage.StageBuilderState()); - #elseif ANIMDEBUG // -DANIMDEBUG + #elseif ANIMDEBUG + // -DANIMDEBUG FlxG.switchState(() -> new funkin.ui.debug.anim.DebugBoundingState()); - #elseif LATENCY // -DLATENCY + #elseif LATENCY + // -DLATENCY FlxG.switchState(() -> new funkin.LatencyState()); #else startGameNormally(); diff --git a/source/funkin/Paths.hx b/source/funkin/Paths.hx index 6006939be..d2c4833f2 100644 --- a/source/funkin/Paths.hx +++ b/source/funkin/Paths.hx @@ -11,144 +11,144 @@ class Paths { static var currentLevel:String; - static public function setCurrentLevel(name:String) + public static function setCurrentLevel(name:String):Void { currentLevel = name.toLowerCase(); } public static function stripLibrary(path:String):String { - var parts = path.split(':'); + var parts:Array = path.split(':'); if (parts.length < 2) return path; return parts[1]; } public static function getLibrary(path:String):String { - var parts = path.split(':'); - if (parts.length < 2) return "preload"; + var parts:Array = path.split(':'); + if (parts.length < 2) return 'preload'; return parts[0]; } - static function getPath(file:String, type:AssetType, library:Null) + static function getPath(file:String, type:AssetType, library:Null):String { if (library != null) return getLibraryPath(file, library); if (currentLevel != null) { - var levelPath = getLibraryPathForce(file, currentLevel); + var levelPath:String = getLibraryPathForce(file, currentLevel); if (OpenFlAssets.exists(levelPath, type)) return levelPath; } - var levelPath = getLibraryPathForce(file, "shared"); + var levelPath:String = getLibraryPathForce(file, 'shared'); if (OpenFlAssets.exists(levelPath, type)) return levelPath; return getPreloadPath(file); } - static public function getLibraryPath(file:String, library = "preload") + public static function getLibraryPath(file:String, library = 'preload'):String { - return if (library == "preload" || library == "default") getPreloadPath(file); else getLibraryPathForce(file, library); + return if (library == 'preload' || library == 'default') getPreloadPath(file); else getLibraryPathForce(file, library); } - inline static function getLibraryPathForce(file:String, library:String) + static inline function getLibraryPathForce(file:String, library:String):String { return '$library:assets/$library/$file'; } - inline static function getPreloadPath(file:String) + static inline function getPreloadPath(file:String):String { return 'assets/$file'; } - inline static public function file(file:String, type:AssetType = TEXT, ?library:String) + public static function file(file:String, type:AssetType = TEXT, ?library:String):String { return getPath(file, type, library); } - public static inline function animateAtlas(path:String, ?library:String) + public static function animateAtlas(path:String, ?library:String):String { return getLibraryPath('images/$path', library); } - inline static public function txt(key:String, ?library:String) + public static function txt(key:String, ?library:String):String { return getPath('data/$key.txt', TEXT, library); } - inline static public function frag(key:String, ?library:String) + public static function frag(key:String, ?library:String):String { return getPath('shaders/$key.frag', TEXT, library); } - inline static public function vert(key:String, ?library:String) + public static function vert(key:String, ?library:String):String { return getPath('shaders/$key.vert', TEXT, library); } - inline static public function xml(key:String, ?library:String) + public static function xml(key:String, ?library:String):String { return getPath('data/$key.xml', TEXT, library); } - inline static public function json(key:String, ?library:String) + public static function json(key:String, ?library:String):String { return getPath('data/$key.json', TEXT, library); } - static public function sound(key:String, ?library:String) + public static function sound(key:String, ?library:String):String { return getPath('sounds/$key.${Constants.EXT_SOUND}', SOUND, library); } - inline static public function soundRandom(key:String, min:Int, max:Int, ?library:String) + public static function soundRandom(key:String, min:Int, max:Int, ?library:String):String { return sound(key + FlxG.random.int(min, max), library); } - inline static public function music(key:String, ?library:String) + public static function music(key:String, ?library:String):String { return getPath('music/$key.${Constants.EXT_SOUND}', MUSIC, library); } - inline static public function videos(key:String, ?library:String) + public static function videos(key:String, ?library:String):String { return getPath('videos/$key.${Constants.EXT_VIDEO}', BINARY, library); } - inline static public function voices(song:String, ?suffix:String = '') + public static function voices(song:String, ?suffix:String = ''):String { - if (suffix == null) suffix = ""; // no suffix, for a sorta backwards compatibility with older-ish voice files + if (suffix == null) suffix = ''; // no suffix, for a sorta backwards compatibility with older-ish voice files return 'songs:assets/songs/${song.toLowerCase()}/Voices$suffix.${Constants.EXT_SOUND}'; } - inline static public function inst(song:String, ?suffix:String = '') + public static function inst(song:String, ?suffix:String = ''):String { return 'songs:assets/songs/${song.toLowerCase()}/Inst$suffix.${Constants.EXT_SOUND}'; } - inline static public function image(key:String, ?library:String) + public static function image(key:String, ?library:String):String { return getPath('images/$key.png', IMAGE, library); } - inline static public function font(key:String) + public static function font(key:String):String { return 'assets/fonts/$key'; } - inline static public function ui(key:String, ?library:String) + public static function ui(key:String, ?library:String):String { return xml('ui/$key', library); } - static public function getSparrowAtlas(key:String, ?library:String) + public static function getSparrowAtlas(key:String, ?library:String):FlxAtlasFrames { return FlxAtlasFrames.fromSparrow(image(key, library), file('images/$key.xml', library)); } - inline static public function getPackerAtlas(key:String, ?library:String) + public static function getPackerAtlas(key:String, ?library:String):FlxAtlasFrames { return FlxAtlasFrames.fromSpriteSheetPacker(image(key, library), file('images/$key.txt', library)); } diff --git a/source/funkin/PlayerSettings.hx b/source/funkin/PlayerSettings.hx index 5a9351bba..b4c871c29 100644 --- a/source/funkin/PlayerSettings.hx +++ b/source/funkin/PlayerSettings.hx @@ -13,6 +13,7 @@ import flixel.util.FlxSignal; */ class PlayerSettings { + // TODO: Finish implementation of second player. public static var numPlayers(default, null) = 0; public static var numAvatars(default, null) = 0; public static var player1(default, null):PlayerSettings; @@ -21,12 +22,21 @@ class PlayerSettings public static var onAvatarAdd(default, null) = new FlxTypedSignalVoid>(); public static var onAvatarRemove(default, null) = new FlxTypedSignalVoid>(); + /** + * The player number associated with this settings object. + */ public var id(default, null):Int; + /** + * The controls handler for this player. + */ public var controls(default, null):Controls; /** * Return the PlayerSettings for the given player number, or `null` if that player isn't active. + * + * @param id The player number this represents. + * @return The PlayerSettings for the given player number, or `null` if that player isn't active. */ public static function get(id:Int):Null { @@ -38,6 +48,9 @@ class PlayerSettings }; } + /** + * Initialize the PlayerSettings singletons for each player. + */ public static function init():Void { if (player1 == null) @@ -56,22 +69,30 @@ class PlayerSettings } } - public static function reset() + /** + * Forcibly destroy the PlayerSettings singletons for each player. + */ + public static function reset():Void { player1 = null; player2 = null; numPlayers = 0; } - static function onGamepadAdded(gamepad:FlxGamepad) + /** + * Callback invoked when a gamepad is added. + * @param gamepad The gamepad that was added. + */ + static function onGamepadAdded(gamepad:FlxGamepad):Void { + // TODO: Make this detect and handle multiple players player1.addGamepad(gamepad); } /** * @param id The player number this represents. This was refactored to START AT `1`. */ - private function new(id:Int) + function new(id:Int) { trace('loading player settings for id: $id'); @@ -83,11 +104,11 @@ class PlayerSettings function addKeyboard():Void { - var useDefault = true; + var useDefault:Bool = true; if (Save.instance.hasControls(id, Keys)) { var keyControlData = Save.instance.getControls(id, Keys); - trace("keyControlData: " + haxe.Json.stringify(keyControlData)); + trace('Loading keyboard control scheme from user save'); useDefault = false; controls.fromSaveData(keyControlData, Keys); } @@ -98,7 +119,7 @@ class PlayerSettings if (useDefault) { - trace("Loading default keyboard control scheme"); + trace('Loading default keyboard control scheme'); controls.setKeyboardScheme(Solo); } @@ -109,13 +130,13 @@ class PlayerSettings * Called after an FlxGamepad has been detected. * @param gamepad The gamepad that was detected. */ - function addGamepad(gamepad:FlxGamepad) + function addGamepad(gamepad:FlxGamepad):Void { var useDefault = true; if (Save.instance.hasControls(id, Gamepad(gamepad.id))) { var padControlData = Save.instance.getControls(id, Gamepad(gamepad.id)); - trace("padControlData: " + haxe.Json.stringify(padControlData)); + trace('Loading gamepad control scheme from user save'); useDefault = false; controls.addGamepadWithSaveData(gamepad.id, padControlData); } @@ -126,7 +147,7 @@ class PlayerSettings if (useDefault) { - trace("Loading gamepad control scheme"); + trace('Loading default gamepad control scheme'); controls.addDefaultGamepad(gamepad.id); } PreciseInputManager.instance.initializeButtons(controls, gamepad); @@ -135,12 +156,12 @@ class PlayerSettings /** * Save this player's controls to the game's persistent save. */ - public function saveControls() + public function saveControls():Void { var keyData = controls.createSaveData(Keys); if (keyData != null) { - trace("saving key data: " + haxe.Json.stringify(keyData)); + trace('Saving keyboard control scheme to user save'); Save.instance.setControls(id, Keys, keyData); } @@ -149,7 +170,7 @@ class PlayerSettings var padData = controls.createSaveData(Gamepad(controls.gamepadsAdded[0])); if (padData != null) { - trace("saving pad data: " + haxe.Json.stringify(padData)); + trace('Saving gamepad control scheme to user save'); Save.instance.setControls(id, Gamepad(controls.gamepadsAdded[0]), padData); } } diff --git a/source/funkin/Preferences.hx b/source/funkin/Preferences.hx index 60c7a996a..d8066c581 100644 --- a/source/funkin/Preferences.hx +++ b/source/funkin/Preferences.hx @@ -20,7 +20,7 @@ class Preferences static function set_naughtyness(value:Bool):Bool { - var save = Save.instance; + var save:Save = Save.instance; save.options.naughtyness = value; save.flush(); return value; @@ -39,7 +39,7 @@ class Preferences static function set_downscroll(value:Bool):Bool { - var save = Save.instance; + var save:Save = Save.instance; save.options.downscroll = value; save.flush(); return value; @@ -58,7 +58,7 @@ class Preferences static function set_flashingLights(value:Bool):Bool { - var save = Save.instance; + var save:Save = Save.instance; save.options.flashingLights = value; save.flush(); return value; @@ -77,7 +77,7 @@ class Preferences static function set_zoomCamera(value:Bool):Bool { - var save = Save.instance; + var save:Save = Save.instance; save.options.zoomCamera = value; save.flush(); return value; @@ -122,15 +122,20 @@ class Preferences { if (value != Save.instance.options.autoPause) FlxG.autoPause = value; - var save = Save.instance; + var save:Save = Save.instance; save.options.autoPause = value; save.flush(); return value; } + /** + * Loads the user's preferences from the save data and apply them. + */ public static function init():Void { + // Apply the autoPause setting (enables automatic pausing on focus lost). FlxG.autoPause = Preferences.autoPause; + // Apply the debugDisplay setting (enables the FPS and RAM display). toggleDebugDisplay(Preferences.debugDisplay); } diff --git a/source/funkin/Preloader.hx b/source/funkin/Preloader.hx index 2a73d8199..157015ba6 100644 --- a/source/funkin/Preloader.hx +++ b/source/funkin/Preloader.hx @@ -3,7 +3,6 @@ package funkin; import flash.Lib; import flash.display.Bitmap; import flash.display.BitmapData; -import flash.display.BlendMode; import flash.display.Sprite; import flixel.system.FlxBasePreloader; import openfl.display.Sprite; @@ -12,7 +11,8 @@ import openfl.text.TextField; import openfl.text.TextFormat; import flixel.system.FlxAssets; -@:bitmap("art/preloaderArt.png") class LogoImage extends BitmapData {} +@:bitmap('art/preloaderArt.png') +class LogoImage extends BitmapData {} class Preloader extends FlxBasePreloader { diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 984f27c26..2db3a27b9 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2,11 +2,7 @@ package funkin.play; import funkin.audio.FunkinSound; import flixel.addons.display.FlxPieDial; -import flixel.addons.display.FlxPieDial; import flixel.addons.transition.FlxTransitionableState; -import flixel.addons.transition.FlxTransitionableState; -import flixel.addons.transition.FlxTransitionableSubState; -import flixel.addons.transition.FlxTransitionableSubState; import flixel.addons.transition.Transition; import flixel.addons.transition.Transition; import flixel.FlxCamera; @@ -923,8 +919,8 @@ class PlayState extends MusicBeatSubState var pauseSubState:FlxSubState = new PauseSubState({mode: isChartingMode ? Charting : Standard}); - FlxTransitionableSubState.skipNextTransIn = true; - FlxTransitionableSubState.skipNextTransOut = true; + FlxTransitionableState.skipNextTransIn = true; + FlxTransitionableState.skipNextTransOut = true; pauseSubState.camera = camHUD; openSubState(pauseSubState); // boyfriendPos.put(); // TODO: Why is this here? @@ -1045,8 +1041,8 @@ class PlayState extends MusicBeatSubState isChartingMode: isChartingMode, transparent: persistentDraw }); - FlxTransitionableSubState.skipNextTransIn = true; - FlxTransitionableSubState.skipNextTransOut = true; + FlxTransitionableState.skipNextTransIn = true; + FlxTransitionableState.skipNextTransOut = true; openSubState(gameOverSubState); } @@ -2610,8 +2606,8 @@ class PlayState extends MusicBeatSubState var pauseSubState:FlxSubState = new PauseSubState({mode: Conversation}); persistentUpdate = false; - FlxTransitionableSubState.skipNextTransIn = true; - FlxTransitionableSubState.skipNextTransOut = true; + FlxTransitionableState.skipNextTransIn = true; + FlxTransitionableState.skipNextTransOut = true; pauseSubState.camera = camCutscene; openSubState(pauseSubState); } @@ -2626,8 +2622,8 @@ class PlayState extends MusicBeatSubState var pauseSubState:FlxSubState = new PauseSubState({mode: Cutscene}); persistentUpdate = false; - FlxTransitionableSubState.skipNextTransIn = true; - FlxTransitionableSubState.skipNextTransOut = true; + FlxTransitionableState.skipNextTransIn = true; + FlxTransitionableState.skipNextTransOut = true; pauseSubState.camera = camCutscene; openSubState(pauseSubState); } diff --git a/source/funkin/ui/MusicBeatSubState.hx b/source/funkin/ui/MusicBeatSubState.hx index dc742874f..e9effdbee 100644 --- a/source/funkin/ui/MusicBeatSubState.hx +++ b/source/funkin/ui/MusicBeatSubState.hx @@ -1,6 +1,6 @@ package funkin.ui; -import flixel.addons.transition.FlxTransitionableSubState; +import flixel.addons.transition.FlxTransitionableState; import flixel.FlxSubState; import flixel.text.FlxText; import funkin.ui.mainmenu.MainMenuState; diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 1892bdec1..f4d48dba3 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -1,6 +1,6 @@ package funkin.ui.mainmenu; -import flixel.addons.transition.FlxTransitionableSubState; +import flixel.addons.transition.FlxTransitionableState; import funkin.ui.debug.DebugMenuSubState; import flixel.FlxObject; import flixel.FlxSprite; @@ -103,8 +103,8 @@ class MainMenuState extends MusicBeatState persistentDraw = true; persistentUpdate = false; // Freeplay has its own custom transition - FlxTransitionableSubState.skipNextTransIn = true; - FlxTransitionableSubState.skipNextTransOut = true; + FlxTransitionableState.skipNextTransIn = true; + FlxTransitionableState.skipNextTransOut = true; openSubState(new FreeplayState()); }); diff --git a/source/funkin/util/MathUtil.hx b/source/funkin/util/MathUtil.hx index 5fed1d3e1..0e7c29d73 100644 --- a/source/funkin/util/MathUtil.hx +++ b/source/funkin/util/MathUtil.hx @@ -13,12 +13,24 @@ class MathUtil /** * Perform linear interpolation between the base and the target, based on the current framerate. + * @param base The starting value, when `progress <= 0`. + * @param target The ending value, when `progress >= 1`. + * @param ratio Value used to interpolate between `base` and `target`. + * + * @return The interpolated value. */ public static function coolLerp(base:Float, target:Float, ratio:Float):Float { return base + cameraLerp(ratio) * (target - base); } + /** + * Perform linear interpolation based on the current framerate. + * @param lerp Value used to interpolate between `base` and `target`. + * + * @return The interpolated value. + */ + @:deprecated('Use smoothLerp instead') public static function cameraLerp(lerp:Float):Float { return lerp * (FlxG.elapsed / (1 / 60)); @@ -30,26 +42,30 @@ class MathUtil * @param value The value to get the logarithm of. * @return `log_base(value)` */ - public static function logBase(base:Float, value:Float) + public static function logBase(base:Float, value:Float):Float { return Math.log(value) / Math.log(base); } /** - * @returns `2^x` + * Get the base-2 logarithm of a value. + * @param x value + * @return `2^x` */ - public static function exp2(x:Float) + public static function exp2(x:Float):Float { return Math.pow(2, x); } /** * Linearly interpolate between two values. + * * @param base The starting value, when `progress <= 0`. * @param target The ending value, when `progress >= 1`. * @param progress Value used to interpolate between `base` and `target`. + * @return The interpolated value. */ - public static function lerp(base:Float, target:Float, progress:Float) + public static function lerp(base:Float, target:Float, progress:Float):Float { return base + progress * (target - base); } @@ -62,9 +78,12 @@ class MathUtil * @param duration The total duration of the interpolation. Nominal duration until remaining distance is less than `precision`. * @param precision The target precision of the interpolation. Defaults to 1% of distance remaining. * @see https://twitter.com/FreyaHolmer/status/1757918211679650262 + * + * @return The interpolated value. */ public static function smoothLerp(current:Float, target:Float, elapsed:Float, duration:Float, precision:Float = 1 / 100):Float { + // An alternative algorithm which uses a separate half-life value: // var halfLife:Float = -duration / logBase(2, precision); // lerp(current, target, 1 - exp2(-elapsed / halfLife)); diff --git a/source/funkin/util/MemoryUtil.hx b/source/funkin/util/MemoryUtil.hx index 6b5f7deea..f5935ed67 100644 --- a/source/funkin/util/MemoryUtil.hx +++ b/source/funkin/util/MemoryUtil.hx @@ -16,7 +16,7 @@ class MemoryUtil public static function buildGCInfo():String { #if cpp - var result = "HXCPP-Immix:"; + var result:String = 'HXCPP-Immix:'; result += '\n- Memory Used: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_USAGE)} bytes'; result += '\n- Memory Reserved: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_RESERVED)} bytes'; result += '\n- Memory Current Pool: ${cpp.vm.Gc.memInfo(cpp.vm.Gc.MEM_INFO_CURRENT)} bytes'; @@ -35,10 +35,10 @@ class MemoryUtil result += '\n- HXCPP C++11: ${#if HXCPP_CPP11 'Enabled' #else 'Disabled' #end}'; result += '\n- Source Annotation: ${#if annotate_source 'Enabled' #else 'Disabled' #end}'; #elseif js - var result = "JS-MNS:"; + var result:String = 'JS-MNS:'; result += '\n- Memory Used: ${getMemoryUsed()} bytes'; #else - var result = "Unknown GC"; + var result:String = 'Unknown GC'; #end return result; @@ -66,7 +66,7 @@ class MemoryUtil #if cpp cpp.vm.Gc.enable(true); #else - throw "Not implemented!"; + throw 'Not implemented!'; #end } @@ -78,7 +78,7 @@ class MemoryUtil #if cpp cpp.vm.Gc.enable(false); #else - throw "Not implemented!"; + throw 'Not implemented!'; #end } @@ -92,7 +92,7 @@ class MemoryUtil #if cpp cpp.vm.Gc.run(major); #else - throw "Not implemented!"; + throw 'Not implemented!'; #end } @@ -107,7 +107,7 @@ class MemoryUtil #if cpp cpp.vm.Gc.compact(); #else - throw "Not implemented!"; + throw 'Not implemented!'; #end } } diff --git a/source/funkin/util/MouseUtil.hx b/source/funkin/util/MouseUtil.hx index c3f46819e..5248437a8 100644 --- a/source/funkin/util/MouseUtil.hx +++ b/source/funkin/util/MouseUtil.hx @@ -38,6 +38,9 @@ class MouseUtil } } + /** + * Increment the zoom level of the current camera by the mouse wheel scroll value. + */ public static function mouseWheelZoom():Void { if (FlxG.mouse.wheel != 0) FlxG.camera.zoom += FlxG.mouse.wheel * (0.1 * FlxG.camera.zoom); diff --git a/source/funkin/util/PlatformUtil.hx b/source/funkin/util/PlatformUtil.hx index eecf6a7ea..ae8c1dcee 100644 --- a/source/funkin/util/PlatformUtil.hx +++ b/source/funkin/util/PlatformUtil.hx @@ -16,9 +16,9 @@ class PlatformUtil #if mac return true; #elseif html5 - return js.Browser.window.navigator.platform.startsWith("Mac") - || js.Browser.window.navigator.platform.startsWith("iPad") - || js.Browser.window.navigator.platform.startsWith("iPhone"); + return js.Browser.window.navigator.platform.startsWith('Mac') + || js.Browser.window.navigator.platform.startsWith('iPad') + || js.Browser.window.navigator.platform.startsWith('iPhone'); #else return false; #end @@ -27,7 +27,7 @@ class PlatformUtil /** * Detects and returns the current host platform. * Always returns `HTML5` on web, regardless of the computer running that browser. - * Returns `null` if the platform could not be detected. + * @return The host platform, or `null` if the platform could not be detected. */ public static function detectHostPlatform():Null { diff --git a/source/funkin/util/SortUtil.hx b/source/funkin/util/SortUtil.hx index d87592c91..c5ac175be 100644 --- a/source/funkin/util/SortUtil.hx +++ b/source/funkin/util/SortUtil.hx @@ -27,29 +27,53 @@ class SortUtil /** * You can use this function in FlxTypedGroup.sort() to sort FlxObjects by their z-index values. * The value defaults to 0, but by assigning it you can easily rearrange objects as desired. + * + * @param order Either `FlxSort.ASCENDING` or `FlxSort.DESCENDING` + * @param a The first FlxObject to compare. + * @param b The second FlxObject to compare. + * @return 1 if `a` has a higher z-index, -1 if `b` has a higher z-index. */ - public static inline function byZIndex(Order:Int, Obj1:FlxBasic, Obj2:FlxBasic):Int + public static inline function byZIndex(order:Int, a:FlxBasic, b:FlxBasic):Int { - if (Obj1 == null || Obj2 == null) return 0; - return FlxSort.byValues(Order, Obj1.zIndex, Obj2.zIndex); + if (a == null || b == null) return 0; + return FlxSort.byValues(order, a.zIndex, b.zIndex); } /** * Given two Notes, returns 1 or -1 based on whether `a` or `b` has an earlier strumtime. * * @param order Either `FlxSort.ASCENDING` or `FlxSort.DESCENDING` + * @param a The first Note to compare. + * @param b The second Note to compare. + * @return 1 if `a` has an earlier strumtime, -1 if `b` has an earlier strumtime. */ - public static inline function byStrumtime(order:Int, a:NoteSprite, b:NoteSprite) + public static inline function byStrumtime(order:Int, a:NoteSprite, b:NoteSprite):Int { return noteDataByTime(order, a.noteData, b.noteData); } - public static inline function noteDataByTime(order:Int, a:SongNoteData, b:SongNoteData) + /** + * Given two Note Data objects, returns 1 or -1 based on whether `a` or `b` has an earlier time. + * + * @param order Either `FlxSort.ASCENDING` or `FlxSort.DESCENDING` + * @param a The first Event to compare. + * @param b The second Event to compare. + * @return 1 if `a` has an earlier time, -1 if `b` has an earlier time. + */ + public static inline function noteDataByTime(order:Int, a:SongNoteData, b:SongNoteData):Int { return FlxSort.byValues(order, a.time, b.time); } - public static inline function eventDataByTime(order:Int, a:SongEventData, b:SongEventData) + /** + * Given two Event Data objects, returns 1 or -1 based on whether `a` or `b` has an earlier time. + * + * @param order Either `FlxSort.ASCENDING` or `FlxSort.DESCENDING` + * @param a The first Event to compare. + * @param b The second Event to compare. + * @return 1 if `a` has an earlier time, -1 if `b` has an earlier time. + */ + public static inline function eventDataByTime(order:Int, a:SongEventData, b:SongEventData):Int { return FlxSort.byValues(order, a.time, b.time); } @@ -58,8 +82,11 @@ class SortUtil * Given two FlxFrames, sort their names alphabetically. * * @param order Either `FlxSort.ASCENDING` or `FlxSort.DESCENDING` + * @param a The first Frame to compare. + * @param b The second Frame to compare. + * @return 1 if `a` has an earlier time, -1 if `b` has an earlier time. */ - public static inline function byFrameName(a:FlxFrame, b:FlxFrame) + public static inline function byFrameName(a:FlxFrame, b:FlxFrame):Int { return alphabetically(a.name, b.name); } @@ -68,6 +95,7 @@ class SortUtil * Sort predicate for sorting strings alphabetically. * @param a The first string to compare. * @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 { @@ -81,9 +109,11 @@ class SortUtil /** * Sort predicate which sorts two strings alphabetically, but prioritizes a specific string first. * Example usage: `array.sort(defaultThenAlphabetical.bind('test'))` will sort the array so that the string 'test' is first. + * + * @param defaultValue The value to prioritize. * @param a The first string to compare. * @param b The second string to compare. - * @param defaultValue The value to prioritize. + * @return 1 if `a` comes before `b`, -1 if `b` comes before `a`, 0 if they are equal */ public static function defaultThenAlphabetically(defaultValue:String, a:String, b:String):Int { @@ -96,9 +126,11 @@ class SortUtil /** * Sort predicate which sorts two strings alphabetically, but prioritizes a specific string first. * Example usage: `array.sort(defaultsThenAlphabetical.bind(['test']))` will sort the array so that the string 'test' is first. + * + * @param defaultValues The values to prioritize. * @param a The first string to compare. * @param b The second string to compare. - * @param defaultValues The values to prioritize. + * @return 1 if `a` comes before `b`, -1 if `b` comes before `a`, 0 if they are equal */ public static function defaultsThenAlphabetically(defaultValues:Array, a:String, b:String):Int { diff --git a/source/funkin/util/TimerUtil.hx b/source/funkin/util/TimerUtil.hx index caf49341b..eb2690e16 100644 --- a/source/funkin/util/TimerUtil.hx +++ b/source/funkin/util/TimerUtil.hx @@ -5,23 +5,42 @@ import haxe.Timer; class TimerUtil { + /** + * Store the current time. + */ public static function start():Float { return Timer.stamp(); } - private static function took(start:Float, ?end:Float):Float + /** + * Return the elapsed time. + */ + static function took(start:Float, ?end:Float):Float { var endOrNow:Float = end != null ? end : Timer.stamp(); return endOrNow - start; } + /** + * Return the elapsed time in seconds as a string. + * @param start The start time. + * @param end The end time. + * @param precision The number of decimal places to round to. + * @return The elapsed time in seconds as a string. + */ public static function seconds(start:Float, ?end:Float, ?precision = 2):String { var seconds:Float = FloatTools.round(took(start, end), precision); return '${seconds} seconds'; } + /** + * Return the elapsed time in milliseconds as a string. + * @param start The start time. + * @param end The end time. + * @return The elapsed time in milliseconds as a string. + */ public static function ms(start:Float, ?end:Float):String { var seconds:Float = took(start, end); diff --git a/source/funkin/util/TrackerUtil.hx b/source/funkin/util/TrackerUtil.hx index ffe374c5f..a2552f63e 100644 --- a/source/funkin/util/TrackerUtil.hx +++ b/source/funkin/util/TrackerUtil.hx @@ -18,7 +18,7 @@ class TrackerUtil public static function initTrackers():Void { #if FLX_DEBUG - Tracker.addProfile(new TrackerProfile(Highscore, ["tallies"])); + Tracker.addProfile(new TrackerProfile(Highscore, ['tallies'])); FlxG.console.registerClass(Highscore); #end } diff --git a/source/funkin/util/VersionUtil.hx b/source/funkin/util/VersionUtil.hx index e8d3d3652..247ba19db 100644 --- a/source/funkin/util/VersionUtil.hx +++ b/source/funkin/util/VersionUtil.hx @@ -15,6 +15,9 @@ class VersionUtil /** * Checks that a given verison number satisisfies a given version rule. * Version rule can be complex, e.g. "1.0.x" or ">=1.0.0,<1.1.0", or anything NPM supports. + * @param version The semantic version to validate. + * @param versionRule The version rule to validate against. + * @return `true` if the version satisfies the rule, `false` otherwise. */ public static function validateVersion(version:thx.semver.Version, versionRule:thx.semver.VersionRule):Bool { @@ -32,6 +35,9 @@ class VersionUtil /** * Checks that a given verison number satisisfies a given version rule. * Version rule can be complex, e.g. "1.0.x" or ">=1.0.0,<1.1.0", or anything NPM supports. + * @param version The semantic version to validate. + * @param versionRule The version rule to validate against. + * @return `true` if the version satisfies the rule, `false` otherwise. */ public static function validateVersionStr(version:String, versionRule:String):Bool { @@ -56,7 +62,7 @@ class VersionUtil public static function getVersionFromJSON(input:Null):Null { if (input == null) return null; - var parsed = SerializerUtil.fromJSON(input); + var parsed:Dynamic = SerializerUtil.fromJSON(input); if (parsed == null) return null; if (parsed.version == null) return null; var versionStr:String = parsed.version; // Dynamic -> String cast @@ -64,6 +70,11 @@ class VersionUtil return version; } + /** + * Get and parse the semantic version from a JSON string. + * @param input The JSON string to parse. + * @return The semantic version, or null if it could not be parsed. + */ public static function parseVersion(input:Dynamic):Null { if (input == null) return null; diff --git a/source/funkin/util/WindowUtil.hx b/source/funkin/util/WindowUtil.hx index 9f623c39d..f73fe11ce 100644 --- a/source/funkin/util/WindowUtil.hx +++ b/source/funkin/util/WindowUtil.hx @@ -24,7 +24,7 @@ class WindowUtil { #if CAN_OPEN_LINKS #if linux - Sys.command('/usr/bin/xdg-open', [targetUrl, "&"]); + Sys.command('/usr/bin/xdg-open', [targetUrl, '&']); #else // This should work on Windows and HTML5. FlxG.openURL(targetUrl); @@ -42,7 +42,7 @@ class WindowUtil { #if CAN_OPEN_LINKS #if windows - Sys.command('explorer', [targetPath.replace("/", "\\")]); + Sys.command('explorer', [targetPath.replace('/', '\\')]); #elseif mac Sys.command('open', [targetPath]); #elseif linux @@ -61,9 +61,9 @@ class WindowUtil { #if CAN_OPEN_LINKS #if windows - Sys.command('explorer', ["/select," + targetPath.replace("/", "\\")]); + Sys.command('explorer', ['/select,' + targetPath.replace('/', '\\')]); #elseif mac - Sys.command('open', ["-R", targetPath]); + Sys.command('open', ['-R', targetPath]); #elseif linux // TODO: unsure of the linux equivalent to opening a folder and then "selecting" a file. Sys.command('open', [targetPath]); @@ -82,7 +82,7 @@ class WindowUtil * Wires up FlxSignals that happen based on window activity. * For example, we can run a callback when the window is closed. */ - public static function initWindowEvents() + public static function initWindowEvents():Void { // onUpdate is called every frame just before rendering. @@ -95,7 +95,7 @@ class WindowUtil /** * Turns off that annoying "Report to Microsoft" dialog that pops up when the game crashes. */ - public static function disableCrashHandler() + public static function disableCrashHandler():Void { #if (cpp && windows) untyped __cpp__('SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);'); diff --git a/source/haxe/ui/backend/flixel/UIStateBase.hx b/source/haxe/ui/backend/flixel/UIStateBase.hx index 9f1a53722..38377206c 100644 --- a/source/haxe/ui/backend/flixel/UIStateBase.hx +++ b/source/haxe/ui/backend/flixel/UIStateBase.hx @@ -1,3 +1,6 @@ package haxe.ui.backend.flixel; +/** + * Override HaxeUI to use `MusicBeatState` instead of `FlxState`. + */ typedef UIStateBase = funkin.ui.MusicBeatState; diff --git a/source/haxe/ui/backend/flixel/UISubStateBase.hx b/source/haxe/ui/backend/flixel/UISubStateBase.hx index 306c9b633..52c9a7231 100644 --- a/source/haxe/ui/backend/flixel/UISubStateBase.hx +++ b/source/haxe/ui/backend/flixel/UISubStateBase.hx @@ -1,3 +1,6 @@ package haxe.ui.backend.flixel; +/** + * Override HaxeUI to use `MusicBeatSubState` instead of `FlxSubState`. + */ typedef UISubStateBase = funkin.ui.MusicBeatSubState; From 50b17b3dcaba13894a827f75e2bc79493b6cd4ea Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 19 Mar 2024 23:25:22 -0400 Subject: [PATCH 13/75] Update 2hot, Lit Up, and Blazin to polished audio --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 0e2c5bf21..26e2b2c44 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0e2c5bf2134c7e517b70cf74afd58abe5c7b5e50 +Subproject commit 26e2b2c4419f6a39b4e7641f0f824117c03eb0b6 From 566a271a634f29e50a91872a37974944be73df3a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 19 Mar 2024 23:25:56 -0400 Subject: [PATCH 14/75] Fix several crashes when reaching a game over in minimal mode --- source/funkin/play/GameOverSubState.hx | 129 +++++++++++++++---------- 1 file changed, 78 insertions(+), 51 deletions(-) diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index 95304d762..70964bff0 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -97,14 +97,14 @@ class GameOverSubState extends MusicBeatSubState /** * Reset the game over configuration to the default. */ - public static function reset() + public static function reset():Void { - animationSuffix = ""; - musicSuffix = ""; - blueBallSuffix = ""; + animationSuffix = ''; + musicSuffix = ''; + blueBallSuffix = ''; } - override public function create() + override public function create():Void { if (instance != null) { @@ -130,23 +130,27 @@ class GameOverSubState extends MusicBeatSubState // Pluck Boyfriend from the PlayState and place him (in the same position) in the GameOverSubState. // We can then play the character's `firstDeath` animation. - boyfriend = PlayState.instance.currentStage.getBoyfriend(true); - boyfriend.isDead = true; - add(boyfriend); - boyfriend.resetCharacter(); + if (PlayState.instance.isMinimalMode) {} + else + { + boyfriend = PlayState.instance.currentStage.getBoyfriend(true); + boyfriend.isDead = true; + add(boyfriend); + boyfriend.resetCharacter(); - // 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; - var offsets:Array = boyfriend.getDeathCameraOffsets(); - cameraFollowPoint.x += offsets[0]; - cameraFollowPoint.y += offsets[1]; - add(cameraFollowPoint); + // 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; + var offsets:Array = boyfriend.getDeathCameraOffsets(); + cameraFollowPoint.x += offsets[0]; + cameraFollowPoint.y += offsets[1]; + add(cameraFollowPoint); - FlxG.camera.target = null; - FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.01); - targetCameraZoom = PlayState?.instance?.currentStage?.camZoom * boyfriend.getDeathCameraZoom(); + FlxG.camera.target = null; + FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.01); + targetCameraZoom = PlayState?.instance?.currentStage?.camZoom * boyfriend.getDeathCameraZoom(); + } // // Set up the audio @@ -164,21 +168,29 @@ class GameOverSubState extends MusicBeatSubState var hasStartedAnimation:Bool = false; - override function update(elapsed:Float) + override function update(elapsed:Float):Void { if (!hasStartedAnimation) { hasStartedAnimation = true; - if (boyfriend.hasAnimation('fakeoutDeath') && FlxG.random.bool((1 / 4096) * 100)) + if (PlayState.instance.isMinimalMode) { - boyfriend.playAnimation('fakeoutDeath', true, false); + // Play the "blue balled" sound. May play a variant if one has been assigned. + playBlueBalledSFX(); } else { - boyfriend.playAnimation('firstDeath', true, false); // ignoreOther is set to FALSE since you WANT to be able to mash and confirm game over! - // Play the "blue balled" sound. May play a variant if one has been assigned. - playBlueBalledSFX(); + if (boyfriend.hasAnimation('fakeoutDeath') && FlxG.random.bool((1 / 4096) * 100)) + { + boyfriend.playAnimation('fakeoutDeath', true, false); + } + else + { + boyfriend.playAnimation('firstDeath', true, false); // ignoreOther is set to FALSE since you WANT to be able to mash and confirm game over! + // Play the "blue balled" sound. May play a variant if one has been assigned. + playBlueBalledSFX(); + } } } @@ -241,27 +253,34 @@ class GameOverSubState extends MusicBeatSubState } else { - // Music hasn't started yet. - switch (PlayStatePlaylist.campaignId) + if (PlayState.instance.isMinimalMode) { - // TODO: Make the behavior for playing Jeff's voicelines generic or un-hardcoded. - // This will simplify the class and make it easier for mods to add death quotes. - case "week7": - if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && boyfriend.isAnimationFinished() && !playingJeffQuote) - { - playingJeffQuote = true; - playJeffQuote(); - // Start music at lower volume - startDeathMusic(0.2, false); - boyfriend.playAnimation('deathLoop' + animationSuffix); - } - default: - // Start music at normal volume once the initial death animation finishes. - if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && boyfriend.isAnimationFinished()) - { - startDeathMusic(1.0, false); - boyfriend.playAnimation('deathLoop' + animationSuffix); - } + // startDeathMusic(1.0, false); + } + else + { + // Music hasn't started yet. + switch (PlayStatePlaylist.campaignId) + { + // TODO: Make the behavior for playing Jeff's voicelines generic or un-hardcoded. + // This will simplify the class and make it easier for mods to add death quotes. + case 'week7': + if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && boyfriend.isAnimationFinished() && !playingJeffQuote) + { + playingJeffQuote = true; + playJeffQuote(); + // Start music at lower volume + startDeathMusic(0.2, false); + boyfriend.playAnimation('deathLoop' + animationSuffix); + } + default: + // Start music at normal volume once the initial death animation finishes. + if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && boyfriend.isAnimationFinished()) + { + startDeathMusic(1.0, false); + boyfriend.playAnimation('deathLoop' + animationSuffix); + } + } } } @@ -279,7 +298,11 @@ class GameOverSubState extends MusicBeatSubState isEnding = true; startDeathMusic(1.0, true); // isEnding changes this function's behavior. - boyfriend.playAnimation('deathConfirm' + animationSuffix, true); + if (PlayState.instance.isMinimalMode) {} + else + { + boyfriend.playAnimation('deathConfirm' + animationSuffix, true); + } // After the animation finishes... new FlxTimer().start(0.7, function(tmr:FlxTimer) { @@ -289,10 +312,14 @@ class GameOverSubState extends MusicBeatSubState FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true); PlayState.instance.needsReset = true; - // Readd Boyfriend to the stage. - boyfriend.isDead = false; - remove(boyfriend); - PlayState.instance.currentStage.addCharacter(boyfriend, BF); + if (PlayState.instance.isMinimalMode) {} + else + { + // Readd Boyfriend to the stage. + boyfriend.isDead = false; + remove(boyfriend); + PlayState.instance.currentStage.addCharacter(boyfriend, BF); + } // Snap reset the camera which may have changed because of the player character data. resetCameraZoom(); From 36a9c29720f5a9c76316d2ab24461b8f54063aa7 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 20 Mar 2024 14:37:24 -0400 Subject: [PATCH 15/75] Work in progress on custom album art for freeplay --- assets | 2 +- source/funkin/InitState.hx | 2 + source/funkin/data/freeplay/AlbumData.hx | 36 ++ source/funkin/data/freeplay/AlbumRegistry.hx | 84 +++++ .../graphics/adobeanimate/FlxAtlasSprite.hx | 7 +- source/funkin/modding/PolymodHandler.hx | 2 + source/funkin/play/song/Song.hx | 20 ++ .../debug/dialogue/ConversationDebugState.hx | 1 + source/funkin/ui/freeplay/Album.hx | 89 +++++ source/funkin/ui/freeplay/AlbumRoll.hx | 173 ++++++++++ source/funkin/ui/freeplay/FreeplayState.hx | 314 +++++++++--------- source/funkin/ui/freeplay/ScriptedAlbum.hx | 9 + source/funkin/ui/freeplay/SongMenuItem.hx | 54 +-- source/funkin/util/Constants.hx | 11 +- 14 files changed, 619 insertions(+), 185 deletions(-) create mode 100644 source/funkin/data/freeplay/AlbumData.hx create mode 100644 source/funkin/data/freeplay/AlbumRegistry.hx create mode 100644 source/funkin/ui/freeplay/Album.hx create mode 100644 source/funkin/ui/freeplay/AlbumRoll.hx create mode 100644 source/funkin/ui/freeplay/ScriptedAlbum.hx diff --git a/assets b/assets index 0e2c5bf21..4e88fb2a5 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0e2c5bf2134c7e517b70cf74afd58abe5c7b5e50 +Subproject commit 4e88fb2a50b284d92404af6afc95b9840d3cda8d diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index a9e8dbffa..8837b578d 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -24,6 +24,7 @@ import funkin.data.stage.StageRegistry; import funkin.data.dialogue.ConversationRegistry; import funkin.data.dialogue.DialogueBoxRegistry; import funkin.data.dialogue.SpeakerRegistry; +import funkin.data.freeplay.AlbumRegistry; import funkin.data.song.SongRegistry; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.modding.module.ModuleHandler; @@ -167,6 +168,7 @@ class InitState extends FlxState ConversationRegistry.instance.loadEntries(); DialogueBoxRegistry.instance.loadEntries(); SpeakerRegistry.instance.loadEntries(); + AlbumRegistry.instance.loadEntries(); StageRegistry.instance.loadEntries(); // TODO: CharacterDataParser doesn't use json2object, so it's way slower than the other parsers. diff --git a/source/funkin/data/freeplay/AlbumData.hx b/source/funkin/data/freeplay/AlbumData.hx new file mode 100644 index 000000000..265a01fce --- /dev/null +++ b/source/funkin/data/freeplay/AlbumData.hx @@ -0,0 +1,36 @@ +package funkin.data.freeplay; + +/** + * 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 AlbumData = +{ + /** + * Semantic version for album data. + */ + public var version:String; + + /** + * Readable name of the album. + */ + public var name:String; + + /** + * Readable name of the artist(s) of the album. + */ + public var artists:Array; + + /** + * Asset key for the album art. + * The album art will be displayed in Freeplay. + */ + public var albumArtAsset:String; + + /** + * Asset key for the album title. + * The album title will be displayed below the album art in Freeplay. + */ + public var albumTitleAsset:String; +} diff --git a/source/funkin/data/freeplay/AlbumRegistry.hx b/source/funkin/data/freeplay/AlbumRegistry.hx new file mode 100644 index 000000000..78fba451b --- /dev/null +++ b/source/funkin/data/freeplay/AlbumRegistry.hx @@ -0,0 +1,84 @@ +package funkin.data.freeplay; + +import funkin.ui.freeplay.Album; +import funkin.data.freeplay.AlbumData; +import funkin.ui.freeplay.ScriptedAlbum; + +class AlbumRegistry extends BaseRegistry +{ + /** + * The current version string for the album data format. + * Handle breaking changes by incrementing this value + * and adding migration to the `migrateAlbumData()` function. + */ + public static final ALBUM_DATA_VERSION:thx.semver.Version = '1.0.0'; + + public static final ALBUM_DATA_VERSION_RULE:thx.semver.VersionRule = '1.0.x'; + + public static final instance:AlbumRegistry = new AlbumRegistry(); + + public function new() + { + super('ALBUM', 'ui/freeplay/albums', ALBUM_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):Album + { + return ScriptedAlbum.init(clsName, 'unknown'); + } + + function getScriptedClassNames():Array + { + return ScriptedAlbum.listScriptClasses(); + } +} diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index 9a2af8913..ec8c8413c 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -3,7 +3,9 @@ package funkin.graphics.adobeanimate; import flixel.util.FlxSignal.FlxTypedSignal; import flxanimate.FlxAnimate; import flxanimate.FlxAnimate.Settings; -import flixel.math.FlxPoint; +import flxanimate.frames.FlxAnimateFrames; +import openfl.display.BitmapData; +import openfl.utils.Assets; /** * A sprite which provides convenience functions for rendering a texture atlas with animations. @@ -19,6 +21,7 @@ class FlxAtlasSprite extends FlxAnimate ShowPivot: #if debug false #else false #end, Antialiasing: true, ScrollFactor: null, + OverrideGraphics: [], // Offset: new FlxPoint(0, 0), // This is just FlxSprite.offset }; @@ -31,7 +34,7 @@ class FlxAtlasSprite extends FlxAnimate var canPlayOtherAnims:Bool = true; - public function new(x:Float, y:Float, path:String, ?settings:Settings) + public function new(x:Float, y:Float, ?path:String, ?settings:Settings) { if (settings == null) settings = SETTINGS; diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index b1c6b511a..a88476d4d 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -8,6 +8,7 @@ import funkin.data.level.LevelRegistry; import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.song.SongRegistry; import funkin.data.stage.StageRegistry; +import funkin.data.freeplay.AlbumRegistry; import funkin.modding.module.ModuleHandler; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.save.Save; @@ -324,6 +325,7 @@ class PolymodHandler 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 1b7740408..d82b60de6 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -213,6 +213,26 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry + { + var result:Map = new Map(); + + for (difficultyId in difficulties.keys()) + { + var meta:Null = difficulties.get(difficultyId); + if (meta != null && meta.album != null) + { + result.set(difficultyId, meta.album); + } + } + + return result; + } + /** * Populate the difficulty data from the provided metadata. * Does not load chart data (that is triggered later when we want to play the song). diff --git a/source/funkin/ui/debug/dialogue/ConversationDebugState.hx b/source/funkin/ui/debug/dialogue/ConversationDebugState.hx index f165865c4..c2edcca5a 100644 --- a/source/funkin/ui/debug/dialogue/ConversationDebugState.hx +++ b/source/funkin/ui/debug/dialogue/ConversationDebugState.hx @@ -11,6 +11,7 @@ import funkin.data.dialogue.DialogueBoxData; import funkin.data.dialogue.DialogueBoxRegistry; import funkin.data.dialogue.SpeakerData; import funkin.data.dialogue.SpeakerRegistry; +import funkin.data.freeplay.AlbumRegistry; import funkin.play.cutscene.dialogue.Conversation; import funkin.play.cutscene.dialogue.DialogueBox; import funkin.play.cutscene.dialogue.Speaker; diff --git a/source/funkin/ui/freeplay/Album.hx b/source/funkin/ui/freeplay/Album.hx new file mode 100644 index 000000000..7291c7357 --- /dev/null +++ b/source/funkin/ui/freeplay/Album.hx @@ -0,0 +1,89 @@ +package funkin.ui.freeplay; + +import funkin.data.freeplay.AlbumData; +import funkin.data.freeplay.AlbumRegistry; +import funkin.data.IRegistryEntry; +import flixel.graphics.FlxGraphic; + +/** + * A class representing the data for an album as displayed in Freeplay. + */ +class Album implements IRegistryEntry +{ + /** + * The internal ID for this album. + */ + public final id:String; + + /** + * The full data for an album. + */ + public final _data:AlbumData; + + public function new(id:String) + { + this.id = id; + this._data = _fetchData(id); + + if (_data == null) + { + throw 'Could not parse album data for id: $id'; + } + } + + /** + * Return the name of the album. + * @ + */ + public function getAlbumName():String + { + return _data.name; + } + + /** + * Return the artists of the album. + * @return The list of artists + */ + public function getAlbumArtists():Array + { + return _data.artists; + } + + /** + * Get the asset key for the album art. + * @return The asset key + */ + public function getAlbumArtAssetKey():String + { + return _data.albumArtAsset; + } + + /** + * Get the album art as a graphic, ready to apply to a sprite. + * @return The built graphic + */ + public function getAlbumArtGraphic():FlxGraphic + { + return FlxG.bitmap.add(Paths.image(getAlbumArtAssetKey())); + } + + /** + * Get the asset key for the album title. + */ + public function getAlbumTitleAssetKey():String + { + return _data.albumTitleAsset; + } + + public function toString():String + { + return 'Album($id)'; + } + + public function destroy():Void {} + + static function _fetchData(id:String):Null + { + return AlbumRegistry.instance.parseEntryDataWithMigration(id, AlbumRegistry.instance.fetchEntryVersion(id)); + } +} diff --git a/source/funkin/ui/freeplay/AlbumRoll.hx b/source/funkin/ui/freeplay/AlbumRoll.hx new file mode 100644 index 000000000..f0c759666 --- /dev/null +++ b/source/funkin/ui/freeplay/AlbumRoll.hx @@ -0,0 +1,173 @@ +package funkin.ui.freeplay; + +import flixel.graphics.FlxGraphic; +import flixel.group.FlxSpriteGroup; +import flixel.util.FlxSort; +import funkin.data.freeplay.AlbumRegistry; +import funkin.graphics.adobeanimate.FlxAtlasSprite; +import funkin.graphics.FunkinSprite; +import funkin.util.SortUtil; + +/** + * The graphic for the album roll in the FreeplayState. + * Simply set `albumID` to fetch the required data and update the textures. + */ +class AlbumRoll extends FlxSpriteGroup +{ + /** + * The ID of the album to display. + * Modify this value to automatically update the album art and title. + */ + public var albumId(default, set):String; + + function set_albumId(value:String):String + { + if (this.albumId != value) + { + this.albumId = value; + updateAlbum(); + } + + return value; + } + + var albumArt:FlxAtlasSprite; + var albumTitle:FunkinSprite; + var difficultyStars:DifficultyStars; + + var albumData:Album; + + public function new() + { + super(); + + albumTitle = new FunkinSprite(947, 491); + albumTitle.visible = false; + albumTitle.zIndex = 200; + add(albumTitle); + + difficultyStars = new DifficultyStars(140, 39); + + difficultyStars.stars.visible = true; + albumTitle.visible = false; + // albumArtist.visible = false; + + // var albumArtist:FlxSprite = new FlxSprite(1010, 607).loadGraphic(Paths.image('freeplay/albumArtist-kawaisprite')); + } + + /** + * Load the album data by ID and update the textures. + */ + function updateAlbum():Void + { + albumData = AlbumRegistry.instance.fetchEntry(albumId); + + if (albumData == null) + { + FlxG.log.warn('Could not find album data for album ID: ${albumId}'); + + return; + }; + + var albumArtGraphics:Array = [null, albumData.getAlbumArtGraphic()]; + + if (albumArt != null) + { + albumArt.visible = false; + albumArt.anim.stop(); + albumArt.destroy(); + remove(albumArt); + } + + // I wasn't able to get replacing to work properly on an existing object, + // so I just throw the old one in the trash and make a new one. + albumArt = new FlxAtlasSprite(640, 360, Paths.animateAtlas('freeplay/albumRoll'), + { + OverrideGraphics: albumArtGraphics, + }); + albumArt.zIndex = 100; + + playIntro(); + add(albumArt); + + albumTitle.loadGraphic(Paths.image(albumData.getAlbumTitleAssetKey())); + + refresh(); + } + + public function refresh():Void + { + sort(SortUtil.byZIndex, FlxSort.ASCENDING); + } + + /** + * Apply exit movers for the album roll. + * @param exitMovers The exit movers to apply. + */ + public function applyExitMovers(exitMovers:FreeplayState.ExitMoverData):Void + { + exitMovers.set([albumArt], + { + x: FlxG.width, + speed: 0.4, + wait: 0 + }); + exitMovers.set([albumTitle], + { + x: FlxG.width, + speed: 0.2, + wait: 0.1 + }); + + /* + exitMovers.set([albumArtist], + { + x: FlxG.width * 1.1, + speed: 0.2, + wait: 0.2 + }); + */ + exitMovers.set([difficultyStars], + { + x: FlxG.width * 1.2, + speed: 0.2, + wait: 0.3 + }); + } + + /** + * Play the intro animation on the album art. + */ + public function playIntro():Void + { + albumArt.visible = true; + albumArt.anim.play(''); + albumArt.anim.onComplete = function() { + albumArt.anim.pause(); + }; + } + + public function setDifficultyStars(?difficulty:Int):Void + { + if (difficulty == null) return; + + difficultyStars.difficulty = difficulty; + } + + /** + * Make the album title graphic visible. + */ + public function showTitle():Void + { + albumTitle.visible = true; + } + + /** + * Make the album stars visible. + */ + public function showStars():Void + { + // albumArtist.visible = false; + difficultyStars.stars.visible = false; + } +} diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 7ade5a2a6..8d7ecb556 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1,19 +1,14 @@ package funkin.ui.freeplay; -import openfl.text.TextField; -import flixel.addons.display.FlxGridOverlay; import flixel.addons.transition.FlxTransitionableState; import flixel.addons.ui.FlxInputText; import flixel.FlxCamera; -import flixel.FlxGame; import flixel.FlxSprite; -import flixel.FlxState; import flixel.group.FlxGroup; 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; @@ -25,7 +20,6 @@ import flixel.util.FlxTimer; import funkin.audio.FunkinSound; import funkin.data.level.LevelRegistry; import funkin.data.song.SongRegistry; -import funkin.graphics.adobeanimate.FlxAtlasSprite; import funkin.graphics.FunkinCamera; import funkin.graphics.FunkinSprite; import funkin.graphics.shaders.AngleMask; @@ -33,28 +27,16 @@ import funkin.graphics.shaders.HSVShader; import funkin.graphics.shaders.PureColor; import funkin.graphics.shaders.StrokeShader; import funkin.input.Controls; -import funkin.input.Controls.Control; -import funkin.play.components.HealthIcon; -import funkin.play.PlayState; import funkin.play.PlayStatePlaylist; import funkin.play.song.Song; import funkin.save.Save; import funkin.save.Save.SaveScoreData; import funkin.ui.AtlasText; -import funkin.ui.freeplay.BGScrollingText; -import funkin.ui.freeplay.DifficultyStars; -import funkin.ui.freeplay.DJBoyfriend; -import funkin.ui.freeplay.FreeplayScore; -import funkin.ui.freeplay.LetterSort; -import funkin.ui.freeplay.SongMenuItem; import funkin.ui.mainmenu.MainMenuState; -import funkin.ui.MusicBeatState; import funkin.ui.MusicBeatSubState; import funkin.ui.transition.LoadingState; import funkin.ui.transition.StickerSubState; import funkin.util.MathUtil; -import funkin.util.MathUtil; -import lime.app.Future; import lime.utils.Assets; /** @@ -65,6 +47,9 @@ typedef FreeplayStateParams = ?character:String, }; +/** + * The state for the freeplay menu, allowing the player to select any song to play. + */ class FreeplayState extends MusicBeatSubState { // @@ -120,30 +105,31 @@ class FreeplayState extends MusicBeatSubState var grpDifficulties:FlxTypedSpriteGroup; var coolColors:Array = [ - 0xff9271fd, - 0xff9271fd, - 0xff223344, + 0xFF9271FD, + 0xFF9271FD, + 0xFF223344, 0xFF941653, - 0xFFfc96d7, - 0xFFa0d1ff, - 0xffff78bf, - 0xfff6b604 + 0xFFFC96D7, + 0xFFA0D1FF, + 0xFFFF78BF, + 0xFFF6B604 ]; var grpSongs:FlxTypedGroup; var grpCapsules:FlxTypedGroup; var curCapsule:SongMenuItem; var curPlaying:Bool = false; - var ostName:FlxText; - var difficultyStars:DifficultyStars; var displayedVariations:Array; var dj:DJBoyfriend; + var ostName:FlxText; + var albumRoll:AlbumRoll; + var letterSort:LetterSort; var typing:FlxInputText; - var exitMovers:Map, MoveData> = new Map(); + var exitMovers:ExitMoverData = new Map(); var stickerSubState:StickerSubState; @@ -179,7 +165,7 @@ class FreeplayState extends MusicBeatSubState #if discord_rpc // Updating Discord Rich Presence - DiscordClient.changePresence("In the Menus", null); + DiscordClient.changePresence('In the Menus', null); #end var isDebug:Bool = false; @@ -195,7 +181,7 @@ class FreeplayState extends MusicBeatSubState // TODO: This makes custom variations disappear from Freeplay. Figure out a better solution later. // Default character (BF) shows default and Erect variations. Pico shows only Pico variations. - displayedVariations = (currentCharacter == "bf") ? [Constants.DEFAULT_VARIATION, "erect"] : [currentCharacter]; + displayedVariations = (currentCharacter == 'bf') ? [Constants.DEFAULT_VARIATION, 'erect'] : [currentCharacter]; // programmatically adds the songs via LevelRegistry and SongRegistry for (levelId in LevelRegistry.instance.listBaseGameLevelIds()) @@ -205,7 +191,7 @@ class FreeplayState extends MusicBeatSubState var song:Song = SongRegistry.instance.fetchEntry(songId); // Only display songs which actually have available charts for the current character. - var availableDifficultiesForSong = song.listDifficulties(displayedVariations); + var availableDifficultiesForSong:Array = song.listDifficulties(displayedVariations); if (availableDifficultiesForSong.length == 0) continue; songs.push(new FreeplaySongData(levelId, songId, song, displayedVariations)); @@ -226,16 +212,16 @@ class FreeplayState extends MusicBeatSubState trace(FlxCamera.defaultZoom); var pinkBack:FunkinSprite = FunkinSprite.create('freeplay/pinkBack'); - pinkBack.color = 0xFFffd4e9; // sets it to pink! + pinkBack.color = 0xFFFFD4E9; // sets it to pink! pinkBack.x -= pinkBack.width; FlxTween.tween(pinkBack, {x: 0}, 0.6, {ease: FlxEase.quartOut}); add(pinkBack); - var orangeBackShit:FunkinSprite = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFfeda00); + var orangeBackShit:FunkinSprite = new FunkinSprite(84, 440).makeSolidColor(Std.int(pinkBack.width), 75, 0xFFFEDA00); add(orangeBackShit); - var alsoOrangeLOL:FunkinSprite = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFffd400); + var alsoOrangeLOL:FunkinSprite = new FunkinSprite(0, orangeBackShit.y).makeSolidColor(100, Std.int(orangeBackShit.height), 0xFFFFD400); add(alsoOrangeLOL); exitMovers.set([pinkBack, orangeBackShit, alsoOrangeLOL], @@ -254,10 +240,10 @@ class FreeplayState extends MusicBeatSubState add(grpTxtScrolls); grpTxtScrolls.visible = false; - FlxG.debugger.addTrackerProfile(new TrackerProfile(BGScrollingText, ["x", "y", "speed", "size"])); + FlxG.debugger.addTrackerProfile(new TrackerProfile(BGScrollingText, ['x', 'y', 'speed', 'size'])); - var moreWays:BGScrollingText = new BGScrollingText(0, 160, "HOT BLOODED IN MORE WAYS THAN ONE", FlxG.width, true, 43); - moreWays.funnyColor = 0xFFfff383; + var moreWays:BGScrollingText = 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); @@ -267,8 +253,8 @@ class FreeplayState extends MusicBeatSubState speed: 0.4, }); - var funnyScroll:BGScrollingText = new BGScrollingText(0, 220, "BOYFRIEND", FlxG.width / 2, false, 60); - funnyScroll.funnyColor = 0xFFff9963; + var funnyScroll:BGScrollingText = new BGScrollingText(0, 220, 'BOYFRIEND', FlxG.width / 2, false, 60); + funnyScroll.funnyColor = 0xFFFF9963; funnyScroll.speed = -3.8; grpTxtScrolls.add(funnyScroll); @@ -280,7 +266,7 @@ class FreeplayState extends MusicBeatSubState wait: 0 }); - var txtNuts:BGScrollingText = new BGScrollingText(0, 285, "PROTECT YO NUTS", FlxG.width / 2, true, 43); + var txtNuts:BGScrollingText = new BGScrollingText(0, 285, 'PROTECT YO NUTS', FlxG.width / 2, true, 43); txtNuts.speed = 3.5; grpTxtScrolls.add(txtNuts); exitMovers.set([txtNuts], @@ -289,8 +275,8 @@ class FreeplayState extends MusicBeatSubState speed: 0.4, }); - var funnyScroll2:BGScrollingText = new BGScrollingText(0, 335, "BOYFRIEND", FlxG.width / 2, false, 60); - funnyScroll2.funnyColor = 0xFFff9963; + var funnyScroll2:BGScrollingText = new BGScrollingText(0, 335, 'BOYFRIEND', FlxG.width / 2, false, 60); + funnyScroll2.funnyColor = 0xFFFF9963; funnyScroll2.speed = -3.8; grpTxtScrolls.add(funnyScroll2); @@ -300,8 +286,8 @@ class FreeplayState extends MusicBeatSubState speed: 0.5, }); - var moreWays2:BGScrollingText = new BGScrollingText(0, 397, "HOT BLOODED IN MORE WAYS THAN ONE", FlxG.width, true, 43); - moreWays2.funnyColor = 0xFFfff383; + var moreWays2:BGScrollingText = 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); @@ -311,8 +297,8 @@ class FreeplayState extends MusicBeatSubState speed: 0.4 }); - var funnyScroll3:BGScrollingText = new BGScrollingText(0, orangeBackShit.y + 10, "BOYFRIEND", FlxG.width / 2, 60); - funnyScroll3.funnyColor = 0xFFfea400; + var funnyScroll3:BGScrollingText = new BGScrollingText(0, orangeBackShit.y + 10, 'BOYFRIEND', FlxG.width / 2, 60); + funnyScroll3.funnyColor = 0xFFFEA400; funnyScroll3.speed = -3.8; grpTxtScrolls.add(funnyScroll3); @@ -328,8 +314,10 @@ class FreeplayState extends MusicBeatSubState x: -dj.width * 1.6, speed: 0.5 }); + // TODO: Replace this. - if (currentCharacter == "pico") dj.visible = false; + if (currentCharacter == 'pico') dj.visible = false; + add(dj); var bgDad:FlxSprite = new FlxSprite(pinkBack.width * 0.75, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad')); @@ -387,62 +375,23 @@ class FreeplayState extends MusicBeatSubState if (diffSprite.difficultyId == currentDifficulty) diffSprite.visible = true; } - // NOTE: This is an AtlasSprite because we use an animation to bring it into view. - // TODO: Add the ability to select the album graphic. - var albumArt:FlxAtlasSprite = new FlxAtlasSprite(640, 360, Paths.animateAtlas("freeplay/albumRoll")); - albumArt.visible = false; - add(albumArt); + albumRoll = new AlbumRoll(); + albumRoll.albumId = 'volume1'; + add(albumRoll); - exitMovers.set([albumArt], - { - x: FlxG.width, - speed: 0.4, - wait: 0 - }); - - var albumTitle:FlxSprite = new FlxSprite(947, 491).loadGraphic(Paths.image('freeplay/albumTitle-fnfvol1')); - var albumArtist:FlxSprite = new FlxSprite(1010, 607).loadGraphic(Paths.image('freeplay/albumArtist-kawaisprite')); - difficultyStars = new DifficultyStars(140, 39); - - difficultyStars.stars.visible = false; - albumTitle.visible = false; - albumArtist.visible = false; - - exitMovers.set([albumTitle], - { - x: FlxG.width, - speed: 0.2, - wait: 0.1 - }); - - exitMovers.set([albumArtist], - { - x: FlxG.width * 1.1, - speed: 0.2, - wait: 0.2 - }); - exitMovers.set([difficultyStars], - { - x: FlxG.width * 1.2, - speed: 0.2, - wait: 0.3 - }); - - add(albumTitle); - add(albumArtist); - add(difficultyStars); + albumRoll.applyExitMovers(exitMovers); var overhangStuff:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 64, FlxColor.BLACK); overhangStuff.y -= overhangStuff.height; add(overhangStuff); FlxTween.tween(overhangStuff, {y: 0}, 0.3, {ease: FlxEase.quartOut}); - var fnfFreeplay:FlxText = new FlxText(8, 8, 0, "FREEPLAY", 48); - fnfFreeplay.font = "VCR OSD Mono"; + var fnfFreeplay:FlxText = new FlxText(8, 8, 0, 'FREEPLAY', 48); + 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 = new FlxText(8, 8, FlxG.width - 8 - 8, 'OFFICIAL OST', 48); + ostName.font = 'VCR OSD Mono'; ostName.alignment = RIGHT; ostName.visible = false; @@ -454,21 +403,21 @@ class FreeplayState extends MusicBeatSubState wait: 0 }); - var sillyStroke = new StrokeShader(0xFFFFFFFF, 2, 2); + var sillyStroke:StrokeShader = new StrokeShader(0xFFFFFFFF, 2, 2); fnfFreeplay.shader = sillyStroke; add(fnfFreeplay); add(ostName); var fnfHighscoreSpr:FlxSprite = new FlxSprite(860, 70); fnfHighscoreSpr.frames = Paths.getSparrowAtlas('freeplay/highscore'); - fnfHighscoreSpr.animation.addByPrefix("highscore", "highscore small instance 1", 24, false); + fnfHighscoreSpr.animation.addByPrefix('highscore', 'highscore small instance 1', 24, false); fnfHighscoreSpr.visible = false; fnfHighscoreSpr.setGraphicSize(0, Std.int(fnfHighscoreSpr.height * 1)); fnfHighscoreSpr.updateHitbox(); add(fnfHighscoreSpr); new FlxTimer().start(FlxG.random.float(12, 50), function(tmr) { - fnfHighscoreSpr.animation.play("highscore"); + fnfHighscoreSpr.animation.play('highscore'); tmr.time = FlxG.random.float(20, 60); }, 0); @@ -479,7 +428,7 @@ class FreeplayState extends MusicBeatSubState var clearBoxSprite:FlxSprite = new FlxSprite(1165, 65).loadGraphic(Paths.image('freeplay/clearBox')); add(clearBoxSprite); - txtCompletion = new AtlasText(1185, 87, "69", AtlasFont.FREEPLAY_CLEAR); + txtCompletion = new AtlasText(1185, 87, '69', AtlasFont.FREEPLAY_CLEAR); txtCompletion.visible = false; add(txtCompletion); @@ -496,9 +445,9 @@ class FreeplayState extends MusicBeatSubState letterSort.changeSelectionCallback = (str) -> { switch (str) { - case "fav": + case 'fav': generateSongList({filterType: FAVORITE}, true); - case "ALL": + case 'ALL': generateSongList(null, true); default: generateSongList({filterType: REGEXP, filterData: str}, true); @@ -514,25 +463,20 @@ class FreeplayState extends MusicBeatSubState dj.onIntroDone.add(function() { // when boyfriend hits dat shiii - albumArt.visible = true; - albumArt.anim.play(""); - albumArt.anim.onComplete = function() { - albumArt.anim.pause(); - }; + albumRoll.playIntro(); new FlxTimer().start(1, function(_) { - albumTitle.visible = true; + albumRoll.showTitle(); }); new FlxTimer().start(35 / 24, function(_) { - albumArtist.visible = true; - difficultyStars.stars.visible = true; + albumRoll.showStars(); }); FlxTween.tween(grpDifficulties, {x: 90}, 0.6, {ease: FlxEase.quartOut}); - var diffSelLeft = new DifficultySelector(20, grpDifficulties.y - 10, false, controls); - var diffSelRight = new DifficultySelector(325, grpDifficulties.y - 10, true, controls); + var diffSelLeft:DifficultySelector = new DifficultySelector(20, grpDifficulties.y - 10, false, controls); + var diffSelRight:DifficultySelector = new DifficultySelector(325, grpDifficulties.y - 10, true, controls); add(diffSelLeft); add(diffSelRight); @@ -562,7 +506,7 @@ class FreeplayState extends MusicBeatSubState }); }); - pinkBack.color = 0xFFffd863; + pinkBack.color = 0xFFFFD863; bgDad.visible = true; orangeBackShit.visible = true; alsoOrangeLOL.visible = true; @@ -571,9 +515,9 @@ class FreeplayState extends MusicBeatSubState generateSongList(null, false); - var swag:Alphabet = new Alphabet(1, 0, "swag"); + // var swag:Alphabet = new Alphabet(1, 0, 'swag'); - var funnyCam = new FunkinCamera(0, 0, FlxG.width, FlxG.height); + var funnyCam:FunkinCamera = new FunkinCamera(0, 0, FlxG.width, FlxG.height); funnyCam.bgColor = FlxColor.TRANSPARENT; FlxG.cameras.add(funnyCam); @@ -588,12 +532,20 @@ class FreeplayState extends MusicBeatSubState }); } + /** + * Given the current filter, rebuild the current song list. + * + * @param filterStuff A filter to apply to the song list (regex, startswith, all, favorite) + * @param force + */ public function generateSongList(?filterStuff:SongFilter, force:Bool = false):Void { curSelected = 1; for (cap in grpCapsules.members) + { cap.kill(); + } var tempSongs:Array = songs; @@ -604,7 +556,7 @@ class FreeplayState extends MusicBeatSubState case REGEXP: // filterStuff.filterData has a string with the first letter of the sorting range, and the second one // this creates a filter to return all the songs that start with a letter between those two - var filterRegexp = new EReg("^[" + filterStuff.filterData + "].*", "i"); + var filterRegexp:EReg = new EReg('^[' + filterStuff.filterData + '].*', 'i'); tempSongs = tempSongs.filter(str -> { if (str == null) return true; // Random return filterRegexp.match(str.songName); @@ -660,14 +612,19 @@ class FreeplayState extends MusicBeatSubState funnyMenu.favIcon.visible = tempSongs[i].isFav; funnyMenu.hsvShader = hsvShader; - if (i < 8) funnyMenu.initJumpIn(Math.min(i, 4), force); + if (i < 8) + { + funnyMenu.initJumpIn(Math.min(i, 4), force); + } else + { funnyMenu.forcePosition(); + } grpCapsules.add(funnyMenu); } - FlxG.console.registerFunction("changeSelection", changeSelection); + FlxG.console.registerFunction('changeSelection', changeSelection); rememberSelection(); @@ -699,7 +656,7 @@ class FreeplayState extends MusicBeatSubState { if (songs[curSelected] != null) { - var realShit = curSelected; + var realShit:Int = curSelected; songs[curSelected].isFav = !songs[curSelected].isFav; if (songs[curSelected].isFav) { @@ -708,7 +665,7 @@ class FreeplayState extends MusicBeatSubState ease: FlxEase.elasticOut, onComplete: _ -> { grpCapsules.members[realShit].favIcon.visible = true; - grpCapsules.members[realShit].favIcon.animation.play("fav"); + grpCapsules.members[realShit].favIcon.animation.play('fav'); } }); } @@ -772,9 +729,9 @@ class FreeplayState extends MusicBeatSubState { if (busy) return; - var upP = controls.UI_UP_P; - var downP = controls.UI_DOWN_P; - var accepted = controls.ACCEPT; + var upP:Bool = controls.UI_UP_P; + var downP:Bool = controls.UI_DOWN_P; + var accepted:Bool = controls.ACCEPT; if (FlxG.onMobile) { @@ -786,14 +743,14 @@ class FreeplayState extends MusicBeatSubState } if (touch.pressed) { - var dx = initTouchPos.x - touch.screenX; - var dy = initTouchPos.y - touch.screenY; + var dx:Float = initTouchPos.x - touch.screenX; + var dy:Float = initTouchPos.y - touch.screenY; - var angle = Math.atan2(dy, dx); - var length = Math.sqrt(dx * dx + dy * dy); + var angle:Float = Math.atan2(dy, dx); + var length:Float = Math.sqrt(dx * dx + dy * dy); - FlxG.watch.addQuick("LENGTH", length); - FlxG.watch.addQuick("ANGLE", Math.round(FlxAngle.asDegrees(angle))); + FlxG.watch.addQuick('LENGTH', length); + FlxG.watch.addQuick('ANGLE', Math.round(FlxAngle.asDegrees(angle))); } } @@ -858,9 +815,14 @@ class FreeplayState extends MusicBeatSubState { spamTimer = 0; - if (controls.UI_UP) changeSelection(-1); + if (controls.UI_UP) + { + changeSelection(-1); + } else + { changeSelection(1); + } } } else if (spamTimer >= 0.9) spamming = true; @@ -904,7 +866,7 @@ class FreeplayState extends MusicBeatSubState { var newParams:FreeplayStateParams = { - character: currentCharacter == "bf" ? "pico" : "bf", + character: currentCharacter == 'bf' ? 'pico' : 'bf', }; openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.freeplay.FreeplayState(newParams, sticker))); } @@ -974,7 +936,7 @@ class FreeplayState extends MusicBeatSubState public override function destroy():Void { super.destroy(); - var daSong = songs[curSelected]; + var daSong:Null = songs[curSelected]; if (daSong != null) { clearDaCache(daSong.songName); @@ -985,7 +947,7 @@ class FreeplayState extends MusicBeatSubState { touchTimer = 0; - var currentDifficultyIndex = diffIdsCurrent.indexOf(currentDifficulty); + var currentDifficultyIndex:Int = diffIdsCurrent.indexOf(currentDifficulty); if (currentDifficultyIndex == -1) currentDifficultyIndex = diffIdsCurrent.indexOf(Constants.DEFAULT_DIFFICULTY); @@ -996,7 +958,7 @@ class FreeplayState extends MusicBeatSubState currentDifficulty = diffIdsCurrent[currentDifficultyIndex]; - var daSong = songs[curSelected]; + var daSong:Null = songs[curSelected]; if (daSong != null) { var songScore:SaveScoreData = Save.instance.getSongScore(songs[curSelected].songId, currentDifficulty); @@ -1060,11 +1022,12 @@ class FreeplayState extends MusicBeatSubState } // Set the difficulty star count on the right. - difficultyStars.difficulty = daSong?.songRating ?? difficultyStars.difficulty; // yay haxe 4.3 + albumRoll.setDifficultyStars(daSong?.songRating); + albumRoll.albumId = daSong?.albumId ?? Constants.DEFAULT_ALBUM_ID; } // Clears the cache of songs, frees up memory, they' ll have to be loaded in later tho function clearDaCache(actualSongTho:String) - function clearDaCache(actualSongTho:String) + function clearDaCache(actualSongTho:String):Void { for (song in songs) { @@ -1079,7 +1042,7 @@ class FreeplayState extends MusicBeatSubState function capsuleOnConfirmRandom(randomCapsule:SongMenuItem):Void { - trace("RANDOM SELECTED"); + trace('RANDOM SELECTED'); busy = true; letterSort.inputEnabled = false; @@ -1095,7 +1058,7 @@ class FreeplayState extends MusicBeatSubState if (availableSongCapsules.length == 0) { - trace("No songs available!"); + trace('No songs available!'); busy = false; letterSort.inputEnabled = true; FlxG.sound.play(Paths.sound('cancelMenu')); @@ -1167,24 +1130,23 @@ class FreeplayState extends MusicBeatSubState } // Set the difficulty star count on the right. - var daSong = songs[curSelected]; - difficultyStars.difficulty = daSong?.songRating ?? 0; + var daSong:Null = songs[curSelected]; + albumRoll.setDifficultyStars(daSong?.songRating ?? 0); } function changeSelection(change:Int = 0):Void { - // NGio.logEvent('Fresh'); FlxG.sound.play(Paths.sound('scrollMenu'), 0.4); // FlxG.sound.playMusic(Paths.inst(songs[curSelected].songName)); - var prevSelected = curSelected; + var prevSelected:Int = curSelected; curSelected += change; if (curSelected < 0) curSelected = grpCapsules.countLiving() - 1; if (curSelected >= grpCapsules.countLiving()) curSelected = 0; - var daSongCapsule = grpCapsules.members[curSelected]; + var daSongCapsule:SongMenuItem = grpCapsules.members[curSelected]; if (daSongCapsule.songData != null) { var songScore:SaveScoreData = Save.instance.getSongScore(daSongCapsule.songData.songId, currentDifficulty); @@ -1235,6 +1197,9 @@ class FreeplayState extends MusicBeatSubState } } +/** + * The difficulty selector arrows to the left and right of the difficulty. + */ class DifficultySelector extends FlxSprite { var controls:Controls; @@ -1247,7 +1212,7 @@ class DifficultySelector extends FlxSprite this.controls = controls; frames = Paths.getSparrowAtlas('freeplay/freeplaySelector'); - animation.addByPrefix('shine', "arrow pointer loop", 24); + animation.addByPrefix('shine', 'arrow pointer loop', 24); animation.play('shine'); whiteShader = new PureColor(FlxColor.WHITE); @@ -1281,34 +1246,62 @@ class DifficultySelector extends FlxSprite } } +/** + * Structure for the current song filter. + */ typedef SongFilter = { var filterType:FilterType; var ?filterData:Dynamic; } +/** + * Possible types to use for the song filter. + */ enum abstract FilterType(String) { - var STARTSWITH; - var REGEXP; - var FAVORITE; - var ALL; + /** + * Filter to songs which start with a string + */ + public var STARTSWITH; + + /** + * Filter to songs which match a regular expression + */ + public var REGEXP; + + /** + * Filter to songs which are favorited + */ + public var FAVORITE; + + /** + * Filter to all songs + */ + public var ALL; } +/** + * Data about a specific song in the freeplay menu. + */ class FreeplaySongData { + /** + * Whether or not the song has been favorited. + */ public var isFav:Bool = false; var song:Song; - public var levelId(default, null):String = ""; - public var songId(default, null):String = ""; + public var levelId(default, null):String = ''; + public var songId(default, null):String = ''; public var songDifficulties(default, null):Array = []; - public var songName(default, null):String = ""; - public var songCharacter(default, null):String = ""; + public var songName(default, null):String = ''; + public var songCharacter(default, null):String = ''; public var songRating(default, null):Int = 0; + public var albumId(default, null):String = ''; public var currentDifficulty(default, set):String = Constants.DEFAULT_DIFFICULTY; public var displayedVariations(default, null):Array = [Constants.DEFAULT_VARIATION]; @@ -1332,19 +1325,28 @@ class FreeplaySongData updateValues(displayedVariations); } - function updateValues(displayedVariations:Array):Void + function updateValues(variations:Array):Void { - this.songDifficulties = song.listDifficulties(displayedVariations); + this.songDifficulties = song.listDifficulties(variations); if (!this.songDifficulties.contains(currentDifficulty)) currentDifficulty = Constants.DEFAULT_DIFFICULTY; - var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, displayedVariations); + var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, variations); if (songDifficulty == null) return; this.songName = songDifficulty.songName; this.songCharacter = songDifficulty.characters.opponent; this.songRating = songDifficulty.difficultyRating; + this.albumId = songDifficulty.album; } } +/** + * The map storing information about the exit movers. + */ +typedef ExitMoverData = Map, MoveData>; + +/** + * The data for an exit mover. + */ typedef MoveData = { var ?x:Float; @@ -1353,8 +1355,14 @@ typedef MoveData = var ?wait:Float; } +/** + * The sprite for the difficulty + */ class DifficultySprite extends FlxSprite { + /** + * The difficulty id which this sprite represents. + */ public var difficultyId:String; public function new(diffId:String) diff --git a/source/funkin/ui/freeplay/ScriptedAlbum.hx b/source/funkin/ui/freeplay/ScriptedAlbum.hx new file mode 100644 index 000000000..737f97ad2 --- /dev/null +++ b/source/funkin/ui/freeplay/ScriptedAlbum.hx @@ -0,0 +1,9 @@ +package funkin.ui.freeplay; + +/** + * A script that can be tied to an Album. + * Create a scripted class that extends Album to use this. + * This allows you to customize how a specific album appears. + */ +@:hscriptClass +class ScriptedAlbum extends funkin.ui.freeplay.Album implements polymod.hscript.HScriptedClass {} diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx index 06d113468..c20d81328 100644 --- a/source/funkin/ui/freeplay/SongMenuItem.hx +++ b/source/funkin/ui/freeplay/SongMenuItem.hx @@ -65,25 +65,26 @@ class SongMenuItem extends FlxSpriteGroup var rank:String = FlxG.random.getObject(ranks); ranking = new FlxSprite(capsule.width * 0.84, 30); - ranking.loadGraphic(Paths.image("freeplay/ranks/" + rank)); + ranking.loadGraphic(Paths.image('freeplay/ranks/' + rank)); ranking.scale.x = ranking.scale.y = realScaled; - ranking.alpha = 0.75; + // ranking.alpha = 0.75; + ranking.visible = false; ranking.origin.set(capsule.origin.x - ranking.x, capsule.origin.y - ranking.y); add(ranking); grpHide.add(ranking); switch (rank) { - case "perfect": + case 'perfect': ranking.x -= 10; } grayscaleShader = new Grayscale(1); - diffRatingSprite = new FlxSprite(145, 90).loadGraphic(Paths.image("freeplay/diffRatings/diff00")); + diffRatingSprite = new FlxSprite(145, 90).loadGraphic(Paths.image('freeplay/diffRatings/diff00')); diffRatingSprite.shader = grayscaleShader; - diffRatingSprite.visible = false; - add(diffRatingSprite); + // TODO: Readd once ratings are fully implemented + // add(diffRatingSprite); diffRatingSprite.origin.set(capsule.origin.x - diffRatingSprite.x, capsule.origin.y - diffRatingSprite.y); grpHide.add(diffRatingSprite); @@ -104,7 +105,7 @@ class SongMenuItem extends FlxSpriteGroup favIcon = new FlxSprite(400, 40); favIcon.frames = Paths.getSparrowAtlas('freeplay/favHeart'); - favIcon.animation.addByPrefix('fav', "favorite heart", 24, false); + favIcon.animation.addByPrefix('fav', 'favorite heart', 24, false); favIcon.animation.play('fav'); favIcon.setGraphicSize(50, 50); favIcon.visible = false; @@ -114,10 +115,11 @@ class SongMenuItem extends FlxSpriteGroup setVisibleGrp(false); } - function updateDifficultyRating(newRating:Int) + function updateDifficultyRating(newRating:Int):Void { var ratingPadded:String = newRating < 10 ? '0$newRating' : '$newRating'; diffRatingSprite.loadGraphic(Paths.image('freeplay/diffRatings/diff${ratingPadded}')); + diffRatingSprite.visible = false; } function set_hsvShader(value:HSVShader):HSVShader @@ -129,7 +131,7 @@ class SongMenuItem extends FlxSpriteGroup return value; } - function textAppear() + function textAppear():Void { songText.scale.x = 1.7; songText.scale.y = 0.2; @@ -144,7 +146,7 @@ class SongMenuItem extends FlxSpriteGroup }); } - function setVisibleGrp(value:Bool) + function setVisibleGrp(value:Bool):Void { for (spr in grpHide.members) { @@ -156,7 +158,7 @@ class SongMenuItem extends FlxSpriteGroup updateSelected(); } - public function init(?x:Float, ?y:Float, songData:Null) + public function init(?x:Float, ?y:Float, songData:Null):Void { if (x != null) this.x = x; if (y != null) this.y = y; @@ -176,7 +178,7 @@ class SongMenuItem extends FlxSpriteGroup * @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) + public function setCharacter(char:String):Void { var charPath:String = "freeplay/icons/"; @@ -186,18 +188,18 @@ class SongMenuItem extends FlxSpriteGroup // TODO: Also, can use CharacterDataParser.getCharPixelIconAsset() switch (char) { - case "monster-christmas": - charPath += "monsterpixel"; - case "mom-car": - charPath += "mommypixel"; - case "dad": - charPath += "daddypixel"; - case "darnell-blazin": - charPath += "darnellpixel"; - case "senpai-angry": - charPath += "senpaipixel"; + case 'monster-christmas': + charPath += 'monsterpixel'; + case 'mom-car': + charPath += 'mommypixel'; + case 'dad': + charPath += 'daddypixel'; + case 'darnell-blazin': + charPath += 'darnellpixel'; + case 'senpai-angry': + charPath += 'senpaipixel'; default: - charPath += char + "pixel"; + charPath += '${char}pixel'; } if (!openfl.utils.Assets.exists(Paths.image(charPath))) @@ -211,7 +213,7 @@ class SongMenuItem extends FlxSpriteGroup switch (char) { - case "parents-christmas": + case 'parents-christmas': pixelIcon.origin.x = 140; default: pixelIcon.origin.x = 100; @@ -262,7 +264,7 @@ class SongMenuItem extends FlxSpriteGroup var grpHide:FlxGroup; - public function forcePosition() + public function forcePosition():Void { visible = true; capsule.alpha = 1; @@ -287,7 +289,7 @@ class SongMenuItem extends FlxSpriteGroup setVisibleGrp(true); } - override function update(elapsed:Float) + override function update(elapsed:Float):Void { if (doJumpIn) { diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index 1005b312e..c9b99ed46 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -175,17 +175,22 @@ class Constants /** * The default name for songs. */ - public static final DEFAULT_SONGNAME:String = "Unknown"; + public static final DEFAULT_SONGNAME:String = 'Unknown'; /** * The default artist for songs. */ - public static final DEFAULT_ARTIST:String = "Unknown"; + public static final DEFAULT_ARTIST:String = 'Unknown'; /** * The default note style for songs. */ - public static final DEFAULT_NOTE_STYLE:String = "funkin"; + public static final DEFAULT_NOTE_STYLE:String = 'funkin'; + + /** + * The default album for songs in Freeplay. + */ + public static final DEFAULT_ALBUM_ID:String = 'volume1'; /** * The default timing format for songs. From 3975d34b70f53e8ff72d714f131184b0547ce004 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 20 Mar 2024 23:06:32 -0400 Subject: [PATCH 16/75] Fix scoring lerp issue on story menu. --- source/funkin/ui/story/StoryMenuState.hx | 2 +- source/funkin/util/MathUtil.hx | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 1f78eb375..60a6defdd 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -311,7 +311,7 @@ class StoryMenuState extends MusicBeatState { Conductor.instance.update(); - highScoreLerp = Std.int(MathUtil.coolLerp(highScoreLerp, highScore, 0.5)); + highScoreLerp = Std.int(MathUtil.smoothLerp(highScoreLerp, highScore, elapsed, 0.5)); scoreText.text = 'LEVEL SCORE: ${Math.round(highScoreLerp)}'; diff --git a/source/funkin/util/MathUtil.hx b/source/funkin/util/MathUtil.hx index 5fed1d3e1..72c592e8b 100644 --- a/source/funkin/util/MathUtil.hx +++ b/source/funkin/util/MathUtil.hx @@ -62,12 +62,22 @@ class MathUtil * @param duration The total duration of the interpolation. Nominal duration until remaining distance is less than `precision`. * @param precision The target precision of the interpolation. Defaults to 1% of distance remaining. * @see https://twitter.com/FreyaHolmer/status/1757918211679650262 + * + * @return A value between the current value and the target value. */ public static function smoothLerp(current:Float, target:Float, elapsed:Float, duration:Float, precision:Float = 1 / 100):Float { // var halfLife:Float = -duration / logBase(2, precision); // lerp(current, target, 1 - exp2(-elapsed / halfLife)); - return lerp(current, target, 1 - Math.pow(precision, elapsed / duration)); + if (current == target) return target; + + var result:Float = lerp(current, target, 1 - Math.pow(precision, elapsed / duration)); + + // TODO: Is there a better way to ensure a lerp which actually reaches the target? + // Research a framerate-independent PID lerp. + if (Math.abs(result - target) < (precision * target)) result = target; + + return result; } } From 1f81e9282795806a856d614595751708aea9a70c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 21 Mar 2024 00:38:52 -0400 Subject: [PATCH 17/75] Scrapped the weird FlxAltasSprite stuff and just used a tween. Also fixed some bugs with story menu --- source/funkin/graphics/FunkinSprite.hx | 12 +++- .../graphics/adobeanimate/FlxAtlasSprite.hx | 1 - source/funkin/ui/freeplay/AlbumRoll.hx | 63 ++++++++++++------- source/funkin/ui/freeplay/FreeplayState.hx | 12 +--- source/funkin/ui/story/StoryMenuState.hx | 41 ++++++------ 5 files changed, 73 insertions(+), 56 deletions(-) diff --git a/source/funkin/graphics/FunkinSprite.hx b/source/funkin/graphics/FunkinSprite.hx index 03382f757..d703a8ac0 100644 --- a/source/funkin/graphics/FunkinSprite.hx +++ b/source/funkin/graphics/FunkinSprite.hx @@ -3,6 +3,7 @@ package funkin.graphics; import flixel.FlxSprite; import flixel.util.FlxColor; import flixel.graphics.FlxGraphic; +import flixel.tweens.FlxTween; /** * An FlxSprite with additional functionality. @@ -217,7 +218,7 @@ class FunkinSprite extends FlxSprite } /** - * Ensure scale is applied when cloning a sprite. + * Ensure scale is applied when cloning a sprite.R * The default `clone()` method acts kinda weird TBH. * @return A clone of this sprite. */ @@ -230,4 +231,13 @@ class FunkinSprite extends FlxSprite return result; } + + public override function destroy():Void + { + frames = null; + // Cancel all tweens so they don't continue to run on a destroyed sprite. + // This prevents crashes. + FlxTween.cancelTweensOf(this); + super.destroy(); + } } diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index ec8c8413c..c5a3a3771 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -21,7 +21,6 @@ class FlxAtlasSprite extends FlxAnimate ShowPivot: #if debug false #else false #end, Antialiasing: true, ScrollFactor: null, - OverrideGraphics: [], // Offset: new FlxPoint(0, 0), // This is just FlxSprite.offset }; diff --git a/source/funkin/ui/freeplay/AlbumRoll.hx b/source/funkin/ui/freeplay/AlbumRoll.hx index f0c759666..507e0b662 100644 --- a/source/funkin/ui/freeplay/AlbumRoll.hx +++ b/source/funkin/ui/freeplay/AlbumRoll.hx @@ -1,12 +1,15 @@ package funkin.ui.freeplay; -import flixel.graphics.FlxGraphic; +import flixel.FlxSprite; import flixel.group.FlxSpriteGroup; import flixel.util.FlxSort; +import flixel.tweens.FlxTween; +import flixel.util.FlxTimer; +import flixel.tweens.FlxEase; import funkin.data.freeplay.AlbumRegistry; -import funkin.graphics.adobeanimate.FlxAtlasSprite; import funkin.graphics.FunkinSprite; import funkin.util.SortUtil; +import openfl.utils.Assets; /** * The graphic for the album roll in the FreeplayState. @@ -31,10 +34,12 @@ class AlbumRoll extends FlxSpriteGroup return value; } - var albumArt:FlxAtlasSprite; + var albumArt:FunkinSprite; var albumTitle:FunkinSprite; var difficultyStars:DifficultyStars; + var _exitMovers:Null; + var albumData:Album; public function new() @@ -42,7 +47,7 @@ class AlbumRoll extends FlxSpriteGroup super(); albumTitle = new FunkinSprite(947, 491); - albumTitle.visible = false; + albumTitle.visible = true; albumTitle.zIndex = 200; add(albumTitle); @@ -69,28 +74,32 @@ class AlbumRoll extends FlxSpriteGroup return; }; - var albumArtGraphics:Array = [null, albumData.getAlbumArtGraphic()]; - if (albumArt != null) { + FlxTween.cancelTweensOf(albumArt); albumArt.visible = false; - albumArt.anim.stop(); albumArt.destroy(); remove(albumArt); } - // I wasn't able to get replacing to work properly on an existing object, - // so I just throw the old one in the trash and make a new one. - albumArt = new FlxAtlasSprite(640, 360, Paths.animateAtlas('freeplay/albumRoll'), - { - OverrideGraphics: albumArtGraphics, - }); + // Paths.animateAtlas('freeplay/albumRoll'), + albumArt = FunkinSprite.create(1500, 360, albumData.getAlbumArtAssetKey()); + albumArt.setGraphicSize(262, 262); // Magic number for size IG albumArt.zIndex = 100; playIntro(); add(albumArt); - albumTitle.loadGraphic(Paths.image(albumData.getAlbumTitleAssetKey())); + applyExitMovers(); + + if (Assets.exists(Paths.image(albumData.getAlbumTitleAssetKey()))) + { + albumTitle.loadGraphic(Paths.image(albumData.getAlbumTitleAssetKey())); + } + else + { + albumTitle.visible = false; + } refresh(); } @@ -104,8 +113,19 @@ 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):Void { + if (exitMovers == null) + { + exitMovers = _exitMovers; + } + else + { + _exitMovers = exitMovers; + } + + if (exitMovers == null) return; + exitMovers.set([albumArt], { x: FlxG.width, @@ -141,10 +161,12 @@ class AlbumRoll extends FlxSpriteGroup public function playIntro():Void { albumArt.visible = true; - albumArt.anim.play(''); - albumArt.anim.onComplete = function() { - albumArt.anim.pause(); - }; + FlxTween.tween(albumArt, {x: 950, y: 320, angle: -340}, 0.5, {ease: FlxEase.quintOut}); + + albumTitle.visible = false; + new FlxTimer().start(0.75, function(_) { + showTitle(); + }); } public function setDifficultyStars(?difficulty:Int):Void @@ -154,9 +176,6 @@ class AlbumRoll extends FlxSpriteGroup difficultyStars.difficulty = difficulty; } - /** - * Make the album title graphic visible. - */ public function showTitle():Void { albumTitle.visible = true; diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 8d7ecb556..fc11eec28 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -465,7 +465,7 @@ class FreeplayState extends MusicBeatSubState albumRoll.playIntro(); - new FlxTimer().start(1, function(_) { + new FlxTimer().start(0.75, function(_) { albumRoll.showTitle(); }); @@ -861,16 +861,6 @@ class FreeplayState extends MusicBeatSubState changeDiff(1); } - // TODO: DEBUG REMOVE THIS - if (FlxG.keys.justPressed.P) - { - var newParams:FreeplayStateParams = - { - character: currentCharacter == 'bf' ? 'pico' : 'bf', - }; - openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.freeplay.FreeplayState(newParams, sticker))); - } - if (controls.BACK && !typing.hasFocus) { FlxTween.globalManager.clear(); diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 60a6defdd..9ce110c73 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -1,37 +1,33 @@ package funkin.ui.story; -import funkin.ui.mainmenu.MainMenuState; -import funkin.save.Save; -import funkin.save.Save.SaveScoreData; -import openfl.utils.Assets; import flixel.addons.transition.FlxTransitionableState; import flixel.FlxSprite; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.text.FlxText; -import flixel.addons.transition.FlxTransitionableState; import flixel.tweens.FlxEase; -import funkin.graphics.FunkinSprite; -import funkin.ui.MusicBeatState; import flixel.tweens.FlxTween; import flixel.util.FlxColor; import flixel.util.FlxTimer; -import funkin.data.level.LevelRegistry; import funkin.audio.FunkinSound; +import funkin.data.level.LevelRegistry; +import funkin.data.song.SongRegistry; +import funkin.graphics.FunkinSprite; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEventDispatcher; -import funkin.play.PlayState; import funkin.play.PlayStatePlaylist; -import funkin.ui.mainmenu.MainMenuState; import funkin.play.song.Song; -import funkin.data.song.SongData.SongMusicData; -import funkin.data.song.SongRegistry; -import funkin.util.MathUtil; +import funkin.save.Save; +import funkin.save.Save.SaveScoreData; +import funkin.ui.mainmenu.MainMenuState; +import funkin.ui.MusicBeatState; import funkin.ui.transition.LoadingState; import funkin.ui.transition.StickerSubState; +import funkin.util.MathUtil; +import openfl.utils.Assets; class StoryMenuState extends MusicBeatState { - static final DEFAULT_BACKGROUND_COLOR:FlxColor = FlxColor.fromString("#F9CF51"); + static final DEFAULT_BACKGROUND_COLOR:FlxColor = FlxColor.fromString('#F9CF51'); static final BACKGROUND_HEIGHT:Int = 400; var currentDifficultyId:String = 'normal'; @@ -166,25 +162,25 @@ class StoryMenuState extends MusicBeatState updateProps(); tracklistText = new FlxText(FlxG.width * 0.05, levelBackground.x + levelBackground.height + 100, 0, "Tracks", 32); - tracklistText.setFormat("VCR OSD Mono", 32); + tracklistText.setFormat('VCR OSD Mono', 32); tracklistText.alignment = CENTER; - tracklistText.color = 0xFFe55777; + tracklistText.color = 0xFFE55777; add(tracklistText); scoreText = new FlxText(10, 10, 0, 'HIGH SCORE: 42069420'); - scoreText.setFormat("VCR OSD Mono", 32); + scoreText.setFormat('VCR OSD Mono', 32); scoreText.zIndex = 1000; add(scoreText); modeText = new FlxText(10, 10, 0, 'Base Game Levels [TAB to switch]'); - modeText.setFormat("VCR OSD Mono", 32); + modeText.setFormat('VCR OSD Mono', 32); modeText.screenCenter(X); modeText.visible = hasModdedLevels(); modeText.zIndex = 1000; add(modeText); levelTitleText = new FlxText(FlxG.width * 0.7, 10, 0, 'LEVEL 1'); - levelTitleText.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT); + levelTitleText.setFormat('VCR OSD Mono', 32, FlxColor.WHITE, RIGHT); levelTitleText.alpha = 0.7; levelTitleText.zIndex = 1000; add(levelTitleText); @@ -217,7 +213,7 @@ class StoryMenuState extends MusicBeatState #if discord_rpc // Updating Discord Rich Presence - DiscordClient.changePresence("In the Menus", null); + DiscordClient.changePresence('In the Menus', null); #end } @@ -307,7 +303,7 @@ class StoryMenuState extends MusicBeatState changeDifficulty(0); } - override function update(elapsed:Float) + override function update(elapsed:Float):Void { Conductor.instance.update(); @@ -552,10 +548,13 @@ class StoryMenuState extends MusicBeatState FlxTransitionableState.skipNextTransIn = false; FlxTransitionableState.skipNextTransOut = false; + var targetVariation:String = targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty); + LoadingState.loadPlayState( { targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty, + targetVariation: targetVariation }, true); }); } From 9db9f4492452544e021832313897fef9a26da763 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 21 Mar 2024 00:38:59 -0400 Subject: [PATCH 18/75] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 4e88fb2a5..75ca95883 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 4e88fb2a50b284d92404af6afc95b9840d3cda8d +Subproject commit 75ca95883342ecc43ea0c8560b49cb736fd88fb2 From 038a92c3147582fc693be65b67a774842ac788eb Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 21 Mar 2024 20:04:10 -0400 Subject: [PATCH 19/75] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 86927859c..4ce901125 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 86927859c8e73a9a0b44738a4b50ec97f38444e0 +Subproject commit 4ce90112505dd69a9943d7912944208f752c5206 From d96ea5e89520ac90ea5553b9bfa729b2c814b068 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 21 Mar 2024 18:51:18 -0700 Subject: [PATCH 20/75] submod update --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 26e2b2c44..907015a5c 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 26e2b2c4419f6a39b4e7641f0f824117c03eb0b6 +Subproject commit 907015a5cd8617d0febccd4a5237b2a25ff1df61 From 5e0de6d1ce9dcf1a9785b9b2b23b2e73dc008a9e Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 21 Mar 2024 23:57:26 -0400 Subject: [PATCH 21/75] Fix some issues with events unintentionally sharing data after being edited via the toolbox. --- source/funkin/data/song/SongData.hx | 2 +- source/funkin/ui/debug/charting/ChartEditorState.hx | 9 +++++++++ .../debug/charting/commands/SetItemSelectionCommand.hx | 7 ++++++- .../charting/components/ChartEditorEventSprite.hx | 10 ++++++++-- .../charting/toolboxes/ChartEditorEventDataToolbox.hx | 7 ++++--- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx index 938859ff2..26380947a 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -706,7 +706,7 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR this = new SongEventDataRaw(time, eventKind, value); } - public inline function valueAsStruct(?defaultKey:String = "key"):Dynamic + public function valueAsStruct(?defaultKey:String = "key"):Dynamic { if (this.value == null) return {}; if (Std.isOfType(this.value, Array)) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index c59a5abdb..bdc0d311e 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -878,6 +878,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState */ var noteDisplayDirty:Bool = true; + var noteTooltipsDirty:Bool = true; + /** * Whether the selected charactesr have been modified and the health icons need to be updated. */ @@ -1541,6 +1543,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // Make sure view is updated when the variation changes. noteDisplayDirty = true; notePreviewDirty = true; + noteTooltipsDirty = true; notePreviewViewportBoundsDirty = true; switchToCurrentInstrumental(); @@ -1562,6 +1565,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // Make sure view is updated when the difficulty changes. noteDisplayDirty = true; notePreviewDirty = true; + noteTooltipsDirty = true; notePreviewViewportBoundsDirty = true; // Make sure the difficulty we selected is in the list of difficulties. @@ -3663,8 +3667,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState selectionSquare.width = eventSprite.width; selectionSquare.height = eventSprite.height; } + + // Additional cleanup on notes. + if (noteTooltipsDirty) eventSprite.updateTooltipText(); } + noteTooltipsDirty = false; + // Sort the notes DESCENDING. This keeps the sustain behind the associated note. renderedNotes.sort(FlxSort.byY, FlxSort.DESCENDING); // TODO: .group.insertionSort() diff --git a/source/funkin/ui/debug/charting/commands/SetItemSelectionCommand.hx b/source/funkin/ui/debug/charting/commands/SetItemSelectionCommand.hx index 46fcca87c..73cf80fa0 100644 --- a/source/funkin/ui/debug/charting/commands/SetItemSelectionCommand.hx +++ b/source/funkin/ui/debug/charting/commands/SetItemSelectionCommand.hx @@ -51,7 +51,12 @@ class SetItemSelectionCommand implements ChartEditorCommand } var eventData = eventSelected.valueAsStruct(defaultKey); - state.eventDataToPlace = eventData; + var eventDataClone = Reflect.copy(eventData); + + if (eventDataClone != null) + { + state.eventDataToPlace = eventDataClone; + } state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENT_DATA_LAYOUT); } diff --git a/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx index f680095d7..c996079bc 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx @@ -164,8 +164,7 @@ class ChartEditorEventSprite extends FlxSprite this.eventData = value; // Update the position to match the note data. updateEventPosition(); - // Update the tooltip text. - this.tooltip.tipData = {text: this.eventData.buildTooltip()}; + updateTooltipText(); return this.eventData; } } @@ -188,6 +187,13 @@ class ChartEditorEventSprite extends FlxSprite this.updateTooltipPosition(); } + public function updateTooltipText():Void + { + if (this.eventData == null) return; + if (this.isGhost) return; + this.tooltip.tipData = {text: this.eventData.buildTooltip()}; + } + public function updateTooltipPosition():Void { // No tooltip for ghost sprites. diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx index 50b341272..f0949846d 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx @@ -258,14 +258,15 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox // Edit the event data of any existing events. if (!_initializing && chartEditorState.currentEventSelection.length > 0) { - for (event in chartEditorState.currentEventSelection) + for (songEvent in chartEditorState.currentEventSelection) { - event.eventKind = chartEditorState.eventKindToPlace; - event.value = chartEditorState.eventDataToPlace; + songEvent.eventKind = chartEditorState.eventKindToPlace; + songEvent.value = Reflect.copy(chartEditorState.eventDataToPlace); } chartEditorState.saveDataDirty = true; chartEditorState.noteDisplayDirty = true; chartEditorState.notePreviewDirty = true; + chartEditorState.noteTooltipsDirty = true; } } } From 433d4968da1245dfc6e5a132a1bcb41ed8b7d7dc Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 21 Mar 2024 23:32:07 -0700 Subject: [PATCH 22/75] change to elasticOut --- source/funkin/ui/freeplay/AlbumRoll.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/freeplay/AlbumRoll.hx b/source/funkin/ui/freeplay/AlbumRoll.hx index 507e0b662..05bf01b76 100644 --- a/source/funkin/ui/freeplay/AlbumRoll.hx +++ b/source/funkin/ui/freeplay/AlbumRoll.hx @@ -161,7 +161,7 @@ class AlbumRoll extends FlxSpriteGroup public function playIntro():Void { albumArt.visible = true; - FlxTween.tween(albumArt, {x: 950, y: 320, angle: -340}, 0.5, {ease: FlxEase.quintOut}); + FlxTween.tween(albumArt, {x: 950, y: 320, angle: -340}, 0.5, {ease: FlxEase.elasticOut}); albumTitle.visible = false; new FlxTimer().start(0.75, function(_) { From 80c938af4f704074a5e9c6a61f1410a772b7b6b6 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 21 Mar 2024 23:34:03 -0700 Subject: [PATCH 23/75] fix for the album roll to come in only once bf hand hits --- source/funkin/ui/freeplay/AlbumRoll.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/freeplay/AlbumRoll.hx b/source/funkin/ui/freeplay/AlbumRoll.hx index 05bf01b76..a1e63c9a1 100644 --- a/source/funkin/ui/freeplay/AlbumRoll.hx +++ b/source/funkin/ui/freeplay/AlbumRoll.hx @@ -87,7 +87,7 @@ class AlbumRoll extends FlxSpriteGroup albumArt.setGraphicSize(262, 262); // Magic number for size IG albumArt.zIndex = 100; - playIntro(); + // playIntro(); add(albumArt); applyExitMovers(); From 5b5eb6f109cf17bb2dec799302b9bd30bfb42b4f Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Thu, 21 Mar 2024 23:35:49 -0700 Subject: [PATCH 24/75] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 75ca95883..203bb6024 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 75ca95883342ecc43ea0c8560b49cb736fd88fb2 +Subproject commit 203bb60249e0a419d473eb6dc1763e62e29ee7fd From d50ff73d3ce42ec4b4d11d0662402f81c039f1b3 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 22 Mar 2024 02:47:21 -0400 Subject: [PATCH 25/75] Registries now get added to the Flixel console (and get lazy instantiated so they don't start before the game does) --- source/funkin/data/BaseRegistry.hx | 7 +++++++ source/funkin/data/dialogue/ConversationRegistry.hx | 9 ++++++++- source/funkin/data/dialogue/DialogueBoxRegistry.hx | 9 ++++++++- source/funkin/data/dialogue/SpeakerRegistry.hx | 9 ++++++++- source/funkin/data/level/LevelRegistry.hx | 9 ++++++++- source/funkin/data/notestyle/NoteStyleRegistry.hx | 9 ++++++++- source/funkin/data/song/SongRegistry.hx | 13 ++++++++++--- source/funkin/data/stage/StageRegistry.hx | 9 ++++++++- 8 files changed, 65 insertions(+), 9 deletions(-) diff --git a/source/funkin/data/BaseRegistry.hx b/source/funkin/data/BaseRegistry.hx index ad028fa94..7419d9425 100644 --- a/source/funkin/data/BaseRegistry.hx +++ b/source/funkin/data/BaseRegistry.hx @@ -55,6 +55,13 @@ abstract class BaseRegistry & Constructible(); this.scriptedEntryIds = []; + + // Lazy initialization of singletons should let this get called, + // but we have this check just in case. + if (FlxG.game != null) + { + FlxG.console.registerObject('registry$registryId', this); + } } /** diff --git a/source/funkin/data/dialogue/ConversationRegistry.hx b/source/funkin/data/dialogue/ConversationRegistry.hx index 9186ef786..4a8af2162 100644 --- a/source/funkin/data/dialogue/ConversationRegistry.hx +++ b/source/funkin/data/dialogue/ConversationRegistry.hx @@ -15,7 +15,14 @@ class ConversationRegistry extends BaseRegistry public static final CONVERSATION_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; - public static final instance:ConversationRegistry = new ConversationRegistry(); + public static var instance(get, never):ConversationRegistry; + static var _instance:Null = null; + + static function get_instance():ConversationRegistry + { + if (_instance == null) _instance = new ConversationRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/data/dialogue/DialogueBoxRegistry.hx b/source/funkin/data/dialogue/DialogueBoxRegistry.hx index 87205d96c..d07ea6da2 100644 --- a/source/funkin/data/dialogue/DialogueBoxRegistry.hx +++ b/source/funkin/data/dialogue/DialogueBoxRegistry.hx @@ -15,7 +15,14 @@ class DialogueBoxRegistry extends BaseRegistry public static final DIALOGUEBOX_DATA_VERSION_RULE:thx.semver.VersionRule = "1.1.x"; - public static final instance:DialogueBoxRegistry = new DialogueBoxRegistry(); + public static var instance(get, never):DialogueBoxRegistry; + static var _instance:Null = null; + + static function get_instance():DialogueBoxRegistry + { + if (_instance == null) _instance = new DialogueBoxRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/data/dialogue/SpeakerRegistry.hx b/source/funkin/data/dialogue/SpeakerRegistry.hx index 6bd301dd7..c76c6d766 100644 --- a/source/funkin/data/dialogue/SpeakerRegistry.hx +++ b/source/funkin/data/dialogue/SpeakerRegistry.hx @@ -15,7 +15,14 @@ class SpeakerRegistry extends BaseRegistry public static final SPEAKER_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; - public static final instance:SpeakerRegistry = new SpeakerRegistry(); + public static var instance(get, never):SpeakerRegistry; + static var _instance:Null = null; + + static function get_instance():SpeakerRegistry + { + if (_instance == null) _instance = new SpeakerRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/data/level/LevelRegistry.hx b/source/funkin/data/level/LevelRegistry.hx index 96712cba5..e37e78d8c 100644 --- a/source/funkin/data/level/LevelRegistry.hx +++ b/source/funkin/data/level/LevelRegistry.hx @@ -15,7 +15,14 @@ class LevelRegistry extends BaseRegistry public static final LEVEL_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; - public static final instance:LevelRegistry = new LevelRegistry(); + public static var instance(get, never):LevelRegistry; + static var _instance:Null = null; + + static function get_instance():LevelRegistry + { + if (_instance == null) _instance = new LevelRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/data/notestyle/NoteStyleRegistry.hx b/source/funkin/data/notestyle/NoteStyleRegistry.hx index ffb9bf490..5e9fa9a3d 100644 --- a/source/funkin/data/notestyle/NoteStyleRegistry.hx +++ b/source/funkin/data/notestyle/NoteStyleRegistry.hx @@ -15,7 +15,14 @@ class NoteStyleRegistry extends BaseRegistry public static final NOTE_STYLE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; - public static final instance:NoteStyleRegistry = new NoteStyleRegistry(); + public static var instance(get, never):NoteStyleRegistry; + static var _instance:Null = null; + + static function get_instance():NoteStyleRegistry + { + if (_instance == null) _instance = new NoteStyleRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/data/song/SongRegistry.hx b/source/funkin/data/song/SongRegistry.hx index e7f74569c..d82e184a5 100644 --- a/source/funkin/data/song/SongRegistry.hx +++ b/source/funkin/data/song/SongRegistry.hx @@ -40,10 +40,17 @@ class SongRegistry extends BaseRegistry } /** - * TODO: What if there was a Singleton macro which created static functions - * that redirected to the instance? + * TODO: What if there was a Singleton macro which automatically created the property for us? */ - public static final instance:SongRegistry = new SongRegistry(); + public static var instance(get, never):SongRegistry; + + static var _instance:Null = null; + + static function get_instance():SongRegistry + { + if (_instance == null) _instance = new SongRegistry(); + return _instance; + } public function new() { diff --git a/source/funkin/data/stage/StageRegistry.hx b/source/funkin/data/stage/StageRegistry.hx index b78292e5b..13a5afb8d 100644 --- a/source/funkin/data/stage/StageRegistry.hx +++ b/source/funkin/data/stage/StageRegistry.hx @@ -15,7 +15,14 @@ class StageRegistry extends BaseRegistry public static final STAGE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; - public static final instance:StageRegistry = new StageRegistry(); + public static var instance(get, never):StageRegistry; + static var _instance:Null = null; + + static function get_instance():StageRegistry + { + if (_instance == null) _instance = new StageRegistry(); + return _instance; + } public function new() { From 4524b66170f1c0391c4a719bce7d090ed0415bc3 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 22 Mar 2024 02:47:43 -0400 Subject: [PATCH 26/75] Only show easy/normal/hard in story mode for now --- source/funkin/ui/story/StoryMenuState.hx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 9ce110c73..a5e133725 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -446,7 +446,11 @@ class StoryMenuState extends MusicBeatState */ function changeDifficulty(change:Int = 0):Void { - var difficultyList:Array = currentLevel.getDifficulties(); + // "For now, NO erect in story mode" -Dave + + var difficultyList:Array = Constants.DEFAULT_DIFFICULTY_LIST; + // Use this line to displays all difficulties + // var difficultyList:Array = currentLevel.getDifficulties(); var currentIndex:Int = difficultyList.indexOf(currentDifficultyId); currentIndex += change; From 2b477c9bd1183ba2a35ced7d22233c9c3f2d722e Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 22 Mar 2024 02:47:56 -0400 Subject: [PATCH 27/75] Make Conductor a lazy instance --- source/funkin/Conductor.hx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/source/funkin/Conductor.hx b/source/funkin/Conductor.hx index 05c23108f..383a49084 100644 --- a/source/funkin/Conductor.hx +++ b/source/funkin/Conductor.hx @@ -36,7 +36,15 @@ class Conductor * You can also do stuff like store a reference to the Conductor and pass it around or temporarily replace it, * or have a second Conductor running at the same time, or other weird stuff like that if you need to. */ - public static var instance:Conductor = new Conductor(); + public static var instance(get, never):Conductor; + + static var _instance:Null = null; + + static function get_instance():Conductor + { + if (_instance == null) _instance = new Conductor(); + return _instance; + } /** * Signal fired when the current Conductor instance advances to a new measure. @@ -505,6 +513,6 @@ class Conductor */ public static function reset():Void { - Conductor.instance = new Conductor(); + _instance = new Conductor(); } } From aea9213eeaca4686acec8f7cd6e5c3e8f4faf95a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 22 Mar 2024 03:04:33 -0400 Subject: [PATCH 28/75] Make sure the BPM label gets updated properly --- source/funkin/ui/debug/charting/ChartEditorState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index bdc0d311e..565488da3 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -4949,7 +4949,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState playbarNoteSnap.text = '1/${noteSnapQuant}'; playbarDifficulty.text = '${selectedDifficulty.toTitleCase()}'; - // playbarBPM.text = 'BPM: ${(Conductor.currentTimeChange?.bpm ?? 0.0)}'; + playbarBPM.text = 'BPM: ${(Conductor.instance.bpm ?? 0.0)}'; } function handlePlayhead():Void From c765249030db27e092a4a544c0f614c0ee306a8f Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 22 Mar 2024 03:04:52 -0400 Subject: [PATCH 29/75] Complain if the main metadata file specifies a variation (like erect) but the game can't find it. --- source/funkin/play/song/Song.hx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 567c388c7..b0b477ff4 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -139,7 +139,16 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry = fetchVariationMetadata(id, vari); - if (variMeta != null) _metadata.set(variMeta.variation, variMeta); + if (variMeta != null) + { + _metadata.set(variMeta.variation, variMeta); + trace(' Loaded variation: $vari'); + } + else + { + FlxG.log.warn('[SONG] Failed to load variation metadata (${id}:${vari}), is the path correct?'); + trace(' FAILED to load variation: $vari'); + } } } From eb5f853e2cf9df81045effd13f70851d8bed6104 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 22 Mar 2024 03:05:18 -0400 Subject: [PATCH 30/75] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 203bb6024..d6f82ec8b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 203bb60249e0a419d473eb6dc1763e62e29ee7fd +Subproject commit d6f82ec8b91823eee809d73e2c4744210a8e7e0b From 40d13619d363a7e47a23ff45adb6699b633b0629 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 22 Mar 2024 00:12:50 -0700 Subject: [PATCH 31/75] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 4ce901125..7cbe6ff4e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 4ce90112505dd69a9943d7912944208f752c5206 +Subproject commit 7cbe6ff4ed7d976e7c69d6677c4aa84988da0e8d From c9f4a0a86d92f2e30ef625bbd9fcc13107d1bd0f Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 22 Mar 2024 15:29:58 -0400 Subject: [PATCH 32/75] Update polymod to fix several script-related crashes --- hmm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hmm.json b/hmm.json index a75dee432..0dfe88ded 100644 --- a/hmm.json +++ b/hmm.json @@ -146,7 +146,7 @@ "name": "polymod", "type": "git", "dir": null, - "ref": "5547763a22858a1f10939e082de421d587c862bf", + "ref": "8553b800965f225bb14c7ab8f04bfa9cdec362ac", "url": "https://github.com/larsiusprime/polymod" }, { From 7d008e724765ae6e7f950fd6077cbc4e8aa4bbe7 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 22 Mar 2024 15:03:17 -0700 Subject: [PATCH 33/75] playstate camera fixie --- source/funkin/play/PlayState.hx | 1 + 1 file changed, 1 insertion(+) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index a5152e727..759ba4742 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1550,6 +1550,7 @@ class PlayState extends MusicBeatSubState public function resetCameraZoom():Void { + if (PlayState.instance.isMinimalMode) return; // Apply camera zoom level from stage data. defaultCameraZoom = currentStage.camZoom; currentCameraZoom = defaultCameraZoom; From 83e8865a24611d515265d1159360dd7171420f2a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 22 Mar 2024 20:29:35 -0400 Subject: [PATCH 34/75] Make the ZoomCamera events use sensible defaults. --- .../funkin/play/event/ZoomCameraSongEvent.hx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/source/funkin/play/event/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx index d1741a463..b913aebe7 100644 --- a/source/funkin/play/event/ZoomCameraSongEvent.hx +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -52,6 +52,11 @@ class ZoomCameraSongEvent extends SongEvent super('ZoomCamera'); } + static final DEFAULT_ZOOM:Float = 1.0; + static final DEFAULT_DURATION:Float = 4.0; + static final DEFAULT_MODE:String = 'direct'; + static final DEFAULT_EASE:String = 'linear'; + public override function handleEvent(data:SongEventData):Void { // Does nothing if there is no PlayState camera or stage. @@ -60,25 +65,20 @@ class ZoomCameraSongEvent extends SongEvent // Does nothing if we are minimal mode. if (PlayState.instance.isMinimalMode) return; - var zoom:Null = data.getFloat('zoom'); - if (zoom == null) zoom = 1.0; + var zoom:Float = data.getFloat('zoom') ?? DEFAULT_ZOOM; - var duration:Null = data.getFloat('duration'); - if (duration == null) duration = 4.0; + var duration:Float = data.getFloat('duration') ?? DEFAULT_DURATION; - var mode:Null = data.getString('mode'); - if (mode == null) mode = 'additive'; + var mode:String = data.getString('mode') ?? DEFAULT_MODE; + var isDirectMode:Bool = mode == 'direct'; - var ease:Null = data.getString('ease'); - if (ease == null) ease = 'linear'; - - var directMode:Bool = mode == 'direct'; + var ease:String = data.getString('ease') ?? DEFAULT_EASE; // If it's a string, check the value. switch (ease) { case 'INSTANT': - PlayState.instance.tweenCameraZoom(zoom, 0, directMode); + PlayState.instance.tweenCameraZoom(zoom, 0, isDirectMode); default: var durSeconds = Conductor.instance.stepLengthMs * duration / 1000; @@ -89,7 +89,7 @@ class ZoomCameraSongEvent extends SongEvent return; } - PlayState.instance.tweenCameraZoom(zoom, durSeconds, directMode, easeFunction); + PlayState.instance.tweenCameraZoom(zoom, durSeconds, isDirectMode, easeFunction); } } @@ -130,7 +130,7 @@ class ZoomCameraSongEvent extends SongEvent { name: 'mode', title: 'Mode', - defaultValue: 'additive', + defaultValue: 'direct', type: SongEventFieldType.ENUM, keys: ['Additive' => 'additive', 'Direct' => 'direct'] }, From 28088146eba83d0cb48e91951faf8a8c09d4522d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 22 Mar 2024 22:17:34 -0400 Subject: [PATCH 35/75] Fix bug where Pico dadbattle was playing over normal dadbattle --- assets | 2 +- source/funkin/play/song/Song.hx | 11 ++++++++--- source/funkin/util/Constants.hx | 5 +++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/assets b/assets index 7cbe6ff4e..9a5767d04 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 7cbe6ff4ed7d976e7c69d6677c4aa84988da0e8d +Subproject commit 9a5767d04203a9cb9857608761646bb6a52b1e13 diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 567c388c7..6c4f27420 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -374,12 +374,17 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry):Null { - if (variations == null) possibleVariations = variations; + if (possibleVariations == null) + { + possibleVariations = variations; + possibleVariations.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_VARIATION_LIST)); + } if (diffId == null) diffId = listDifficulties(null, possibleVariations)[0]; - for (variation in variations) + for (variationId in possibleVariations) { - if (difficulties.exists('$diffId-$variation')) return variation; + var variationSuffix = (variationId != Constants.DEFAULT_VARIATION) ? '-$variationId' : ''; + if (difficulties.exists('$diffId$variationSuffix')) return variationId; } return null; diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index c9b99ed46..c7bc03139 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -157,6 +157,11 @@ class Constants */ public static final DEFAULT_VARIATION:String = 'default'; + /** + * Standard variations used by the game. + */ + public static final DEFAULT_VARIATION_LIST:Array = ['default', 'erect', 'pico']; + /** * The default intensity for camera zooms. */ From 01e83d4713144f5bbe607879861b48cd2d404f4c Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 22 Mar 2024 20:21:09 -0700 Subject: [PATCH 36/75] the funkin crew presents --- source/funkin/ui/title/TitleState.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/ui/title/TitleState.hx b/source/funkin/ui/title/TitleState.hx index 9dca759be..5cc7b8cc1 100644 --- a/source/funkin/ui/title/TitleState.hx +++ b/source/funkin/ui/title/TitleState.hx @@ -453,9 +453,9 @@ class TitleState extends MusicBeatState switch (i + 1) { case 1: - createCoolText(['ninjamuffin99', 'phantomArcade', 'kawaisprite', 'evilsk8r']); + createCoolText(['The', 'Funkin Crew Inc']); case 3: - addMoreText('present'); + addMoreText('presents'); case 4: deleteCoolText(); case 5: From 471b015d1ad8a4906be231fb3c75151df50a753c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 22 Mar 2024 23:46:03 -0400 Subject: [PATCH 37/75] Add some FlxSignals and showVideo/hideVideo --- source/funkin/play/cutscene/VideoCutscene.hx | 71 ++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index ff56e0919..7e500f05d 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -28,6 +28,31 @@ class VideoCutscene static var vid:FlxVideoSprite; #end + /** + * Called when the video is started. + */ + public static final onVideoStarted:FlxSignal = new FlxSignal(); + + /** + * Called if the video is paused. + */ + public static final onVideoPaused:FlxSignal = new FlxSignal(); + + /** + * Called if the video is resumed. + */ + public static final onVideoResumed:FlxSignal = new FlxSignal(); + + /** + * Called if the video is restarted. onVideoStarted is not called. + */ + public static final onVideoRestarted:FlxSignal = new FlxSignal(); + + /** + * Called when the video is ended or skipped. + */ + public static final onVideoEnded:FlxSignal = new FlxSignal(); + /** * Play a video cutscene. * TODO: Currently this is hardcoded to start the countdown after the video is done. @@ -94,6 +119,8 @@ class VideoCutscene PlayState.instance.add(vid); PlayState.instance.refresh(); + + onVideoStarted.dispatch(); } else { @@ -129,6 +156,8 @@ class VideoCutscene vid.y = 0; // vid.scale.set(0.5, 0.5); }); + + onVideoStarted.dispatch(); } else { @@ -143,6 +172,7 @@ class VideoCutscene if (vid != null) { vid.restartVideo(); + onVideoRestarted.dispatch(); } #end @@ -156,6 +186,8 @@ class VideoCutscene // Resume the video if it was paused. vid.resume(); } + + onVideoRestarted.dispatch(); } #end } @@ -166,6 +198,7 @@ class VideoCutscene if (vid != null) { vid.pauseVideo(); + onVideoPaused.dispatch(); } #end @@ -173,6 +206,41 @@ class VideoCutscene if (vid != null) { vid.pause(); + onVideoPaused.dispatch(); + } + #end + } + + public static function hideVideo():Void + { + #if html5 + if (vid != null) + { + vid.visible = false; + } + #end + + #if hxCodec + if (vid != null) + { + vid.visible = false; + } + #end + } + + public static function showVideo():Void + { + #if html5 + if (vid != null) + { + vid.visible = true; + } + #end + + #if hxCodec + if (vid != null) + { + vid.visible = true; } #end } @@ -183,6 +251,7 @@ class VideoCutscene if (vid != null) { vid.resumeVideo(); + onVideoResumed.dispatch(); } #end @@ -190,6 +259,7 @@ class VideoCutscene if (vid != null) { vid.resume(); + onVideoResumed.dispatch(); } #end } @@ -240,6 +310,7 @@ class VideoCutscene { ease: FlxEase.quadInOut, onComplete: function(twn:FlxTween) { + onVideoEnded.dispatch(); onCutsceneFinish(cutsceneType); } }); From e4a9d25ac01bf65b261076ffad753414967195ef Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 22 Mar 2024 23:53:29 -0400 Subject: [PATCH 38/75] Syntax fix --- source/funkin/play/cutscene/VideoCutscene.hx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx index 7e500f05d..3da51185f 100644 --- a/source/funkin/play/cutscene/VideoCutscene.hx +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -5,6 +5,7 @@ import flixel.FlxSprite; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; import flixel.util.FlxColor; +import flixel.util.FlxSignal; import flixel.util.FlxTimer; #if html5 import funkin.graphics.video.FlxVideo; @@ -217,6 +218,7 @@ class VideoCutscene if (vid != null) { vid.visible = false; + blackScreen.visible = false; } #end @@ -224,6 +226,7 @@ class VideoCutscene if (vid != null) { vid.visible = false; + blackScreen.visible = false; } #end } @@ -234,6 +237,7 @@ class VideoCutscene if (vid != null) { vid.visible = true; + blackScreen.visible = false; } #end @@ -241,6 +245,7 @@ class VideoCutscene if (vid != null) { vid.visible = true; + blackScreen.visible = false; } #end } From 8bbb528a9e1326bf01d9a235cadcbc16f0b85b68 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 23 Mar 2024 13:44:27 -0400 Subject: [PATCH 39/75] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 526743915..8b0aa1d56 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 52674391511577300cdb8c08df293ea72099aa82 +Subproject commit 8b0aa1d5633f2f2e7f0eb03498ee71d19cc99564 From 6407770ed6c17b510924fbb019c118608808fb7d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 23 Mar 2024 17:50:48 -0400 Subject: [PATCH 40/75] Rework every class to use FunkinSound. --- source/funkin/audio/FunkinSound.hx | 108 ++++++++++++++++-- source/funkin/audio/SoundGroup.hx | 1 - source/funkin/modding/PolymodHandler.hx | 1 - source/funkin/play/Countdown.hx | 3 +- source/funkin/play/GameOverSubState.hx | 5 +- source/funkin/play/GitarooPause.hx | 6 +- source/funkin/play/PauseSubState.hx | 2 +- source/funkin/play/PlayState.hx | 65 +++++++---- .../funkin/play/components/ComboMilestone.hx | 3 +- .../funkin/play/cutscene/VanillaCutscenes.hx | 3 +- .../play/cutscene/dialogue/Conversation.hx | 36 +++--- source/funkin/ui/Alphabet.hx | 3 +- source/funkin/ui/MenuList.hx | 5 +- source/funkin/ui/MusicBeatState.hx | 3 + source/funkin/ui/debug/DebugMenuSubState.hx | 3 +- .../ui/debug/anim/DebugBoundingState.hx | 3 +- .../ui/debug/charting/ChartEditorState.hx | 7 +- .../handlers/ChartEditorAudioHandler.hx | 4 +- .../funkin/ui/debug/latency/LatencyState.hx | 7 -- .../ui/debug/stage/StageBuilderState.hx | 2 - source/funkin/ui/freeplay/DJBoyfriend.hx | 6 +- source/funkin/ui/freeplay/FreeplayState.hx | 21 +++- source/funkin/ui/mainmenu/MainMenuState.hx | 8 +- source/funkin/ui/options/OptionsState.hx | 4 +- source/funkin/ui/story/StoryMenuState.hx | 12 +- source/funkin/ui/title/AttractState.hx | 6 +- source/funkin/ui/title/TitleState.hx | 16 ++- source/funkin/ui/transition/LoadingState.hx | 10 +- .../funkin/ui/transition/StickerSubState.hx | 5 +- source/funkin/util/MathUtil.hx | 1 + .../funkin/util/plugins/ScreenshotPlugin.hx | 2 +- 31 files changed, 252 insertions(+), 109 deletions(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index c64240909..52bd1c7e8 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -8,6 +8,7 @@ import funkin.util.tools.ICloneable; import funkin.data.song.SongData.SongMusicData; import funkin.data.song.SongRegistry; import funkin.audio.waveform.WaveformData; +import openfl.media.SoundMixer; import funkin.audio.waveform.WaveformDataParser; import flixel.math.FlxMath; import openfl.Assets; @@ -18,6 +19,7 @@ import openfl.utils.AssetType; /** * A FlxSound which adds additional functionality: * - Delayed playback via negative song position. + * - Easy functions for immediate playback and recycling. */ @:nullSafety class FunkinSound extends FlxSound implements ICloneable @@ -286,15 +288,27 @@ class FunkinSound extends FlxSound implements ICloneable * Creates a new `FunkinSound` object and loads it as the current music track. * * @param key The key of the music you want to play. Music should be at `music//.ogg`. - * @param overrideExisting Whether to override music if it is already playing. - * @param mapTimeChanges Whether to check for `SongMusicData` to update the Conductor with. + * @param params A set of additional optional parameters. * Data should be at `music//-metadata.json`. */ - public static function playMusic(key:String, overrideExisting:Bool = false, mapTimeChanges:Bool = true):Void + public static function playMusic(key:String, params:FunkinSoundPlayMusicParams):Void { - if (!overrideExisting && FlxG.sound.music?.playing) return; + if (!(params.overrideExisting ?? false) && (FlxG.sound.music?.exists ?? false) && FlxG.sound.music.playing) return; - if (mapTimeChanges) + if (!(params.restartTrack ?? false) && FlxG.sound.music?.playing) + { + if (FlxG.sound.music != null && Std.isOfType(FlxG.sound.music, FunkinSound)) + { + var existingSound:FunkinSound = cast FlxG.sound.music; + // Stop here if we would play a matching music track. + if (existingSound._label == Paths.music('$key/$key')) + { + return; + } + } + } + + if (params?.mapTimeChanges ?? true) { var songMusicData:Null = SongRegistry.instance.parseMusicData(key); // Will fall back and return null if the metadata doesn't exist or can't be parsed. @@ -308,7 +322,14 @@ class FunkinSound extends FlxSound implements ICloneable } } - FlxG.sound.music = FunkinSound.load(Paths.music('$key/$key')); + if (FlxG.sound.music != null) + { + FlxG.sound.music.stop(); + FlxG.sound.music.kill(); + } + + var music = FunkinSound.load(Paths.music('$key/$key'), params?.startingVolume ?? 1.0, true, false, true); + if (music != null) FlxG.sound.music = music; // Prevent repeat update() and onFocus() calls. FlxG.sound.list.remove(FlxG.sound.music); @@ -326,11 +347,18 @@ class FunkinSound extends FlxSound implements ICloneable * @param autoPlay Whether to play the sound immediately or wait for a `play()` call. * @param onComplete Called when the sound finished playing. * @param onLoad Called when the sound finished loading. Called immediately for succesfully loaded embedded sounds. - * @return A `FunkinSound` object. + * @return A `FunkinSound` object, or `null` if the sound could not be loaded. */ public static function load(embeddedSound:FlxSoundAsset, volume:Float = 1.0, looped:Bool = false, autoDestroy:Bool = false, autoPlay:Bool = false, - ?onComplete:Void->Void, ?onLoad:Void->Void):FunkinSound + ?onComplete:Void->Void, ?onLoad:Void->Void):Null { + @:privateAccess + if (SoundMixer.__soundChannels.length >= SoundMixer.MAX_ACTIVE_CHANNELS) + { + FlxG.log.error('FunkinSound could not play sound, channels exhausted! Found ${SoundMixer.__soundChannels.length} active sound channels.'); + return null; + } + var sound:FunkinSound = pool.recycle(construct); // Load the sound. @@ -341,6 +369,10 @@ class FunkinSound extends FlxSound implements ICloneable { sound._label = embeddedSound; } + else + { + sound._label = 'unknown'; + } sound.volume = volume; sound.group = FlxG.sound.defaultSoundGroup; @@ -355,6 +387,36 @@ class FunkinSound extends FlxSound implements ICloneable return sound; } + public override function destroy():Void + { + // trace('[FunkinSound] Destroying sound "${this._label}"'); + super.destroy(); + } + + /** + * Play a sound effect once, then destroy it. + * @param key + * @param volume + * @return static function construct():FunkinSound + */ + public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):Void + { + var result = FunkinSound.load(key, volume, false, true, true, onComplete, onLoad); + } + + /** + * Stop all sounds in the pool and allow them to be recycled. + */ + public static function stopAllAudio(musicToo:Bool = false):Void + { + for (sound in pool) + { + if (sound == null) continue; + if (!musicToo && sound == FlxG.sound.music) continue; + sound.destroy(); + } + } + static function construct():FunkinSound { var sound:FunkinSound = new FunkinSound(); @@ -365,3 +427,33 @@ class FunkinSound extends FlxSound implements ICloneable return sound; } } + +/** + * Additional parameters for `FunkinSound.playMusic()` + */ +typedef FunkinSoundPlayMusicParams = +{ + /** + * The volume you want the music to start at. + * @default `1.0` + */ + var ?startingVolume:Float; + + /** + * Whether to override music if a different track is already playing. + * @default `false` + */ + var ?overrideExisting:Bool; + + /** + * Whether to override music if the same track is already playing. + * @default `false` + */ + var ?restartTrack:Bool; + + /** + * Whether to check for `SongMusicData` to update the Conductor with. + * @default `true` + */ + var ?mapTimeChanges:Bool; +} diff --git a/source/funkin/audio/SoundGroup.hx b/source/funkin/audio/SoundGroup.hx index a26537c2a..9a754049b 100644 --- a/source/funkin/audio/SoundGroup.hx +++ b/source/funkin/audio/SoundGroup.hx @@ -1,7 +1,6 @@ package funkin.audio; import flixel.group.FlxGroup.FlxTypedGroup; -import flixel.sound.FlxSound; import funkin.audio.FunkinSound; import flixel.tweens.FlxTween; diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index a88476d4d..78f660d3f 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -209,7 +209,6 @@ class PolymodHandler // Add import aliases for certain classes. // NOTE: Scripted classes are automatically aliased to their parent class. Polymod.addImportAlias('flixel.math.FlxPoint', flixel.math.FlxPoint.FlxBasePoint); - Polymod.addImportAlias('flixel.system.FlxSound', flixel.sound.FlxSound); // Add blacklisting for prohibited classes and packages. // `polymod.*` diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 747565100..10636afdf 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.audio.FunkinSound; class Countdown { @@ -282,7 +283,7 @@ class Countdown if (soundPath == null) return; - FlxG.sound.play(Paths.sound(soundPath), Constants.COUNTDOWN_VOLUME); + FunkinSound.playOnce(Paths.sound(soundPath), Constants.COUNTDOWN_VOLUME); } public static function decrement(step:CountdownStep):CountdownStep diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index ec3282164..a1796e912 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -3,7 +3,6 @@ package funkin.play; import flixel.FlxG; import flixel.FlxObject; import flixel.FlxSprite; -import flixel.sound.FlxSound; import funkin.audio.FunkinSound; import flixel.util.FlxColor; import flixel.util.FlxTimer; @@ -418,7 +417,7 @@ class GameOverSubState extends MusicBeatSubState blueballed = true; if (Assets.exists(Paths.sound('gameplay/gameover/fnf_loss_sfx' + blueBallSuffix))) { - FlxG.sound.play(Paths.sound('gameplay/gameover/fnf_loss_sfx' + blueBallSuffix)); + FunkinSound.playOnce(Paths.sound('gameplay/gameover/fnf_loss_sfx' + blueBallSuffix)); } else { @@ -438,7 +437,7 @@ class GameOverSubState extends MusicBeatSubState if (!Preferences.naughtyness) randomCensor = [1, 3, 8, 13, 17, 21]; - FlxG.sound.play(Paths.sound('jeffGameover/jeffGameover-' + FlxG.random.int(1, 25, randomCensor)), 1, false, null, true, function() { + FunkinSound.playOnce(Paths.sound('jeffGameover/jeffGameover-' + FlxG.random.int(1, 25, randomCensor)), function() { // Once the quote ends, fade in the game over music. if (!isEnding && gameOverMusic != null) { diff --git a/source/funkin/play/GitarooPause.hx b/source/funkin/play/GitarooPause.hx index eae56a9c3..6abe78cd8 100644 --- a/source/funkin/play/GitarooPause.hx +++ b/source/funkin/play/GitarooPause.hx @@ -26,7 +26,11 @@ class GitarooPause extends MusicBeatState override function create():Void { - if (FlxG.sound.music != null) FlxG.sound.music.stop(); + if (FlxG.sound.music != null) + { + FlxG.sound.music.destroy(); + FlxG.sound.music = null; + } var bg:FunkinSprite = FunkinSprite.create('pauseAlt/pauseBG'); add(bg); diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index 03681ce13..f16aa00d8 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -366,7 +366,7 @@ class PauseSubState extends MusicBeatSubState */ function changeSelection(change:Int = 0):Void { - FlxG.sound.play(Paths.sound('scrollMenu'), 0.4); + FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4); currentEntry += change; diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 12b290afd..8734d0c1d 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1,20 +1,15 @@ package funkin.play; -import funkin.audio.FunkinSound; import flixel.addons.display.FlxPieDial; import flixel.addons.transition.FlxTransitionableState; import flixel.addons.transition.Transition; -import flixel.addons.transition.Transition; import flixel.FlxCamera; import flixel.FlxObject; import flixel.FlxState; -import funkin.graphics.FunkinSprite; import flixel.FlxSubState; -import funkin.graphics.FunkinSprite; import flixel.math.FlxMath; import flixel.math.FlxPoint; import flixel.math.FlxRect; -import funkin.graphics.FunkinSprite; import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; @@ -22,18 +17,19 @@ import flixel.ui.FlxBar; import flixel.util.FlxColor; import flixel.util.FlxTimer; import funkin.api.newgrounds.NGio; -import funkin.audio.VoicesGroup; +import funkin.audio.FunkinSound; import funkin.audio.VoicesGroup; import funkin.data.dialogue.ConversationRegistry; import funkin.data.event.SongEventRegistry; import funkin.data.notestyle.NoteStyleData; import funkin.data.notestyle.NoteStyleRegistry; -import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.song.SongData.SongCharacterData; import funkin.data.song.SongData.SongEventData; import funkin.data.song.SongData.SongNoteData; import funkin.data.song.SongRegistry; import funkin.data.stage.StageRegistry; +import funkin.graphics.FunkinCamera; +import funkin.graphics.FunkinSprite; import funkin.Highscore.Tallies; import funkin.input.PreciseInputManager; import funkin.modding.events.ScriptEvent; @@ -44,14 +40,11 @@ import funkin.play.components.ComboMilestone; import funkin.play.components.HealthIcon; import funkin.play.components.PopUpStuff; import funkin.play.cutscene.dialogue.Conversation; -import funkin.play.cutscene.dialogue.Conversation; import funkin.play.cutscene.VanillaCutscenes; import funkin.play.cutscene.VideoCutscene; import funkin.play.notes.NoteDirection; import funkin.play.notes.NoteSplash; import funkin.play.notes.NoteSprite; -import funkin.play.notes.NoteSprite; -import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.Strumline; import funkin.play.notes.SustainTrail; @@ -65,7 +58,6 @@ import funkin.ui.mainmenu.MainMenuState; import funkin.ui.MusicBeatSubState; import funkin.ui.options.PreferencesMenu; import funkin.ui.story.StoryMenuState; -import funkin.graphics.FunkinCamera; import funkin.ui.transition.LoadingState; import funkin.util.SerializerUtil; import haxe.Int64; @@ -1293,16 +1285,35 @@ class PlayState extends MusicBeatSubState currentStage = null; } - // Stop the instrumental. - if (FlxG.sound.music != null) + if (!overrideMusic) { - FlxG.sound.music.stop(); - } + // Stop the instrumental. + if (FlxG.sound.music != null) + { + FlxG.sound.music.destroy(); + FlxG.sound.music = null; + } - // Stop the vocals. - if (vocals != null && vocals.exists) + // Stop the vocals. + if (vocals != null && vocals.exists) + { + vocals.destroy(); + vocals = null; + } + } + else { - vocals.stop(); + // 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(); @@ -1899,6 +1910,12 @@ class PlayState extends MusicBeatSubState currentChart.playInst(1.0, false); } + if (FlxG.sound.music == null) + { + FlxG.log.error('PlayState failed to initialize instrumental!'); + return; + } + FlxG.sound.music.onComplete = endSong.bind(false); // 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. @@ -2428,7 +2445,7 @@ class PlayState extends MusicBeatSubState if (playSound) { vocals.playerVolume = 0; - FlxG.sound.play(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2)); + FunkinSound.playOnce(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.5, 0.6)); } } @@ -2483,7 +2500,7 @@ class PlayState extends MusicBeatSubState if (event.playSound) { vocals.playerVolume = 0; - FlxG.sound.play(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2)); + FunkinSound.playOnce(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2)); } } @@ -2766,7 +2783,11 @@ class PlayState extends MusicBeatSubState if (targetSongId == null) { - FunkinSound.playMusic('freakyMenu'); + FunkinSound.playMusic('freakyMenu', + { + overrideExisting: true, + restartTrack: false + }); // transIn = FlxTransitionableState.defaultTransIn; // transOut = FlxTransitionableState.defaultTransOut; @@ -2844,7 +2865,7 @@ class PlayState extends MusicBeatSubState camHUD.visible = false; isInCutscene = true; - FlxG.sound.play(Paths.sound('Lights_Shut_off'), function() { + FunkinSound.playOnce(Paths.sound('Lights_Shut_off'), function() { // no camFollow so it centers on horror tree var targetSong:Song = SongRegistry.instance.fetchEntry(targetSongId); LoadingState.loadPlayState( diff --git a/source/funkin/play/components/ComboMilestone.hx b/source/funkin/play/components/ComboMilestone.hx index 4119e45c2..22ce2d671 100644 --- a/source/funkin/play/components/ComboMilestone.hx +++ b/source/funkin/play/components/ComboMilestone.hx @@ -4,6 +4,7 @@ import flixel.FlxSprite; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; import flixel.util.FlxTimer; +import funkin.audio.FunkinSound; class ComboMilestone extends FlxTypedSpriteGroup { @@ -78,7 +79,7 @@ class ComboMilestone extends FlxTypedSpriteGroup function setupCombo(daCombo:Int) { - FlxG.sound.play(Paths.sound('comboSound')); + FunkinSound.playOnce(Paths.sound('comboSound')); wasComboSetup = true; var loopNum:Int = 0; diff --git a/source/funkin/play/cutscene/VanillaCutscenes.hx b/source/funkin/play/cutscene/VanillaCutscenes.hx index a332d0795..7a4349e6a 100644 --- a/source/funkin/play/cutscene/VanillaCutscenes.hx +++ b/source/funkin/play/cutscene/VanillaCutscenes.hx @@ -4,6 +4,7 @@ import flixel.FlxSprite; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; import flixel.util.FlxColor; +import funkin.audio.FunkinSound; import flixel.util.FlxTimer; /** @@ -40,7 +41,7 @@ class VanillaCutscenes FlxG.camera.zoom = 2.5; // Play the Sound effect. - FlxG.sound.play(Paths.sound('Lights_Turn_On'), function() { + FunkinSound.playOnce(Paths.sound('Lights_Turn_On'), function() { // Fade in the HUD. trace('SFX done...'); PlayState.instance.camHUD.visible = true; diff --git a/source/funkin/play/cutscene/dialogue/Conversation.hx b/source/funkin/play/cutscene/dialogue/Conversation.hx index f865f3b7b..c520c3e25 100644 --- a/source/funkin/play/cutscene/dialogue/Conversation.hx +++ b/source/funkin/play/cutscene/dialogue/Conversation.hx @@ -1,28 +1,28 @@ package funkin.play.cutscene.dialogue; -import funkin.data.IRegistryEntry; +import flixel.addons.display.FlxPieDial; import flixel.FlxSprite; import flixel.group.FlxSpriteGroup; -import flixel.util.FlxColor; -import funkin.graphics.FunkinSprite; -import flixel.tweens.FlxTween; import flixel.tweens.FlxEase; -import flixel.sound.FlxSound; -import funkin.util.SortUtil; +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; import flixel.util.FlxSort; -import funkin.modding.events.ScriptEvent; -import funkin.modding.IScriptedClass.IEventHandler; -import funkin.play.cutscene.dialogue.DialogueBox; -import funkin.modding.IScriptedClass.IDialogueScriptedClass; -import funkin.modding.events.ScriptEventDispatcher; -import flixel.addons.display.FlxPieDial; +import funkin.audio.FunkinSound; import funkin.data.dialogue.ConversationData; import funkin.data.dialogue.ConversationData.DialogueEntryData; import funkin.data.dialogue.ConversationRegistry; -import funkin.data.dialogue.SpeakerData; -import funkin.data.dialogue.SpeakerRegistry; import funkin.data.dialogue.DialogueBoxData; import funkin.data.dialogue.DialogueBoxRegistry; +import funkin.data.dialogue.SpeakerData; +import funkin.data.dialogue.SpeakerRegistry; +import funkin.data.IRegistryEntry; +import funkin.graphics.FunkinSprite; +import funkin.modding.events.ScriptEvent; +import funkin.modding.events.ScriptEventDispatcher; +import funkin.modding.IScriptedClass.IDialogueScriptedClass; +import funkin.modding.IScriptedClass.IEventHandler; +import funkin.play.cutscene.dialogue.DialogueBox; +import funkin.util.SortUtil; /** * A high-level handler for dialogue. @@ -90,7 +90,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl /** * AUDIO */ - var music:FlxSound; + var music:FunkinSound; /** * GRAPHICS @@ -129,8 +129,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl { if (_data.music == null) return; - music = new FlxSound().loadEmbedded(Paths.music(_data.music.asset), true, true); - music.volume = 0; + music = FunkinSound.load(Paths.music(_data.music.asset), 0.0, true, true, true); if (_data.music.fadeTime > 0.0) { @@ -140,9 +139,6 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass impl { music.volume = 1.0; } - - FlxG.sound.list.add(music); - music.play(); } public function pauseMusic():Void diff --git a/source/funkin/ui/Alphabet.hx b/source/funkin/ui/Alphabet.hx index 66b95f5b8..e0492bee5 100644 --- a/source/funkin/ui/Alphabet.hx +++ b/source/funkin/ui/Alphabet.hx @@ -5,6 +5,7 @@ import flixel.group.FlxSpriteGroup; import flixel.math.FlxMath; import flixel.util.FlxTimer; import funkin.util.MathUtil; +import funkin.audio.FunkinSound; /** * Loosley based on FlxTypeText lolol @@ -200,7 +201,7 @@ class Alphabet extends FlxSpriteGroup if (FlxG.random.bool(40)) { var daSound:String = "GF_"; - FlxG.sound.play(Paths.soundRandom(daSound, 1, 4)); + FunkinSound.playOnce(Paths.soundRandom(daSound, 1, 4)); } add(letter); diff --git a/source/funkin/ui/MenuList.hx b/source/funkin/ui/MenuList.hx index 3ffe3c330..63a688778 100644 --- a/source/funkin/ui/MenuList.hx +++ b/source/funkin/ui/MenuList.hx @@ -5,6 +5,7 @@ import flixel.effects.FlxFlicker; import flixel.group.FlxGroup; import flixel.math.FlxPoint; import flixel.util.FlxSignal; +import funkin.audio.FunkinSound; class MenuTypedList extends FlxTypedGroup { @@ -93,7 +94,7 @@ class MenuTypedList extends FlxTypedGroup if (newIndex != selectedIndex) { - FlxG.sound.play(Paths.sound('scrollMenu')); + FunkinSound.playOnce(Paths.sound('scrollMenu')); selectItem(newIndex); } @@ -163,7 +164,7 @@ class MenuTypedList extends FlxTypedGroup else { busy = true; - FlxG.sound.play(Paths.sound('confirmMenu')); + FunkinSound.playOnce(Paths.sound('confirmMenu')); FlxFlicker.flicker(selected, 1, 0.06, true, false, function(_) { busy = false; selected.callback(); diff --git a/source/funkin/ui/MusicBeatState.hx b/source/funkin/ui/MusicBeatState.hx index 98197a0e7..2cdc747ef 100644 --- a/source/funkin/ui/MusicBeatState.hx +++ b/source/funkin/ui/MusicBeatState.hx @@ -7,6 +7,7 @@ import flixel.FlxSubState; import flixel.addons.transition.FlxTransitionableState; import flixel.text.FlxText; import flixel.util.FlxColor; +import funkin.audio.FunkinSound; import flixel.util.FlxSort; import funkin.modding.PolymodHandler; import funkin.modding.events.ScriptEvent; @@ -151,6 +152,8 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler } else { + FunkinSound.stopAllAudio(); + onComplete(); } } diff --git a/source/funkin/ui/debug/DebugMenuSubState.hx b/source/funkin/ui/debug/DebugMenuSubState.hx index 375fb8f5c..56a05eb86 100644 --- a/source/funkin/ui/debug/DebugMenuSubState.hx +++ b/source/funkin/ui/debug/DebugMenuSubState.hx @@ -4,6 +4,7 @@ import flixel.math.FlxPoint; import flixel.FlxObject; import flixel.FlxSprite; import funkin.ui.MusicBeatSubState; +import funkin.audio.FunkinSound; import funkin.ui.TextMenuList; import funkin.ui.debug.charting.ChartEditorState; import funkin.ui.MusicBeatSubState; @@ -71,7 +72,7 @@ class DebugMenuSubState extends MusicBeatSubState if (controls.BACK) { - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); exitDebugMenu(); } } diff --git a/source/funkin/ui/debug/anim/DebugBoundingState.hx b/source/funkin/ui/debug/anim/DebugBoundingState.hx index 46a095a65..86e84117a 100644 --- a/source/funkin/ui/debug/anim/DebugBoundingState.hx +++ b/source/funkin/ui/debug/anim/DebugBoundingState.hx @@ -12,7 +12,6 @@ import flixel.graphics.frames.FlxAtlasFrames; import flixel.graphics.frames.FlxFrame; import flixel.group.FlxGroup; import flixel.math.FlxPoint; -import flixel.sound.FlxSound; import flixel.text.FlxText; import flixel.util.FlxColor; import funkin.util.MouseUtil; @@ -179,7 +178,7 @@ class DebugBoundingState extends FlxState var objShit = js.html.URL.createObjectURL(swagList.item(0)); trace(objShit); - var funnysound = new FlxSound().loadStream('https://cdn.discordapp.com/attachments/767500676166451231/817821618251759666/Flutter.mp3', false, false, + var funnysound = new FunkinSound().loadStream('https://cdn.discordapp.com/attachments/767500676166451231/817821618251759666/Flutter.mp3', false, false, null, function() { trace('LOADED SHIT??'); }); diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index bdc0d311e..8e82c0fd8 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -15,7 +15,6 @@ import flixel.input.mouse.FlxMouseEvent; import flixel.math.FlxMath; import flixel.math.FlxPoint; import flixel.math.FlxRect; -import flixel.sound.FlxSound; import flixel.system.debug.log.LogStyle; import flixel.system.FlxAssets.FlxSoundAsset; import flixel.text.FlxText; @@ -1091,7 +1090,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState * The chill audio track that plays in the chart editor. * Plays when the main music is NOT being played. */ - var welcomeMusic:FlxSound = new FlxSound(); + var welcomeMusic:FunkinSound = new FunkinSound(); /** * The audio track for the instrumental. @@ -3888,8 +3887,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState function handleCursor():Void { // Mouse sounds - if (FlxG.mouse.justPressed) FlxG.sound.play(Paths.sound("chartingSounds/ClickDown")); - if (FlxG.mouse.justReleased) FlxG.sound.play(Paths.sound("chartingSounds/ClickUp")); + if (FlxG.mouse.justPressed) FunkinSound.playOnce(Paths.sound("chartingSounds/ClickDown")); + if (FlxG.mouse.justReleased) FunkinSound.playOnce(Paths.sound("chartingSounds/ClickUp")); // Note: If a menu is open in HaxeUI, don't handle cursor behavior. var shouldHandleCursor:Bool = !(isHaxeUIFocused || playbarHeadDragging || isHaxeUIDialogOpen) diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx index 1e1d165f3..26e246371 100644 --- a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx +++ b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx @@ -1,7 +1,6 @@ package funkin.ui.debug.charting.handlers; import flixel.system.FlxAssets.FlxSoundAsset; -import flixel.sound.FlxSound; import funkin.audio.VoicesGroup; import funkin.audio.FunkinSound; import funkin.play.character.BaseCharacter.CharacterType; @@ -302,7 +301,8 @@ class ChartEditorAudioHandler trace('WARN: Failed to play sound $path, asset not found.'); return; } - var snd:FunkinSound = FunkinSound.load(asset); + var snd:Null = FunkinSound.load(asset); + if (snd == null) return; snd.autoDestroy = true; snd.play(true); snd.volume = volume; diff --git a/source/funkin/ui/debug/latency/LatencyState.hx b/source/funkin/ui/debug/latency/LatencyState.hx index 9ebd29537..bce97b92d 100644 --- a/source/funkin/ui/debug/latency/LatencyState.hx +++ b/source/funkin/ui/debug/latency/LatencyState.hx @@ -242,13 +242,6 @@ class LatencyState extends MusicBeatSubState } } - /* if (FlxG.keys.justPressed.SPACE) - { - FlxG.sound.music.stop(); - - FlxG.resetState(); - }*/ - noteGrp.forEach(function(daNote:NoteSprite) { daNote.y = (strumLine.y - ((Conductor.instance.songPosition - Conductor.instance.instrumentalOffset) - daNote.noteData.time) * 0.45); daNote.x = strumLine.x + 30; diff --git a/source/funkin/ui/debug/stage/StageBuilderState.hx b/source/funkin/ui/debug/stage/StageBuilderState.hx index 074914f58..b556b9fde 100644 --- a/source/funkin/ui/debug/stage/StageBuilderState.hx +++ b/source/funkin/ui/debug/stage/StageBuilderState.hx @@ -37,8 +37,6 @@ class StageBuilderState extends MusicBeatState FlxG.mouse.visible = true; - // var alsoSnd:FlxSound = new FlxSound(); - // snd = new Sound(); // var swagBytes:ByteArray = new ByteArray(8192); diff --git a/source/funkin/ui/freeplay/DJBoyfriend.hx b/source/funkin/ui/freeplay/DJBoyfriend.hx index 55f43d2ef..8cd63dba1 100644 --- a/source/funkin/ui/freeplay/DJBoyfriend.hx +++ b/source/funkin/ui/freeplay/DJBoyfriend.hx @@ -4,8 +4,8 @@ import flixel.FlxSprite; import flixel.util.FlxSignal; import funkin.util.assets.FlxAnimationUtil; import funkin.graphics.adobeanimate.FlxAtlasSprite; -import flixel.sound.FlxSound; import flixel.util.FlxTimer; +import funkin.audio.FunkinSound; import funkin.audio.FlxStreamSound; class DJBoyfriend extends FlxAtlasSprite @@ -178,7 +178,7 @@ class DJBoyfriend extends FlxAtlasSprite if (cartoonSnd == null) { // tv is OFF, but getting turned on - FlxG.sound.play(Paths.sound('tv_on')); + FunkinSound.playOnce(Paths.sound('tv_on')); cartoonSnd = new FlxStreamSound(); FlxG.sound.defaultSoundGroup.add(cartoonSnd); @@ -187,7 +187,7 @@ class DJBoyfriend extends FlxAtlasSprite { // plays it smidge after the click new FlxTimer().start(0.1, function(_) { - FlxG.sound.play(Paths.sound('channel_switch')); + FunkinSound.playOnce(Paths.sound('channel_switch')); }); } // cartoonSnd.loadEmbedded(Paths.sound("cartoons/peck")); diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index fc11eec28..9b59663cf 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -174,7 +174,11 @@ class FreeplayState extends MusicBeatSubState isDebug = true; #end - FunkinSound.playMusic('freakyMenu'); + FunkinSound.playMusic('freakyMenu', + { + overrideExisting: true, + restartTrack: false + }); // Add a null entry that represents the RANDOM option songs.push(null); @@ -867,7 +871,7 @@ class FreeplayState extends MusicBeatSubState FlxTimer.globalManager.clear(); dj.onIntroDone.removeAll(); - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); var longestTimer:Float = 0; @@ -1051,7 +1055,7 @@ class FreeplayState extends MusicBeatSubState trace('No songs available!'); busy = false; letterSort.inputEnabled = true; - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); return; } @@ -1084,7 +1088,7 @@ class FreeplayState extends MusicBeatSubState PlayStatePlaylist.campaignId = cap.songData.levelId; // Visual and audio effects. - FlxG.sound.play(Paths.sound('confirmMenu')); + FunkinSound.playOnce(Paths.sound('confirmMenu')); dj.confirm(); new FlxTimer().start(1, function(tmr:FlxTimer) { @@ -1126,7 +1130,7 @@ class FreeplayState extends MusicBeatSubState function changeSelection(change:Int = 0):Void { - FlxG.sound.play(Paths.sound('scrollMenu'), 0.4); + FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4); // FlxG.sound.playMusic(Paths.inst(songs[curSelected].songName)); var prevSelected:Int = curSelected; @@ -1178,7 +1182,12 @@ class FreeplayState extends MusicBeatSubState // TODO: Stream the instrumental of the selected song? if (prevSelected == 0) { - FunkinSound.playMusic('freakyMenu'); + FunkinSound.playMusic('freakyMenu', + { + startingVolume: 0.0, + overrideExisting: true, + restartTrack: false + }); FlxG.sound.music.fadeIn(2, 0, 0.8); } } diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index f4d48dba3..a8c2039ab 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -155,7 +155,11 @@ class MainMenuState extends MusicBeatState function playMenuMusic():Void { - FunkinSound.playMusic('freakyMenu'); + FunkinSound.playMusic('freakyMenu', + { + overrideExisting: true, + restartTrack: false + }); } function resetCamStuff() @@ -321,7 +325,7 @@ class MainMenuState extends MusicBeatState if (controls.BACK && menuItems.enabled && !menuItems.busy) { - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); FlxG.switchState(() -> new TitleState()); } } diff --git a/source/funkin/ui/options/OptionsState.hx b/source/funkin/ui/options/OptionsState.hx index 7b233f03d..0f33a0780 100644 --- a/source/funkin/ui/options/OptionsState.hx +++ b/source/funkin/ui/options/OptionsState.hx @@ -5,9 +5,11 @@ import flixel.FlxSubState; import flixel.addons.transition.FlxTransitionableState; import flixel.group.FlxGroup; import flixel.util.FlxSignal; +import funkin.audio.FunkinSound; import funkin.ui.mainmenu.MainMenuState; import funkin.ui.MusicBeatState; import funkin.util.WindowUtil; +import funkin.audio.FunkinSound; import funkin.input.Controls; class OptionsState extends MusicBeatState @@ -143,7 +145,7 @@ class Page extends FlxGroup { if (canExit && controls.BACK) { - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); exit(); } } diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 9ce110c73..315444fb0 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -231,7 +231,11 @@ class StoryMenuState extends MusicBeatState function playMenuMusic():Void { - FunkinSound.playMusic('freakyMenu'); + FunkinSound.playMusic('freakyMenu', + { + overrideExisting: true, + restartTrack: false + }); } function updateData():Void @@ -382,7 +386,7 @@ class StoryMenuState extends MusicBeatState if (controls.BACK && !exitingMenu && !selectedLevel) { - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); exitingMenu = true; FlxG.switchState(() -> new MainMenuState()); } @@ -511,7 +515,7 @@ class StoryMenuState extends MusicBeatState { if (!currentLevel.isUnlocked()) { - FlxG.sound.play(Paths.sound('cancelMenu')); + FunkinSound.playOnce(Paths.sound('cancelMenu')); return; } @@ -519,7 +523,7 @@ class StoryMenuState extends MusicBeatState selectedLevel = true; - FlxG.sound.play(Paths.sound('confirmMenu')); + FunkinSound.playOnce(Paths.sound('confirmMenu')); currentLevelTitle.isFlashing = true; diff --git a/source/funkin/ui/title/AttractState.hx b/source/funkin/ui/title/AttractState.hx index 63a9e63ad..0af97afd9 100644 --- a/source/funkin/ui/title/AttractState.hx +++ b/source/funkin/ui/title/AttractState.hx @@ -22,7 +22,11 @@ class AttractState extends MusicBeatState public override function create():Void { // Pause existing music. - FlxG.sound.music.stop(); + if (FlxG.sound.music != null) + { + FlxG.sound.music.destroy(); + FlxG.sound.music = null; + } #if html5 playVideoHTML5(ATTRACT_VIDEO_PATH); diff --git a/source/funkin/ui/title/TitleState.hx b/source/funkin/ui/title/TitleState.hx index 5cc7b8cc1..0a32b5890 100644 --- a/source/funkin/ui/title/TitleState.hx +++ b/source/funkin/ui/title/TitleState.hx @@ -222,10 +222,14 @@ class TitleState extends MusicBeatState { var shouldFadeIn = (FlxG.sound.music == null); // Load music. Includes logic to handle BPM changes. - FunkinSound.playMusic('freakyMenu', false, true); - FlxG.sound.music.volume = 0; + FunkinSound.playMusic('freakyMenu', + { + startingVolume: 0.0, + overrideExisting: true, + restartTrack: true + }); // Fade from 0.0 to 0.7 over 4 seconds - if (shouldFadeIn) FlxG.sound.music.fadeIn(4, 0, 0.7); + if (shouldFadeIn) FlxG.sound.music.fadeIn(4.0, 0.0, 0.7); } function getIntroTextShit():Array> @@ -323,7 +327,7 @@ class TitleState extends MusicBeatState if (Date.now().getDay() == 5) NGio.unlockMedal(61034); titleText.animation.play('press'); FlxG.camera.flash(FlxColor.WHITE, 1); - FlxG.sound.play(Paths.sound('confirmMenu'), 0.7); + FunkinSound.playOnce(Paths.sound('confirmMenu'), 0.7); transitioning = true; var targetState:NextState = () -> new MainMenuState(); @@ -338,7 +342,7 @@ class TitleState extends MusicBeatState // ngSpr?? FlxG.switchState(targetState); }); - // FlxG.sound.play(Paths.music('titleShoot'), 0.7); + // FunkinSound.playOnce(Paths.music('titleShoot'), 0.7); } if (pressedEnter && !skippedIntro && initialized) skipIntro(); @@ -392,7 +396,7 @@ class TitleState extends MusicBeatState Conductor.instance.forceBPM(190); FlxG.camera.flash(FlxColor.WHITE, 1); - FlxG.sound.play(Paths.sound('confirmMenu'), 0.7); + FunkinSound.playOnce(Paths.sound('confirmMenu'), 0.7); } function createCoolText(textArray:Array) diff --git a/source/funkin/ui/transition/LoadingState.hx b/source/funkin/ui/transition/LoadingState.hx index 23b3db6a9..980c264e3 100644 --- a/source/funkin/ui/transition/LoadingState.hx +++ b/source/funkin/ui/transition/LoadingState.hx @@ -171,7 +171,12 @@ class LoadingState extends MusicBeatState function onLoad():Void { - if (stopMusic && FlxG.sound.music != null) FlxG.sound.music.stop(); + // Stop the instrumental. + if (stopMusic && FlxG.sound.music != null) + { + FlxG.sound.music.destroy(); + FlxG.sound.music = null; + } FlxG.switchState(target); } @@ -200,7 +205,8 @@ class LoadingState extends MusicBeatState // All assets preloaded, switch directly to play state (defualt on other targets). if (shouldStopMusic && FlxG.sound.music != null) { - FlxG.sound.music.stop(); + FlxG.sound.music.destroy(); + FlxG.sound.music = null; } // Load and cache the song's charts. diff --git a/source/funkin/ui/transition/StickerSubState.hx b/source/funkin/ui/transition/StickerSubState.hx index 981a30e09..0b5e16f97 100644 --- a/source/funkin/ui/transition/StickerSubState.hx +++ b/source/funkin/ui/transition/StickerSubState.hx @@ -18,6 +18,7 @@ import flixel.addons.transition.FlxTransitionableState; import openfl.display.BitmapData; import funkin.ui.freeplay.FreeplayState; import openfl.geom.Matrix; +import funkin.audio.FunkinSound; import openfl.display.Sprite; import openfl.display.Bitmap; import flixel.FlxState; @@ -137,7 +138,7 @@ class StickerSubState extends MusicBeatSubState new FlxTimer().start(sticker.timing, _ -> { sticker.visible = false; var daSound:String = FlxG.random.getObject(sounds); - FlxG.sound.play(Paths.sound(daSound)); + FunkinSound.playOnce(Paths.sound(daSound)); if (grpStickers == null || ind == grpStickers.members.length - 1) { @@ -227,7 +228,7 @@ class StickerSubState extends MusicBeatSubState sticker.visible = true; var daSound:String = FlxG.random.getObject(sounds); - FlxG.sound.play(Paths.sound(daSound)); + FunkinSound.playOnce(Paths.sound(daSound)); var frameTimer:Int = FlxG.random.int(0, 2); diff --git a/source/funkin/util/MathUtil.hx b/source/funkin/util/MathUtil.hx index 532e92f15..5db8f8d76 100644 --- a/source/funkin/util/MathUtil.hx +++ b/source/funkin/util/MathUtil.hx @@ -19,6 +19,7 @@ class MathUtil * * @return The interpolated value. */ + @:deprecated('Use smoothLerp instead') public static function coolLerp(base:Float, target:Float, ratio:Float):Float { return base + cameraLerp(ratio) * (target - base); diff --git a/source/funkin/util/plugins/ScreenshotPlugin.hx b/source/funkin/util/plugins/ScreenshotPlugin.hx index d7e8109b8..9ac21d4b8 100644 --- a/source/funkin/util/plugins/ScreenshotPlugin.hx +++ b/source/funkin/util/plugins/ScreenshotPlugin.hx @@ -174,7 +174,7 @@ class ScreenshotPlugin extends FlxBasic FlxTween.tween(flashSpr, {alpha: 0}, 0.15, {ease: FlxEase.quadOut, onComplete: _ -> FlxG.stage.removeChild(flashSpr)}); // Play a sound (auto-play is true). - FunkinSound.load(Paths.sound('screenshot'), 1.0, false, true, true); + FunkinSound.playOnce(Paths.sound('screenshot'), 1.0); } static final PREVIEW_INITIAL_DELAY = 0.25; // How long before the preview starts fading in. From ecea5e89e2377d5dc01e97fde91098f0033fcbc1 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 23 Mar 2024 17:51:19 -0400 Subject: [PATCH 41/75] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 526743915..024ade791 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 52674391511577300cdb8c08df293ea72099aa82 +Subproject commit 024ade791ffc39afcf8b075841b19ca0f67b00d5 From 709fbc859443f753b548f23760758d69b734101c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 23 Mar 2024 18:11:06 -0400 Subject: [PATCH 42/75] Fix a bug where lag could cause the opponent to miss. --- source/funkin/play/PlayState.hx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 12b290afd..f7bf6fc22 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2089,8 +2089,7 @@ class PlayState extends MusicBeatSubState holdNote.handledMiss = true; // We dropped a hold note. - // Mute vocals and play miss animation, but don't penalize. - vocals.opponentVolume = 0; + // Play miss animation, but don't penalize. currentStage.getOpponent().playSingAnimation(holdNote.noteData.getDirection(), true); } } From a8486a47dfd879e3370f804900cb61ee811b7675 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 23 Mar 2024 18:22:15 -0400 Subject: [PATCH 43/75] Fix a bug where the album disappears never to return --- source/funkin/ui/freeplay/FreeplayState.hx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index fc11eec28..4e0d7ccf3 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1013,7 +1013,14 @@ class FreeplayState extends MusicBeatSubState // Set the difficulty star count on the right. albumRoll.setDifficultyStars(daSong?.songRating); - albumRoll.albumId = daSong?.albumId ?? Constants.DEFAULT_ALBUM_ID; + + // Set the album graphic and play the animation if relevant. + var newAlbumId:String = daSong?.albumId ?? Constants.DEFAULT_ALBUM_ID; + if (albumRoll.albumId != newAlbumId) + { + albumRoll.albumId = newAlbumId; + albumRoll.playIntro(); + } } // Clears the cache of songs, frees up memory, they' ll have to be loaded in later tho function clearDaCache(actualSongTho:String) From 24b03e4f6bfe9f6d0dcf32195b4c0a44f434ea52 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 23 Mar 2024 23:38:07 -0400 Subject: [PATCH 44/75] Replace calls to FlxG.sound.playMusic with ones that set the BPM. --- assets | 2 +- source/funkin/audio/FunkinSound.hx | 30 ++++++++++++++----- source/funkin/play/ResultState.hx | 20 +++++++++---- .../funkin/ui/debug/latency/LatencyState.hx | 2 -- source/funkin/ui/freeplay/FreeplayState.hx | 22 ++++++++------ source/funkin/ui/title/TitleState.hx | 2 -- 6 files changed, 51 insertions(+), 27 deletions(-) diff --git a/assets b/assets index 024ade791..3e8bea70e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 024ade791ffc39afcf8b075841b19ca0f67b00d5 +Subproject commit 3e8bea70e7dcc76df67a0e87b85ef28ed5140371 diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 52bd1c7e8..687860265 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -290,10 +290,11 @@ class FunkinSound extends FlxSound implements ICloneable * @param key The key of the music you want to play. Music should be at `music//.ogg`. * @param params A set of additional optional parameters. * Data should be at `music//-metadata.json`. + * @return Whether the music was started. `false` if music was already playing or could not be started */ - public static function playMusic(key:String, params:FunkinSoundPlayMusicParams):Void + public static function playMusic(key:String, params:FunkinSoundPlayMusicParams):Bool { - if (!(params.overrideExisting ?? false) && (FlxG.sound.music?.exists ?? false) && FlxG.sound.music.playing) return; + if (!(params.overrideExisting ?? false) && (FlxG.sound.music?.exists ?? false) && FlxG.sound.music.playing) return false; if (!(params.restartTrack ?? false) && FlxG.sound.music?.playing) { @@ -303,7 +304,7 @@ class FunkinSound extends FlxSound implements ICloneable // Stop here if we would play a matching music track. if (existingSound._label == Paths.music('$key/$key')) { - return; + return false; } } } @@ -328,11 +329,20 @@ class FunkinSound extends FlxSound implements ICloneable FlxG.sound.music.kill(); } - var music = FunkinSound.load(Paths.music('$key/$key'), params?.startingVolume ?? 1.0, true, false, true); - if (music != null) FlxG.sound.music = music; + var music = FunkinSound.load(Paths.music('$key/$key'), params?.startingVolume ?? 1.0, params.loop ?? true, false, true); + if (music != null) + { + FlxG.sound.music = music; - // Prevent repeat update() and onFocus() calls. - FlxG.sound.list.remove(FlxG.sound.music); + // Prevent repeat update() and onFocus() calls. + FlxG.sound.list.remove(FlxG.sound.music); + + return true; + } + else + { + return false; + } } /** @@ -451,6 +461,12 @@ typedef FunkinSoundPlayMusicParams = */ var ?restartTrack:Bool; + /** + * Whether the music should loop or play once. + * @default `true` + */ + var ?loop:Bool; + /** * Whether to check for `SongMusicData` to update the Conductor with. * @default `true` diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index a78d61583..c695b1db4 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -13,6 +13,7 @@ import flixel.text.FlxBitmapText; import flixel.tweens.FlxEase; import funkin.ui.freeplay.FreeplayState; import flixel.tweens.FlxTween; +import funkin.audio.FunkinSound; import flixel.util.FlxGradient; import flixel.util.FlxTimer; import funkin.graphics.shaders.LeftMaskShader; @@ -48,9 +49,13 @@ class ResultState extends MusicBeatSubState else resultsVariation = NORMAL; - var loops:Bool = resultsVariation != SHIT; - - FlxG.sound.playMusic(Paths.music("results" + resultsVariation), 1, loops); + FunkinSound.playMusic('results$resultsVariation', + { + startingVolume: 1.0, + overrideExisting: true, + restartTrack: true, + loop: resultsVariation != SHIT + }); // TEMP-ish, just used to sorta "cache" the 3000x3000 image! var cacheBullShit:FlxSprite = new FlxSprite().loadGraphic(Paths.image("resultScreen/soundSystem")); @@ -348,9 +353,12 @@ class ResultState extends MusicBeatSubState if (controls.PAUSE) { FlxTween.tween(FlxG.sound.music, {volume: 0}, 0.8); - FlxTween.tween(FlxG.sound.music, {pitch: 3}, 0.1, {onComplete: _ -> { - FlxTween.tween(FlxG.sound.music, {pitch: 0.5}, 0.4); - }}); + FlxTween.tween(FlxG.sound.music, {pitch: 3}, 0.1, + { + onComplete: _ -> { + FlxTween.tween(FlxG.sound.music, {pitch: 0.5}, 0.4); + } + }); if (params.storyMode) { openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new StoryMenuState(sticker))); diff --git a/source/funkin/ui/debug/latency/LatencyState.hx b/source/funkin/ui/debug/latency/LatencyState.hx index bce97b92d..7b2eabb1c 100644 --- a/source/funkin/ui/debug/latency/LatencyState.hx +++ b/source/funkin/ui/debug/latency/LatencyState.hx @@ -71,8 +71,6 @@ class LatencyState extends MusicBeatSubState // trace("EVENT LISTENER: " + key); }); - // FlxG.sound.playMusic(Paths.sound('soundTest')); - // funnyStatsGraph.hi Conductor.instance.forceBPM(60); diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 9b59663cf..249c7ffae 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -1131,7 +1131,6 @@ class FreeplayState extends MusicBeatSubState function changeSelection(change:Int = 0):Void { FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4); - // FlxG.sound.playMusic(Paths.inst(songs[curSelected].songName)); var prevSelected:Int = curSelected; @@ -1174,20 +1173,25 @@ class FreeplayState extends MusicBeatSubState { if (curSelected == 0) { - FlxG.sound.playMusic(Paths.music('freeplay/freeplayRandom'), 0); + FunkinSound.playMusic('freeplayRandom', + { + startingVolume: 0.0, + overrideExisting: true, + restartTrack: true + }); FlxG.sound.music.fadeIn(2, 0, 0.8); } else { // TODO: Stream the instrumental of the selected song? - if (prevSelected == 0) + var didReplace:Bool = FunkinSound.playMusic('freakyMenu', + { + startingVolume: 0.0, + overrideExisting: true, + restartTrack: false + }); + if (didReplace) { - FunkinSound.playMusic('freakyMenu', - { - startingVolume: 0.0, - overrideExisting: true, - restartTrack: false - }); FlxG.sound.music.fadeIn(2, 0, 0.8); } } diff --git a/source/funkin/ui/title/TitleState.hx b/source/funkin/ui/title/TitleState.hx index 0a32b5890..1a4e13ab1 100644 --- a/source/funkin/ui/title/TitleState.hx +++ b/source/funkin/ui/title/TitleState.hx @@ -389,8 +389,6 @@ class TitleState extends MusicBeatState { cheatActive = true; - FlxG.sound.playMusic(Paths.music('tutorialTitle'), 1); - var spec:SpectogramSprite = new SpectogramSprite(FlxG.sound.music); add(spec); From 138dfd61e8a42e2fe1c1d7c217346e5632c8f687 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sat, 23 Mar 2024 23:52:08 -0400 Subject: [PATCH 45/75] Fix a bug where the Camera Zoom toggle didn't work. --- 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 12b290afd..6c3d790d7 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1353,7 +1353,10 @@ class PlayState extends MusicBeatSubState } // Only zoom camera if we are zoomed by less than 35%. - if (FlxG.camera.zoom < (1.35 * defaultCameraZoom) && cameraZoomRate > 0 && Conductor.instance.currentBeat % cameraZoomRate == 0) + if (Preferences.zoomCamera + && FlxG.camera.zoom < (1.35 * defaultCameraZoom) + && cameraZoomRate > 0 + && Conductor.instance.currentBeat % cameraZoomRate == 0) { // Zoom camera in (1.5%) currentCameraZoom += cameraZoomIntensity * defaultCameraZoom; From 51d36ea25e11466de297457cca75caba65e0556e Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Sun, 24 Mar 2024 04:53:05 -0400 Subject: [PATCH 46/75] Work in progress on fixing Boyfriend's spook/tv animations --- .../graphics/adobeanimate/FlxAtlasSprite.hx | 17 +++ source/funkin/ui/freeplay/DJBoyfriend.hx | 133 ++++++++++++++---- source/funkin/ui/freeplay/FreeplayState.hx | 56 ++++---- 3 files changed, 145 insertions(+), 61 deletions(-) diff --git a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx index c5a3a3771..5394bce1a 100644 --- a/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx +++ b/source/funkin/graphics/adobeanimate/FlxAtlasSprite.hx @@ -79,6 +79,23 @@ 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; + } + /** * Plays an animation. * @param id A string ID of the animation to play. diff --git a/source/funkin/ui/freeplay/DJBoyfriend.hx b/source/funkin/ui/freeplay/DJBoyfriend.hx index 8cd63dba1..c77762d73 100644 --- a/source/funkin/ui/freeplay/DJBoyfriend.hx +++ b/source/funkin/ui/freeplay/DJBoyfriend.hx @@ -4,6 +4,7 @@ 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; @@ -26,8 +27,8 @@ class DJBoyfriend extends FlxAtlasSprite var gotSpooked:Bool = false; - static final SPOOK_PERIOD:Float = 120.0; - static final TV_PERIOD:Float = 180.0; + static final SPOOK_PERIOD:Float = 10.0; + static final TV_PERIOD:Float = 10.0; // Time since dad last SPOOKED you. var timeSinceSpook:Float = 0; @@ -87,20 +88,21 @@ class DJBoyfriend extends FlxAtlasSprite timeSinceSpook = 0; case Idle: // We are in this state the majority of the time. - if (getCurrentAnimation() != 'Boyfriend DJ' || anim.finished) + if (getCurrentAnimation() != 'Boyfriend DJ') { - if (timeSinceSpook > SPOOK_PERIOD && !gotSpooked) + playFlashAnimation('Boyfriend DJ', true); + } + + if (getCurrentAnimation() == 'Boyfriend DJ' && this.isLoopFinished()) + { + if (timeSinceSpook >= SPOOK_PERIOD && !gotSpooked) { currentState = Spook; } - else if (timeSinceSpook > TV_PERIOD) + else if (timeSinceSpook >= TV_PERIOD) { currentState = TV; } - else - { - playFlashAnimation('Boyfriend DJ', false); - } } timeSinceSpook += elapsed; case Confirm: @@ -111,6 +113,7 @@ class DJBoyfriend extends FlxAtlasSprite { onSpook.dispatch(); playFlashAnimation('bf dj afk', false); + gotSpooked = true; } timeSinceSpook = 0; case TV: @@ -119,6 +122,34 @@ class DJBoyfriend extends FlxAtlasSprite 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 @@ -139,11 +170,15 @@ class DJBoyfriend extends FlxAtlasSprite case "Boyfriend DJ watchin tv OG": var frame:Int = FlxG.random.bool(33) ? 112 : 166; - if (FlxG.random.bool(10)) + + // 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'); } @@ -152,24 +187,31 @@ class DJBoyfriend extends FlxAtlasSprite public function resetAFKTimer():Void { timeSinceSpook = 0; + gotSpooked = false; } + var offsetX:Float = 0.0; + var offsetY:Float = 0.0; + function setupAnimations():Void { - // animation.addByPrefix('intro', "boyfriend dj intro", 24, false); - addOffset('boyfriend dj intro', 8, 3); + // Intro + addOffset('boyfriend dj intro', 8.0 - 1.3, 3.0 - 0.4); - // animation.addByPrefix('idle', "Boyfriend DJ0", 24, false); + // Idle addOffset('Boyfriend DJ', 0, 0); - // animation.addByPrefix('confirm', "Boyfriend DJ confirm", 24, false); + // Confirm addOffset('Boyfriend DJ confirm', 0, 0); - // animation.addByPrefix('spook', "bf dj afk0", 24, false); - addOffset('bf dj afk', 0, 0); + // AFK: Spook + addOffset('bf dj afk', 649.5, 58.5); + + // AFK: TV + addOffset('Boyfriend DJ watchin tv OG', 0, 0); } - var cartoonSnd:FlxStreamSound; + var cartoonSnd:Null = null; public var playingCartoon:Bool = false; @@ -178,39 +220,47 @@ class DJBoyfriend extends FlxAtlasSprite if (cartoonSnd == null) { // tv is OFF, but getting turned on - FunkinSound.playOnce(Paths.sound('tv_on')); - - cartoonSnd = new FlxStreamSound(); - FlxG.sound.defaultSoundGroup.add(cartoonSnd); + // Eric got FUCKING TROLLED there is no `tv_on` or `channel_switch` sound! + // FunkinSound.playOnce(Paths.sound('tv_on'), 1.0, function() { + // }); + loadCartoon(); } else { // plays it smidge after the click - new FlxTimer().start(0.1, function(_) { - FunkinSound.playOnce(Paths.sound('channel_switch')); - }); + // new FlxTimer().start(0.1, function(_) { + // // FunkinSound.playOnce(Paths.sound('channel_switch')); + // }); + cartoonSnd.destroy(); + loadCartoon(); } - // cartoonSnd.loadEmbedded(Paths.sound("cartoons/peck")); - // cartoonSnd.play(); - loadCartoon(); + // loadCartoon(); } function loadCartoon() { - cartoonSnd.loadEmbedded(Paths.sound(getRandomFlashToon()), false, false, function() { + cartoonSnd = FunkinSound.load(Paths.sound(getRandomFlashToon()), 1.0, false, true, true, function() { anim.play("Boyfriend DJ watchin tv OG", true, false, 60); }); - cartoonSnd.play(true, FlxG.random.float(0, cartoonSnd.length)); + + // 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.4); + + // 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)); } - var cartoonList:Array = openfl.utils.Assets.list().filter(function(path) return path.startsWith("assets/sounds/cartoons/")); + 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; @@ -244,10 +294,31 @@ class DJBoyfriend extends FlxAtlasSprite var daOffset = animOffsets.get(AnimName); if (animOffsets.exists(AnimName)) { - offset.set(daOffset[0], daOffset[1]); + 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; + } } } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 249c7ffae..55c36e03f 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -688,14 +688,6 @@ class FreeplayState extends MusicBeatSubState if (FlxG.keys.justPressed.T) typing.hasFocus = true; - if (FlxG.sound.music != null) - { - if (FlxG.sound.music.volume < 0.7) - { - FlxG.sound.music.volume += 0.5 * elapsed; - } - } - lerpScore = MathUtil.coolLerp(lerpScore, intendedScore, 0.2); lerpCompletion = MathUtil.coolLerp(lerpCompletion, intendedCompletion, 0.9); @@ -733,9 +725,9 @@ class FreeplayState extends MusicBeatSubState { if (busy) return; - var upP:Bool = controls.UI_UP_P; - var downP:Bool = controls.UI_DOWN_P; - var accepted:Bool = controls.ACCEPT; + 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; if (FlxG.onMobile) { @@ -809,10 +801,8 @@ class FreeplayState extends MusicBeatSubState } #end - if (controls.UI_UP || controls.UI_DOWN) + if (!FlxG.keys.pressed.CONTROL && (controls.UI_UP || controls.UI_DOWN)) { - spamTimer += elapsed; - if (spamming) { if (spamTimer >= 0.07) @@ -829,7 +819,24 @@ class FreeplayState extends MusicBeatSubState } } } - else if (spamTimer >= 0.9) spamming = true; + else if (spamTimer >= 0.9) + { + spamming = true; + } + else if (spamTimer <= 0) + { + if (controls.UI_UP) + { + changeSelection(-1); + } + else + { + changeSelection(1); + } + } + + spamTimer += elapsed; + dj.resetAFKTimer(); } else { @@ -837,29 +844,18 @@ class FreeplayState extends MusicBeatSubState spamTimer = 0; } - if (upP) - { - dj.resetAFKTimer(); - changeSelection(-1); - } - if (downP) - { - dj.resetAFKTimer(); - changeSelection(1); - } - if (FlxG.mouse.wheel != 0) { dj.resetAFKTimer(); changeSelection(-Math.round(FlxG.mouse.wheel / 4)); } - if (controls.UI_LEFT_P) + if (controls.UI_LEFT_P && !FlxG.keys.pressed.CONTROL) { dj.resetAFKTimer(); changeDiff(-1); } - if (controls.UI_RIGHT_P) + if (controls.UI_RIGHT_P && !FlxG.keys.pressed.CONTROL) { dj.resetAFKTimer(); changeDiff(1); @@ -1227,8 +1223,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 && !FlxG.keys.pressed.CONTROL) moveShitDown(); + if (!flipX && controls.UI_LEFT_P && !FlxG.keys.pressed.CONTROL) moveShitDown(); super.update(elapsed); } From 2b1fc1e7d722bf5e5663c432c35b4697a389e149 Mon Sep 17 00:00:00 2001 From: Hazel Date: Mon, 25 Mar 2024 16:12:37 +0100 Subject: [PATCH 47/75] bugfix: html5 builds (#418) --- .github/workflows/build-shit.yml | 6 ++++-- assets | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-shit.yml b/.github/workflows/build-shit.yml index 49bab1ac1..e217d1f18 100644 --- a/.github/workflows/build-shit.yml +++ b/.github/workflows/build-shit.yml @@ -13,8 +13,9 @@ jobs: apt update apt install -y sudo git curl unzip - name: Fix git config on posix runner + # this can't be {{ github.workspace }} because that's not docker-aware run: | - git config --global --add safe.directory ${{ github.workspace }} + git config --global --add safe.directory $GITHUB_WORKSPACE - name: Get checkout token uses: actions/create-github-app-token@v1 id: app_token @@ -90,8 +91,9 @@ jobs: runs-on: [self-hosted, macos] steps: - name: Fix git config on posix runner + # this can't be {{ github.workspace }} because that's not docker-aware run: | - git config --global --add safe.directory ${{ github.workspace }} + git config --global --add safe.directory $GITHUB_WORKSPACE - name: Get checkout token uses: actions/create-github-app-token@v1 id: app_token diff --git a/assets b/assets index 8b0aa1d56..526743915 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8b0aa1d5633f2f2e7f0eb03498ee71d19cc99564 +Subproject commit 52674391511577300cdb8c08df293ea72099aa82 From 275a58e633f18b05320b41441789d58b5b1f4a9a Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 25 Mar 2024 13:42:35 -0400 Subject: [PATCH 48/75] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 9a5767d04..3b6008899 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 9a5767d04203a9cb9857608761646bb6a52b1e13 +Subproject commit 3b6008899aefa1fe952c1cc5ebf9506464a86d3c From 6a5026b7146ce1e7b1986b606662e6e4388e8202 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 25 Mar 2024 15:06:47 -0400 Subject: [PATCH 49/75] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 3e8bea70e..3b6008899 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 3e8bea70e7dcc76df67a0e87b85ef28ed5140371 +Subproject commit 3b6008899aefa1fe952c1cc5ebf9506464a86d3c From a66b746e4393220b03acf53e38a2a7990eac8653 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Mon, 25 Mar 2024 16:01:38 -0400 Subject: [PATCH 50/75] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index d6f82ec8b..8bb6214e1 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit d6f82ec8b91823eee809d73e2c4744210a8e7e0b +Subproject commit 8bb6214e16c823b8b5a522a39b7a7e01d6283abf From 07ad25006008910a4c8c9a706b64267cfbca306f Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 25 Mar 2024 21:08:39 -0400 Subject: [PATCH 51/75] Fix an audio crash when exiting a cutscene --- source/funkin/play/PlayState.hx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index a8cb879a3..169809a63 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2941,7 +2941,10 @@ class PlayState extends MusicBeatSubState if (overrideMusic) { // Stop the music. Do NOT destroy it, something still references it! - FlxG.sound.music.pause(); + if (FlxG.sound.music != null) + { + FlxG.sound.music.pause(); + } if (vocals != null) { vocals.pause(); @@ -2951,7 +2954,10 @@ class PlayState extends MusicBeatSubState else { // Stop and destroy the music. - FlxG.sound.music.pause(); + if (FlxG.sound.music != null) + { + FlxG.sound.music.pause(); + } if (vocals != null) { vocals.destroy(); From f4617cbbda6cb3b62ea7a7bdf351d8d3f366d533 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 25 Mar 2024 21:08:53 -0400 Subject: [PATCH 52/75] Add mac builds with proper arguments --- .vscode/settings.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 84af3a3fd..13a1862d2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -204,6 +204,21 @@ "label": "HTML5 / Debug (Watch)", "target": "html5", "args": ["-debug", "-watch", "-DFORCE_DEBUG_VERSION"] + }, + { + "label": "macOS / Debug", + "target": "mac", + "args": ["-debug", "-DFORCE_DEBUG_VERSION"] + }, + { + "label": "macOS / Release", + "target": "mac", + "args": ["-release"] + }, + { + "label": "macOS / Release (GitHub Actions)", + "target": "mac", + "args": ["-release", "-DGITHUB_BUILD"] } ], "cmake.configureOnOpen": false, From 90f861628d6869b9b148782ed0ce1c5bf8f66ccc Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 25 Mar 2024 21:09:02 -0400 Subject: [PATCH 53/75] Fix an HTML5 build issue. --- .../ui/debug/anim/DebugBoundingState.hx | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/source/funkin/ui/debug/anim/DebugBoundingState.hx b/source/funkin/ui/debug/anim/DebugBoundingState.hx index 86e84117a..1aa5d6e12 100644 --- a/source/funkin/ui/debug/anim/DebugBoundingState.hx +++ b/source/funkin/ui/debug/anim/DebugBoundingState.hx @@ -1,31 +1,35 @@ package funkin.ui.debug.anim; -import funkin.util.SerializerUtil; -import funkin.play.character.CharacterData; -import flixel.FlxCamera; -import flixel.FlxSprite; -import flixel.FlxState; import flixel.addons.display.FlxGridOverlay; import flixel.addons.ui.FlxInputText; import flixel.addons.ui.FlxUIDropDownMenu; +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 funkin.util.MouseUtil; 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 haxe.ui.RuntimeComponentBuilder; +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.core.Component; +import haxe.ui.core.Screen; import haxe.ui.events.ItemEvent; import haxe.ui.events.UIEvent; -import funkin.ui.mainmenu.MainMenuState; +import haxe.ui.RuntimeComponentBuilder; import lime.utils.Assets as LimeAssets; import openfl.Assets; import openfl.events.Event; @@ -33,13 +37,8 @@ import openfl.events.IOErrorEvent; import openfl.geom.Rectangle; import openfl.net.FileReference; import openfl.net.URLLoader; -import funkin.ui.mainmenu.MainMenuState; import openfl.net.URLRequest; import openfl.utils.ByteArray; -import funkin.input.Cursor; -import funkin.play.character.CharacterData.CharacterDataParser; -import funkin.util.SortUtil; -import haxe.ui.core.Screen; using flixel.util.FlxSpriteUtil; From 5ae075e947c1fdbdd50614f0750f02f5b3f47fc2 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 25 Mar 2024 21:09:14 -0400 Subject: [PATCH 54/75] Fix a couple Flixel warnings on the results screen. --- source/funkin/play/ResultState.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index c695b1db4..821f4ba3c 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -109,7 +109,7 @@ class ResultState extends MusicBeatSubState add(gf); var boyfriend:FlxSprite = FunkinSprite.createSparrow(640, -200, 'resultScreen/resultBoyfriendGOOD'); - boyfriend.animation.addByPrefix("fall", "Boyfriend Good", 24, false); + boyfriend.animation.addByPrefix("fall", "Boyfriend Good Anim0", 24, false); boyfriend.visible = false; boyfriend.animation.finishCallback = function(_) { boyfriend.animation.play('fall', true, false, 14); @@ -164,7 +164,7 @@ class ResultState extends MusicBeatSubState add(blackTopBar); var resultsAnim:FunkinSprite = FunkinSprite.createSparrow(-200, -10, "resultScreen/results"); - resultsAnim.animation.addByPrefix("result", "results", 24, false); + resultsAnim.animation.addByPrefix("result", "results instance 1", 24, false); resultsAnim.animation.play("result"); add(resultsAnim); From 368b82846ec0d2b46cf1d9466de7e19aa7eff2f7 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 25 Mar 2024 21:15:20 -0400 Subject: [PATCH 55/75] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 3b6008899..9d2bfff5b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 3b6008899aefa1fe952c1cc5ebf9506464a86d3c +Subproject commit 9d2bfff5b595f6c0b1c3d7228d7cad7430694f57 From 32b21e21b275a1626e8ade58a8a2ef462045660a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 26 Mar 2024 01:12:22 -0400 Subject: [PATCH 56/75] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 526743915..201e342c7 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 52674391511577300cdb8c08df293ea72099aa82 +Subproject commit 201e342c7f879b829446f6caf4e4052bd5852cfb From 8943cd117e6a95e26f01d962966e44ed03c40539 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 26 Mar 2024 01:12:46 -0400 Subject: [PATCH 57/75] Make the error message a bit better if an Animate atlas fails to parse. --- .../funkin/play/character/AnimateAtlasCharacter.hx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/source/funkin/play/character/AnimateAtlasCharacter.hx b/source/funkin/play/character/AnimateAtlasCharacter.hx index 9e7aa98bf..f1dadf3e2 100644 --- a/source/funkin/play/character/AnimateAtlasCharacter.hx +++ b/source/funkin/play/character/AnimateAtlasCharacter.hx @@ -76,10 +76,17 @@ class AnimateAtlasCharacter extends BaseCharacter { trace('Creating Animate Atlas character: ' + this.characterId); - var atlasSprite:FlxAtlasSprite = loadAtlasSprite(); - setSprite(atlasSprite); + try + { + var atlasSprite:FlxAtlasSprite = loadAtlasSprite(); + setSprite(atlasSprite); - loadAnimations(); + loadAnimations(); + } + catch (e) + { + throw "Exception thrown while building FlxAtlasSprite: " + e; + } super.onCreate(event); } From 364652a22f8878b3fa87503ce372b2ab7f2ca039 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 26 Mar 2024 01:28:58 -0400 Subject: [PATCH 58/75] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 9d2bfff5b..366aab684 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 9d2bfff5b595f6c0b1c3d7228d7cad7430694f57 +Subproject commit 366aab68406311782e1c1c7a5e9de0a224ccf6fd From 6d4c2e804fb5e10c8ed93e982207fc61a0534d22 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 26 Mar 2024 01:30:48 -0400 Subject: [PATCH 59/75] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 201e342c7..5f1726f1b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 201e342c7f879b829446f6caf4e4052bd5852cfb +Subproject commit 5f1726f1b0c11fc747b7473708cf4e5f28be05f1 From e84fdf9fac2def3e6b880732b767585477ae5cc9 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 26 Mar 2024 19:38:42 -0400 Subject: [PATCH 60/75] Resolve FlxG.sound.music crash --- 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 a8cb879a3..62801028f 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2941,7 +2941,7 @@ class PlayState extends MusicBeatSubState if (overrideMusic) { // Stop the music. Do NOT destroy it, something still references it! - FlxG.sound.music.pause(); + if (FlxG.sound.music != null) FlxG.sound.music.pause(); if (vocals != null) { vocals.pause(); @@ -2951,7 +2951,7 @@ class PlayState extends MusicBeatSubState else { // Stop and destroy the music. - FlxG.sound.music.pause(); + if (FlxG.sound.music != null) FlxG.sound.music.pause(); if (vocals != null) { vocals.destroy(); From 9ebb253b53a931c019a54794b0805a4d3a830654 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 26 Mar 2024 19:38:56 -0400 Subject: [PATCH 61/75] Fix music updating issue. --- source/funkin/audio/FunkinSound.hx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 687860265..9d6dee6ef 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -50,6 +50,11 @@ class FunkinSound extends FlxSound implements ICloneable */ static var pool(default, null):FlxTypedGroup = new FlxTypedGroup(); + /** + * Calculate the current time of the sound. + * NOTE: You need to `add()` the sound to the scene for `update()` to increment the time. + */ + // public var muted(default, set):Bool = false; function set_muted(value:Bool):Bool @@ -392,8 +397,6 @@ class FunkinSound extends FlxSound implements ICloneable // Call onLoad() because the sound already loaded if (onLoad != null && sound._sound != null) onLoad(); - FlxG.sound.list.remove(FlxG.sound.music); - return sound; } From 291a9f630d9a29406d75fe8a50b49a24ab07bd2d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 26 Mar 2024 19:39:02 -0400 Subject: [PATCH 62/75] Update assets submodule --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 8bb6214e1..d2946604e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8bb6214e16c823b8b5a522a39b7a7e01d6283abf +Subproject commit d2946604e1dfb4fb80f6bf3cb73da8538963eade From 0e93f2ab278a8f4ff863ea267a037b026af97333 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 26 Mar 2024 21:30:08 -0400 Subject: [PATCH 63/75] Improve error logging when the game can't find a chart file. --- source/funkin/data/song/SongRegistry.hx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/funkin/data/song/SongRegistry.hx b/source/funkin/data/song/SongRegistry.hx index d82e184a5..4fdf5d0df 100644 --- a/source/funkin/data/song/SongRegistry.hx +++ b/source/funkin/data/song/SongRegistry.hx @@ -431,7 +431,11 @@ class SongRegistry extends BaseRegistry { variation = variation == null ? Constants.DEFAULT_VARIATION : variation; var entryFilePath:String = Paths.json('$dataFilePath/$id/$id-metadata${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}'); - if (!openfl.Assets.exists(entryFilePath)) return null; + if (!openfl.Assets.exists(entryFilePath)) + { + trace(' [WARN] Could not locate file $entryFilePath'); + return null; + } var rawJson:Null = openfl.Assets.getText(entryFilePath); if (rawJson == null) return null; rawJson = rawJson.trim(); From d373e1bfed44bb38daef38c43615bafdde14ba6c Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 26 Mar 2024 21:30:25 -0400 Subject: [PATCH 64/75] Implement a bunch of updated charts. --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 5f1726f1b..08e095b6e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5f1726f1b0c11fc747b7473708cf4e5f28be05f1 +Subproject commit 08e095b6e876b9241a0634a4021e9fbb14fe1304 From 6b35898ed9235c5f205aebc4fee3d9693dccb049 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 26 Mar 2024 23:24:59 -0400 Subject: [PATCH 65/75] Fix some issues with Weekend 1. --- assets | 2 +- source/funkin/audio/FunkinSound.hx | 18 +++++++++++------- source/funkin/audio/SoundGroup.hx | 9 ++++++--- source/funkin/audio/VoicesGroup.hx | 2 ++ source/funkin/play/PlayState.hx | 8 +++++--- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/assets b/assets index 5f1726f1b..0a2b524a8 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5f1726f1b0c11fc747b7473708cf4e5f28be05f1 +Subproject commit 0a2b524a8dd1d94909e2f078dbdcb191e1b55b8c diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 687860265..6520ff27f 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -1,17 +1,18 @@ package funkin.audio; -import flixel.sound.FlxSound; import flixel.group.FlxGroup.FlxTypedGroup; -import flixel.util.FlxSignal.FlxTypedSignal; +import flixel.math.FlxMath; +import flixel.sound.FlxSound; import flixel.system.FlxAssets.FlxSoundAsset; -import funkin.util.tools.ICloneable; +import flixel.tweens.FlxTween; +import flixel.util.FlxSignal.FlxTypedSignal; +import funkin.audio.waveform.WaveformData; +import funkin.audio.waveform.WaveformDataParser; import funkin.data.song.SongData.SongMusicData; import funkin.data.song.SongRegistry; -import funkin.audio.waveform.WaveformData; -import openfl.media.SoundMixer; -import funkin.audio.waveform.WaveformDataParser; -import flixel.math.FlxMath; +import funkin.util.tools.ICloneable; import openfl.Assets; +import openfl.media.SoundMixer; #if (openfl >= "8.0.0") import openfl.utils.AssetType; #end @@ -325,6 +326,7 @@ class FunkinSound extends FlxSound implements ICloneable if (FlxG.sound.music != null) { + FlxG.sound.music.fadeTween?.cancel(); FlxG.sound.music.stop(); FlxG.sound.music.kill(); } @@ -401,6 +403,8 @@ class FunkinSound extends FlxSound implements ICloneable { // trace('[FunkinSound] Destroying sound "${this._label}"'); super.destroy(); + FlxTween.cancelTweensOf(this); + this._label = 'unknown'; } /** diff --git a/source/funkin/audio/SoundGroup.hx b/source/funkin/audio/SoundGroup.hx index 9a754049b..020d5f5bb 100644 --- a/source/funkin/audio/SoundGroup.hx +++ b/source/funkin/audio/SoundGroup.hx @@ -152,9 +152,12 @@ class SoundGroup extends FlxTypedGroup */ public function stop() { - forEachAlive(function(sound:FunkinSound) { - sound.stop(); - }); + if (members != null) + { + forEachAlive(function(sound:FunkinSound) { + sound.stop(); + }); + } } public override function destroy() diff --git a/source/funkin/audio/VoicesGroup.hx b/source/funkin/audio/VoicesGroup.hx index 5daebc89d..91054cfb0 100644 --- a/source/funkin/audio/VoicesGroup.hx +++ b/source/funkin/audio/VoicesGroup.hx @@ -160,7 +160,9 @@ class VoicesGroup extends SoundGroup public override function destroy():Void { playerVoices.destroy(); + playerVoices = null; opponentVoices.destroy(); + opponentVoices = null; super.destroy(); } } diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 169809a63..6dc41e3f9 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1925,14 +1925,14 @@ class PlayState extends MusicBeatSubState FlxG.sound.music.play(true, startTimestamp - Conductor.instance.instrumentalOffset); FlxG.sound.music.pitch = playbackRate; - // I am going insane. + // Prevent the volume from being wrong. FlxG.sound.music.volume = 1.0; - FlxG.sound.music.fadeTween?.cancel(); trace('Playing vocals...'); add(vocals); vocals.play(); + vocals.volume = 1.0; vocals.pitch = playbackRate; resyncVocals(); @@ -2927,6 +2927,9 @@ class PlayState extends MusicBeatSubState // If the camera is being tweened, stop it. cancelAllCameraTweens(); + // Dispatch the destroy event. + dispatchEvent(new ScriptEvent(DESTROY, false)); + if (currentConversation != null) { remove(currentConversation); @@ -2970,7 +2973,6 @@ class PlayState extends MusicBeatSubState { remove(currentStage); currentStage.kill(); - dispatchEvent(new ScriptEvent(DESTROY, false)); currentStage = null; } From 5ec3546bbc8f25e7131b8de6460720e6ca310fbb Mon Sep 17 00:00:00 2001 From: FabsTheFabs Date: Wed, 27 Mar 2024 04:31:03 +0000 Subject: [PATCH 66/75] fixed song delay and made volume consistent across all tracks --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 5f1726f1b..18af5434e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 5f1726f1b0c11fc747b7473708cf4e5f28be05f1 +Subproject commit 18af5434e9aaaec5cdd1b20c4cfe11786db303d8 From 4209c5156ee6e211463808d83e0d34ec0769318b Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 27 Mar 2024 00:39:48 -0400 Subject: [PATCH 67/75] Readd a missing sound in the story menu --- source/funkin/ui/story/StoryMenuState.hx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index d3aa68c49..5881ef624 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -438,6 +438,8 @@ class StoryMenuState extends MusicBeatState } } + FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4); + updateText(); updateBackground(previousLevelId); updateProps(); @@ -481,6 +483,7 @@ class StoryMenuState extends MusicBeatState if (hasChanged) { buildDifficultySprite(); + FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4); // Disable the funny music thing for now. // funnyMusicThing(); } From 982616e2ba1b2338f5bd5e03b1255730f0abba21 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 27 Mar 2024 01:05:53 -0400 Subject: [PATCH 68/75] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 0a2b524a8..90d7bcfaf 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 0a2b524a8dd1d94909e2f078dbdcb191e1b55b8c +Subproject commit 90d7bcfaf9f46303c1b644bd36350de80df9f2c6 From b3076c7ec479f70255155878358fadad3508963e Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 27 Mar 2024 01:07:37 -0400 Subject: [PATCH 69/75] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 18af5434e..fda21459b 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 18af5434e9aaaec5cdd1b20c4cfe11786db303d8 +Subproject commit fda21459bdbf6198c1c39d60e347782d7286eda6 From 75b8336dce4ca4b04f01fd9e8a715af3cdbbe62c Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 27 Mar 2024 01:09:24 -0400 Subject: [PATCH 70/75] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index 08e095b6e..8013845e3 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 08e095b6e876b9241a0634a4021e9fbb14fe1304 +Subproject commit 8013845e331015b40c4cc35230f6d02bd2148d52 From 843095e3deb7619bab5d0a97200370072b579acc Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 27 Mar 2024 01:25:38 -0400 Subject: [PATCH 71/75] Add debug logging to Flixel debugger for miss judgements --- source/funkin/play/scoring/Scoring.hx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/funkin/play/scoring/Scoring.hx b/source/funkin/play/scoring/Scoring.hx index edfb2cae7..744091b44 100644 --- a/source/funkin/play/scoring/Scoring.hx +++ b/source/funkin/play/scoring/Scoring.hx @@ -43,7 +43,7 @@ class Scoring case WEEK7: scoreNoteWEEK7(msTiming); case PBOT1: scoreNotePBOT1(msTiming); default: - trace('ERROR: Unknown scoring system: ' + scoringSystem); + FlxG.log.error('Unknown scoring system: ${scoringSystem}'); 0; } } @@ -62,7 +62,7 @@ class Scoring case WEEK7: judgeNoteWEEK7(msTiming); case PBOT1: judgeNotePBOT1(msTiming); default: - trace('ERROR: Unknown scoring system: ' + scoringSystem); + FlxG.log.error('Unknown scoring system: ${scoringSystem}'); 'miss'; } } @@ -145,7 +145,9 @@ class Scoring case(_ < PBOT1_PERFECT_THRESHOLD) => true: PBOT1_MAX_SCORE; default: + // Fancy equation. var factor:Float = 1.0 - (1.0 / (1.0 + Math.exp(-PBOT1_SCORING_SLOPE * (absTiming - PBOT1_SCORING_OFFSET)))); + var score:Int = Std.int(PBOT1_MAX_SCORE * factor + PBOT1_MIN_SCORE); score; @@ -169,6 +171,7 @@ class Scoring case(_ < PBOT1_SHIT_THRESHOLD) => true: 'shit'; default: + FlxG.log.warn('Missed note: Bad timing ($absTiming < $PBOT1_SHIT_THRESHOLD)'); 'miss'; } } @@ -257,6 +260,7 @@ class Scoring case(_ < LEGACY_HIT_WINDOW * LEGACY_SHIT_THRESHOLD) => true: 'shit'; default: + FlxG.log.warn('Missed note: Bad timing ($absTiming < $LEGACY_SHIT_THRESHOLD)'); 'miss'; } } @@ -336,6 +340,7 @@ class Scoring } else { + FlxG.log.warn('Missed note: Bad timing ($absTiming < $WEEK7_HIT_WINDOW)'); return 'miss'; } } From 31817959855104d50f54b814697e0862c1b1901a Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 27 Mar 2024 20:58:58 -0400 Subject: [PATCH 72/75] remove trace() calls from ages ago while im here --- source/funkin/ui/freeplay/DJBoyfriend.hx | 1 - source/funkin/ui/freeplay/SongMenuItem.hx | 2 -- 2 files changed, 3 deletions(-) diff --git a/source/funkin/ui/freeplay/DJBoyfriend.hx b/source/funkin/ui/freeplay/DJBoyfriend.hx index c77762d73..33f264301 100644 --- a/source/funkin/ui/freeplay/DJBoyfriend.hx +++ b/source/funkin/ui/freeplay/DJBoyfriend.hx @@ -49,7 +49,6 @@ class DJBoyfriend extends FlxAtlasSprite }; setupAnimations(); - trace(listAnimations()); FlxG.debugger.track(this); FlxG.console.registerObject("dj", this); diff --git a/source/funkin/ui/freeplay/SongMenuItem.hx b/source/funkin/ui/freeplay/SongMenuItem.hx index c20d81328..bffa821b3 100644 --- a/source/funkin/ui/freeplay/SongMenuItem.hx +++ b/source/funkin/ui/freeplay/SongMenuItem.hx @@ -182,8 +182,6 @@ class SongMenuItem extends FlxSpriteGroup { var charPath:String = "freeplay/icons/"; - trace(char); - // TODO: Put this in the character metadata where it belongs. // TODO: Also, can use CharacterDataParser.getCharPixelIconAsset() switch (char) From 17df6a515fa3c79e2bd25f7fbdd4f9d545564128 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Wed, 27 Mar 2024 22:11:31 -0400 Subject: [PATCH 73/75] assets submod --- assets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets b/assets index d2946604e..485243fdd 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit d2946604e1dfb4fb80f6bf3cb73da8538963eade +Subproject commit 485243fdd44acbc4db6a97ec7bf10a8b18350be9 From 4cd2a622a966433a14dfa8ff5d656bf3a2e56b98 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 28 Mar 2024 04:34:20 -0400 Subject: [PATCH 74/75] Update ART submodule! --- art | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/art b/art index 03e7c2a23..00463685f 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit 03e7c2a2353b184e45955c96d763b7cdf1acbc34 +Subproject commit 00463685fa570f0c853d08e250b46ef80f30bc48 From 8a9eee3aa3aab6280139fbc33b9c45e8a33017f9 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 28 Mar 2024 04:34:43 -0400 Subject: [PATCH 75/75] Rework Funkin Preloader and added "Touch to Play" graphic --- Project.xml | 12 +- source/funkin/Preloader.hx | 65 -- .../ui/transition/preload/FunkinPreloader.hx | 994 ++++++++++++++++++ source/funkin/ui/transition/preload/README.md | 17 + source/funkin/util/Constants.hx | 64 +- source/funkin/util/MathUtil.hx | 30 + 6 files changed, 1114 insertions(+), 68 deletions(-) delete mode 100644 source/funkin/Preloader.hx create mode 100644 source/funkin/ui/transition/preload/FunkinPreloader.hx create mode 100644 source/funkin/ui/transition/preload/README.md diff --git a/Project.xml b/Project.xml index ffc8382a4..c0da3c89a 100644 --- a/Project.xml +++ b/Project.xml @@ -4,12 +4,19 @@ - + + + + - + @@ -95,6 +102,7 @@ + diff --git a/source/funkin/Preloader.hx b/source/funkin/Preloader.hx deleted file mode 100644 index 157015ba6..000000000 --- a/source/funkin/Preloader.hx +++ /dev/null @@ -1,65 +0,0 @@ -package funkin; - -import flash.Lib; -import flash.display.Bitmap; -import flash.display.BitmapData; -import flash.display.Sprite; -import flixel.system.FlxBasePreloader; -import openfl.display.Sprite; -import funkin.util.CLIUtil; -import openfl.text.TextField; -import openfl.text.TextFormat; -import flixel.system.FlxAssets; - -@:bitmap('art/preloaderArt.png') -class LogoImage extends BitmapData {} - -class Preloader extends FlxBasePreloader -{ - public function new(MinDisplayTime:Float = 0, ?AllowedURLs:Array) - { - super(MinDisplayTime, AllowedURLs); - - CLIUtil.resetWorkingDir(); // Bug fix for drag-and-drop. - } - - var logo:Sprite; - var _text:TextField; - - override function create():Void - { - this._width = Lib.current.stage.stageWidth; - this._height = Lib.current.stage.stageHeight; - - _text = new TextField(); - _text.width = 500; - _text.text = "Loading FNF"; - _text.defaultTextFormat = new TextFormat(FlxAssets.FONT_DEFAULT, 16, 0xFFFFFFFF); - _text.embedFonts = true; - _text.selectable = false; - _text.multiline = false; - _text.wordWrap = false; - _text.autoSize = LEFT; - _text.x = 2; - _text.y = 2; - addChild(_text); - - var ratio:Float = this._width / 2560; // This allows us to scale assets depending on the size of the screen. - - logo = new Sprite(); - logo.addChild(new Bitmap(new LogoImage(0, 0))); // Sets the graphic of the sprite to a Bitmap object, which uses our embedded BitmapData class. - logo.scaleX = logo.scaleY = ratio; - logo.x = ((this._width) / 2) - ((logo.width) / 2); - logo.y = (this._height / 2) - ((logo.height) / 2); - // addChild(logo); // Adds the graphic to the NMEPreloader's buffer. - - super.create(); - } - - override function update(Percent:Float):Void - { - _text.text = "FNF: " + Math.round(Percent * 100) + "%"; - - super.update(Percent); - } -} diff --git a/source/funkin/ui/transition/preload/FunkinPreloader.hx b/source/funkin/ui/transition/preload/FunkinPreloader.hx new file mode 100644 index 000000000..89a8c1140 --- /dev/null +++ b/source/funkin/ui/transition/preload/FunkinPreloader.hx @@ -0,0 +1,994 @@ +package funkin.ui.transition.preload; + +import openfl.events.MouseEvent; +import flash.display.Bitmap; +import flash.display.BitmapData; +import flash.display.BlendMode; +import flash.display.Sprite; +import flash.Lib; +import flixel.system.FlxBasePreloader; +import funkin.modding.PolymodHandler; +import funkin.play.character.CharacterData.CharacterDataParser; +import funkin.util.MathUtil; +import lime.app.Future; +import lime.math.Rectangle; +import openfl.display.Sprite; +import openfl.text.TextField; +import openfl.text.TextFormat; +import openfl.text.TextFormatAlign; + +using StringTools; + +// Annotation embeds the asset in the executable for faster loading. +// Polymod can't override this, so we can't use this technique elsewhere. + +@:bitmap("art/preloaderArt.png") +class LogoImage extends BitmapData {} + +#if TOUCH_HERE_TO_PLAY +@:bitmap('art/touchHereToPlay.png') +class TouchHereToPlayImage extends BitmapData {} +#end + +/** + * This preloader displays a logo while the game downloads assets. + */ +class FunkinPreloader extends FlxBasePreloader +{ + /** + * The logo image width at the base resolution. + * Scaled up/down appropriately as needed. + */ + static final BASE_WIDTH:Float = 1280; + + /** + * Margin at the sides and bottom, around the loading bar. + */ + static final BAR_PADDING:Float = 20; + + static final BAR_HEIGHT:Int = 20; + + /** + * Logo takes this long (in seconds) to fade in. + */ + static final LOGO_FADE_TIME:Float = 2.5; + + // Ratio between window size and BASE_WIDTH + var ratio:Float = 0; + + var currentState:FunkinPreloaderState = FunkinPreloaderState.NotStarted; + + // private var downloadingAssetsStartTime:Float = -1; + private var downloadingAssetsPercent:Float = -1; + private var downloadingAssetsComplete:Bool = false; + + private var preloadingPlayAssetsPercent:Float = -1; + private var preloadingPlayAssetsStartTime:Float = -1; + private var preloadingPlayAssetsComplete:Bool = false; + + private var cachingGraphicsPercent:Float = -1; + private var cachingGraphicsStartTime:Float = -1; + private var cachingGraphicsComplete:Bool = false; + + private var cachingAudioPercent:Float = -1; + private var cachingAudioStartTime:Float = -1; + private var cachingAudioComplete:Bool = false; + + private var cachingDataPercent:Float = -1; + private var cachingDataStartTime:Float = -1; + private var cachingDataComplete:Bool = false; + + private var parsingSpritesheetsPercent:Float = -1; + private var parsingSpritesheetsStartTime:Float = -1; + private var parsingSpritesheetsComplete:Bool = false; + + private var parsingStagesPercent:Float = -1; + private var parsingStagesStartTime:Float = -1; + private var parsingStagesComplete:Bool = false; + + private var parsingCharactersPercent:Float = -1; + private var parsingCharactersStartTime:Float = -1; + private var parsingCharactersComplete:Bool = false; + + private var parsingSongsPercent:Float = -1; + private var parsingSongsStartTime:Float = -1; + private var parsingSongsComplete:Bool = false; + + private var initializingScriptsPercent:Float = -1; + + private var cachingCoreAssetsPercent:Float = -1; + + /** + * The timestamp when the other steps completed and the `Finishing up` step started. + */ + private var completeTime:Float = -1; + + // Graphics + var logo:Bitmap; + #if TOUCH_HERE_TO_PLAY + var touchHereToPlay:Bitmap; + #end + var progressBar:Bitmap; + var progressLeftText:TextField; + var progressRightText:TextField; + + public function new() + { + super(Constants.PRELOADER_MIN_STAGE_TIME, Constants.SITE_LOCK); + + // We can't even call trace() yet, until Flixel loads. + trace('Initializing custom preloader...'); + + this.siteLockTitleText = Constants.SITE_LOCK_TITLE; + this.siteLockBodyText = Constants.SITE_LOCK_DESC; + } + + override function create():Void + { + // Nothing happens in the base preloader. + super.create(); + + // Background color. + Lib.current.stage.color = Constants.COLOR_PRELOADER_BG; + + // Width and height of the preloader. + this._width = Lib.current.stage.stageWidth; + this._height = Lib.current.stage.stageHeight; + + // Scale assets to the screen size. + ratio = this._width / BASE_WIDTH / 2.0; + + // Create the logo. + logo = createBitmap(LogoImage, function(bmp:Bitmap) { + // Scale and center the logo. + // We have to do this inside the async call, after the image size is known. + bmp.scaleX = bmp.scaleY = ratio; + bmp.x = (this._width - bmp.width) / 2; + bmp.y = (this._height - bmp.height) / 2; + }); + addChild(logo); + + #if TOUCH_HERE_TO_PLAY + touchHereToPlay = createBitmap(TouchHereToPlayImage, function(bmp:Bitmap) { + // Scale and center the touch to start image. + // We have to do this inside the async call, after the image size is known. + bmp.scaleX = bmp.scaleY = ratio; + bmp.x = (this._width - bmp.width) / 2; + bmp.y = (this._height - bmp.height) / 2; + }); + touchHereToPlay.alpha = 0.0; + addChild(touchHereToPlay); + #end + + // Create the progress bar. + progressBar = new Bitmap(new BitmapData(1, BAR_HEIGHT, true, Constants.COLOR_PRELOADER_BAR)); + progressBar.x = BAR_PADDING; + progressBar.y = this._height - BAR_PADDING - BAR_HEIGHT; + addChild(progressBar); + + // Create the progress message. + progressLeftText = new TextField(); + + var progressLeftTextFormat = new TextFormat("VCR OSD Mono", 16, Constants.COLOR_PRELOADER_BAR, true); + progressLeftTextFormat.align = TextFormatAlign.LEFT; + progressLeftText.defaultTextFormat = progressLeftTextFormat; + + progressLeftText.selectable = false; + progressLeftText.width = this._width - BAR_PADDING * 2; + progressLeftText.text = 'Downloading assets...'; + progressLeftText.x = BAR_PADDING; + progressLeftText.y = this._height - BAR_PADDING - BAR_HEIGHT - 16 - 4; + addChild(progressLeftText); + + // Create the progress %. + progressRightText = new TextField(); + + var progressRightTextFormat = new TextFormat("VCR OSD Mono", 16, Constants.COLOR_PRELOADER_BAR, true); + progressRightTextFormat.align = TextFormatAlign.RIGHT; + progressRightText.defaultTextFormat = progressRightTextFormat; + + progressRightText.selectable = false; + progressRightText.width = this._width - BAR_PADDING * 2; + progressRightText.text = '0%'; + progressRightText.x = BAR_PADDING; + progressRightText.y = this._height - BAR_PADDING - BAR_HEIGHT - 16 - 4; + addChild(progressRightText); + } + + var lastElapsed:Float = 0.0; + + override function update(percent:Float):Void + { + var elapsed:Float = (Date.now().getTime() - this._startTime) / 1000.0; + // trace('Time since last frame: ' + (lastElapsed - elapsed)); + + downloadingAssetsPercent = percent; + var loadPercent:Float = updateState(percent, elapsed); + updateGraphics(loadPercent, elapsed); + + lastElapsed = elapsed; + } + + function updateState(percent:Float, elapsed:Float):Float + { + switch (currentState) + { + case FunkinPreloaderState.NotStarted: + if (downloadingAssetsPercent > 0.0) currentState = FunkinPreloaderState.DownloadingAssets; + + return percent; + + case FunkinPreloaderState.DownloadingAssets: + // Sometimes percent doesn't go to 100%, it's a floating point error. + if (downloadingAssetsPercent >= 1.0 + || (elapsed > Constants.PRELOADER_MIN_STAGE_TIME + && downloadingAssetsComplete)) currentState = FunkinPreloaderState.PreloadingPlayAssets; + + return percent; + + case FunkinPreloaderState.PreloadingPlayAssets: + if (preloadingPlayAssetsPercent < 0.0) + { + preloadingPlayAssetsStartTime = elapsed; + preloadingPlayAssetsPercent = 0.0; + + // This is quick enough to do synchronously. + // Assets.initialize(); + + /* + // Make a future to retrieve the manifest + var future:Future = Assets.preloadLibrary('gameplay'); + + future.onProgress((loaded:Int, total:Int) -> { + preloadingPlayAssetsPercent = loaded / total; + }); + future.onComplete((library:lime.utils.AssetLibrary) -> { + }); + */ + + // TODO: Reimplement this. + preloadingPlayAssetsPercent = 1.0; + preloadingPlayAssetsComplete = true; + return 0.0; + } + else if (Constants.PRELOADER_MIN_STAGE_TIME > 0) + { + var elapsedPreloadingPlayAssets:Float = elapsed - preloadingPlayAssetsStartTime; + if (preloadingPlayAssetsComplete && elapsedPreloadingPlayAssets >= Constants.PRELOADER_MIN_STAGE_TIME) + { + currentState = FunkinPreloaderState.InitializingScripts; + return 0.0; + } + else + { + // We need to return SIMULATED progress here. + if (preloadingPlayAssetsPercent < (elapsedPreloadingPlayAssets / Constants.PRELOADER_MIN_STAGE_TIME)) return preloadingPlayAssetsPercent; + else + return elapsedPreloadingPlayAssets / Constants.PRELOADER_MIN_STAGE_TIME; + } + } + else + { + if (preloadingPlayAssetsComplete) currentState = FunkinPreloaderState.InitializingScripts; + } + + return preloadingPlayAssetsPercent; + + case FunkinPreloaderState.InitializingScripts: + if (initializingScriptsPercent < 0.0) + { + initializingScriptsPercent = 0.0; + + /* + var future:Future> = []; // PolymodHandler.loadNoModsAsync(); + + future.onProgress((loaded:Int, total:Int) -> { + trace('PolymodHandler.loadNoModsAsync() progress: ' + loaded + '/' + total); + initializingScriptsPercent = loaded / total; + }); + future.onComplete((result:Array) -> { + trace('Completed initializing scripts: ' + result); + }); + */ + + initializingScriptsPercent = 1.0; + currentState = FunkinPreloaderState.CachingGraphics; + return 0.0; + } + + return initializingScriptsPercent; + + case CachingGraphics: + if (cachingGraphicsPercent < 0) + { + cachingGraphicsPercent = 0.0; + cachingGraphicsStartTime = elapsed; + + /* + var assetsToCache:Array = []; // Assets.listGraphics('core'); + + var future:Future> = []; // Assets.cacheAssets(assetsToCache); + future.onProgress((loaded:Int, total:Int) -> { + cachingGraphicsPercent = loaded / total; + }); + future.onComplete((_result) -> { + trace('Completed caching graphics.'); + }); + */ + + // TODO: Reimplement this. + cachingGraphicsPercent = 1.0; + cachingGraphicsComplete = true; + return 0.0; + } + else if (Constants.PRELOADER_MIN_STAGE_TIME > 0) + { + var elapsedCachingGraphics:Float = elapsed - cachingGraphicsStartTime; + if (cachingGraphicsComplete && elapsedCachingGraphics >= Constants.PRELOADER_MIN_STAGE_TIME) + { + currentState = FunkinPreloaderState.CachingAudio; + return 0.0; + } + else + { + if (cachingGraphicsPercent < (elapsedCachingGraphics / Constants.PRELOADER_MIN_STAGE_TIME)) + { + // Return real progress if it's lower. + return cachingGraphicsPercent; + } + else + { + // Return simulated progress if it's higher. + return elapsedCachingGraphics / Constants.PRELOADER_MIN_STAGE_TIME; + } + } + } + else + { + if (cachingGraphicsComplete) + { + currentState = FunkinPreloaderState.CachingAudio; + return 0.0; + } + else + { + return cachingGraphicsPercent; + } + } + + case CachingAudio: + if (cachingAudioPercent < 0) + { + cachingAudioPercent = 0.0; + cachingAudioStartTime = elapsed; + + var assetsToCache:Array = []; // Assets.listSound('core'); + + /* + var future:Future> = []; // Assets.cacheAssets(assetsToCache); + + future.onProgress((loaded:Int, total:Int) -> { + cachingAudioPercent = loaded / total; + }); + future.onComplete((_result) -> { + trace('Completed caching audio.'); + }); + */ + + // TODO: Reimplement this. + cachingAudioPercent = 1.0; + cachingAudioComplete = true; + return 0.0; + } + else if (Constants.PRELOADER_MIN_STAGE_TIME > 0) + { + var elapsedCachingAudio:Float = elapsed - cachingAudioStartTime; + if (cachingAudioComplete && elapsedCachingAudio >= Constants.PRELOADER_MIN_STAGE_TIME) + { + currentState = FunkinPreloaderState.CachingData; + return 0.0; + } + else + { + // We need to return SIMULATED progress here. + if (cachingAudioPercent < (elapsedCachingAudio / Constants.PRELOADER_MIN_STAGE_TIME)) + { + return cachingAudioPercent; + } + else + { + return elapsedCachingAudio / Constants.PRELOADER_MIN_STAGE_TIME; + } + } + } + else + { + if (cachingAudioComplete) + { + currentState = FunkinPreloaderState.CachingData; + return 0.0; + } + else + { + return cachingAudioPercent; + } + } + + case CachingData: + if (cachingDataPercent < 0) + { + cachingDataPercent = 0.0; + cachingDataStartTime = elapsed; + + var assetsToCache:Array = []; + var sparrowFramesToCache:Array = []; + + // Core files + // assetsToCache = assetsToCache.concat(Assets.listText('core')); + // assetsToCache = assetsToCache.concat(Assets.listJSON('core')); + // Core spritesheets + // assetsToCache = assetsToCache.concat(Assets.listXML('core')); + + // Gameplay files + // assetsToCache = assetsToCache.concat(Assets.listText('gameplay')); + // assetsToCache = assetsToCache.concat(Assets.listJSON('gameplay')); + // We're not caching gameplay spritesheets here because they're fetched on demand. + + /* + var future:Future> = []; + // Assets.cacheAssets(assetsToCache, true); + future.onProgress((loaded:Int, total:Int) -> { + cachingDataPercent = loaded / total; + }); + future.onComplete((_result) -> { + trace('Completed caching data.'); + }); + */ + cachingDataPercent = 1.0; + cachingDataComplete = true; + return 0.0; + } + else if (Constants.PRELOADER_MIN_STAGE_TIME > 0) + { + var elapsedCachingData:Float = elapsed - cachingDataStartTime; + if (cachingDataComplete && elapsedCachingData >= Constants.PRELOADER_MIN_STAGE_TIME) + { + currentState = FunkinPreloaderState.ParsingSpritesheets; + return 0.0; + } + else + { + // We need to return SIMULATED progress here. + if (cachingDataPercent < (elapsedCachingData / Constants.PRELOADER_MIN_STAGE_TIME)) return cachingDataPercent; + else + return elapsedCachingData / Constants.PRELOADER_MIN_STAGE_TIME; + } + } + else + { + if (cachingDataComplete) + { + currentState = FunkinPreloaderState.ParsingSpritesheets; + return 0.0; + } + } + + return cachingDataPercent; + + case ParsingSpritesheets: + if (parsingSpritesheetsPercent < 0) + { + parsingSpritesheetsPercent = 0.0; + parsingSpritesheetsStartTime = elapsed; + + // Core spritesheets + var sparrowFramesToCache = []; // Assets.listXML('core').map((xml:String) -> xml.replace('.xml', '').replace('core:assets/core/', '')); + // We're not caching gameplay spritesheets here because they're fetched on demand. + + /* + var future:Future> = []; // Assets.cacheSparrowFrames(sparrowFramesToCache, true); + future.onProgress((loaded:Int, total:Int) -> { + parsingSpritesheetsPercent = loaded / total; + }); + future.onComplete((_result) -> { + trace('Completed parsing spritesheets.'); + }); + */ + parsingSpritesheetsPercent = 1.0; + parsingSpritesheetsComplete = true; + return 0.0; + } + else if (Constants.PRELOADER_MIN_STAGE_TIME > 0) + { + var elapsedParsingSpritesheets:Float = elapsed - parsingSpritesheetsStartTime; + if (parsingSpritesheetsComplete && elapsedParsingSpritesheets >= Constants.PRELOADER_MIN_STAGE_TIME) + { + currentState = FunkinPreloaderState.ParsingStages; + return 0.0; + } + else + { + // We need to return SIMULATED progress here. + if (parsingSpritesheetsPercent < (elapsedParsingSpritesheets / Constants.PRELOADER_MIN_STAGE_TIME)) return parsingSpritesheetsPercent; + else + return elapsedParsingSpritesheets / Constants.PRELOADER_MIN_STAGE_TIME; + } + } + else + { + if (parsingSpritesheetsComplete) + { + currentState = FunkinPreloaderState.ParsingStages; + return 0.0; + } + } + + return parsingSpritesheetsPercent; + + case ParsingStages: + if (parsingStagesPercent < 0) + { + parsingStagesPercent = 0.0; + parsingStagesStartTime = elapsed; + + /* + // TODO: Reimplement this. + var future:Future> = []; // StageDataParser.loadStageCacheAsync(); + + future.onProgress((loaded:Int, total:Int) -> { + parsingStagesPercent = loaded / total; + }); + + future.onComplete((_result) -> { + trace('Completed parsing stages.'); + }); + */ + + parsingStagesPercent = 1.0; + parsingStagesComplete = true; + return 0.0; + } + else if (Constants.PRELOADER_MIN_STAGE_TIME > 0) + { + var elapsedParsingStages:Float = elapsed - parsingStagesStartTime; + if (parsingStagesComplete && elapsedParsingStages >= Constants.PRELOADER_MIN_STAGE_TIME) + { + currentState = FunkinPreloaderState.ParsingCharacters; + return 0.0; + } + else + { + // We need to return SIMULATED progress here. + if (parsingStagesPercent < (elapsedParsingStages / Constants.PRELOADER_MIN_STAGE_TIME)) return parsingStagesPercent; + else + return elapsedParsingStages / Constants.PRELOADER_MIN_STAGE_TIME; + } + } + else + { + if (parsingStagesComplete) + { + currentState = FunkinPreloaderState.ParsingCharacters; + return 0.0; + } + } + + return parsingStagesPercent; + + case ParsingCharacters: + if (parsingCharactersPercent < 0) + { + parsingCharactersPercent = 0.0; + parsingCharactersStartTime = elapsed; + + /* + // TODO: Reimplement this. + var future:Future> = []; // CharacterDataParser.loadCharacterCacheAsync(); + + future.onProgress((loaded:Int, total:Int) -> { + parsingCharactersPercent = loaded / total; + }); + + future.onComplete((_result) -> { + trace('Completed parsing characters.'); + }); + */ + + parsingCharactersPercent = 1.0; + parsingCharactersComplete = true; + return 0.0; + } + else if (Constants.PRELOADER_MIN_STAGE_TIME > 0) + { + var elapsedParsingCharacters:Float = elapsed - parsingCharactersStartTime; + if (parsingCharactersComplete && elapsedParsingCharacters >= Constants.PRELOADER_MIN_STAGE_TIME) + { + currentState = FunkinPreloaderState.ParsingSongs; + return 0.0; + } + else + { + // We need to return SIMULATED progress here. + if (parsingCharactersPercent < (elapsedParsingCharacters / Constants.PRELOADER_MIN_STAGE_TIME)) return parsingCharactersPercent; + else + return elapsedParsingCharacters / Constants.PRELOADER_MIN_STAGE_TIME; + } + } + else + { + if (parsingStagesComplete) + { + currentState = FunkinPreloaderState.ParsingSongs; + return 0.0; + } + } + + return parsingCharactersPercent; + + case ParsingSongs: + if (parsingSongsPercent < 0) + { + parsingSongsPercent = 0.0; + parsingSongsStartTime = elapsed; + + /* + // TODO: Reimplement this. + var future:Future> = ; + // SongDataParser.loadSongCacheAsync(); + + future.onProgress((loaded:Int, total:Int) -> { + parsingSongsPercent = loaded / total; + }); + + future.onComplete((_result) -> { + trace('Completed parsing songs.'); + }); + */ + + parsingSongsPercent = 1.0; + parsingSongsComplete = true; + + return 0.0; + } + else if (Constants.PRELOADER_MIN_STAGE_TIME > 0) + { + var elapsedParsingSongs:Float = elapsed - parsingSongsStartTime; + if (parsingSongsComplete && elapsedParsingSongs >= Constants.PRELOADER_MIN_STAGE_TIME) + { + currentState = FunkinPreloaderState.Complete; + return 0.0; + } + else + { + // We need to return SIMULATED progress here. + if (parsingSongsPercent < (elapsedParsingSongs / Constants.PRELOADER_MIN_STAGE_TIME)) + { + return parsingSongsPercent; + } + else + { + return elapsedParsingSongs / Constants.PRELOADER_MIN_STAGE_TIME; + } + } + } + else + { + if (parsingSongsComplete) + { + currentState = FunkinPreloaderState.Complete; + return 0.0; + } + else + { + return parsingSongsPercent; + } + } + case FunkinPreloaderState.Complete: + if (completeTime < 0) + { + completeTime = elapsed; + } + + return 1.0; + #if TOUCH_HERE_TO_PLAY + case FunkinPreloaderState.TouchHereToPlay: + if (completeTime < 0) + { + completeTime = elapsed; + } + + if (touchHereToPlay.alpha < 1.0) + { + touchHereToPlay.alpha = 1.0; + + addEventListener(MouseEvent.CLICK, onTouchHereToPlay); + } + + return 1.0; + #end + + default: + // Do nothing. + } + + return 0.0; + } + + #if TOUCH_HERE_TO_PLAY + function onTouchHereToPlay(e:MouseEvent):Void + { + removeEventListener(MouseEvent.CLICK, onTouchHereToPlay); + + // This is the actual thing that makes the game load. + immediatelyStartGame(); + } + #end + + static final TOTAL_STEPS:Int = 11; + static final ELLIPSIS_TIME:Float = 0.5; + + function updateGraphics(percent:Float, elapsed:Float):Void + { + // Render logo (including transitions) + if (completeTime > 0.0) + { + var elapsedFinished:Float = renderLogoFadeOut(elapsed); + // trace('Fading out logo... (' + elapsedFinished + 's)'); + if (elapsedFinished > LOGO_FADE_TIME) + { + #if TOUCH_HERE_TO_PLAY + // The logo has faded out, but we're not quite done yet. + // In order to prevent autoplay issues, we need the user to click after the loading finishes. + currentState = FunkinPreloaderState.TouchHereToPlay; + #else + immediatelyStartGame(); + #end + } + } + else + { + renderLogoFadeIn(elapsed); + } + + // Render progress bar + var maxWidth = this._width - BAR_PADDING * 2; + var barWidth = maxWidth * percent; + progressBar.width = barWidth; + + // Cycle ellipsis count to show loading + var ellipsisCount:Int = Std.int(elapsed / ELLIPSIS_TIME) % 3 + 1; + var ellipsis:String = ''; + for (i in 0...ellipsisCount) + ellipsis += '.'; + + // Render status text + switch (currentState) + { + // case FunkinPreloaderState.NotStarted: + default: + updateProgressLeftText('Loading (0/$TOTAL_STEPS)$ellipsis'); + case FunkinPreloaderState.DownloadingAssets: + updateProgressLeftText('Downloading assets (1/$TOTAL_STEPS)$ellipsis'); + case FunkinPreloaderState.PreloadingPlayAssets: + updateProgressLeftText('Preloading assets (2/$TOTAL_STEPS)$ellipsis'); + case FunkinPreloaderState.InitializingScripts: + updateProgressLeftText('Initializing scripts (3/$TOTAL_STEPS)$ellipsis'); + case FunkinPreloaderState.CachingGraphics: + updateProgressLeftText('Caching graphics (4/$TOTAL_STEPS)$ellipsis'); + case FunkinPreloaderState.CachingAudio: + updateProgressLeftText('Caching audio (5/$TOTAL_STEPS)$ellipsis'); + case FunkinPreloaderState.CachingData: + updateProgressLeftText('Caching data (6/$TOTAL_STEPS)$ellipsis'); + case FunkinPreloaderState.ParsingSpritesheets: + updateProgressLeftText('Parsing spritesheets (7/$TOTAL_STEPS)$ellipsis'); + case FunkinPreloaderState.ParsingStages: + updateProgressLeftText('Parsing stages (8/$TOTAL_STEPS)$ellipsis'); + case FunkinPreloaderState.ParsingCharacters: + updateProgressLeftText('Parsing characters (9/$TOTAL_STEPS)$ellipsis'); + case FunkinPreloaderState.ParsingSongs: + updateProgressLeftText('Parsing songs (10/$TOTAL_STEPS)$ellipsis'); + case FunkinPreloaderState.Complete: + updateProgressLeftText('Finishing up ($TOTAL_STEPS/$TOTAL_STEPS)$ellipsis'); + #if TOUCH_HERE_TO_PLAY + case FunkinPreloaderState.TouchHereToPlay: + updateProgressLeftText(null); + #end + } + + var percentage:Int = Math.floor(percent * 100); + trace('Preloader state: ' + currentState + ' (' + percentage + '%, ' + elapsed + 's)'); + + // Render percent text + progressRightText.text = '$percentage%'; + + super.update(percent); + } + + function updateProgressLeftText(text:Null):Void + { + if (progressLeftText != null) + { + if (text == null) + { + progressLeftText.alpha = 0.0; + } + else if (progressLeftText.text != text) + { + // We have to keep updating the text format, because the font can take a frame or two to load. + var progressLeftTextFormat = new TextFormat("VCR OSD Mono", 16, Constants.COLOR_PRELOADER_BAR, true); + progressLeftTextFormat.align = TextFormatAlign.LEFT; + progressLeftText.defaultTextFormat = progressLeftTextFormat; + progressLeftText.text = text; + } + } + } + + function immediatelyStartGame():Void + { + _loaded = true; + } + + /** + * Fade out the logo. + * @param elapsed Elapsed time since the preloader started. + * @return Elapsed time since the logo started fading out. + */ + function renderLogoFadeOut(elapsed:Float):Float + { + // Fade-out takes LOGO_FADE_TIME seconds. + var elapsedFinished = elapsed - completeTime; + + logo.alpha = 1.0 - MathUtil.easeInOutCirc(elapsedFinished / LOGO_FADE_TIME); + logo.scaleX = (1.0 - MathUtil.easeInBack(elapsedFinished / LOGO_FADE_TIME)) * ratio; + logo.scaleY = (1.0 - MathUtil.easeInBack(elapsedFinished / LOGO_FADE_TIME)) * ratio; + logo.x = (this._width - logo.width) / 2; + logo.y = (this._height - logo.height) / 2; + + // Fade out progress bar too. + progressBar.alpha = logo.alpha; + progressLeftText.alpha = logo.alpha; + progressRightText.alpha = logo.alpha; + + return elapsedFinished; + } + + function renderLogoFadeIn(elapsed:Float):Void + { + // Fade-in takes LOGO_FADE_TIME seconds. + logo.alpha = MathUtil.easeInOutCirc(elapsed / LOGO_FADE_TIME); + logo.scaleX = MathUtil.easeOutBack(elapsed / LOGO_FADE_TIME) * ratio; + logo.scaleY = MathUtil.easeOutBack(elapsed / LOGO_FADE_TIME) * ratio; + logo.x = (this._width - logo.width) / 2; + logo.y = (this._height - logo.height) / 2; + } + + #if html5 + // These fields only exist on Web builds. + + /** + * Format the layout of the site lock screen. + */ + override function createSiteLockFailureScreen():Void + { + addChild(createSiteLockFailureBackground(Constants.COLOR_PRELOADER_LOCK_BG, Constants.COLOR_PRELOADER_LOCK_BG)); + addChild(createSiteLockFailureIcon(Constants.COLOR_PRELOADER_LOCK_FG, 0.9)); + addChild(createSiteLockFailureText(30)); + } + + /** + * Format the text of the site lock screen. + */ + override function adjustSiteLockTextFields(titleText:TextField, bodyText:TextField, hyperlinkText:TextField):Void + { + var titleFormat = titleText.defaultTextFormat; + titleFormat.align = TextFormatAlign.CENTER; + titleFormat.color = Constants.COLOR_PRELOADER_LOCK_FONT; + titleText.setTextFormat(titleFormat); + + var bodyFormat = bodyText.defaultTextFormat; + bodyFormat.align = TextFormatAlign.CENTER; + bodyFormat.color = Constants.COLOR_PRELOADER_LOCK_FONT; + bodyText.setTextFormat(bodyFormat); + + var hyperlinkFormat = hyperlinkText.defaultTextFormat; + hyperlinkFormat.align = TextFormatAlign.CENTER; + hyperlinkFormat.color = Constants.COLOR_PRELOADER_LOCK_LINK; + hyperlinkText.setTextFormat(hyperlinkFormat); + } + #end + + override function destroy():Void + { + // Ensure the graphics are properly destroyed and GC'd. + removeChild(logo); + removeChild(progressBar); + logo = progressBar = null; + super.destroy(); + } + + override function onLoaded():Void + { + super.onLoaded(); + // We're not ACTUALLY finished. + // This function gets called when the DownloadingAssets step is done. + // We need to wait for the other steps, then the logo to fade out. + _loaded = false; + downloadingAssetsComplete = true; + } +} + +enum FunkinPreloaderState +{ + /** + * The state before downloading has begun. + * Moves to either `DownloadingAssets` or `CachingGraphics` based on platform. + */ + NotStarted; + + /** + * Downloading assets. + * On HTML5, Lime will do this for us, before calling `onLoaded`. + * On Desktop, this step will be completed immediately, and we'll go straight to `CachingGraphics`. + */ + DownloadingAssets; + + /** + * Preloading play assets. + * Loads the `manifest.json` for the `gameplay` library. + * If we make the base preloader do this, it will download all the assets as well, + * so we have to do it ourselves. + */ + PreloadingPlayAssets; + + /** + * Loading FireTongue, loading Polymod, parsing and instantiating module scripts. + */ + InitializingScripts; + + /** + * Loading all graphics from the `core` library to the cache. + */ + CachingGraphics; + + /** + * Loading all audio from the `core` library to the cache. + */ + CachingAudio; + + /** + * Loading all data files from the `core` library to the cache. + */ + CachingData; + + /** + * Parsing all XML files from the `core` library into FlxFramesCollections and caching them. + */ + ParsingSpritesheets; + + /** + * Parsing stage data and scripts. + */ + ParsingStages; + + /** + * Parsing character data and scripts. + */ + ParsingCharacters; + + /** + * Parsing song data and scripts. + */ + ParsingSongs; + + /** + * Finishing up. + */ + Complete; + + #if TOUCH_HERE_TO_PLAY + /** + * Touch Here to Play is displayed. + */ + TouchHereToPlay; + #end +} diff --git a/source/funkin/ui/transition/preload/README.md b/source/funkin/ui/transition/preload/README.md new file mode 100644 index 000000000..91efcf8e8 --- /dev/null +++ b/source/funkin/ui/transition/preload/README.md @@ -0,0 +1,17 @@ +# funkin.ui.loading.preload + +This package contains code powering the HTML5 preloader screen. + +The preloader performs the following tasks: +- **Downloading assets**: Downloads the `core` asset library and loads its manifest +- **Preloading play assets**: Downloads the `gameplay` asset library (manifest only) +- **Initializing scripts**: Downloads and registers stage scripts, character scripts, song scripts, and module scripts. +- **Caching graphics**: Downloads all graphics from the `core` asset library, uploads them to the GPU, then dumps them from RAM. This prepares them to be used very quickly in-game. +- **Caching audio**: Downloads all audio files from the `core` asset library, and caches them. This prepares them to be used very quickly in-game. +- **Caching data**: Downloads and caches all TXT files, all JSON files (it also parses them), and XML files (from the `core` library only). This prepares them to be used in the next steps. +- **Parsing stages**: Parses all stage data and instantiates associated stage scripts. This prepares them to be used in-game. +- **Parsing characters**: Parses all character data and instantiates associated character scripts. This prepares them to be used in-game. +- **Parsing songs**: Parses all song data and instantiates associated song scripts. This prepares them to be used in-game. +- **Finishing up**: Waits for the screen to fade out. Then, it loads the first state of the app. + +Due to the first few steps not being relevant on desktop, and due to this preloader being built in Lime rather than HaxeFlixel because of how Lime handles asset loading, this preloader is not used on desktop. The splash loader is used instead. diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index c7bc03139..5d355f2da 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -1,8 +1,9 @@ package funkin.util; +import flixel.system.FlxBasePreloader; import flixel.util.FlxColor; -import lime.app.Application; import funkin.data.song.SongData.SongTimeFormat; +import lime.app.Application; /** * A store of unchanging, globally relevant values. @@ -59,6 +60,16 @@ class Constants */ // ============================== + /** + * Preloader sitelock. + * Matching is done by `FlxStringUtil.getDomain`, so any URL on the domain will work. + * The first link in this list is the one users will be redirected to if they try to access the game from a different URL. + */ + public static final SITE_LOCK:Array = [ + "https://www.newgrounds.com/portal/view/770371", // Newgrounds, baybee! + FlxBasePreloader.LOCAL // localhost for dev stuff + ]; + /** * Link to download the game on Itch.io. */ @@ -116,6 +127,44 @@ class Constants 0xFFCC1111 // right (3) ]; + /** + * Color for the preloader background + */ + public static final COLOR_PRELOADER_BG:FlxColor = 0xFF000000; + + /** + * Color for the preloader progress bar + */ + public static final COLOR_PRELOADER_BAR:FlxColor = 0xFF00FF00; + + /** + * Color for the preloader site lock background + */ + public static final COLOR_PRELOADER_LOCK_BG:FlxColor = 0xFF1B1717; + + /** + * Color for the preloader site lock foreground + */ + public static final COLOR_PRELOADER_LOCK_FG:FlxColor = 0xB96F10; + + /** + * Color for the preloader site lock text + */ + public static final COLOR_PRELOADER_LOCK_FONT:FlxColor = 0xCCCCCC; + + /** + * Color for the preloader site lock link + */ + public static final COLOR_PRELOADER_LOCK_LINK:FlxColor = 0xEEB211; + + /** + * LANGUAGE + */ + // ============================== + public static final SITE_LOCK_TITLE:String = "You Loser!"; + + public static final SITE_LOCK_DESC:String = "This isn't Newgrounds!\nGo play Friday Night Funkin' on Newgrounds:"; + /** * GAME DEFAULTS */ @@ -289,6 +338,19 @@ class Constants */ public static final MP3_DELAY_MS:Float = 528 / 44100 * Constants.MS_PER_SEC; + /** + * Each step of the preloader has to be on screen at least this long. + * + * 0 = The preloader immediately moves to the next step when it's ready. + * 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 = 1.0; + #else + public static final PRELOADER_MIN_STAGE_TIME:Float = 0.1; + #end + /** * HEALTH VALUES */ diff --git a/source/funkin/util/MathUtil.hx b/source/funkin/util/MathUtil.hx index 5db8f8d76..93b9ca666 100644 --- a/source/funkin/util/MathUtil.hx +++ b/source/funkin/util/MathUtil.hx @@ -48,6 +48,36 @@ class MathUtil return Math.log(value) / Math.log(base); } + public static function easeInOutCirc(x:Float):Float + { + if (x <= 0.0) return 0.0; + if (x >= 1.0) return 1.0; + var result:Float = (x < 0.5) ? (1 - Math.sqrt(1 - 4 * x * x)) / 2 : (Math.sqrt(1 - 4 * (1 - x) * (1 - x)) + 1) / 2; + return (result == Math.NaN) ? 1.0 : result; + } + + public static function easeInOutBack(x:Float, ?c:Float = 1.70158):Float + { + if (x <= 0.0) return 0.0; + if (x >= 1.0) return 1.0; + var result:Float = (x < 0.5) ? (2 * x * x * ((c + 1) * 2 * x - c)) / 2 : (1 - 2 * (1 - x) * (1 - x) * ((c + 1) * 2 * (1 - x) - c)) / 2; + return (result == Math.NaN) ? 1.0 : result; + } + + public static function easeInBack(x:Float, ?c:Float = 1.70158):Float + { + if (x <= 0.0) return 0.0; + if (x >= 1.0) return 1.0; + return (1 + c) * x * x * x - c * x * x; + } + + public static function easeOutBack(x:Float, ?c:Float = 1.70158):Float + { + if (x <= 0.0) return 0.0; + if (x >= 1.0) return 1.0; + return 1 + (c + 1) * Math.pow(x - 1, 3) + c * Math.pow(x - 1, 2); + } + /** * Get the base-2 logarithm of a value. * @param x value