From 26de08c0059f52c9799194eb65c2b5107d53b2aa Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Sun, 2 Mar 2025 21:44:41 -0500 Subject: [PATCH 1/2] note hold and splash in progress --- assets | 2 +- source/funkin/data/notestyle/CHANGELOG.md | 2 + source/funkin/data/notestyle/NoteStyleData.hx | 22 +++ source/funkin/play/notes/NoteHoldCover.hx | 6 +- source/funkin/play/notes/NoteSplash.hx | 8 +- .../funkin/play/notes/notestyle/NoteStyle.hx | 169 +++++++++++++++--- 6 files changed, 170 insertions(+), 39 deletions(-) diff --git a/assets b/assets index 26b7f54a0..c15d7a7df 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 26b7f54a062f6961decd9c828754c9076cb19674 +Subproject commit c15d7a7dfac5fd60294695c73d185018a4e30e14 diff --git a/source/funkin/data/notestyle/CHANGELOG.md b/source/funkin/data/notestyle/CHANGELOG.md index d85c11cad..de9a19f11 100644 --- a/source/funkin/data/notestyle/CHANGELOG.md +++ b/source/funkin/data/notestyle/CHANGELOG.md @@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + ## [1.1.0] ### Added - Added several new `assets`: diff --git a/source/funkin/data/notestyle/NoteStyleData.hx b/source/funkin/data/notestyle/NoteStyleData.hx index dff48fa16..e6deb2569 100644 --- a/source/funkin/data/notestyle/NoteStyleData.hx +++ b/source/funkin/data/notestyle/NoteStyleData.hx @@ -290,4 +290,26 @@ typedef NoteStyleData_HoldNoteCover = @:optional @:default(true) var enabled:Bool; + + var left:NoteStyleData_HoldNoteCoverDirectionData; + var down:NoteStyleData_HoldNoteCoverDirectionData; + var up:NoteStyleData_HoldNoteCoverDirectionData; + var right:NoteStyleData_HoldNoteCoverDirectionData; }; + +typedef NoteStyleData_HoldNoteCoverDirectionData = +{ + /** + * Optionally specify an asset path to use for this specific animation. + * @:default The assetPath of the main holdNoteCover asset + */ + @:optional + var assetPath:String; + + @:optional + var start:UnnamedAnimationData; + @:optional + var hold:UnnamedAnimationData; + @:optional + var end:UnnamedAnimationData; +} diff --git a/source/funkin/play/notes/NoteHoldCover.hx b/source/funkin/play/notes/NoteHoldCover.hx index 0d7842ad9..afd09ea70 100644 --- a/source/funkin/play/notes/NoteHoldCover.hx +++ b/source/funkin/play/notes/NoteHoldCover.hx @@ -20,7 +20,7 @@ class NoteHoldCover extends FlxTypedSpriteGroup { super(0, 0); - setup(); + setupHoldNoteCover(); } public static function preloadFrames():Void @@ -47,7 +47,7 @@ class NoteHoldCover extends FlxTypedSpriteGroup /** * Add ALL the animations to this sprite. We will recycle and reuse the FlxSprite multiple times. */ - function setup():Void + function setupHoldNoteCover():Void { glow = new FlxSprite(); add(glow); @@ -63,7 +63,7 @@ class NoteHoldCover extends FlxTypedSpriteGroup glow.animation.addByPrefix('holdCoverEnd$directionName', 'holdCoverEnd${directionName}0', FRAMERATE_DEFAULT, false, false, false); } - glow.animation.finishCallback = this.onAnimationFinished; + glow.animation.onFinish.add(this.onAnimationFinished); if (glow.animation.getAnimationList().length < 3 * 4) { diff --git a/source/funkin/play/notes/NoteSplash.hx b/source/funkin/play/notes/NoteSplash.hx index eb9ba5813..514a0f7ff 100644 --- a/source/funkin/play/notes/NoteSplash.hx +++ b/source/funkin/play/notes/NoteSplash.hx @@ -12,19 +12,13 @@ class NoteSplash extends FlxSprite static var frameCollection:FlxFramesCollection; - public static function preloadFrames():Void - { - frameCollection = Paths.getSparrowAtlas('noteSplashes'); - frameCollection.parent.persist = true; - } - public function new(noteStyle:NoteStyle) { super(0, 0); setupSplashGraphic(noteStyle); - this.animation.finishCallback = this.onAnimationFinished; + this.animation.onFinish.add(this.onAnimationFinished); } /** diff --git a/source/funkin/play/notes/notestyle/NoteStyle.hx b/source/funkin/play/notes/notestyle/NoteStyle.hx index 7e3076919..2fe156fa2 100644 --- a/source/funkin/play/notes/notestyle/NoteStyle.hx +++ b/source/funkin/play/notes/notestyle/NoteStyle.hx @@ -99,6 +99,15 @@ class NoteStyle implements IRegistryEntry target.updateHitbox(); } + function getNoteAssetLibrary():Null + { + // library:path + var parts = getNoteAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length == 0) return null; + if (parts.length == 1) return null; + return parts[0]; + } + var noteFrames:Null = null; function buildNoteFrames(force:Bool = false):Null @@ -123,7 +132,7 @@ class NoteStyle implements IRegistryEntry if (noteFrames != null && !force) return noteFrames; - noteFrames = Paths.getSparrowAtlas(noteAssetPath, getNoteAssetLibrary()); + noteFrames = Paths.getSparrowAtlas(noteAssetPath, getAssetLibrary(getNoteAssetPath(true))); if (noteFrames == null) { @@ -149,15 +158,6 @@ class NoteStyle implements IRegistryEntry return parts[1]; } - function getNoteAssetLibrary():Null - { - // library:path - var parts = getNoteAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; - if (parts.length == 0) return null; - if (parts.length == 1) return null; - return parts[0]; - } - function buildNoteAnimations(target:NoteSprite):Void { var leftData:Null = fetchNoteAnimationData(LEFT); @@ -232,7 +232,7 @@ class NoteStyle implements IRegistryEntry // TODO: Add support for multi-Sparrow. // Will be less annoying after this is merged: https://github.com/HaxeFlixel/flixel/pull/2772 - var atlas:FlxAtlasFrames = Paths.getSparrowAtlas(getStrumlineAssetPath() ?? '', getStrumlineAssetLibrary()); + var atlas:FlxAtlasFrames = Paths.getSparrowAtlas(getStrumlineAssetPath() ?? '', getAssetLibrary(getStrumlineAssetPath(true))); if (atlas == null) { @@ -258,14 +258,6 @@ class NoteStyle implements IRegistryEntry return parts[1]; } - function getStrumlineAssetLibrary():Null - { - // library:path - var parts = getStrumlineAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; - if (parts.length <= 1) return null; - return parts[0]; - } - public function applyStrumlineAnimations(target:StrumlineNote, dir:NoteDirection):Void { FlxAnimationUtil.addAtlasAnimations(target, getStrumlineAnimationData(dir)); @@ -921,7 +913,7 @@ class NoteStyle implements IRegistryEntry if (splashFrames != null && !force) return splashFrames; - splashFrames = Paths.getSparrowAtlas(splashAssetPath, getSplashAssetLibrary()); + splashFrames = Paths.getSparrowAtlas(splashAssetPath, getAssetLibrary(getSplashAssetPath(true))); splashFrames.parent.persist = true; if (splashFrames == null) { @@ -945,14 +937,6 @@ class NoteStyle implements IRegistryEntry return parts[1]; } - function getSplashAssetLibrary():Null - { - var parts = getSplashAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; - if (parts.length == 0) return null; - if (parts.length == 1) return null; - return parts[0]; - } - function buildSplashAnimations(target:NoteSplash):Void { final addSplashAnim:NoteDirection->Void = function(dir:NoteDirection) { @@ -1007,8 +991,137 @@ class NoteStyle implements IRegistryEntry return _data?.assets?.noteSplash?.data?.framerateVariance ?? fallback?.getSplashFramerateVariance() ?? 2; } + public function buildHoldCoverSprite(target:NoteHoldCover):Void + { + var atlas:Null = buildHoldCoverFrames(false); + if (atlas == null) + { + throw 'Could not load spritesheet for note style: $id'; + } + target.frames = atlas; + target.antialiasing = !(_data.assets.holdNoteCover?.isPixel ?? false); + + buildHoldCoverAnimations(target); + } + + var holdCoverFrames:Null = null; + + function buildHoldCoverFrames(force:Bool = false):Null + { + @:nullSafety(Off) + { + if (splashFrames?.parent?.isDestroyed ?? false) splashFrames = null; + } + if (holdCoverFrames != null && !force) return holdCoverFrames; + + for (direction in Strumline.DIRECTIONS) + { + var atlas = buildHoldCoverFrameForDirection(direction); + if (holdCoverFrames == null) + { + holdCoverFrames = atlas; + } + else + { + if (atlas != null) + { + holdCoverFrames = FlxAnimationUtil.combineFramesCollection(holdCoverFrames, atlas); + } + } + } + + return holdCoverFrames; + } + + function buildHoldCoverFrameForDirection(direction:NoteDirection):Null + { + var directionData = switch (direction) + { + case LEFT: _data.assets?.holdNoteCover?.data?.left; + case DOWN: _data.assets?.holdNoteCover?.data?.down; + case UP: _data.assets?.holdNoteCover?.data?.up; + case RIGHT: _data.assets?.holdNoteCover?.data?.right; + }; + + if (directionData == null) return null; + + var holdCoverAssetPath:Null = getHoldCoverAssetPath(direction) ?? fallback?.getHoldCoverAssetPath(direction); + if (holdCoverAssetPath == null) + { + FlxG.log.warn('Hold Note Cover asset path not found: ${id}'); + return null; + } + + if (!FunkinSprite.isTextureCached(Paths.image(holdCoverAssetPath))) + { + FlxG.log.warn('Hold Note Cover texture not cached: ${holdCoverAssetPath}'); + } + + var atlas:FlxFramesCollection = Paths.getSparrowAtlas(holdCoverAssetPath, getAssetLibrary(getHoldCoverDirectionAssetPath(direction, true))); + atlas.parent.persist = true; + if (atlas == null) return null; + + return atlas; + } + + function buildHoldCoverAnimations(target:NoteHoldCover):Void + { + return; + } + + // fallbacks: + // assetPath for a direction -> root assetpath -> fallback assetpath for direction -> fallback root assetpath + function getHoldCoverAssetPath(direction:NoteDirection):Null + { + return getHoldCoverDirectionAssetPath(direction) ?? getHoldCoverRootAssetPath(); + } + + function getHoldCoverRootAssetPath():Null + { + if (raw) + { + var rawPath:Null = _data?.assets?.holdNoteCover?.assetPath; + if (rawPath == null) return fallback?.getHoldCoverRootAssetPath(); + return rawPath; + } + + var parts:Array = getHoldCoverRootAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length == 0) return null; + if (parts.length == 1) return getHoldCoverRootAssetPath(true); + return parts[1]; + } + + function getHoldCoverDirectionAssetPath(direction:NoteDirection, raw:Bool = false):Null + { + if (raw) + { + var rawPath:Null = switch (direction) + { + case LEFT: _data?.assets?.holdNoteCover?.data?.left?.assetPath; + case DOWN: _data?.assets?.holdNoteCover?.data?.down?.assetPath; + case UP: _data?.assets?.holdNoteCover?.data?.up?.assetPath; + case RIGHT: _data?.assets?.holdNoteCover?.data?.right?.assetPath; + }; + + if (rawPath == null) return fallback?.getHoldCoverDirectionAssetPath(direction, true); + return rawPath; + } + var parts:Array = getHoldCoverDirectionAssetPath(direction, true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; + if (parts.length == 0) return null; + if (parts.length == 1) return getHoldCoverDirectionAssetPath(direction, true); + return parts[1]; + return switch (direction) + } + public function destroy():Void {} + function getAssetLibrary(?id:String):Null + { + var parts = id?.split(Constants.LIBRARY_SEPARATOR); + if (parts.length <= 1) return null; + return parts[0]; + } + public function toString():String { return 'NoteStyle($id)'; From 6b9e6c9ebc0226d9371baf4e6753b7810c009420 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 11 Mar 2025 21:06:28 -0400 Subject: [PATCH 2/2] holdnotecover implementation stuff --- assets | 2 +- source/funkin/data/notestyle/NoteStyleData.hx | 7 + source/funkin/play/notes/NoteHoldCover.hx | 45 ++---- source/funkin/play/notes/NoteSplash.hx | 1 + source/funkin/play/notes/Strumline.hx | 12 +- .../funkin/play/notes/notestyle/NoteStyle.hx | 131 ++++++++++++------ source/funkin/util/assets/FlxAnimationUtil.hx | 10 +- 7 files changed, 124 insertions(+), 84 deletions(-) diff --git a/assets b/assets index c15d7a7df..51f7f0df7 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit c15d7a7dfac5fd60294695c73d185018a4e30e14 +Subproject commit 51f7f0df72bb9722ca66b5928ab3872875f6ac83 diff --git a/source/funkin/data/notestyle/NoteStyleData.hx b/source/funkin/data/notestyle/NoteStyleData.hx index e6deb2569..fb27e3813 100644 --- a/source/funkin/data/notestyle/NoteStyleData.hx +++ b/source/funkin/data/notestyle/NoteStyleData.hx @@ -291,9 +291,16 @@ typedef NoteStyleData_HoldNoteCover = @:default(true) var enabled:Bool; + @:optional var left:NoteStyleData_HoldNoteCoverDirectionData; + + @:optional var down:NoteStyleData_HoldNoteCoverDirectionData; + + @:optional var up:NoteStyleData_HoldNoteCoverDirectionData; + + @:optional var right:NoteStyleData_HoldNoteCoverDirectionData; }; diff --git a/source/funkin/play/notes/NoteHoldCover.hx b/source/funkin/play/notes/NoteHoldCover.hx index afd09ea70..478b18b53 100644 --- a/source/funkin/play/notes/NoteHoldCover.hx +++ b/source/funkin/play/notes/NoteHoldCover.hx @@ -4,64 +4,35 @@ import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; import flixel.graphics.frames.FlxFramesCollection; import funkin.util.assets.FlxAnimationUtil; import flixel.FlxSprite; +import funkin.play.notes.notestyle.NoteStyle; class NoteHoldCover extends FlxTypedSpriteGroup { static final FRAMERATE_DEFAULT:Int = 24; - static var glowFrames:FlxFramesCollection; - public var holdNote:SustainTrail; - var glow:FlxSprite; + public var glow:FlxSprite; + var sparks:FlxSprite; - public function new() + public function new(noteStyle:NoteStyle) { super(0, 0); - setupHoldNoteCover(); - } - - public static function preloadFrames():Void - { - glowFrames = null; - for (direction in Strumline.DIRECTIONS) - { - var directionName = direction.colorName.toTitleCase(); - - var atlas:FlxFramesCollection = Paths.getSparrowAtlas('holdCover${directionName}'); - atlas.parent.persist = true; - - if (glowFrames != null) - { - glowFrames = FlxAnimationUtil.combineFramesCollections(glowFrames, atlas); - } - else - { - glowFrames = atlas; - } - } + setupHoldNoteCover(noteStyle); } /** * Add ALL the animations to this sprite. We will recycle and reuse the FlxSprite multiple times. */ - function setupHoldNoteCover():Void + function setupHoldNoteCover(noteStyle:NoteStyle):Void { glow = new FlxSprite(); add(glow); - if (glowFrames == null) preloadFrames(); - glow.frames = glowFrames; - for (direction in Strumline.DIRECTIONS) - { - var directionName = direction.colorName.toTitleCase(); - - glow.animation.addByPrefix('holdCoverStart$directionName', 'holdCoverStart${directionName}0', FRAMERATE_DEFAULT, false, false, false); - glow.animation.addByPrefix('holdCover$directionName', 'holdCover${directionName}0', FRAMERATE_DEFAULT, true, false, false); - glow.animation.addByPrefix('holdCoverEnd$directionName', 'holdCoverEnd${directionName}0', FRAMERATE_DEFAULT, false, false, false); - } + // TODO: null check here like how NoteSplash does + noteStyle.buildHoldCoverSprite(this); glow.animation.onFinish.add(this.onAnimationFinished); diff --git a/source/funkin/play/notes/NoteSplash.hx b/source/funkin/play/notes/NoteSplash.hx index 514a0f7ff..744ea068e 100644 --- a/source/funkin/play/notes/NoteSplash.hx +++ b/source/funkin/play/notes/NoteSplash.hx @@ -49,6 +49,7 @@ class NoteSplash extends FlxSprite } // splashUP0, splashUP1, splashRIGHT0, etc. + // the animations are processed via `NoteStyle.fetchSplashAnimationData()` in this format this.playAnimation('splash${direction.nameUpper}${variant}'); if (animation.curAnim == null) return; diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index c721cb9d6..2c941d31d 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -779,10 +779,10 @@ class Strumline extends FlxSpriteGroup splash.x = this.x; splash.x += getXPos(direction); splash.x += INITIAL_OFFSET; + splash.x += noteStyle.getSplashOffsets()[0] * splash.scale.x; + splash.y = this.y; splash.y -= INITIAL_OFFSET; - splash.y += 0; - splash.x += noteStyle.getSplashOffsets()[0] * splash.scale.x; splash.y += noteStyle.getSplashOffsets()[1] * splash.scale.y; } } @@ -806,12 +806,14 @@ class Strumline extends FlxSpriteGroup cover.x += getXPos(holdNote.noteDirection); cover.x += STRUMLINE_SIZE / 2; cover.x -= cover.width / 2; - cover.x += -12; // Manual tweaking because fuck. + cover.x += noteStyle.getHoldCoverOffsets()[0] * cover.scale.x; + cover.x += -12; // hardcoded adjustment, because we are evil. cover.y = this.y; cover.y += INITIAL_OFFSET; cover.y += STRUMLINE_SIZE / 2; - cover.y += -96; // Manual tweaking because fuck. + cover.y += noteStyle.getHoldCoverOffsets()[1] * cover.scale.y; + cover.y += -96; // hardcoded adjustment, because we are evil. } } @@ -916,7 +918,7 @@ class Strumline extends FlxSpriteGroup if (noteHoldCovers.length < noteHoldCovers.maxSize) { // Create a new note hold cover. - result = new NoteHoldCover(); + result = new NoteHoldCover(noteStyle); this.noteHoldCovers.add(result); } else diff --git a/source/funkin/play/notes/notestyle/NoteStyle.hx b/source/funkin/play/notes/notestyle/NoteStyle.hx index 2fe156fa2..3e8a9b6ff 100644 --- a/source/funkin/play/notes/notestyle/NoteStyle.hx +++ b/source/funkin/play/notes/notestyle/NoteStyle.hx @@ -2,6 +2,7 @@ package funkin.play.notes.notestyle; import funkin.play.Countdown; import flixel.graphics.frames.FlxAtlasFrames; +import flixel.graphics.frames.FlxFramesCollection; import funkin.data.animation.AnimationData; import funkin.data.IRegistryEntry; import funkin.graphics.FunkinSprite; @@ -993,47 +994,41 @@ class NoteStyle implements IRegistryEntry public function buildHoldCoverSprite(target:NoteHoldCover):Void { - var atlas:Null = buildHoldCoverFrames(false); - if (atlas == null) + // NoteHoldCover has "glow" and "sparks". Right now it only implements "glow" + // but "sparks" I believe is meant to be used for the ending of the hold note + var glowAtlas:Null = buildHoldCoverFrames(false); + if (glowAtlas == null) { throw 'Could not load spritesheet for note style: $id'; } - target.frames = atlas; + target.glow.frames = glowAtlas; + target.antialiasing = !(_data.assets.holdNoteCover?.isPixel ?? false); + target.glow.antialiasing = !(_data.assets.holdNoteCover?.isPixel ?? false); + target.scale.set(_data.assets.holdNoteCover?.scale ?? 1.0, _data.assets.holdNoteCover?.scale ?? 1.0); + target.updateHitbox(); + target.glow.updateHitbox(); buildHoldCoverAnimations(target); } - var holdCoverFrames:Null = null; + // we use FlxFramesCollection here so we can combine em into one atlas later + var holdCoverFrames:Null = null; - function buildHoldCoverFrames(force:Bool = false):Null + function buildHoldCoverFrames(force:Bool = false):Null { - @:nullSafety(Off) - { - if (splashFrames?.parent?.isDestroyed ?? false) splashFrames = null; - } if (holdCoverFrames != null && !force) return holdCoverFrames; - for (direction in Strumline.DIRECTIONS) { - var atlas = buildHoldCoverFrameForDirection(direction); - if (holdCoverFrames == null) - { - holdCoverFrames = atlas; - } - else - { - if (atlas != null) - { - holdCoverFrames = FlxAnimationUtil.combineFramesCollection(holdCoverFrames, atlas); - } - } + // We make a FlxFramesCollection here so we can combine em into one atlas later + var atlas:Null = buildHoldCoverFrameForDirection(direction); + if (holdCoverFrames == null) holdCoverFrames = atlas; + else if (atlas != null) holdCoverFrames = FlxAnimationUtil.combineFramesCollections(holdCoverFrames, atlas); } - return holdCoverFrames; } - function buildHoldCoverFrameForDirection(direction:NoteDirection):Null + function buildHoldCoverFrameForDirection(direction:NoteDirection):Null { var directionData = switch (direction) { @@ -1045,7 +1040,7 @@ class NoteStyle implements IRegistryEntry if (directionData == null) return null; - var holdCoverAssetPath:Null = getHoldCoverAssetPath(direction) ?? fallback?.getHoldCoverAssetPath(direction); + var holdCoverAssetPath:Null = getHoldCoverDirectionAssetPath(direction) ?? fallback?.getHoldCoverDirectionAssetPath(direction); if (holdCoverAssetPath == null) { FlxG.log.warn('Hold Note Cover asset path not found: ${id}'); @@ -1058,36 +1053,80 @@ class NoteStyle implements IRegistryEntry } var atlas:FlxFramesCollection = Paths.getSparrowAtlas(holdCoverAssetPath, getAssetLibrary(getHoldCoverDirectionAssetPath(direction, true))); - atlas.parent.persist = true; if (atlas == null) return null; + atlas.parent.persist = true; + return atlas; } function buildHoldCoverAnimations(target:NoteHoldCover):Void { - return; + for (direction in Strumline.DIRECTIONS) + { + var animData:Null> = fetchHoldCoverAnimationData(direction); + if (animData != null) + { + // this entry is the base animation that plays ("holdCover$noteColor" animation), which we want to loop + animData[1].looped = true; + FlxAnimationUtil.addAtlasAnimations(target.glow, animData); + } + } + } + + function fetchHoldCoverAnimationData(dir:NoteDirection):Null> + { + var noteColor:String = dir.colorName.toTitleCase(); + var result:Array> = switch (dir) + { + case LEFT: [ + _data.assets?.holdNoteCover?.data?.left?.start?.toNamed('holdCoverStart$noteColor'), + _data.assets?.holdNoteCover?.data?.left?.hold?.toNamed('holdCover$noteColor'), + _data.assets?.holdNoteCover?.data?.left?.end?.toNamed('holdCoverEnd$noteColor'), + ]; + case DOWN: [ + _data.assets?.holdNoteCover?.data?.down?.start?.toNamed('holdCoverStart$noteColor'), + _data.assets?.holdNoteCover?.data?.down?.hold?.toNamed('holdCover$noteColor'), + _data.assets?.holdNoteCover?.data?.down?.end?.toNamed('holdCoverEnd$noteColor'), + ]; + case UP: [ + _data.assets?.holdNoteCover?.data?.up?.start?.toNamed('holdCoverStart$noteColor'), + _data.assets?.holdNoteCover?.data?.up?.hold?.toNamed('holdCover$noteColor'), + _data.assets?.holdNoteCover?.data?.up?.end?.toNamed('holdCoverEnd$noteColor'), + ]; + case RIGHT: [ + _data.assets?.holdNoteCover?.data?.right?.start?.toNamed('holdCoverStart$noteColor'), + _data.assets?.holdNoteCover?.data?.right?.hold?.toNamed('holdCover$noteColor'), + _data.assets?.holdNoteCover?.data?.right?.end?.toNamed('holdCoverEnd$noteColor'), + ]; + default: []; + }; + + // New variable so we can change the type. + var filteredResult:Array = thx.Arrays.filterNull(result); + + if (filteredResult.length == 0) return fallback?.fetchHoldCoverAnimationData(dir) ?? []; + + return filteredResult; } // fallbacks: // assetPath for a direction -> root assetpath -> fallback assetpath for direction -> fallback root assetpath - function getHoldCoverAssetPath(direction:NoteDirection):Null - { - return getHoldCoverDirectionAssetPath(direction) ?? getHoldCoverRootAssetPath(); - } - - function getHoldCoverRootAssetPath():Null + // `direction` here is required, however it only is used for fallback purposes + function getHoldCoverRootAssetPath(direction:NoteDirection, raw:Bool = false):Null { if (raw) { var rawPath:Null = _data?.assets?.holdNoteCover?.assetPath; - if (rawPath == null) return fallback?.getHoldCoverRootAssetPath(); + // remember, if we need a fallback for our *root* asset path, + // we fallback and look for the *direction* asset path first + if (rawPath == null) return fallback?.getHoldCoverDirectionAssetPath(direction, true); return rawPath; } - var parts:Array = getHoldCoverRootAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; + var parts:Array = getHoldCoverRootAssetPath(direction, true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; if (parts.length == 0) return null; - if (parts.length == 1) return getHoldCoverRootAssetPath(true); + if (parts.length == 1) return getHoldCoverRootAssetPath(direction, true); return parts[1]; } @@ -1103,22 +1142,36 @@ class NoteStyle implements IRegistryEntry case RIGHT: _data?.assets?.holdNoteCover?.data?.right?.assetPath; }; - if (rawPath == null) return fallback?.getHoldCoverDirectionAssetPath(direction, true); + // we don't want to fallback directly just yet, we want to check our root assetpath first + if (rawPath == null) return getHoldCoverRootAssetPath(direction, true); return rawPath; } + var parts:Array = getHoldCoverDirectionAssetPath(direction, true)?.split(Constants.LIBRARY_SEPARATOR) ?? []; if (parts.length == 0) return null; if (parts.length == 1) return getHoldCoverDirectionAssetPath(direction, true); return parts[1]; - return switch (direction) + } + + public function getHoldCoverOffsets():Array + { + return _data?.assets?.holdNoteCover?.offsets ?? fallback?.getHoldCoverOffsets() ?? [0.0, 0.0]; } public function destroy():Void {} + /** + * Returns a string of the library name for the given asset id + * `default:assets/images/awesome.png` returns `default` + * If you pass a asset path with no library name (no `:` aka LIBRARY_SEPARATOR) it will return null + * @param id The asset id to get the library name from + * @return Null The library name, or null if no library name is present + */ function getAssetLibrary(?id:String):Null { - var parts = id?.split(Constants.LIBRARY_SEPARATOR); + var parts:Array = id?.split(Constants.LIBRARY_SEPARATOR) ?? []; if (parts.length <= 1) return null; + return parts[0]; } diff --git a/source/funkin/util/assets/FlxAnimationUtil.hx b/source/funkin/util/assets/FlxAnimationUtil.hx index f94dc4dc3..ee5b0edec 100644 --- a/source/funkin/util/assets/FlxAnimationUtil.hx +++ b/source/funkin/util/assets/FlxAnimationUtil.hx @@ -9,7 +9,7 @@ class FlxAnimationUtil /** * Properly adds an animation to a sprite based on the provided animation data. */ - public static function addAtlasAnimation(target:FlxSprite, anim:AnimationData) + public static function addAtlasAnimation(target:FlxSprite, anim:AnimationData):Void { var frameRate = anim.frameRate == null ? 24 : anim.frameRate; var looped = anim.looped == null ? false : anim.looped; @@ -33,7 +33,7 @@ class FlxAnimationUtil /** * Properly adds multiple animations to a sprite based on the provided animation data. */ - public static function addAtlasAnimations(target:FlxSprite, animations:Array) + public static function addAtlasAnimations(target:FlxSprite, animations:Array):Void { for (anim in animations) { @@ -41,6 +41,12 @@ class FlxAnimationUtil } } + /** + * Combine two FlxFramesCollection objects into one. + * @param a The first FlxFramesCollection + * @param b The second FlxFramesCollection + * @return FlxFramesCollection The combined FlxFramesCollection + */ public static function combineFramesCollections(a:FlxFramesCollection, b:FlxFramesCollection):FlxFramesCollection { var result:FlxFramesCollection = new FlxFramesCollection(null, ATLAS, null);