From 5d0c1521d58c4819b14847d620feb1064e556014 Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Tue, 9 Jan 2024 13:16:00 -0500 Subject: [PATCH] strumline refactor stuffy --- source/funkin/play/PlayState.hx | 110 +----------------- source/funkin/play/notes/Strumline.hx | 81 +++++++++++++ source/funkin/ui/debug/DebugMenuSubState.hx | 4 +- .../funkin/ui/debug/latency/CoolStatsGraph.hx | 33 ++++-- .../funkin/ui/debug/latency/LatencyState.hx | 76 +++++------- 5 files changed, 142 insertions(+), 162 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 995797dd1..1eaad0b06 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1791,63 +1791,7 @@ class PlayState extends MusicBeatSubState { if (playerStrumline?.notes?.members == null || opponentStrumline?.notes?.members == null) return; - // Process notes on the opponent's side. - for (note in opponentStrumline.notes.members) - { - if (note == null) continue; - - var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS; - var hitWindowCenter = note.strumTime; - var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS; - - if (Conductor.instance.songPosition > hitWindowEnd) - { - if (note.hasMissed) continue; - - note.tooEarly = false; - note.mayHit = false; - note.hasMissed = true; - - if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = true; - } - else if (Conductor.instance.songPosition > hitWindowCenter) - { - if (note.hasBeenHit) continue; - - // Call an event to allow canceling the note hit. - // NOTE: This is what handles the character animations! - var event:NoteScriptEvent = new NoteScriptEvent(NOTE_HIT, note, 0, true); - dispatchEvent(event); - - // Calling event.cancelEvent() skips all the other logic! Neat! - if (event.eventCanceled) continue; - - // Command the opponent to hit the note on time. - // NOTE: This is what handles the strumline and cleaning up the note itself! - opponentStrumline.hitNote(note); - - if (note.holdNoteSprite != null) - { - opponentStrumline.playNoteHoldCover(note.holdNoteSprite); - } - } - else if (Conductor.instance.songPosition > hitWindowStart) - { - if (note.hasBeenHit || note.hasMissed) continue; - - note.tooEarly = false; - note.mayHit = true; - note.hasMissed = false; - if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = false; - } - else - { - note.tooEarly = true; - note.mayHit = false; - note.hasMissed = false; - if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = false; - } - } + opponentStrumline.processNotes(null, dispatchEvent); // Process hold notes on the opponent's side. for (holdNote in opponentStrumline.holdNotes.members) @@ -1868,57 +1812,7 @@ class PlayState extends MusicBeatSubState // if (holdNote.missedNote && !holdNote.handledMiss) { holdNote.handledMiss = true; } } - // Process notes on the player's side. - for (note in playerStrumline.notes.members) - { - if (note == null || note.hasBeenHit) continue; - - var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS; - var hitWindowCenter = note.strumTime; - var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS; - - if (Conductor.instance.songPosition > hitWindowEnd) - { - note.tooEarly = false; - note.mayHit = false; - note.hasMissed = true; - if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = true; - } - else if (Conductor.instance.songPosition > hitWindowStart) - { - note.tooEarly = false; - note.mayHit = true; - note.hasMissed = false; - if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = false; - } - else - { - note.tooEarly = true; - note.mayHit = false; - note.hasMissed = false; - if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = false; - } - - // This becomes true when the note leaves the hit window. - // It might still be on screen. - if (note.hasMissed && !note.handledMiss) - { - // Call an event to allow canceling the note miss. - // NOTE: This is what handles the character animations! - var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, 0, true); - dispatchEvent(event); - - // Calling event.cancelEvent() skips all the other logic! Neat! - if (event.eventCanceled) continue; - - // Judge the miss. - // NOTE: This is what handles the scoring. - trace('Missed note! ${note.noteData}'); - onNoteMiss(note); - - note.handledMiss = true; - } - } + playerStrumline.processNotes(onNoteMiss, dispatchEvent); // Process hold notes on the player's side. // This handles scoring so we don't need it on the opponent's side. diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index b312494cf..b0ab1ba1c 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -14,6 +14,7 @@ import funkin.play.notes.SustainTrail; import funkin.data.song.SongData.SongNoteData; import funkin.ui.options.PreferencesMenu; import funkin.util.SortUtil; +import funkin.modding.events.ScriptEvent; /** * A group of sprites which handles the receptor, the note splashes, and the notes (with sustains) for a given player. @@ -142,6 +143,86 @@ class Strumline extends FlxSpriteGroup updateNotes(); } + /** + * Process the notes in this strumline. + * @param onNoteMiss + * @param dispatchEvent TODO: better way to do this? Maybe passing in current dispatchEvent function from current state? + */ + public function processNotes(?onNoteMiss:NoteSprite->Void, ?dispatchEvent:ScriptEvent->Void) + { + for (note in notes.members) + { + if (note == null || (isPlayer && note.hasBeenHit)) continue; + + var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS; + var hitWindowCenter = note.strumTime; + var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS; + + if (Conductor.instance.songPosition > hitWindowEnd) + { + if (!isPlayer && note.hasMissed) continue; + + note.tooEarly = false; + note.mayHit = false; + note.hasMissed = true; + + if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = true; + } + else if (Conductor.instance.songPosition > hitWindowCenter) + { + // only run this on opponent strumlines! + if (!isPlayer) continue; + + // Call an event to allow canceling the note hit. + // NOTE: This is what handles the character animations! + var event:NoteScriptEvent = new NoteScriptEvent(NOTE_HIT, note, 0, true); + if (dispatchEvent != null) dispatchEvent(event); + + // Calling event.cancelEvent() skips all other logic! Neat! + if (event.eventCanceled) continue; + + // Command the opponent to hit the note on time. + // NOTE: This is what handles the strumline and cleaning up the note itself! + + hitNote(note); + + if (note.holdNoteSprite != null) + { + playNoteHoldCover(note.holdNoteSprite); + } + } + else if (Conductor.instance.songPosition > hitWindowStart) + { + if (!isPlayer && (note.hasBeenHit || note.hasMissed)) continue; + + note.tooEarly = false; + note.mayHit = true; + note.hasMissed = false; + if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = false; + } + else + { + note.tooEarly = true; + note.mayHit = false; + note.hasMissed = false; + if (note.holdNoteSprite != null) note.holdNoteSprite.missedNote = false; + } + + if (note.hasMissed && !note.handledMiss) + { + var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, 0, true); + + if (dispatchEvent != null) dispatchEvent(event); + + if (event.eventCanceled) continue; + + if (onNoteMiss != null) onNoteMiss(note); + + note.handledMiss = true; + } + } + } + var frameMax:Int; var animFinishedEver:Bool; diff --git a/source/funkin/ui/debug/DebugMenuSubState.hx b/source/funkin/ui/debug/DebugMenuSubState.hx index 11c2e3625..f797dfbad 100644 --- a/source/funkin/ui/debug/DebugMenuSubState.hx +++ b/source/funkin/ui/debug/DebugMenuSubState.hx @@ -48,6 +48,8 @@ class DebugMenuSubState extends MusicBeatSubState items.onChange.add(onMenuChange); add(items); + FlxTransitionableState.skipNextTransIn = true; + // Create each menu item. // Call onMenuChange when the first item is created to move the camera . onMenuChange(createItem("CHART EDITOR", openChartEditor)); @@ -88,8 +90,6 @@ class DebugMenuSubState extends MusicBeatSubState function openChartEditor() { - FlxTransitionableState.skipNextTransIn = true; - FlxG.switchState(new ChartEditorState()); } diff --git a/source/funkin/ui/debug/latency/CoolStatsGraph.hx b/source/funkin/ui/debug/latency/CoolStatsGraph.hx index 01689f494..fc733ec28 100644 --- a/source/funkin/ui/debug/latency/CoolStatsGraph.hx +++ b/source/funkin/ui/debug/latency/CoolStatsGraph.hx @@ -7,7 +7,6 @@ import flash.text.TextField; import flash.text.TextFormatAlign; import flixel.math.FlxMath; import flixel.system.debug.DebuggerUtil; -import flixel.system.debug.stats.Stats; import flixel.util.FlxColor; import flixel.util.FlxDestroyUtil; @@ -16,13 +15,31 @@ import flixel.util.FlxDestroyUtil; * SHAMELESSLY STOLEN FROM FLIXEL * https://github.com/HaxeFlixel/flixel/blob/master/flixel/system/debug/stats/StatsGraph.hx */ -#if FLX_DEBUG class CoolStatsGraph extends Sprite { static inline var AXIS_COLOR:FlxColor = 0xffffff; static inline var AXIS_ALPHA:Float = 0.5; static inline var HISTORY_MAX:Int = 500; + /** + * How often to update the stats, in ms. The lower, the more performance-intense! + */ + static inline var UPDATE_DELAY:Int = 250; + + /** + * The initial width of the stats window. + */ + static inline var INITIAL_WIDTH:Int = 160; + + static inline var FPS_COLOR:FlxColor = 0xff96ff00; + static inline var MEMORY_COLOR:FlxColor = 0xff009cff; + static inline var DRAW_TIME_COLOR:FlxColor = 0xffA60004; + static inline var UPDATE_TIME_COLOR:FlxColor = 0xffdcd400; + + public static inline var LABEL_COLOR:FlxColor = 0xaaffffff; + public static inline var TEXT_SIZE:Int = 11; + public static inline var DECIMALS:Int = 1; + public var minLabel:TextField; public var curLabel:TextField; public var maxLabel:TextField; @@ -45,6 +62,7 @@ class CoolStatsGraph extends Sprite public function new(X:Int, Y:Int, Width:Int, Height:Int, GraphColor:FlxColor, Unit:String, LabelWidth:Int = 45, ?Label:String) { super(); + x = X; y = Y; _width = Width - LabelWidth; @@ -57,11 +75,11 @@ class CoolStatsGraph extends Sprite _axis = new Shape(); _axis.x = _labelWidth + 10; - maxLabel = DebuggerUtil.createTextField(0, 0, Stats.LABEL_COLOR, Stats.TEXT_SIZE); - curLabel = DebuggerUtil.createTextField(0, (_height / 2) - (Stats.TEXT_SIZE / 2), graphColor, Stats.TEXT_SIZE); - minLabel = DebuggerUtil.createTextField(0, _height - Stats.TEXT_SIZE, Stats.LABEL_COLOR, Stats.TEXT_SIZE); + maxLabel = DebuggerUtil.createTextField(0, 0, LABEL_COLOR, TEXT_SIZE); + curLabel = DebuggerUtil.createTextField(0, (_height / 2) - (TEXT_SIZE / 2), graphColor, TEXT_SIZE); + minLabel = DebuggerUtil.createTextField(0, _height - TEXT_SIZE, LABEL_COLOR, TEXT_SIZE); - avgLabel = DebuggerUtil.createTextField(_labelWidth + 20, (_height / 2) - (Stats.TEXT_SIZE / 2) - 10, Stats.LABEL_COLOR, Stats.TEXT_SIZE); + avgLabel = DebuggerUtil.createTextField(_labelWidth + 20, (_height / 2) - (TEXT_SIZE / 2) - 10, LABEL_COLOR, TEXT_SIZE); avgLabel.width = _width; avgLabel.defaultTextFormat.align = TextFormatAlign.CENTER; avgLabel.alpha = 0.5; @@ -136,7 +154,7 @@ class CoolStatsGraph extends Sprite function formatValue(value:Float):String { - return FlxMath.roundDecimal(value, Stats.DECIMALS) + " " + _unit; + return FlxMath.roundDecimal(value, DECIMALS) + " " + _unit; } public function average():Float @@ -157,4 +175,3 @@ class CoolStatsGraph extends Sprite history = null; } } -#end diff --git a/source/funkin/ui/debug/latency/LatencyState.hx b/source/funkin/ui/debug/latency/LatencyState.hx index c1321f19c..ae922612b 100644 --- a/source/funkin/ui/debug/latency/LatencyState.hx +++ b/source/funkin/ui/debug/latency/LatencyState.hx @@ -8,7 +8,6 @@ import flixel.group.FlxGroup.FlxTypedGroup; import flixel.math.FlxMath; import funkin.ui.MusicBeatSubState; import flixel.sound.FlxSound; -import flixel.system.debug.stats.StatsGraph; import flixel.text.FlxText; import flixel.util.FlxColor; import funkin.audio.visualize.PolygonSpectogram; @@ -16,12 +15,17 @@ import funkin.play.notes.NoteSprite; import funkin.ui.debug.latency.CoolStatsGraph; import haxe.Timer; import openfl.events.KeyboardEvent; +import funkin.input.PreciseInputManager; +import funkin.play.notes.Strumline; +import funkin.play.notes.notestyle.NoteStyle; +import funkin.data.notestyle.NoteStyleData; +import funkin.data.notestyle.NoteStyleRegistry; class LatencyState extends MusicBeatSubState { var offsetText:FlxText; var noteGrp:FlxTypedGroup; - var strumLine:FlxSprite; + var strumLine:Strumline; var blocks:FlxTypedGroup; @@ -34,10 +38,8 @@ class LatencyState extends MusicBeatSubState var offsetsPerBeat:Array = []; var swagSong:HomemadeMusic; - #if FLX_DEBUG var funnyStatsGraph:CoolStatsGraph; var realStats:CoolStatsGraph; - #end override function create() { @@ -51,33 +53,24 @@ class LatencyState extends MusicBeatSubState FlxG.sound.music = swagSong; FlxG.sound.music.play(); - #if FLX_DEBUG - funnyStatsGraph = new CoolStatsGraph(0, Std.int(FlxG.height / 2), FlxG.width, Std.int(FlxG.height / 2), FlxColor.PINK, "time"); + funnyStatsGraph = new CoolStatsGraph(0, Std.int(FlxG.height / 3), Std.int(FlxG.width / 2), Std.int(FlxG.height / 3), FlxColor.PINK, "time"); + funnyStatsGraph.curLabel.y += 32; FlxG.addChildBelowMouse(funnyStatsGraph); - realStats = new CoolStatsGraph(0, Std.int(FlxG.height / 2), FlxG.width, Std.int(FlxG.height / 2), FlxColor.YELLOW, "REAL"); + realStats = new CoolStatsGraph(0, Std.int(FlxG.height / 3), Std.int(FlxG.width / 2), Std.int(FlxG.height / 3), FlxColor.YELLOW, "REAL"); + realStats.curLabel.y -= 32; FlxG.addChildBelowMouse(realStats); - #end - FlxG.stage.addEventListener(KeyboardEvent.KEY_DOWN, key -> { - trace(key.charCode); - - if (key.charCode == 120) generateBeatStuff(); - - trace("\tEVENT PRESS: \t" + FlxG.sound.music.time + " " + Timer.stamp()); - // trace(FlxG.sound.music.prevTimestamp); - trace(FlxG.sound.music.time); - trace("\tFR FR PRESS: \t" + swagSong.getTimeWithDiff()); - - // trace("\tREDDIT: \t" + swagSong.frfrTime + " " + Timer.stamp()); - // @:privateAccess - // trace("\tREDDIT: \t" + FlxG.sound.music._channel.position + " " + Timer.stamp()); - // trace("EVENT LISTENER: " + key); + PreciseInputManager.instance.onInputPressed.add(function(event:PreciseInputEvent) { + strumLine.pressKey(event.noteDirection); + strumLine.playPress(event.noteDirection); + generateBeatStuff(event); }); - // FlxG.sound.playMusic(Paths.sound('soundTest')); - - // funnyStatsGraph.hi + PreciseInputManager.instance.onInputReleased.add(function(event:PreciseInputEvent) { + strumLine.playStatic(event.noteDirection); + strumLine.releaseKey(event.noteDirection); + }); Conductor.instance.forceBPM(60); @@ -139,10 +132,11 @@ class LatencyState extends MusicBeatSubState } offsetText = new FlxText(); + offsetText.size = 20; offsetText.screenCenter(); add(offsetText); - strumLine = new FlxSprite(FlxG.width / 2, 100).makeGraphic(FlxG.width, 5); + strumLine = new Strumline(NoteStyleRegistry.instance.fetchDefault(), true); add(strumLine); } @@ -175,15 +169,8 @@ class LatencyState extends MusicBeatSubState trace(FlxG.sound.music._channel.position); */ - #if FLX_DEBUG - funnyStatsGraph.update(FlxG.sound.music.time % 500); + funnyStatsGraph.update(Conductor.instance.songPosition % 500); realStats.update(swagSong.getTimeWithDiff() % 500); - #end - - if (FlxG.keys.justPressed.S) - { - trace("\tUPDATE PRESS: \t" + FlxG.sound.music.time + " " + Timer.stamp()); - } // if (FlxG.keys.justPressed.SPACE) // { @@ -192,17 +179,15 @@ class LatencyState extends MusicBeatSubState // FlxG.sound.music.resume(); // } - if (FlxG.keys.pressed.D) FlxG.sound.music.time += 1000 * FlxG.elapsed; - - Conductor.instance.update(swagSong.getTimeWithDiff() - Conductor.instance.inputOffset); + Conductor.instance.update(); // Conductor.instance.songPosition += (Timer.stamp() * 1000) - FlxG.sound.music.prevTimestamp; songPosVis.x = songPosToX(Conductor.instance.songPosition); songVisFollowAudio.x = songPosToX(Conductor.instance.songPosition - Conductor.instance.instrumentalOffset); songVisFollowVideo.x = songPosToX(Conductor.instance.songPosition - Conductor.instance.inputOffset); - offsetText.text = "INST Offset: " + Conductor.instance.instrumentalOffset + "ms"; - offsetText.text += "\nINPUT Offset: " + Conductor.instance.inputOffset + "ms"; + offsetText.text = "INST Offset (CTRL+Left/Right to change): " + Conductor.instance.instrumentalOffset + "ms"; + offsetText.text += "\nINPUT Offset (Left/Right to change): " + Conductor.instance.inputOffset + "ms"; offsetText.text += "\ncurrentStep: " + Conductor.instance.currentStep; offsetText.text += "\ncurrentBeat: " + Conductor.instance.currentBeat; @@ -267,19 +252,23 @@ class LatencyState extends MusicBeatSubState super.update(elapsed); } - function generateBeatStuff() + function generateBeatStuff(event:PreciseInputEvent) { - Conductor.instance.update(swagSong.getTimeWithDiff()); + // Conductor.instance.update(swagSong.getTimeWithDiff()); + + var inputLatencyMs:Float = haxe.Int64.toInt(PreciseInputManager.getCurrentTimestamp() - event.timestamp) / 1000.0 / 1000.0; + trace("input latency: " + inputLatencyMs + "ms"); + trace("cur timestamp: " + PreciseInputManager.getCurrentTimestamp() + "ns"); + trace("event timestamp: " + event.timestamp + "ns"); var closestBeat:Int = Math.round(Conductor.instance.songPosition / (Conductor.instance.stepLengthMs * 2)) % diffGrp.members.length; var getDiff:Float = Conductor.instance.songPosition - (closestBeat * (Conductor.instance.stepLengthMs * 2)); getDiff -= Conductor.instance.inputOffset; + getDiff -= inputLatencyMs; // lil fix for end of song if (closestBeat == 0 && getDiff >= Conductor.instance.stepLengthMs * 2) getDiff -= FlxG.sound.music.length; - trace("\tDISTANCE TO CLOSEST BEAT: " + getDiff + "ms"); - trace("\tCLOSEST BEAT: " + closestBeat); beatTrail.x = songPosVis.x; diffGrp.members[closestBeat].text = getDiff + "ms"; @@ -295,7 +284,6 @@ class LatencyState extends MusicBeatSubState class HomemadeMusic extends FlxSound { public var prevTimestamp:Int = 0; - public var timeWithDiff:Float = 0; public function new() {