mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2024-11-25 08:13:45 +00:00
Added ability to start song at a specific timestamp
This commit is contained in:
parent
5ff546bacc
commit
2048e65bf2
|
@ -191,7 +191,7 @@ class LatencyState extends MusicBeatSubState
|
|||
|
||||
if (FlxG.keys.pressed.D) FlxG.sound.music.time += 1000 * FlxG.elapsed;
|
||||
|
||||
Conductor.songPosition = swagSong.getTimeWithDiff() - Conductor.offset;
|
||||
Conductor.update(swagSong.getTimeWithDiff() - Conductor.offset);
|
||||
// Conductor.songPosition += (Timer.stamp() * 1000) - FlxG.sound.music.prevTimestamp;
|
||||
|
||||
songPosVis.x = songPosToX(Conductor.songPosition);
|
||||
|
|
|
@ -61,6 +61,15 @@ class MusicBeatSubState extends FlxTransitionableSubState implements IEventHandl
|
|||
|
||||
// This can now be used in EVERY STATE YAY!
|
||||
if (FlxG.keys.justPressed.F5) debug_refreshModules();
|
||||
|
||||
// Display Conductor info in the watch window.
|
||||
FlxG.watch.addQuick("songPosition", Conductor.songPosition);
|
||||
FlxG.watch.addQuick("bpm", Conductor.bpm);
|
||||
FlxG.watch.addQuick("currentMeasureTime", Conductor.currentBeatTime);
|
||||
FlxG.watch.addQuick("currentBeatTime", Conductor.currentBeatTime);
|
||||
FlxG.watch.addQuick("currentStepTime", Conductor.currentStepTime);
|
||||
|
||||
dispatchEvent(new UpdateScriptEvent(elapsed));
|
||||
}
|
||||
|
||||
function debug_refreshModules()
|
||||
|
|
|
@ -256,7 +256,7 @@ class TitleState extends MusicBeatState
|
|||
FlxTween.tween(FlxG.stage.window, {y: FlxG.stage.window.y + 100}, 0.7, {ease: FlxEase.quadInOut, type: PINGPONG});
|
||||
}
|
||||
|
||||
if (FlxG.sound.music != null) Conductor.songPosition = FlxG.sound.music.time;
|
||||
if (FlxG.sound.music != null) Conductor.update(FlxG.sound.music.time);
|
||||
if (FlxG.keys.justPressed.F) FlxG.fullscreen = !FlxG.fullscreen;
|
||||
|
||||
// do controls.PAUSE | controls.ACCEPT instead?
|
||||
|
|
|
@ -37,7 +37,7 @@ class Countdown
|
|||
stopCountdown();
|
||||
|
||||
PlayState.instance.isInCountdown = true;
|
||||
Conductor.songPosition = Conductor.beatLengthMs * -5;
|
||||
Conductor.update(PlayState.instance.startTimestamp + Conductor.beatLengthMs * -5);
|
||||
// Handle onBeatHit events manually
|
||||
@:privateAccess
|
||||
PlayState.instance.dispatchEvent(new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, 0, 0));
|
||||
|
@ -46,6 +46,12 @@ class Countdown
|
|||
countdownTimer = new FlxTimer();
|
||||
|
||||
countdownTimer.start(Conductor.beatLengthMs / 1000, function(tmr:FlxTimer) {
|
||||
if (PlayState.instance == null)
|
||||
{
|
||||
tmr.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
countdownStep = decrement(countdownStep);
|
||||
|
||||
// Handle onBeatHit events manually
|
||||
|
@ -146,7 +152,7 @@ class Countdown
|
|||
{
|
||||
stopCountdown();
|
||||
// This will trigger PlayState.startSong()
|
||||
Conductor.songPosition = 0;
|
||||
Conductor.update(0);
|
||||
// PlayState.isInCountdown = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ class GameOverSubState extends MusicBeatSubState
|
|||
gameOverMusic.stop();
|
||||
|
||||
// The conductor now represents the BPM of the game over music.
|
||||
Conductor.songPosition = 0;
|
||||
Conductor.update(0);
|
||||
}
|
||||
|
||||
var hasStartedAnimation:Bool = false;
|
||||
|
@ -183,7 +183,7 @@ class GameOverSubState extends MusicBeatSubState
|
|||
{
|
||||
// Match the conductor to the music.
|
||||
// This enables the stepHit and beatHit events.
|
||||
Conductor.songPosition = gameOverMusic.time;
|
||||
Conductor.update(gameOverMusic.time);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -88,6 +88,10 @@ typedef PlayStateParams =
|
|||
* @default `false`
|
||||
*/
|
||||
?minimalMode:Bool,
|
||||
/**
|
||||
* If specified, the game will jump to the specified timestamp after the countdown ends.
|
||||
*/
|
||||
?startTimestamp:Float,
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -236,6 +240,8 @@ class PlayState extends MusicBeatSubState
|
|||
*/
|
||||
public var disableKeys:Bool = false;
|
||||
|
||||
public var startTimestamp:Float = 0.0;
|
||||
|
||||
public var isSubState(get, null):Bool;
|
||||
|
||||
function get_isSubState():Bool
|
||||
|
@ -471,6 +477,7 @@ class PlayState extends MusicBeatSubState
|
|||
if (params.targetCharacter != null) currentPlayerId = params.targetCharacter;
|
||||
isPracticeMode = params.practiceMode ?? false;
|
||||
isMinimalMode = params.minimalMode ?? false;
|
||||
startTimestamp = params.startTimestamp ?? 0.0;
|
||||
|
||||
// Don't do anything else here! Wait until create() when we attach to the camera.
|
||||
}
|
||||
|
@ -560,7 +567,7 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
// Prepare the Conductor.
|
||||
Conductor.mapTimeChanges(currentChart.timeChanges);
|
||||
Conductor.update(-5000);
|
||||
Conductor.update((Conductor.beatLengthMs * -5) + startTimestamp);
|
||||
|
||||
// The song is now loaded. We can continue to initialize the play state.
|
||||
initCameras();
|
||||
|
@ -669,7 +676,7 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
FlxG.sound.music.pause();
|
||||
vocals.pause();
|
||||
FlxG.sound.music.time = 0;
|
||||
FlxG.sound.music.time = (startTimestamp);
|
||||
vocals.time = 0;
|
||||
|
||||
FlxG.sound.music.volume = 1;
|
||||
|
@ -700,8 +707,8 @@ class PlayState extends MusicBeatSubState
|
|||
{
|
||||
if (isInCountdown)
|
||||
{
|
||||
Conductor.songPosition += elapsed * 1000;
|
||||
if (Conductor.songPosition >= 0) startSong();
|
||||
Conductor.update(Conductor.songPosition + elapsed * 1000);
|
||||
if (Conductor.songPosition >= startTimestamp) startSong();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1067,7 +1074,8 @@ class PlayState extends MusicBeatSubState
|
|||
// super.stepHit() returns false if a module cancelled the event.
|
||||
if (!super.stepHit()) return false;
|
||||
|
||||
if (FlxG.sound.music != null
|
||||
if (!startingSong
|
||||
&& FlxG.sound.music != null
|
||||
&& (Math.abs(FlxG.sound.music.time - (Conductor.songPosition - Conductor.offset)) > 200
|
||||
|| Math.abs(vocals.checkSyncError(Conductor.songPosition - Conductor.offset)) > 200))
|
||||
{
|
||||
|
@ -1515,7 +1523,7 @@ class PlayState extends MusicBeatSubState
|
|||
/**
|
||||
* Read note data from the chart and generate the notes.
|
||||
*/
|
||||
function regenNoteData():Void
|
||||
function regenNoteData(startTime:Float = 0):Void
|
||||
{
|
||||
Highscore.tallies.combo = 0;
|
||||
Highscore.tallies = new Tallies();
|
||||
|
@ -1531,6 +1539,8 @@ class PlayState extends MusicBeatSubState
|
|||
for (songNote in currentChart.notes)
|
||||
{
|
||||
var strumTime:Float = songNote.time;
|
||||
if (strumTime < startTime) continue; // Skip notes that are before the start time.
|
||||
|
||||
var noteData:Int = songNote.getDirection();
|
||||
|
||||
var playerNote:Bool = true;
|
||||
|
@ -1617,14 +1627,22 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
|
||||
FlxG.sound.music.onComplete = endSong;
|
||||
FlxG.sound.music.play(false, startTimestamp);
|
||||
trace('Playing vocals...');
|
||||
add(vocals);
|
||||
vocals.play();
|
||||
resyncVocals();
|
||||
|
||||
#if discord_rpc
|
||||
// Updating Discord Rich Presence (with Time Left)
|
||||
DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true, currentSongLengthMs);
|
||||
#end
|
||||
|
||||
if (startTimestamp > 0)
|
||||
{
|
||||
FlxG.sound.music.time = startTimestamp;
|
||||
handleSkippedNotes();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1640,7 +1658,7 @@ class PlayState extends MusicBeatSubState
|
|||
Conductor.update();
|
||||
|
||||
vocals.time = FlxG.sound.music.time;
|
||||
vocals.play();
|
||||
vocals.play(false, FlxG.sound.music.time);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1836,6 +1854,23 @@ class PlayState extends MusicBeatSubState
|
|||
*/
|
||||
var inputSpitter:Array<ScoreInput> = [];
|
||||
|
||||
function handleSkippedNotes():Void
|
||||
{
|
||||
for (note in playerStrumline.notes.members)
|
||||
{
|
||||
var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS;
|
||||
var hitWindowCenter = note.strumTime;
|
||||
var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS;
|
||||
|
||||
if (Conductor.songPosition > hitWindowEnd)
|
||||
{
|
||||
// We have passed this note.
|
||||
// Flag the note for deletion without actually penalizing the player.
|
||||
note.handledMiss = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PreciseInputEvents are put into a queue between update() calls,
|
||||
* and then processed here.
|
||||
|
@ -2064,11 +2099,10 @@ class PlayState extends MusicBeatSubState
|
|||
if (event.eventCanceled) return;
|
||||
|
||||
health -= Constants.HEALTH_MISS_PENALTY;
|
||||
songScore -= 10;
|
||||
|
||||
if (!isPracticeMode)
|
||||
{
|
||||
songScore -= 10;
|
||||
|
||||
// messy copy paste rn lol
|
||||
var pressArray:Array<Bool> = [
|
||||
controls.NOTE_LEFT_P,
|
||||
|
@ -2139,11 +2173,10 @@ class PlayState extends MusicBeatSubState
|
|||
if (event.eventCanceled) return;
|
||||
|
||||
health += event.healthChange;
|
||||
songScore += event.scoreChange;
|
||||
|
||||
if (!isPracticeMode)
|
||||
{
|
||||
songScore += event.scoreChange;
|
||||
|
||||
var pressArray:Array<Bool> = [
|
||||
controls.NOTE_LEFT_P,
|
||||
controls.NOTE_DOWN_P,
|
||||
|
@ -2282,11 +2315,10 @@ class PlayState extends MusicBeatSubState
|
|||
playerStrumline.playNoteSplash(daNote.noteData.getDirection());
|
||||
}
|
||||
|
||||
// Only add the score if you're not on practice mode
|
||||
songScore += score;
|
||||
|
||||
if (!isPracticeMode)
|
||||
{
|
||||
songScore += score;
|
||||
|
||||
// TODO: Input splitter uses old input system, make it pull from the precise input queue directly.
|
||||
var pressArray:Array<Bool> = [
|
||||
controls.NOTE_LEFT_P,
|
||||
|
@ -2643,30 +2675,16 @@ class PlayState extends MusicBeatSubState
|
|||
{
|
||||
FlxG.sound.music.pause();
|
||||
|
||||
FlxG.sound.music.time += sections * Conductor.measureLengthMs;
|
||||
var targetTimeSteps:Float = Conductor.currentStepTime + (Conductor.timeSignatureNumerator * Constants.STEPS_PER_BEAT * sections);
|
||||
var targetTimeMs:Float = Conductor.getStepTimeInMs(targetTimeSteps);
|
||||
|
||||
FlxG.sound.music.time = targetTimeMs;
|
||||
|
||||
handleSkippedNotes();
|
||||
// regenNoteData(FlxG.sound.music.time);
|
||||
|
||||
Conductor.update(FlxG.sound.music.time);
|
||||
|
||||
/**
|
||||
*
|
||||
// TODO: Redo this for the new conductor.
|
||||
var daBPM:Float = Conductor.bpm;
|
||||
var daPos:Float = 0;
|
||||
for (i in 0...(Std.int(Conductor.currentStep / 16 + sec)))
|
||||
{
|
||||
var section = .getSong()[i];
|
||||
if (section == null) continue;
|
||||
if (section.changeBPM)
|
||||
{
|
||||
daBPM = .getSong()[i].bpm;
|
||||
}
|
||||
daPos += 4 * (1000 * 60 / daBPM);
|
||||
}
|
||||
Conductor.songPosition = FlxG.sound.music.time = daPos;
|
||||
Conductor.songPosition += Conductor.offset;
|
||||
|
||||
*/
|
||||
|
||||
resyncVocals();
|
||||
}
|
||||
#end
|
||||
|
|
|
@ -220,6 +220,7 @@ class Strumline extends FlxSpriteGroup
|
|||
{
|
||||
if (noteData.length == 0) return;
|
||||
|
||||
var songStart:Float = PlayState.instance.startTimestamp ?? 0.0;
|
||||
var renderWindowStart:Float = Conductor.songPosition + RENDER_DISTANCE_MS;
|
||||
|
||||
for (noteIndex in nextNoteIndex...noteData.length)
|
||||
|
@ -227,6 +228,7 @@ class Strumline extends FlxSpriteGroup
|
|||
var note:Null<SongNoteData> = noteData[noteIndex];
|
||||
|
||||
if (note == null) continue;
|
||||
if (note.time < songStart) continue;
|
||||
if (note.time > renderWindowStart) break;
|
||||
|
||||
var noteSprite = buildNoteSprite(note);
|
||||
|
|
|
@ -400,6 +400,11 @@ class ChartEditorState extends HaxeUIState
|
|||
return isViewDownscroll;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, playtesting a chart will skip to the current playhead position.
|
||||
*/
|
||||
var playtestStartTime:Bool = false;
|
||||
|
||||
/**
|
||||
* Whether hitsounds are enabled for at least one character.
|
||||
*/
|
||||
|
@ -1305,6 +1310,9 @@ class ChartEditorState extends HaxeUIState
|
|||
addUIChangeListener('menubarItemDownscroll', event -> isViewDownscroll = event.value);
|
||||
setUICheckboxSelected('menubarItemDownscroll', isViewDownscroll);
|
||||
|
||||
addUIChangeListener('menubarItemPlaytestStartTime', event -> playtestStartTime = event.value);
|
||||
setUICheckboxSelected('menubarItemPlaytestStartTime', playtestStartTime);
|
||||
|
||||
addUIChangeListener('menuBarItemThemeLight', function(event:UIEvent) {
|
||||
if (event.target.value) currentTheme = ChartEditorTheme.Light;
|
||||
});
|
||||
|
@ -3049,6 +3057,9 @@ class ChartEditorState extends HaxeUIState
|
|||
*/
|
||||
public function testSongInPlayState(?minimal:Bool = false):Void
|
||||
{
|
||||
var startTimestamp:Float = 0;
|
||||
if (playtestStartTime) startTimestamp = scrollPositionInMs + playheadPositionInMs;
|
||||
|
||||
var targetSong:Song = Song.buildRaw(currentSongId, songMetadata.values(), availableVariations, songChartData, false);
|
||||
|
||||
subStateClosed.add(fixCamera);
|
||||
|
@ -3061,6 +3072,7 @@ class ChartEditorState extends HaxeUIState
|
|||
// targetCharacter: targetCharacter,
|
||||
practiceMode: true,
|
||||
minimalMode: minimal,
|
||||
startTimestamp: startTimestamp,
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue