diff --git a/assets b/assets index be892870e..2a0afcd76 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit be892870e4d4397240d0b47275c4a8259583a942 +Subproject commit 2a0afcd76a26251dbbebb6901df4651f25a92c23 diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 56b36d5df..3bea9cca2 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -51,6 +51,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 @@ -394,8 +399,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; } 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/play/PlayState.hx b/source/funkin/play/PlayState.hx index 6dc41e3f9..fd6463bb1 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2944,10 +2944,7 @@ class PlayState extends MusicBeatSubState if (overrideMusic) { // Stop the music. Do NOT destroy it, something still references it! - if (FlxG.sound.music != null) - { - FlxG.sound.music.pause(); - } + if (FlxG.sound.music != null) FlxG.sound.music.pause(); if (vocals != null) { vocals.pause(); @@ -2957,10 +2954,7 @@ class PlayState extends MusicBeatSubState else { // Stop and destroy the music. - if (FlxG.sound.music != null) - { - FlxG.sound.music.pause(); - } + if (FlxG.sound.music != null) FlxG.sound.music.pause(); if (vocals != null) { vocals.destroy(); 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'; } } diff --git a/source/funkin/ui/freeplay/DJBoyfriend.hx b/source/funkin/ui/freeplay/DJBoyfriend.hx index 8cd63dba1..33f264301 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; @@ -48,7 +49,6 @@ class DJBoyfriend extends FlxAtlasSprite }; setupAnimations(); - trace(listAnimations()); FlxG.debugger.track(this); FlxG.console.registerObject("dj", this); @@ -87,20 +87,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 +112,7 @@ class DJBoyfriend extends FlxAtlasSprite { onSpook.dispatch(); playFlashAnimation('bf dj afk', false); + gotSpooked = true; } timeSinceSpook = 0; case TV: @@ -119,6 +121,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 +169,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 +186,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 +219,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 +293,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 f7554197f..6cb0d1d9a 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); @@ -1234,8 +1230,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); } 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)