mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-26 17:46:08 -05:00
Working Blazin cutscene and fixed time travel
This commit is contained in:
parent
66c91d8b3e
commit
90360de0d0
4 changed files with 155 additions and 44 deletions
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit 7d031153cf073e9d49ab59d7c72956cf4a68bcda
|
||||
Subproject commit 1b0e097508ec694012043a4c059885b05a569e2a
|
|
@ -148,6 +148,29 @@ class SongEventRegistry
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The currentTime has jumped far ahead or back.
|
||||
* If we moved back in time, we need to reset all the events in that space.
|
||||
* If we moved forward in time, we need to skip all the events in that space.
|
||||
*/
|
||||
public static function handleSkippedEvents(events:Array<SongEventData>, currentTime:Float):Void
|
||||
{
|
||||
for (event in events)
|
||||
{
|
||||
// Deactivate future events.
|
||||
if (event.time > currentTime)
|
||||
{
|
||||
event.activated = false;
|
||||
}
|
||||
|
||||
// Skip past events.
|
||||
if (event.time < currentTime)
|
||||
{
|
||||
event.activated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset activation of all the provided events.
|
||||
*/
|
||||
|
|
|
@ -987,8 +987,21 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
|
||||
processSongEvents();
|
||||
|
||||
// Handle keybinds.
|
||||
processInputQueue();
|
||||
if (!isInCutscene && !disableKeys) debugKeyShit();
|
||||
if (isInCutscene && !disableKeys) handleCutsceneKeys(elapsed);
|
||||
|
||||
// Moving notes into position is now done by Strumline.update().
|
||||
processNotes(elapsed);
|
||||
}
|
||||
|
||||
function processSongEvents():Void
|
||||
{
|
||||
// Query and activate song events.
|
||||
// TODO: Check that these work even when songPosition is less than 0.
|
||||
// TODO: Check that these work appropriately even when songPosition is less than 0, to play events during countdown.
|
||||
if (songEvents != null && songEvents.length > 0)
|
||||
{
|
||||
var songEventsToActivate:Array<SongEventData> = SongEventRegistry.queryEvents(songEvents, Conductor.instance.songPosition);
|
||||
|
@ -998,8 +1011,9 @@ class PlayState extends MusicBeatSubState
|
|||
trace('Found ${songEventsToActivate.length} event(s) to activate.');
|
||||
for (event in songEventsToActivate)
|
||||
{
|
||||
// If an event is trying to play, but it's over 5 seconds old, skip it.
|
||||
if (event.time - Conductor.instance.songPosition < -5000)
|
||||
// If an event is trying to play, but it's over 1 second old, skip it.
|
||||
var eventAge:Float = Conductor.instance.songPosition - event.time;
|
||||
if (eventAge > 1000)
|
||||
{
|
||||
event.activated = true;
|
||||
continue;
|
||||
|
@ -1015,14 +1029,6 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle keybinds.
|
||||
processInputQueue();
|
||||
if (!isInCutscene && !disableKeys) debugKeyShit();
|
||||
if (isInCutscene && !disableKeys) handleCutsceneKeys(elapsed);
|
||||
|
||||
// Moving notes into position is now done by Strumline.update().
|
||||
processNotes(elapsed);
|
||||
}
|
||||
|
||||
public override function dispatchEvent(event:ScriptEvent):Void
|
||||
|
@ -1761,7 +1767,7 @@ class PlayState extends MusicBeatSubState
|
|||
currentChart.playInst(1.0, false);
|
||||
}
|
||||
|
||||
FlxG.sound.music.onComplete = endSong;
|
||||
FlxG.sound.music.onComplete = endSong.bind(false);
|
||||
// A negative instrumental offset means the song skips the first few milliseconds of the track.
|
||||
// This just gets added into the startTimestamp behavior so we don't need to do anything extra.
|
||||
FlxG.sound.music.time = startTimestamp - Conductor.instance.instrumentalOffset;
|
||||
|
@ -2041,6 +2047,7 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
}
|
||||
|
||||
// Respawns notes that were b
|
||||
playerStrumline.handleSkippedNotes();
|
||||
opponentStrumline.handleSkippedNotes();
|
||||
}
|
||||
|
@ -2303,7 +2310,7 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
#if (debug || FORCE_DEBUG_VERSION)
|
||||
// 1: End the song immediately.
|
||||
if (FlxG.keys.justPressed.ONE) endSong();
|
||||
if (FlxG.keys.justPressed.ONE) endSong(true);
|
||||
|
||||
// 2: Gain 10% health.
|
||||
if (FlxG.keys.justPressed.TWO) health += 0.1 * Constants.HEALTH_MAX;
|
||||
|
@ -2497,16 +2504,35 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
if (skipHeldTimer >= 1.5)
|
||||
{
|
||||
VideoCutscene.finishVideo();
|
||||
skipVideoCutscene();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End the song. Handle saving high scores and transitioning to the results screen.
|
||||
* Handle logic for actually skipping a video cutscene after it has been held.
|
||||
*/
|
||||
function endSong():Void
|
||||
function skipVideoCutscene():Void
|
||||
{
|
||||
dispatchEvent(new ScriptEvent(SONG_END));
|
||||
VideoCutscene.finishVideo();
|
||||
}
|
||||
|
||||
/**
|
||||
* End the song. Handle saving high scores and transitioning to the results screen.
|
||||
*
|
||||
* Broadcasts an `onSongEnd` event, which can be cancelled to prevent the song from ending (for a cutscene or something).
|
||||
* Remember to call `endSong` again when the song should actually end!
|
||||
* @param rightGoddamnNow If true, don't play the fancy animation where you zoom onto Girlfriend. Used after a cutscene.
|
||||
*/
|
||||
public function endSong(rightGoddamnNow:Bool = false):Void
|
||||
{
|
||||
FlxG.sound.music.volume = 0;
|
||||
vocals.volume = 0;
|
||||
mayPauseGame = false;
|
||||
|
||||
// Check if any events want to prevent the song from ending.
|
||||
var event = new ScriptEvent(SONG_END, true);
|
||||
dispatchEvent(event);
|
||||
if (event.eventCanceled) return;
|
||||
|
||||
#if sys
|
||||
// spitter for ravy, teehee!!
|
||||
|
@ -2516,9 +2542,7 @@ class PlayState extends MusicBeatSubState
|
|||
#end
|
||||
|
||||
deathCounter = 0;
|
||||
mayPauseGame = false;
|
||||
FlxG.sound.music.volume = 0;
|
||||
vocals.volume = 0;
|
||||
|
||||
if (currentSong != null && currentSong.validScore)
|
||||
{
|
||||
// crackhead double thingie, sets whether was new highscore, AND saves the song!
|
||||
|
@ -2605,7 +2629,14 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
else
|
||||
{
|
||||
moveToResultsScreen();
|
||||
if (rightGoddamnNow)
|
||||
{
|
||||
moveToResultsScreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
zoomIntoResultsScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -2663,7 +2694,14 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
else
|
||||
{
|
||||
moveToResultsScreen();
|
||||
if (rightGoddamnNow)
|
||||
{
|
||||
moveToResultsScreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
zoomIntoResultsScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2717,9 +2755,9 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
|
||||
/**
|
||||
* Play the camera zoom animation and move to the results screen.
|
||||
* Play the camera zoom animation and then move to the results screen once it's done.
|
||||
*/
|
||||
function moveToResultsScreen():Void
|
||||
function zoomIntoResultsScreen():Void
|
||||
{
|
||||
trace('WENT TO RESULTS SCREEN!');
|
||||
|
||||
|
@ -2773,22 +2811,30 @@ class PlayState extends MusicBeatSubState
|
|||
{
|
||||
ease: FlxEase.expoIn,
|
||||
onComplete: function(_) {
|
||||
persistentUpdate = false;
|
||||
vocals.stop();
|
||||
camHUD.alpha = 1;
|
||||
var res:ResultState = new ResultState(
|
||||
{
|
||||
storyMode: PlayStatePlaylist.isStoryMode,
|
||||
title: PlayStatePlaylist.isStoryMode ? ('${PlayStatePlaylist.campaignTitle}') : ('${currentChart.songName} by ${currentChart.songArtist}'),
|
||||
tallies: Highscore.tallies,
|
||||
});
|
||||
res.camera = camHUD;
|
||||
openSubState(res);
|
||||
moveToResultsScreen();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Move to the results screen right goddamn now.
|
||||
*/
|
||||
function moveToResultsScreen():Void
|
||||
{
|
||||
persistentUpdate = false;
|
||||
vocals.stop();
|
||||
camHUD.alpha = 1;
|
||||
var res:ResultState = new ResultState(
|
||||
{
|
||||
storyMode: PlayStatePlaylist.isStoryMode,
|
||||
title: PlayStatePlaylist.isStoryMode ? ('${PlayStatePlaylist.campaignTitle}') : ('${currentChart.songName} by ${currentChart.songArtist}'),
|
||||
tallies: Highscore.tallies,
|
||||
});
|
||||
res.camera = camHUD;
|
||||
openSubState(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses music and vocals easily.
|
||||
*/
|
||||
|
@ -2818,14 +2864,18 @@ class PlayState extends MusicBeatSubState
|
|||
*/
|
||||
function changeSection(sections:Int):Void
|
||||
{
|
||||
FlxG.sound.music.pause();
|
||||
// FlxG.sound.music.pause();
|
||||
|
||||
var targetTimeSteps:Float = Conductor.instance.currentStepTime + (Conductor.instance.timeSignatureNumerator * Constants.STEPS_PER_BEAT * sections);
|
||||
var targetTimeSteps:Float = Conductor.instance.currentStepTime + (Conductor.instance.stepsPerMeasure * sections);
|
||||
var targetTimeMs:Float = Conductor.instance.getStepTimeInMs(targetTimeSteps);
|
||||
|
||||
// Don't go back in time to before the song started.
|
||||
targetTimeMs = Math.max(0, targetTimeMs);
|
||||
|
||||
FlxG.sound.music.time = targetTimeMs;
|
||||
|
||||
handleSkippedNotes();
|
||||
SongEventRegistry.handleSkippedEvents(songEvents, Conductor.instance.songPosition);
|
||||
// regenNoteData(FlxG.sound.music.time);
|
||||
|
||||
Conductor.instance.update(FlxG.sound.music.time);
|
||||
|
|
|
@ -19,13 +19,22 @@ import hxcodec.flixel.FlxVideoSprite;
|
|||
class VideoCutscene
|
||||
{
|
||||
static var blackScreen:FlxSprite;
|
||||
static var cutsceneType:CutsceneType;
|
||||
|
||||
#if html5
|
||||
static var vid:FlxVideo;
|
||||
#end
|
||||
#if hxCodec
|
||||
static var vid:FlxVideoSprite;
|
||||
#end
|
||||
|
||||
/**
|
||||
* Play a video cutscene.
|
||||
* TODO: Currently this is hardcoded to start the countdown after the video is done.
|
||||
* @param path The path to the video file. Use Paths.file(path) to get the correct path.
|
||||
* @param cutseneType The type of cutscene to play, determines what the game does after. Defaults to `CutsceneType.STARTING`.
|
||||
*/
|
||||
public static function play(filePath:String):Void
|
||||
public static function play(filePath:String, ?cutsceneType:CutsceneType = STARTING):Void
|
||||
{
|
||||
if (PlayState.instance == null) return;
|
||||
|
||||
|
@ -49,6 +58,8 @@ class VideoCutscene
|
|||
blackScreen.cameras = [PlayState.instance.camCutscene];
|
||||
PlayState.instance.add(blackScreen);
|
||||
|
||||
VideoCutscene.cutsceneType = cutsceneType;
|
||||
|
||||
#if html5
|
||||
playVideoHTML5(filePath);
|
||||
#elseif hxCodec
|
||||
|
@ -68,8 +79,6 @@ class VideoCutscene
|
|||
}
|
||||
|
||||
#if html5
|
||||
static var vid:FlxVideo;
|
||||
|
||||
static function playVideoHTML5(filePath:String):Void
|
||||
{
|
||||
// Video displays OVER the FlxState.
|
||||
|
@ -94,8 +103,6 @@ class VideoCutscene
|
|||
#end
|
||||
|
||||
#if hxCodec
|
||||
static var vid:FlxVideoSprite;
|
||||
|
||||
static function playVideoNative(filePath:String):Void
|
||||
{
|
||||
// Video displays OVER the FlxState.
|
||||
|
@ -129,10 +136,17 @@ class VideoCutscene
|
|||
}
|
||||
#end
|
||||
|
||||
/**
|
||||
* Finish the active video cutscene. Done when the video is finished or when the player skips the cutscene.
|
||||
* @param transitionTime The duration of the transition to the next state. Defaults to 0.5 seconds (this time is always used when cancelling the video).
|
||||
* @param finishCutscene The callback to call when the transition is finished.
|
||||
*/
|
||||
public static function finishVideo(?transitionTime:Float = 0.5):Void
|
||||
{
|
||||
trace('ALERT: Finish video cutscene called!');
|
||||
|
||||
var cutsceneType:CutsceneType = VideoCutscene.cutsceneType;
|
||||
|
||||
#if html5
|
||||
if (vid != null)
|
||||
{
|
||||
|
@ -168,8 +182,32 @@ class VideoCutscene
|
|||
{
|
||||
ease: FlxEase.quadInOut,
|
||||
onComplete: function(twn:FlxTween) {
|
||||
PlayState.instance.startCountdown();
|
||||
onCutsceneFinish(cutsceneType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* The default callback used when a cutscene is finished.
|
||||
* You can specify your own callback when calling `VideoCutscene#play()`.
|
||||
*/
|
||||
static function onCutsceneFinish(cutsceneType:CutsceneType):Void
|
||||
{
|
||||
switch (cutsceneType)
|
||||
{
|
||||
case CutsceneType.STARTING:
|
||||
PlayState.instance.startCountdown();
|
||||
case CutsceneType.ENDING:
|
||||
PlayState.instance.endSong(true); // true = right goddamn now
|
||||
case CutsceneType.MIDSONG:
|
||||
throw "Not implemented!";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CutsceneType
|
||||
{
|
||||
STARTING; // The default cutscene type. Starts the countdown after the video is done.
|
||||
MIDSONG; // TODO: Implement this!
|
||||
ENDING; // Ends the song after the video is done.
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue