diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 686453603..97e451320 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -145,9 +145,9 @@ class InitState extends FlxTransitionableState // NOTE: tileData is ignored if TransitionData.type is FADE instead of TILES. var tileData:TransitionTileData = {asset: diamond, width: 32, height: 32}; - FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(1, 1), tileData, + FlxTransitionableState.defaultTransIn = new TransitionData(FADE, FlxColor.BLACK, 1, new FlxPoint(0, -1), tileData, new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4)); - FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 0.7, new FlxPoint(1, 1), tileData, + FlxTransitionableState.defaultTransOut = new TransitionData(FADE, FlxColor.BLACK, 0.7, new FlxPoint(0, 1), tileData, new FlxRect(-200, -200, FlxG.width * 1.4, FlxG.height * 1.4)); // Don't play transition in when entering the title state. FlxTransitionableState.skipNextTransIn = true; diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 6ebffd0c7..fffc9a549 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2190,6 +2190,11 @@ class PlayState extends MusicBeatState playerStrumline.playNoteSplash(daNote.noteData.getDirection()); } + if (daNote.noteData.isHoldNote) + { + playerStrumline.playNoteHoldCover(daNote.noteData.getDirection()); + } + // Only add the score if you're not on practice mode if (!isPracticeMode) { diff --git a/source/funkin/play/notes/NoteHoldCover.hx b/source/funkin/play/notes/NoteHoldCover.hx new file mode 100644 index 000000000..e64238681 --- /dev/null +++ b/source/funkin/play/notes/NoteHoldCover.hx @@ -0,0 +1,75 @@ +package funkin.play.notes; + +import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; +import funkin.play.notes.NoteDirection; +import flixel.graphics.frames.FlxFramesCollection; +import flixel.FlxG; +import flixel.graphics.frames.FlxAtlasFrames; +import flixel.FlxSprite; + +class NoteHoldCover extends FlxTypedSpriteGroup +{ + static final FRAMERATE_DEFAULT:Int = 24; + + static var glowFrames:FlxAtlasFrames; + + var glow:FlxSprite; + var sparks:FlxSprite; + + public static function preloadFrames():Void + { + glowFrames = Paths.getSparrowAtlas('holdCoverRed'); + } + + public function new() + { + super(0, 0); + + setup(); + } + + /** + * Add ALL the animations to this sprite. We will recycle and reuse the FlxSprite multiple times. + */ + function setup():Void + { + glow = new FlxSprite(); + add(glow); + if (glowFrames == null) preloadFrames(); + glow.frames = glowFrames; + + glow.animation.addByPrefix('holdCoverRed', 'holdCoverRed0', FRAMERATE_DEFAULT, true, false, false); + glow.animation.addByPrefix('holdCoverEndRed', 'holdCoverEndRed0', FRAMERATE_DEFAULT, true, false, false); + + glow.animation.finishCallback = this.onAnimationFinished; + + if (glow.animation.getAnimationList().length < 2) + { + trace('WARNING: NoteHoldCover failed to initialize all animations.'); + } + } + + public function playStart(direction:NoteDirection):Void + { + glow.animation.play('holdCoverRed'); + } + + public function playContinue(direction:NoteDirection):Void + { + glow.animation.play('holdCoverRed'); + } + + public function playEnd(direction:NoteDirection):Void + { + glow.animation.play('holdCoverEndRed'); + } + + public function onAnimationFinished(animationName:String):Void + { + if (animationName.startsWith('holdCoverEnd')) + { + // *lightning* *zap* *crackle* + this.kill(); + } + } +} diff --git a/source/funkin/play/notes/NoteSprite.hx b/source/funkin/play/notes/NoteSprite.hx index ed6417f90..dd378bf02 100644 --- a/source/funkin/play/notes/NoteSprite.hx +++ b/source/funkin/play/notes/NoteSprite.hx @@ -21,19 +21,6 @@ class NoteSprite extends FlxSprite return this.strumTime; } - /** - * The length of the note's sustain, in milliseconds. - * If 0, the note is a tap note. - */ - public var length(default, set):Float; - - function set_length(value:Float):Float - { - this.length = value; - this.isSustainNote = (this.length > 0); - return this.length; - } - /** * The time at which the note should be hit, in steps. */ diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index 0edc4435a..298c429c0 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -1,16 +1,18 @@ package funkin.play.notes; -import flixel.tweens.FlxEase; -import flixel.tweens.FlxTween; -import funkin.ui.PreferencesMenu; -import funkin.play.notes.NoteSprite; -import flixel.util.FlxSort; -import funkin.play.notes.SustainTrail; -import funkin.util.SortUtil; -import funkin.play.song.SongData.SongNoteData; import flixel.FlxG; import flixel.group.FlxSpriteGroup; import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.util.FlxSort; +import funkin.play.notes.NoteHoldCover; +import funkin.play.notes.NoteSplash; +import funkin.play.notes.NoteSprite; +import funkin.play.notes.SustainTrail; +import funkin.play.song.SongData.SongNoteData; +import funkin.ui.PreferencesMenu; +import funkin.util.SortUtil; /** * A group of sprites which handles the receptor, the note splashes, and the notes (with sustains) for a given player. @@ -48,7 +50,7 @@ class Strumline extends FlxSpriteGroup var strumlineNotes:FlxTypedSpriteGroup; var noteSplashes:FlxTypedSpriteGroup; - var sustainSplashes:FlxTypedSpriteGroup; + var noteHoldCovers:FlxTypedSpriteGroup; var noteData:Array = []; var nextNoteIndex:Int = -1; @@ -74,8 +76,12 @@ class Strumline extends FlxSpriteGroup this.notes.zIndex = 30; this.add(this.notes); + this.noteHoldCovers = new FlxTypedSpriteGroup(0, 0, 4); + this.noteHoldCovers.zIndex = 40; + this.add(this.noteHoldCovers); + this.noteSplashes = new FlxTypedSpriteGroup(0, 0, NOTE_SPLASH_CAP); - this.noteSplashes.zIndex = 40; + this.noteSplashes.zIndex = 50; this.add(this.noteSplashes); for (i in 0...KEY_COUNT) @@ -450,6 +456,27 @@ class Strumline extends FlxSpriteGroup } } + public function playNoteHoldCover(direction:NoteDirection):Void + { + // TODO: Add a setting to disable note splashes. + // if (Settings.noSplash) return; + + var cover:NoteHoldCover = this.constructNoteHoldCover(); + + if (cover != null) + { + cover.playStart(direction); + + cover.x = this.x; + cover.x += getXPos(direction); + cover.x -= cover.width / 8; + cover.x += INITIAL_OFFSET; + cover.y = this.y; + cover.y -= cover.height / 4; + // + } + } + public function buildNoteSprite(note:SongNoteData):NoteSprite { var noteSprite:NoteSprite = constructNoteSprite(); @@ -530,6 +557,40 @@ class Strumline extends FlxSpriteGroup return result; } + /** + * Custom recycling behavior. + */ + function constructNoteHoldCover():NoteHoldCover + { + var result:NoteHoldCover = null; + + // If we haven't filled the pool yet... + if (noteHoldCovers.length < noteHoldCovers.maxSize) + { + // Create a new note hold cover. + result = new NoteHoldCover(); + this.noteHoldCovers.add(result); + } + else + { + // Else, find a note splash which is inactive so we can revive it. + result = this.noteHoldCovers.getFirstAvailable(); + + if (result != null) + { + result.revive(); + } + else + { + // The note hold cover pool is full and all note hold covers are active, + // so we just pick one at random to destroy and restart. + result = FlxG.random.getObject(this.noteHoldCovers.members); + } + } + + return result; + } + /** * Custom recycling behavior. */ diff --git a/source/funkin/play/song/SongData.hx b/source/funkin/play/song/SongData.hx index dc46ae365..7122d130e 100644 --- a/source/funkin/play/song/SongData.hx +++ b/source/funkin/play/song/SongData.hx @@ -450,6 +450,13 @@ abstract SongNoteData(RawSongNoteData) return this.l = value; } + public var isHoldNote(get, never):Bool; + + public function get_isHoldNote():Bool + { + return this.l > 0; + } + public var kind(get, set):String; public function get_kind():String