From 37ae96fba79c378a114eab0b2280f78102ba7422 Mon Sep 17 00:00:00 2001 From: Eric Myllyoja Date: Tue, 15 Nov 2022 02:23:44 -0500 Subject: [PATCH] Added confirm and AFK animations to BF on freeplay menu --- source/funkin/FreeplayState.hx | 31 ++- source/funkin/freeplayStuff/DJBoyfriend.hx | 179 +++++++++++++++--- source/funkin/util/assets/FlxAnimationUtil.hx | 17 ++ 3 files changed, 197 insertions(+), 30 deletions(-) diff --git a/source/funkin/FreeplayState.hx b/source/funkin/FreeplayState.hx index 844347b8d..0e4be4e24 100644 --- a/source/funkin/FreeplayState.hx +++ b/source/funkin/FreeplayState.hx @@ -65,6 +65,8 @@ class FreeplayState extends MusicBeatSubstate private var grpCapsules:FlxTypedGroup; private var curPlaying:Bool = false; + private var dj:DJBoyfriend; + private var iconArray:Array = []; override function create() @@ -178,7 +180,7 @@ class FreeplayState extends MusicBeatSubstate funnyScroll3.speed = -0.8; grpTxtScrolls.add(funnyScroll3); - var dj:DJBoyfriend = new DJBoyfriend(0, -100); + dj = new DJBoyfriend(0, -100); add(dj); var bgDad:FlxSprite = new FlxSprite(pinkBack.width * 0.75, 0).loadGraphic(Paths.image('freeplay/freeplayBGdad')); @@ -231,7 +233,7 @@ class FreeplayState extends MusicBeatSubstate fp.visible = false; add(fp); - dj.animHITsignal.add(function() + dj.onIntroDone.add(function() { FlxTween.tween(grpDifficulties, {x: 90}, 0.6, {ease: FlxEase.quartOut}); @@ -483,17 +485,32 @@ class FreeplayState extends MusicBeatSubstate #end 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) + { + dj.resetAFKTimer(); changeDiff(-1); + } if (controls.UI_RIGHT_P) + { + dj.resetAFKTimer(); changeDiff(1); + } if (controls.BACK) { @@ -540,7 +557,15 @@ class FreeplayState extends MusicBeatSubstate PlayState.storyWeek = songs[curSelected].week; trace(' CUR WEEK ' + PlayState.storyWeek); - LoadingState.loadAndSwitchState(new PlayState()); + + // Visual and audio effects. + FlxG.sound.play(Paths.sound('confirmMenu')); + dj.confirm(); + + new FlxTimer().start(1, function(tmr:FlxTimer) + { + LoadingState.loadAndSwitchState(new PlayState(), true); + }); } } diff --git a/source/funkin/freeplayStuff/DJBoyfriend.hx b/source/funkin/freeplayStuff/DJBoyfriend.hx index 20449c25e..2e0954563 100644 --- a/source/funkin/freeplayStuff/DJBoyfriend.hx +++ b/source/funkin/freeplayStuff/DJBoyfriend.hx @@ -2,46 +2,168 @@ package funkin.freeplayStuff; import flixel.FlxSprite; import flixel.util.FlxSignal; +import funkin.util.assets.FlxAnimationUtil; class DJBoyfriend extends FlxSprite { - public var animHITsignal:FlxSignal; + // Represents the sprite's current status. + // Without state machines I would have driven myself crazy years ago. + public var currentState:DJBoyfriendState = Intro; + + // A callback activated when the intro animation finishes. + public var onIntroDone:FlxSignal = new FlxSignal(); + + // A callback activated when Boyfriend gets spooked. + public var onSpook:FlxSignal = new FlxSignal(); + + // playAnim stolen from Character.hx, cuz im lazy lol! + // TODO: Switch this class to use SwagSprite instead. + public var animOffsets:Map>; + + static final SPOOK_PERIOD:Float = 180.0; + + // Time since dad last SPOOKED you. + var timeSinceSpook:Float = 0; public function new(x:Float, y:Float) { super(x, y); - animHITsignal = new FlxSignal(); - animOffsets = new Map>(); - frames = Paths.getSparrowAtlas('freeplay/bfFreeplay'); - animation.addByPrefix('intro', "boyfriend dj intro", 24, false); - animation.addByPrefix('idle', "Boyfriend DJ0", 24); - animation.addByPrefix('confirm', "Boyfriend DJ confirm", 24); + setupAnimations(); - addOffset('intro', 0, 0); - addOffset('idle', -4, -426); - - playAnimation('intro'); - animation.finishCallback = function(anim) - { - switch (anim) - { - case "intro": - animHITsignal.dispatch(); - playAnimation('idle'); // plays idle anim after playing intro - } - }; + animation.finishCallback = onFinishAnim; } - // playAnim stolen from Character.hx, cuz im lazy lol! - public var animOffsets:Map>; + public override function update(elapsed:Float):Void + { + super.update(elapsed); + + if (FlxG.keys.justPressed.LEFT) + { + animOffsets["confirm"] = [animOffsets["confirm"][0] + 1, animOffsets["confirm"][1]]; + applyAnimOffset(); + } + else if (FlxG.keys.justPressed.RIGHT) + { + animOffsets["confirm"] = [animOffsets["confirm"][0] - 1, animOffsets["confirm"][1]]; + applyAnimOffset(); + } + else if (FlxG.keys.justPressed.UP) + { + animOffsets["confirm"] = [animOffsets["confirm"][0], animOffsets["confirm"][1] + 1]; + applyAnimOffset(); + } + else if (FlxG.keys.justPressed.DOWN) + { + animOffsets["confirm"] = [animOffsets["confirm"][0], animOffsets["confirm"][1] - 1]; + applyAnimOffset(); + } + + switch (currentState) + { + case Intro: + // Play the intro animation then leave this state immediately. + if (getCurrentAnimation() != 'intro') + playAnimation('intro', true); + timeSinceSpook = 0; + case Idle: + // We are in this state the majority of the time. + if (getCurrentAnimation() != 'idle' || animation.finished) + { + if (timeSinceSpook > SPOOK_PERIOD) + { + currentState = Spook; + } + else + { + playAnimation('idle', false); + } + } + timeSinceSpook += elapsed; + case Confirm: + if (getCurrentAnimation() != 'confirm') + playAnimation('confirm', false); + timeSinceSpook = 0; + case Spook: + if (getCurrentAnimation() != 'spook') + { + onSpook.dispatch(); + playAnimation('spook', false); + } + timeSinceSpook = 0; + default: + // I shit myself. + } + } + + function onFinishAnim(name:String):Void + { + switch (name) + { + case "intro": + trace('Finished intro'); + currentState = Idle; + onIntroDone.dispatch(); + case "idle": + trace('Finished idle'); + case "spook": + trace('Finished spook'); + currentState = Idle; + case "confirm": + trace('Finished confirm'); + } + } + + public function resetAFKTimer():Void + { + timeSinceSpook = 0; + } + + function setupAnimations():Void + { + frames = FlxAnimationUtil.combineFramesCollections(Paths.getSparrowAtlas('freeplay/bfFreeplay'), Paths.getSparrowAtlas('freeplay/bf-freeplay-afk')); + + animation.addByPrefix('intro', "boyfriend dj intro", 24, false); + addOffset('intro', 0, 0); + + animation.addByPrefix('idle', "Boyfriend DJ0", 24, false); + addOffset('idle', -4, -426); + + animation.addByPrefix('confirm', "Boyfriend DJ confirm", 24, false); + addOffset('confirm', 40, -451); + + animation.addByPrefix('spook', "bf dj afk0", 24, false); + addOffset('spook', -3, -272); + } + + public function confirm():Void + { + currentState = Confirm; + } + + public inline function addOffset(name:String, x:Float = 0, y:Float = 0) + { + animOffsets[name] = [x, y]; + } + + public function getCurrentAnimation():String + { + if (this.animation == null || this.animation.curAnim == null) + return ""; + return this.animation.curAnim.name; + } public function playAnimation(AnimName:String, Force:Bool = false, Reversed:Bool = false, Frame:Int = 0):Void { animation.play(AnimName, Force, Reversed, Frame); + applyAnimOffset(); + } + function applyAnimOffset() + { + var AnimName = getCurrentAnimation(); var daOffset = animOffsets.get(AnimName); if (animOffsets.exists(AnimName)) { @@ -50,9 +172,12 @@ class DJBoyfriend extends FlxSprite else offset.set(0, 0); } - - public function addOffset(name:String, x:Float = 0, y:Float = 0) - { - animOffsets[name] = [x, y]; - } +} + +enum DJBoyfriendState +{ + Intro; + Idle; + Confirm; + Spook; } diff --git a/source/funkin/util/assets/FlxAnimationUtil.hx b/source/funkin/util/assets/FlxAnimationUtil.hx index d40dfa73c..52b25999c 100644 --- a/source/funkin/util/assets/FlxAnimationUtil.hx +++ b/source/funkin/util/assets/FlxAnimationUtil.hx @@ -1,6 +1,7 @@ package funkin.util.assets; import flixel.FlxSprite; +import flixel.graphics.frames.FlxFramesCollection; import funkin.play.AnimationData; class FlxAnimationUtil @@ -39,4 +40,20 @@ class FlxAnimationUtil addAtlasAnimation(target, anim); } } + + public static function combineFramesCollections(a:FlxFramesCollection, b:FlxFramesCollection):FlxFramesCollection + { + var result:FlxFramesCollection = new FlxFramesCollection(null, ATLAS, null); + + for (frame in a.frames) + { + result.pushFrame(frame); + } + for (frame in b.frames) + { + result.pushFrame(frame); + } + + return result; + } }