diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index e7ce68d08..0e6bd6893 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -128,6 +128,7 @@ class FunkinSound extends FlxSound implements ICloneable function fixMaxVolume():Void { + return; #if lime_openal // This code is pretty fragile, it reaches through 5 layers of private access. @:privateAccess diff --git a/source/funkin/data/event/SongEventRegistry.hx b/source/funkin/data/event/SongEventRegistry.hx index dc5589813..8732e3b98 100644 --- a/source/funkin/data/event/SongEventRegistry.hx +++ b/source/funkin/data/event/SongEventRegistry.hx @@ -108,8 +108,8 @@ class SongEventRegistry public static function handleEvent(data:SongEventData):Void { - var eventType:String = data.event; - var eventHandler:SongEvent = eventCache.get(eventType); + var eventKind:String = data.eventKind; + var eventHandler:SongEvent = eventCache.get(eventKind); if (eventHandler != null) { @@ -117,7 +117,7 @@ class SongEventRegistry } else { - trace('WARNING: No event handler for event with id: ${eventType}'); + trace('WARNING: No event handler for event with kind: ${eventKind}'); } data.activated = true; diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx index cc568ec66..24febea86 100644 --- a/source/funkin/data/song/SongData.hx +++ b/source/funkin/data/song/SongData.hx @@ -650,7 +650,7 @@ class SongEventDataRaw implements ICloneable * Custom events can be added by scripts with the `ScriptedSongEvent` class. */ @:alias("e") - public var event:String; + public var eventKind:String; /** * The data for the event. @@ -670,10 +670,10 @@ class SongEventDataRaw implements ICloneable @:jignored public var activated:Bool = false; - public function new(time:Float, event:String, value:Dynamic = null) + public function new(time:Float, eventKind:String, value:Dynamic = null) { this.time = time; - this.event = event; + this.eventKind = eventKind; this.value = value; } @@ -689,19 +689,19 @@ class SongEventDataRaw implements ICloneable public function clone():SongEventDataRaw { - return new SongEventDataRaw(this.time, this.event, this.value); + return new SongEventDataRaw(this.time, this.eventKind, this.value); } } /** * Wrap SongEventData in an abstract so we can overload operators. */ -@:forward(time, event, value, activated, getStepTime, clone) +@:forward(time, eventKind, value, activated, getStepTime, clone) abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataRaw { - public function new(time:Float, event:String, value:Dynamic = null) + public function new(time:Float, eventKind:String, value:Dynamic = null) { - this = new SongEventDataRaw(time, event, value); + this = new SongEventDataRaw(time, eventKind, value); } public inline function valueAsStruct(?defaultKey:String = "key"):Dynamic @@ -728,12 +728,12 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR public inline function getHandler():Null { - return SongEventRegistry.getEvent(this.event); + return SongEventRegistry.getEvent(this.eventKind); } public inline function getSchema():Null { - return SongEventRegistry.getEventSchema(this.event); + return SongEventRegistry.getEventSchema(this.eventKind); } public inline function getDynamic(key:String):Null @@ -786,7 +786,7 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR var eventHandler = getHandler(); var eventSchema = getSchema(); - if (eventSchema == null) return 'Unknown Event: ${this.event}'; + if (eventSchema == null) return 'Unknown Event: ${this.eventKind}'; var result = '${eventHandler.getTitle()}'; @@ -811,19 +811,19 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR public function clone():SongEventData { - return new SongEventData(this.time, this.event, this.value); + return new SongEventData(this.time, this.eventKind, this.value); } @:op(A == B) public function op_equals(other:SongEventData):Bool { - return this.time == other.time && this.event == other.event && this.value == other.value; + return this.time == other.time && this.eventKind == other.eventKind && this.value == other.value; } @:op(A != B) public function op_notEquals(other:SongEventData):Bool { - return this.time != other.time || this.event != other.event || this.value != other.value; + return this.time != other.time || this.eventKind != other.eventKind || this.value != other.value; } @:op(A > B) @@ -855,7 +855,7 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR */ public function toString():String { - return 'SongEventData(${this.time}ms, ${this.event}: ${this.value})'; + return 'SongEventData(${this.time}ms, ${this.eventKind}: ${this.value})'; } } diff --git a/source/funkin/data/song/SongDataUtils.hx b/source/funkin/data/song/SongDataUtils.hx index 7f3b01eb4..c93c5379a 100644 --- a/source/funkin/data/song/SongDataUtils.hx +++ b/source/funkin/data/song/SongDataUtils.hx @@ -47,7 +47,7 @@ class SongDataUtils public static function offsetSongEventData(events:Array, offset:Float):Array { return events.map(function(event:SongEventData):SongEventData { - return new SongEventData(event.time + offset, event.event, event.value); + return new SongEventData(event.time + offset, event.eventKind, event.value); }); } diff --git a/source/funkin/modding/events/ScriptEvent.hx b/source/funkin/modding/events/ScriptEvent.hx index 68265a103..5d522e3ae 100644 --- a/source/funkin/modding/events/ScriptEvent.hx +++ b/source/funkin/modding/events/ScriptEvent.hx @@ -189,17 +189,17 @@ class SongEventScriptEvent extends ScriptEvent * The note associated with this event. * You cannot replace it, but you can edit it. */ - public var event(default, null):funkin.data.song.SongData.SongEventData; + public var eventData(default, null):funkin.data.song.SongData.SongEventData; - public function new(event:funkin.data.song.SongData.SongEventData):Void + public function new(eventData:funkin.data.song.SongData.SongEventData):Void { super(SONG_EVENT, true); - this.event = event; + this.eventData = eventData; } public override function toString():String { - return 'SongEventScriptEvent(event=' + event + ')'; + return 'SongEventScriptEvent(event=' + eventData + ')'; } } diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx index 62c3409b7..b7e92d10f 100644 --- a/source/funkin/play/GameOverSubState.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -4,6 +4,7 @@ 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; import funkin.graphics.FunkinSprite; @@ -64,7 +65,7 @@ class GameOverSubState extends MusicBeatSubState /** * The music playing in the background of the state. */ - var gameOverMusic:FlxSound = new FlxSound(); + var gameOverMusic:Null = null; /** * Whether the player has confirmed and prepared to restart the level. @@ -72,6 +73,11 @@ class GameOverSubState extends MusicBeatSubState */ var isEnding:Bool = false; + /** + * Whether the death music is on its first loop. + */ + var isStarting:Bool = true; + var isChartingMode:Bool = false; var transparent:Bool; @@ -141,10 +147,6 @@ class GameOverSubState extends MusicBeatSubState // Set up the audio // - // Prepare the game over music. - FlxG.sound.list.add(gameOverMusic); - gameOverMusic.stop(); - // The conductor now represents the BPM of the game over music. Conductor.instance.update(0); } @@ -223,7 +225,7 @@ class GameOverSubState extends MusicBeatSubState } } - if (gameOverMusic.playing) + if (gameOverMusic != null && gameOverMusic.playing) { // Match the conductor to the music. // This enables the stepHit and beatHit events. @@ -298,24 +300,71 @@ class GameOverSubState extends MusicBeatSubState ScriptEventDispatcher.callEvent(boyfriend, event); } + /** + * Rather than hardcoding stuff, we look for the presence of a music file + * with the given suffix, and strip it down until we find one that's valid. + */ + function resolveMusicPath(suffix:String, starting:Bool = false, ending:Bool = false):Null + { + var basePath = 'gameplay/gameover/gameOver'; + if (starting) basePath += 'Start'; + else if (ending) basePath += 'End'; + + var musicPath = Paths.music(basePath + suffix); + while (!Assets.exists(musicPath) && suffix.length > 0) + { + suffix = suffix.split('-').slice(0, -1).join('-'); + musicPath = Paths.music(basePath + suffix); + } + if (!Assets.exists(musicPath)) return null; + trace('Resolved music path: ' + musicPath); + return musicPath; + } + /** * Starts the death music at the appropriate volume. * @param startingVolume */ - public function startDeathMusic(?startingVolume:Float = 1, force:Bool = false):Void + public function startDeathMusic(startingVolume:Float = 1, force:Bool = false):Void { - var musicPath = Paths.music('gameplay/gameover/gameOver' + musicSuffix); - if (isEnding) + var musicPath = resolveMusicPath(musicSuffix, isStarting, isEnding); + var onComplete = null; + if (isStarting) { - musicPath = Paths.music('gameplay/gameover/gameOverEnd' + musicSuffix); + if (musicPath == null) + { + isStarting = false; + musicPath = resolveMusicPath(musicSuffix, isStarting, isEnding); + } + else + { + isStarting = false; + onComplete = function() { + // We need to force to ensure that the non-starting music plays. + startDeathMusic(1.0, true); + }; + } } - if (!gameOverMusic.playing || force) + + if (musicPath == null) { - gameOverMusic.loadEmbedded(musicPath); + trace('Could not find game over music!'); + return; + } + else if (gameOverMusic == null || !gameOverMusic.playing || force) + { + if (gameOverMusic != null) gameOverMusic.stop(); + gameOverMusic = FunkinSound.load(musicPath); gameOverMusic.volume = startingVolume; - gameOverMusic.looped = !isEnding; + gameOverMusic.looped = !(isEnding || isStarting); + gameOverMusic.onComplete = onComplete; gameOverMusic.play(); } + else + { + @:privateAccess + trace('Music already playing! ${gameOverMusic?._label}'); + } } static var blueballed:Bool = false; @@ -358,6 +407,14 @@ class GameOverSubState extends MusicBeatSubState }); } + public override function destroy() + { + super.destroy(); + if (gameOverMusic != null) gameOverMusic.stop(); + gameOverMusic = null; + instance = null; + } + public override function toString():String { return "GameOverSubState"; diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 48a6e70c9..e9748a4ba 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -3417,7 +3417,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState // Update the event sprite's position. eventSprite.updateEventPosition(renderedEvents); // Update the sprite's graphic. TODO: Is this inefficient? - eventSprite.playAnimation(eventSprite.eventData.event); + eventSprite.playAnimation(eventSprite.eventData.eventKind); } else { @@ -4678,9 +4678,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState var eventData:SongEventData = gridGhostEvent.eventData != null ? gridGhostEvent.eventData : new SongEventData(cursorMs, eventKindToPlace, null); - if (eventKindToPlace != eventData.event) + if (eventKindToPlace != eventData.eventKind) { - eventData.event = eventKindToPlace; + eventData.eventKind = eventKindToPlace; } eventData.time = cursorSnappedMs; diff --git a/source/funkin/ui/debug/charting/commands/SelectItemsCommand.hx b/source/funkin/ui/debug/charting/commands/SelectItemsCommand.hx index 891ac9ebd..423295f1a 100644 --- a/source/funkin/ui/debug/charting/commands/SelectItemsCommand.hx +++ b/source/funkin/ui/debug/charting/commands/SelectItemsCommand.hx @@ -38,7 +38,7 @@ class SelectItemsCommand implements ChartEditorCommand { var eventSelected = this.events[0]; - state.eventKindToPlace = eventSelected.event; + state.eventKindToPlace = eventSelected.eventKind; // This code is here to parse event data that's not built as a struct for some reason. // TODO: Clean this up or get rid of it. @@ -46,7 +46,7 @@ class SelectItemsCommand implements ChartEditorCommand var defaultKey = null; if (eventSchema == null) { - trace('[WARNING] Event schema not found for event ${eventSelected.event}.'); + trace('[WARNING] Event schema not found for event ${eventSelected.eventKind}.'); } else { diff --git a/source/funkin/ui/debug/charting/commands/SetItemSelectionCommand.hx b/source/funkin/ui/debug/charting/commands/SetItemSelectionCommand.hx index 0b540dbeb..46fcca87c 100644 --- a/source/funkin/ui/debug/charting/commands/SetItemSelectionCommand.hx +++ b/source/funkin/ui/debug/charting/commands/SetItemSelectionCommand.hx @@ -35,7 +35,7 @@ class SetItemSelectionCommand implements ChartEditorCommand { var eventSelected = this.events[0]; - state.eventKindToPlace = eventSelected.event; + state.eventKindToPlace = eventSelected.eventKind; // This code is here to parse event data that's not built as a struct for some reason. // TODO: Clean this up or get rid of it. @@ -43,7 +43,7 @@ class SetItemSelectionCommand implements ChartEditorCommand var defaultKey = null; if (eventSchema == null) { - trace('[WARNING] Event schema not found for event ${eventSelected.event}.'); + trace('[WARNING] Event schema not found for event ${eventSelected.eventKind}.'); } else { diff --git a/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx index e3dae37cf..f680095d7 100644 --- a/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx +++ b/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx @@ -133,7 +133,7 @@ class ChartEditorEventSprite extends FlxSprite public function playAnimation(?name:String):Void { - if (name == null) name = eventData?.event ?? DEFAULT_EVENT; + if (name == null) name = eventData?.eventKind ?? DEFAULT_EVENT; var correctedName = correctAnimationName(name); this.animation.play(correctedName); @@ -160,7 +160,7 @@ class ChartEditorEventSprite extends FlxSprite else { this.visible = true; - playAnimation(value.event); + playAnimation(value.eventKind); this.eventData = value; // Update the position to match the note data. updateEventPosition(); diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx index 7b163ad3d..ec46e1f85 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorEventDataToolbox.hx @@ -90,7 +90,7 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox // Edit the event data of any selected events. for (event in chartEditorState.currentEventSelection) { - event.event = chartEditorState.eventKindToPlace; + event.eventKind = chartEditorState.eventKindToPlace; event.value = chartEditorState.eventDataToPlace; } chartEditorState.saveDataDirty = true; @@ -255,7 +255,7 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox { for (event in chartEditorState.currentEventSelection) { - event.event = chartEditorState.eventKindToPlace; + event.eventKind = chartEditorState.eventKindToPlace; event.value = chartEditorState.eventDataToPlace; } chartEditorState.saveDataDirty = true;