diff --git a/hmm.json b/hmm.json index d849bcf54..50c9a7e24 100644 --- a/hmm.json +++ b/hmm.json @@ -55,13 +55,6 @@ "ref": "b9118f47f43a66bc0e5fbfcfd9903f0425e918ee", "url": "https://github.com/FunkinCrew/flixel-addons" }, - { - "name": "flixel-text-input", - "type": "git", - "dir": null, - "ref": "951a0103a17bfa55eed86703ce50b4fb0d7590bc", - "url": "https://github.com/FunkinCrew/flixel-text-input" - }, { "name": "flxanimate", "type": "git", @@ -97,14 +90,14 @@ "name": "haxeui-core", "type": "git", "dir": null, - "ref": "07fc7aa8098deaea633b0726d01f83eb4ef8a832", + "ref": "47ee9f5fa02d422e186e844f87e68220cabfcc5b", "url": "https://github.com/haxeui/haxeui-core" }, { "name": "haxeui-flixel", "type": "git", "dir": null, - "ref": "b899a4c7d7318c5ff2b1bb645fbc73728fad1ac9", + "ref": "100f2c96beab619cfe72c567a058c41c71e3e998", "url": "https://github.com/haxeui/haxeui-flixel" }, { @@ -177,7 +170,7 @@ "name": "lime", "type": "git", "dir": null, - "ref": "71c0f93e91c10975426d7518b826c1584a4f4b73", + "ref": "045ede0363120ff0425529a4902e4da44fd6d293", "url": "https://github.com/FunkinCrew/lime-mobile" }, { diff --git a/project.hxp b/project.hxp index c314b5734..8b98f4303 100644 --- a/project.hxp +++ b/project.hxp @@ -1036,7 +1036,6 @@ class Project extends HXProject // addHaxelib('flixel-ui'); // UI framework (DEPRECATED) addHaxelib('haxeui-core'); // UI framework addHaxelib('haxeui-flixel'); // Integrate HaxeUI with Flixel - addHaxelib('flixel-text-input'); // Improved text field rendering for HaxeUI addHaxelib('polymod'); // Modding framework addHaxelib('flxanimate'); // Texture atlas rendering diff --git a/source/funkin/mobile/input/PreciseInputHandler.hx b/source/funkin/mobile/input/PreciseInputHandler.hx index b4c243ce6..f240a2c94 100644 --- a/source/funkin/mobile/input/PreciseInputHandler.hx +++ b/source/funkin/mobile/input/PreciseInputHandler.hx @@ -31,7 +31,6 @@ class PreciseInputHandler static function handleHintDown(hint:FunkinHint):Void { final timestamp:Int64 = PreciseInputManager.getCurrentTimestamp(); - @:privateAccess if (hint.input?.justPressed ?? false) { @@ -48,7 +47,6 @@ class PreciseInputHandler static function handleHintUp(hint:FunkinHint):Void { final timestamp:Int64 = PreciseInputManager.getCurrentTimestamp(); - @:privateAccess if (hint.input?.justReleased ?? false) { diff --git a/source/funkin/mobile/ui/options/ControlsSchemeMenu.hx b/source/funkin/mobile/ui/options/ControlsSchemeMenu.hx index 34ba6dd01..78960af63 100644 --- a/source/funkin/mobile/ui/options/ControlsSchemeMenu.hx +++ b/source/funkin/mobile/ui/options/ControlsSchemeMenu.hx @@ -290,6 +290,7 @@ class ControlsSchemeMenu extends MusicBeatSubState }); } + #if FEATURE_TOUCH_CONTROLS /** * Handles touch dragging. */ @@ -344,7 +345,7 @@ class ControlsSchemeMenu extends MusicBeatSubState if (TouchUtil.pressed && dragDistance != 0) { final showcasesTargetX:Float = originX + dragDistance * 10; - hitboxShowcases.x = MathUtil.smoothLerp(hitboxShowcases.x, showcasesTargetX, elapsed, 0.5); + hitboxShowcases.x = MathUtil.smoothLerpPrecision(hitboxShowcases.x, showcasesTargetX, elapsed, 0.5); final minShowcasesX:Float = -1500 * availableSchemes.length; hitboxShowcases.x = FlxMath.bound(hitboxShowcases.x, minShowcasesX, 400); @@ -355,7 +356,8 @@ class ControlsSchemeMenu extends MusicBeatSubState } else { - hitboxShowcases.x = MathUtil.smoothLerp(hitboxShowcases.x, (-1500 * currentIndex) + (-1500 / (availableSchemes.length + 1) * currentIndex), elapsed, 0.5); + hitboxShowcases.x = MathUtil.smoothLerpPrecision(hitboxShowcases.x, (-1500 * currentIndex) + (-1500 / (availableSchemes.length + 1) * currentIndex), elapsed, 0.5); } + #end } } diff --git a/source/funkin/mobile/ui/options/objects/HitboxShowcase.hx b/source/funkin/mobile/ui/options/objects/HitboxShowcase.hx index 9e515b969..270ebb729 100644 --- a/source/funkin/mobile/ui/options/objects/HitboxShowcase.hx +++ b/source/funkin/mobile/ui/options/objects/HitboxShowcase.hx @@ -110,7 +110,7 @@ class HitboxShowcase extends FlxSpriteGroup { super.update(elapsed); - alpha = MathUtil.smoothLerp(alpha, HITBOX_SHOWCASE_ALPHA[selected ? 1 : 0], elapsed, 0.2); + alpha = MathUtil.smoothLerpPrecision(alpha, HITBOX_SHOWCASE_ALPHA[selected ? 1 : 0], elapsed, 0.2); } function get_selected():Bool diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index 7ffb3e310..946598cb7 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -630,6 +630,7 @@ class PauseSubState extends MusicBeatSubState changeSelection(1); } + #if FEATURE_TOUCH_CONTROLS if (!SwipeUtil.justSwipedAny && !justOpened && currentMenuEntries.length > 0) { for (i in 0...menuEntryText.members.length) @@ -649,6 +650,7 @@ class PauseSubState extends MusicBeatSubState break; } } + #end if (controls.ACCEPT && currentMenuEntries.length > 0) { diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index c546f7b13..b7e056b73 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -620,6 +620,12 @@ class PlayState extends MusicBeatSubState return FlxG?.sound?.music?.length; } + /** + * The threshold for resyncing the song. + * If the vocals deviate from the instrumental by more than this amount, then `resyncVocals()` will be called. + */ + static final RESYNC_THRESHOLD:Float = 30; + // TODO: Refactor or document var generatedMusic:Bool = false; @@ -1690,7 +1696,9 @@ class PlayState extends MusicBeatSubState } if (!startingSong - && (Math.abs(FlxG.sound.music.time - correctSync) > 30 || Math.abs(playerVoicesError) > 30 || Math.abs(opponentVoicesError) > 30)) + && (Math.abs(FlxG.sound.music.time - correctSync) > RESYNC_THRESHOLD + || Math.abs(playerVoicesError) > RESYNC_THRESHOLD + || Math.abs(opponentVoicesError) > RESYNC_THRESHOLD)) { trace("VOCALS NEED RESYNC"); if (vocals != null) @@ -2368,6 +2376,8 @@ class PlayState extends MusicBeatSubState #if FEATURE_NEWGROUNDS Events.logStartSong(currentSong.id, currentVariation); #end + + resyncVocals(); } /** diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 8da7a18dd..ed7295abc 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -199,7 +199,6 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry = SongRegistry.instance.getScriptedEntryClassName(songId); - @:privateAccess if (songClassName != null) result = SongRegistry.instance.createScriptedEntry(songClassName); } diff --git a/source/funkin/ui/charSelect/CharSelectSubState.hx b/source/funkin/ui/charSelect/CharSelectSubState.hx index b2192dac2..b1d113e14 100644 --- a/source/funkin/ui/charSelect/CharSelectSubState.hx +++ b/source/funkin/ui/charSelect/CharSelectSubState.hx @@ -785,12 +785,14 @@ class CharSelectSubState extends MusicBeatSubState mobileAccept = false; - if (controls.UI_UP_R || controls.UI_DOWN_R || controls.UI_LEFT_R || controls.UI_RIGHT_R || TouchUtil.justReleased) selectSound.pitch = 1; + if (controls.UI_UP_R || controls.UI_DOWN_R || controls.UI_LEFT_R || controls.UI_RIGHT_R #if FEATURE_TOUCH_CONTROLS || TouchUtil.justReleased #end) + selectSound.pitch = 1; syncAudio(elapsed); if (allowInput && !pressedSelect) { + #if FEATURE_TOUCH_CONTROLS if (TouchUtil.pressed || TouchUtil.justReleased) { for (i => hitbox in grpHitboxes.members) @@ -821,6 +823,7 @@ class CharSelectSubState extends MusicBeatSubState { mobileAccept = true; } + #end // if (controls.UI_UP) holdTmrUp += elapsed; @@ -912,7 +915,7 @@ class CharSelectSubState extends MusicBeatSubState { curChar = availableChars.get(getCurrentSelected()); - if (allowInput && pressedSelect && (controls.BACK || (mobileDeny && TouchUtil.justReleased))) + if (allowInput && pressedSelect && (controls.BACK #if FEATURE_TOUCH_CONTROLS || (mobileDeny && TouchUtil.justReleased) #end)) { mobileDeny = false; cursorConfirmed.visible = false; @@ -968,7 +971,9 @@ class CharSelectSubState extends MusicBeatSubState goToFreeplay(); }); } + #if FEATURE_TOUCH_CONTROLS else if (pressedSelect && TouchUtil.justReleased) mobileDeny = true; + #end mobileAccept = false; } diff --git a/source/funkin/ui/credits/CreditsState.hx b/source/funkin/ui/credits/CreditsState.hx index e12a542d6..990c763e2 100644 --- a/source/funkin/ui/credits/CreditsState.hx +++ b/source/funkin/ui/credits/CreditsState.hx @@ -7,6 +7,8 @@ import flixel.FlxSprite; import funkin.ui.mainmenu.MainMenuState; import flixel.group.FlxSpriteGroup; import funkin.util.TouchUtil; +import funkin.ui.credits.CreditsData.CreditsDataRole; +import funkin.ui.credits.CreditsData.CreditsDataMember; /** * The state used to display the credits scroll. @@ -77,6 +79,9 @@ class CreditsState extends MusicBeatState var scrollPaused:Bool = false; + var backersToBuild:Array; + var entriesToBuild:Array; + public function new() { super(); @@ -86,6 +91,22 @@ class CreditsState extends MusicBeatState { super.create(); + backersToBuild = CreditsDataHandler.fetchBackerEntries(); + + entriesToBuild = []; + for (entry in CreditsDataHandler.CREDITS_DATA.entries) + { + entriesToBuild.push( + { + data: entry, + lineIndexToBuild: 0, + backerIndexToBuild: 0, + hasBuiltHeader: (entry.header == null), + hasBuiltBody: (entry.body.length == 0), + hasBuiltBackers: (!entry.appendBackers || backersToBuild.length == 0) + }); + } + // Background var bg = new FlxSprite(Paths.image('menuDesat')); bg.scrollFactor.x = 0; @@ -105,7 +126,7 @@ class CreditsState extends MusicBeatState creditsGroup.x = Math.max(funkin.ui.FullScreenScaleMode.gameNotchSize.x, SCREEN_PAD); creditsGroup.y = STARTING_HEIGHT; - buildCreditsGroup(); + // buildCreditsGroup(); add(creditsGroup); @@ -124,39 +145,44 @@ class CreditsState extends MusicBeatState #end } - function buildCreditsGroup():Void + var creditsLineY:Float = 0; + + function buildCreditsEntryLine(entry:CreditsEntry):Void { - var y:Float = 0; - - for (entry in CreditsDataHandler.CREDITS_DATA.entries) + if (!entry.hasBuiltHeader) { - if (entry.header != null) - { - var header = buildCreditsLine(entry.header, y, true, CreditsSide.Left); - header.bold = true; - creditsGroup.add(header); - y += CREDITS_HEADER_FONT_SIZE + (header.textField.numLines * CREDITS_HEADER_FONT_SIZE); - } + var header:FlxText = buildCreditsLine(entry.data.header, creditsLineY, true, CreditsSide.Left); + creditsLineY += CREDITS_HEADER_FONT_SIZE + (header.textField.numLines * CREDITS_HEADER_FONT_SIZE); + entry.hasBuiltHeader = true; + return; + } - for (line in entry?.body ?? []) - { - var entry = buildCreditsLine(line.line, y, false, CreditsSide.Left); - creditsGroup.add(entry); - y += CREDITS_FONT_SIZE * entry.textField.numLines; - } + if (!entry.hasBuiltBody) + { + var lineData:CreditsDataMember = entry.data.body[entry.lineIndexToBuild]; + var line:FlxText = buildCreditsLine(lineData.line, creditsLineY, false, CreditsSide.Left); + creditsLineY += CREDITS_FONT_SIZE * line.textField.numLines; + entry.lineIndexToBuild++; - if (entry.appendBackers) + if (entry.lineIndexToBuild >= entry.data.body.length) { - var backers = CreditsDataHandler.fetchBackerEntries(); - for (backer in backers) - { - creditsGroup.add(buildCreditsLine(backer, y, false, CreditsSide.Left)); - y += CREDITS_FONT_SIZE; - } + entry.hasBuiltBody = true; } + return; + } - // Padding between each role. - y += CREDITS_FONT_SIZE * 2.5; + if (!entry.hasBuiltBackers) + { + var backer:String = backersToBuild[entry.backerIndexToBuild]; + creditsGroup.add(buildCreditsLine(backer, creditsLineY, false, CreditsSide.Left)); + creditsLineY += CREDITS_FONT_SIZE; + + entry.backerIndexToBuild++; + if (entry.backerIndexToBuild >= backersToBuild.length) + { + entry.hasBuiltBackers = true; + } + return; } } @@ -169,16 +195,66 @@ class CreditsState extends MusicBeatState var width = (side == CreditsSide.Center) ? FULL_WIDTH : (FULL_WIDTH / 2); var size = header ? CREDITS_HEADER_FONT_SIZE : CREDITS_FONT_SIZE; - var creditsLine:FlxText = new FlxText(xPos, yPos, width, text); + // using a cast since creditsGroup is FlxSpriteGroup + // we could also do FlxTypedSpriteGroup + var creditsLine:FlxText = cast(creditsGroup.recycle(() -> new FlxText()), FlxText); + creditsLine.x = xPos + creditsGroup.x; + creditsLine.y = yPos + creditsGroup.y; + creditsLine.fieldWidth = width; + creditsLine.text = text; + creditsLine.bold = header; creditsLine.setFormat(CREDITS_FONT, size, CREDITS_FONT_COLOR, FlxTextAlign.LEFT, FlxTextBorderStyle.OUTLINE, CREDITS_FONT_STROKE_COLOR, true); return creditsLine; } + function killOffScreenLines():Void + { + creditsGroup.forEachExists(function(creditsLine:FlxSprite) { + if (creditsLine.y + creditsLine.height <= 0) + { + creditsLine.kill(); + trace("killed line"); + } + }); + } + + function buildNextLine():Void + { + // no more entriesToBuild + if (entriesToBuild.length == 0) + { + return; + } + + // line is off-screen + if (creditsGroup.y + creditsLineY >= FlxG.height) + { + return; + } + + var entry:CreditsEntry = entriesToBuild[0]; + buildCreditsEntryLine(entry); + + // check if everything has been built + if (!entry.hasBuiltHeader || !entry.hasBuiltBody || !entry.hasBuiltBackers) + { + return; + } + + entriesToBuild.shift(); + + // offset that each entry has + creditsLineY += CREDITS_FONT_SIZE * 2.5; + } + public override function update(elapsed:Float):Void { super.update(elapsed); + killOffScreenLines(); + buildNextLine(); + if (!scrollPaused) { // TODO: Replace with whatever the special note button is. @@ -206,7 +282,7 @@ class CreditsState extends MusicBeatState function hasEnded():Bool { - return creditsGroup.y < -creditsGroup.height; + return creditsGroup.getFirstExisting() == null && entriesToBuild.length == 0; } function exit():Void @@ -226,3 +302,13 @@ enum CreditsSide Center; Right; } + +typedef CreditsEntry = +{ + var data:CreditsDataRole; + var lineIndexToBuild:Int; + var backerIndexToBuild:Int; + var hasBuiltHeader:Bool; + var hasBuiltBody:Bool; + var hasBuiltBackers:Bool; +} diff --git a/source/funkin/ui/debug/charting/toolboxes/ChartEditorMetadataToolbox.hx b/source/funkin/ui/debug/charting/toolboxes/ChartEditorMetadataToolbox.hx index d7ac0e042..640c62491 100644 --- a/source/funkin/ui/debug/charting/toolboxes/ChartEditorMetadataToolbox.hx +++ b/source/funkin/ui/debug/charting/toolboxes/ChartEditorMetadataToolbox.hx @@ -118,7 +118,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox if (valid) { - chartEditorState.currentSongNoteStyle = event.data.id; + chartEditorState.currentSongNoteStyle = event.data.id; } }; var startingValueNoteStyle = ChartEditorDropdowns.populateDropdownWithNoteStyles(inputNoteStyle, chartEditorState.currentSongMetadata.playData.noteStyle); diff --git a/source/funkin/ui/debug/latency/LatencyState.hx b/source/funkin/ui/debug/latency/LatencyState.hx new file mode 100644 index 000000000..be2bd0f46 --- /dev/null +++ b/source/funkin/ui/debug/latency/LatencyState.hx @@ -0,0 +1,500 @@ +package funkin.ui.debug.latency; + +import funkin.data.notestyle.NoteStyleRegistry; +import flixel.FlxSprite; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.math.FlxMath; +import funkin.ui.MusicBeatSubState; +import flixel.sound.FlxSound; +import flixel.text.FlxText; +import flixel.util.FlxColor; +import funkin.input.PreciseInputManager; +import funkin.play.notes.Strumline; +import funkin.data.song.SongData.SongNoteData; +#if mobile +import funkin.util.TouchUtil; +import funkin.mobile.ui.FunkinHitbox; +import funkin.play.notes.NoteDirection; +import funkin.graphics.FunkinSprite; +#end +import haxe.Timer; +import flixel.FlxCamera; + +class LatencyState extends MusicBeatSubState +{ + var visualOffsetText:FlxText; + #if mobile + var visualOffsetLeftArrow:FlxText; + var visualOffsetRightArrow:FlxText; + #end + + var offsetText:FlxText; + #if mobile + var offsetLeftArrow:FlxText; + var offsetRightArrow:FlxText; + #end + + var helpText:FlxText; + var noteGrp:Array = []; + var strumLine:Strumline; + + var blocks:FlxTypedGroup; + + var songPosVis:FlxSprite; + var songVisFollowVideo:FlxSprite; + var songVisFollowAudio:FlxSprite; + + var beatTrail:FlxSprite; + var diffGrp:FlxTypedGroup; + var offsetsPerBeat:Array> = []; + var swagSong:HomemadeMusic; + + var previousVolume:Float; + + var stateCamera:FlxCamera; + + /** + * A local conductor instance for this testing class, in-case we are in a PlayState + * because I'm too lazy to set the old variables for conductor stuff ! + */ + var localConductor:Conductor; + + // stores values of what the previous persistent draw/update stuff was, example if opened + // from pause menu, we want to NOT draw persistently, but then resume drawing once closed + var prevPersistentDraw:Bool; + var prevPersistentUpdate:Bool; + + override function create() + { + super.create(); + + prevPersistentDraw = FlxG.state.persistentDraw; + prevPersistentUpdate = FlxG.state.persistentUpdate; + + FlxG.state.persistentDraw = false; + FlxG.state.persistentUpdate = false; + + localConductor = new Conductor(); + conductorInUse = localConductor; + + stateCamera = new FlxCamera(0, 0, FlxG.width, FlxG.height); + stateCamera.bgColor = FlxColor.BLACK; + FlxG.cameras.add(stateCamera); + + var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK); + add(bg); + + if (FlxG.sound.music != null) + { + previousVolume = FlxG.sound.music.volume; + FlxG.sound.music.volume = 0; // only want to mute the volume, incase we are coming from pause menu + } + else + previousVolume = 1; // defaults to 1 if no music is playing 🤔 also fuck it, emoji in code comment + + swagSong = new HomemadeMusic(); + swagSong.loadEmbedded(Paths.sound('soundTest'), true); + swagSong.looped = true; + swagSong.play(); + FlxG.sound.list.add(swagSong); + + PreciseInputManager.instance.onInputPressed.add(preciseInputPressed); + + PreciseInputManager.instance.onInputReleased.add(preciseInputReleased); + + localConductor.forceBPM(60); + + Conductor.instance.forceBPM(60); + + diffGrp = new FlxTypedGroup(); + add(diffGrp); + + for (beat in 0...Math.floor(swagSong.length / (localConductor.stepLengthMs * 2))) + { + var beatTick:FlxSprite = new FlxSprite(songPosToX(beat * (localConductor.stepLengthMs * 2)), FlxG.height - 15); + beatTick.makeGraphic(2, 15); + beatTick.alpha = 0.3; + add(beatTick); + + var offsetTxt:FlxText = new FlxText(songPosToX(beat * (localConductor.stepLengthMs * 2)), FlxG.height - 26, 0, ""); + offsetTxt.alpha = 0.5; + diffGrp.add(offsetTxt); + + offsetsPerBeat.push(null); + } + + songVisFollowAudio = new FlxSprite(0, FlxG.height - 20).makeGraphic(2, 20, FlxColor.YELLOW); + add(songVisFollowAudio); + + songVisFollowVideo = new FlxSprite(0, FlxG.height - 20).makeGraphic(2, 20, FlxColor.BLUE); + add(songVisFollowVideo); + + songPosVis = new FlxSprite(0, FlxG.height - 20).makeGraphic(2, 20, FlxColor.RED); + add(songPosVis); + + beatTrail = new FlxSprite(0, songPosVis.y).makeGraphic(2, 20, FlxColor.PURPLE); + beatTrail.alpha = 0.7; + add(beatTrail); + + blocks = new FlxTypedGroup(); + add(blocks); + + for (i in 0...8) + { + var block = new FlxSprite(2, ((FlxG.height / 8) + 2) * i).makeGraphic(Std.int(FlxG.height / 8), Std.int((FlxG.height / 8) - 4)); + block.alpha = 0.1; + blocks.add(block); + } + + var strumlineBG:FlxSprite = new FlxSprite(); + add(strumlineBG); + + strumLine = new Strumline(NoteStyleRegistry.instance.fetchDefault(), true); + strumLine.conductorInUse = localConductor; + strumLine.screenCenter(); + add(strumLine); + + strumlineBG.x = strumLine.x; + strumlineBG.makeGraphic(Std.int(strumLine.width), FlxG.height, 0xFFFFFFFF); + strumlineBG.alpha = 0.1; + + visualOffsetText = new FlxText(); + visualOffsetText.setFormat(Paths.font("vcr.ttf"), 20); + visualOffsetText.x = (FlxG.height / 8) + 10; + visualOffsetText.y = 10; + visualOffsetText.fieldWidth = strumLine.x - visualOffsetText.x - 10; + add(visualOffsetText); + + #if mobile + visualOffsetLeftArrow = new FlxText(); + visualOffsetLeftArrow.setFormat(Paths.font("vcr.ttf"), 60); + visualOffsetLeftArrow.text = "<"; + visualOffsetLeftArrow.x = visualOffsetText.x; + visualOffsetLeftArrow.y = visualOffsetText.y * 4; + add(visualOffsetLeftArrow); + + visualOffsetRightArrow = new FlxText(); + visualOffsetRightArrow.setFormat(Paths.font("vcr.ttf"), 60); + visualOffsetRightArrow.text = ">"; + visualOffsetRightArrow.x = visualOffsetLeftArrow.x * 2; + visualOffsetRightArrow.y = visualOffsetLeftArrow.y; + add(visualOffsetRightArrow); + #end + + offsetText = new FlxText(); + offsetText.setFormat(Paths.font("vcr.ttf"), 20); + offsetText.x = strumLine.x + strumLine.width + 10; + offsetText.y = 10; + offsetText.fieldWidth = FlxG.width - offsetText.x - 10; + add(offsetText); + + #if mobile + offsetLeftArrow = new FlxText(); + offsetLeftArrow.setFormat(Paths.font("vcr.ttf"), 60); + offsetLeftArrow.text = "<"; + offsetLeftArrow.x = offsetText.x; + offsetLeftArrow.y = offsetText.y * 9; + add(offsetLeftArrow); + + offsetRightArrow = new FlxText(); + offsetRightArrow.setFormat(Paths.font("vcr.ttf"), 60); + offsetRightArrow.text = ">"; + offsetRightArrow.x = offsetLeftArrow.x * 1.2; + offsetRightArrow.y = offsetLeftArrow.y; + add(offsetRightArrow); + #end + + helpText = new FlxText(); + helpText.setFormat(Paths.font("vcr.ttf"), 20 #if mobile * 3 #end); + helpText.text = #if mobile "Back" #else "Press BACK to return to options menu" #end; + helpText.x = FlxG.width - helpText.width #if mobile * 1.5 #end; + helpText.y = FlxG.height - (helpText.height * 2) - 2; + add(helpText); + + #if mobile + addHitbox(true, true, FunkinHitboxControlSchemes.FourLanes, [NoteDirection.DOWN], [0xFF00FFFF]); + + hitbox.forEachAlive(function(hint:FunkinHint) { + @:privateAccess + if (hint.label != null) hint.label.visible = false; + + for (text in [ + helpText, + visualOffsetLeftArrow, + visualOffsetRightArrow, + offsetLeftArrow, + offsetRightArrow + ]) + hint.deadZones.push(cast(text, FunkinSprite)); + }); + #end + + regenNoteData(); + } + + function preciseInputPressed(event:PreciseInputEvent) + { + generateBeatStuff(event); + strumLine.pressKey(event.noteDirection); + strumLine.playPress(event.noteDirection); + } + + function preciseInputReleased(event:PreciseInputEvent) + { + strumLine.playStatic(event.noteDirection); + strumLine.releaseKey(event.noteDirection); + } + + override public function destroy():Void + { + cleanup(); + super.destroy(); + } + + function cleanup():Void + { + PreciseInputManager.instance.onInputPressed.remove(preciseInputPressed); + PreciseInputManager.instance.onInputReleased.remove(preciseInputReleased); + + if (FlxG.sound.music != null) + { + FlxG.sound.music.volume = previousVolume; + } + + swagSong.stop(); + FlxG.sound.list.remove(swagSong); + + FlxG.cameras.remove(stateCamera); + + FlxG.state.persistentDraw = prevPersistentDraw; + FlxG.state.persistentUpdate = prevPersistentUpdate; + } + + function regenNoteData() + { + for (i in 0...32) + { + var note:SongNoteData = new SongNoteData((localConductor.stepLengthMs * 2) * i, 1); + noteGrp.push(note); + } + + strumLine.applyNoteData(noteGrp); + } + + override function stepHit():Bool + { + if (localConductor.currentStep % 4 == 2) + { + blocks.members[((localConductor.currentBeat % 8) + 1) % 8].alpha = 0.5; + } + + return super.stepHit(); + } + + override function beatHit():Bool + { + if (localConductor.currentBeat % 8 == 0) blocks.forEach(blok -> { + blok.alpha = 0.1; + }); + + blocks.members[localConductor.currentBeat % 8].alpha = 1; + // block.visible = !block.visible; + + return super.beatHit(); + } + + override function update(elapsed:Float) + { + /* trace("1: " + swagSong.frfrTime); + @:privateAccess + trace(FlxG.sound.music._channel.position); + */ + + localConductor.update(swagSong.time, false); + + // localConductor.songPosition += (Timer.stamp() * 1000) - FlxG.sound.music.prevTimestamp; + + songPosVis.x = songPosToX(localConductor.songPosition); + songVisFollowAudio.x = songPosToX(localConductor.songPosition - localConductor.audioVisualOffset); + songVisFollowVideo.x = songPosToX(localConductor.songPosition - localConductor.inputOffset); + + visualOffsetText.text = "Visual Offset: " + localConductor.audioVisualOffset + "ms"; + #if !mobile + visualOffsetText.text += "\n\nYou can press SPACE+Left/Right to change this value."; + visualOffsetText.text += "\n\nYou can hold SHIFT to step 1ms at a time"; + #end + + #if mobile + offsetText.text = "INPUT Offset: " + localConductor.inputOffset + "ms"; + #else + offsetText.text = "INPUT Offset (Left/Right to change): " + localConductor.inputOffset + "ms"; + offsetText.text += "\n\nYou can hold SHIFT to step 1ms at a time"; + #end + + var avgOffsetInput:Float = 0; + + var loopInd:Int = 0; + for (offsetThing in offsetsPerBeat) + { + if (offsetThing == null) continue; + avgOffsetInput += offsetThing; + loopInd++; + } + + avgOffsetInput /= loopInd; + + offsetText.text += "\n\nEstimated average input offset needed: " + avgOffsetInput; + + var multiply:Int = 10; + + if (FlxG.keys.pressed.SHIFT || FlxG.onMobile) multiply = 1; + + #if mobile + handleSelectionInputMobile(multiply); + #else + handleSelectionInput(multiply); + #end + + if (controls.BACK #if mobile || (TouchUtil.pressAction(helpText)) #end) + { + // close(); + cleanup(); + FlxG.switchState(() -> new funkin.ui.options.OptionsState()); + } + + super.update(elapsed); + } + + function handleSelectionInput(multiply:Int) + { + if (FlxG.keys.pressed.CONTROL || FlxG.keys.pressed.SPACE) + { + if (FlxG.keys.justPressed.RIGHT) + { + localConductor.audioVisualOffset += 1 * multiply; + } + + if (FlxG.keys.justPressed.LEFT) + { + localConductor.audioVisualOffset -= 1 * multiply; + } + } + else + { + if (FlxG.keys.anyJustPressed([LEFT, RIGHT])) + { + if (FlxG.keys.justPressed.RIGHT) + { + localConductor.inputOffset += 1 * multiply; + } + + if (FlxG.keys.justPressed.LEFT) + { + localConductor.inputOffset -= 1 * multiply; + } + + // reset the average, so you don't need to wait a full loop to start getting averages + // also reset each text member + offsetsPerBeat = []; + diffGrp.forEach(memb -> memb.text = ""); + } + } + } + + #if mobile + function handleSelectionInputMobile(multiply:Int) + { + final touchPressCheck:Bool = TouchUtil.justPressed + || TouchUtil.pressed + && TouchUtil.touch != null + && TouchUtil.touch.ticksDeltaSincePress >= 200; + + if (TouchUtil.overlapsComplex(visualOffsetRightArrow) && touchPressCheck) + { + cleanup(); + FlxG.switchState(() -> new funkin.ui.options.OptionsState()); + localConductor.audioVisualOffset += 1 * multiply; + } + + if (TouchUtil.overlapsComplex(visualOffsetLeftArrow) && touchPressCheck) + { + localConductor.audioVisualOffset -= 1 * multiply; + } + + if ((TouchUtil.overlapsComplex(offsetRightArrow) || TouchUtil.overlapsComplex(offsetLeftArrow)) && touchPressCheck) + { + if (TouchUtil.overlapsComplex(offsetRightArrow) && touchPressCheck) + { + localConductor.inputOffset += 1 * multiply; + } + + if (TouchUtil.overlapsComplex(offsetLeftArrow) && touchPressCheck) + { + localConductor.inputOffset -= 1 * multiply; + } + + // reset the average, so you don't need to wait a full loop to start getting averages + // also reset each text member + offsetsPerBeat = []; + diffGrp.forEach(memb -> memb.text = ""); + } + } + #end + + function generateBeatStuff(event:PreciseInputEvent) + { + // localConductor.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"); + // trace("songtime: " + localConductor.getTimeWithDiff(swagSong) + "ms"); + + var closestBeat:Int = Math.round(localConductor.getTimeWithDiff(swagSong) / (localConductor.stepLengthMs * 2)) % diffGrp.members.length; + var getDiff:Float = localConductor.getTimeWithDiff(swagSong) - (closestBeat * (localConductor.stepLengthMs * 2)); + // getDiff -= localConductor.inputOffset; + getDiff -= inputLatencyMs; + getDiff -= localConductor.audioVisualOffset; + + // lil fix for end of song + if (closestBeat == 0 && getDiff >= localConductor.stepLengthMs * 2) getDiff -= swagSong.length; + + beatTrail.x = songPosVis.x; + + diffGrp.members[closestBeat].text = getDiff + "ms"; + offsetsPerBeat[closestBeat] = Math.round(getDiff); + } + + function songPosToX(pos:Float):Float + { + return FlxMath.remapToRange(pos, 0, swagSong.length, 0, FlxG.width); + } +} + +class HomemadeMusic extends FlxSound +{ + public var prevTimestamp:Int = 0; + + public function new() + { + super(); + } + + var prevTime:Float = 0; + + override function update(elapsed:Float) + { + super.update(elapsed); + if (prevTime != time) + { + prevTime = time; + prevTimestamp = Std.int(Timer.stamp() * 1000); + } + } + + public function getTimeWithDiff():Float + { + return time + (Std.int(Timer.stamp() * 1000) - prevTimestamp); + } +} diff --git a/source/funkin/ui/debug/stageeditor/StageEditorState.hx b/source/funkin/ui/debug/stageeditor/StageEditorState.hx index b05ae1f15..266ff7a7c 100644 --- a/source/funkin/ui/debug/stageeditor/StageEditorState.hx +++ b/source/funkin/ui/debug/stageeditor/StageEditorState.hx @@ -1260,14 +1260,13 @@ class StageEditorState extends UIState if (exitConfirmDialog == null) { exitConfirmDialog = Dialogs.messageBox("You are about to leave the Editor without Saving.\n\nAre you sure? ", "Leave Editor", - MessageBoxType.TYPE_YESNO, true, - function(btn:DialogButton) { + MessageBoxType.TYPE_YESNO, true, function(btn:DialogButton) { exitConfirmDialog = null; - if (btn == DialogButton.YES) - { - saved = true; - onMenuItemClick("exit"); - } + if (btn == DialogButton.YES) + { + saved = true; + onMenuItemClick("exit"); + } }); } diff --git a/source/funkin/ui/debug/stageeditor/toolboxes/StageEditorObjectAnimsToolbox.hx b/source/funkin/ui/debug/stageeditor/toolboxes/StageEditorObjectAnimsToolbox.hx index 6cd0b4f4e..548feb444 100644 --- a/source/funkin/ui/debug/stageeditor/toolboxes/StageEditorObjectAnimsToolbox.hx +++ b/source/funkin/ui/debug/stageeditor/toolboxes/StageEditorObjectAnimsToolbox.hx @@ -114,7 +114,7 @@ class StageEditorObjectAnimsToolbox extends StageEditorDefaultToolbox objAnims.selectedIndex = objAnims.dataSource.size - 1; } - + this.onDialogClosed = onClose; } diff --git a/source/funkin/ui/debug/stageeditor/toolboxes/StageEditorObjectPropertiesToolbox.hx b/source/funkin/ui/debug/stageeditor/toolboxes/StageEditorObjectPropertiesToolbox.hx index 8c0ae8655..5dd3aceaa 100644 --- a/source/funkin/ui/debug/stageeditor/toolboxes/StageEditorObjectPropertiesToolbox.hx +++ b/source/funkin/ui/debug/stageeditor/toolboxes/StageEditorObjectPropertiesToolbox.hx @@ -121,7 +121,7 @@ class StageEditorObjectPropertiesToolbox extends StageEditorDefaultToolbox linkedObj.color = FlxColor.fromString(_.value) ?? 0xFFFFFFFF; } } - + this.onDialogClosed = onClose; } diff --git a/source/funkin/ui/debug/stageeditor/toolboxes/StageEditorStageToolbox.hx b/source/funkin/ui/debug/stageeditor/toolboxes/StageEditorStageToolbox.hx index c8077c667..0224e693a 100644 --- a/source/funkin/ui/debug/stageeditor/toolboxes/StageEditorStageToolbox.hx +++ b/source/funkin/ui/debug/stageeditor/toolboxes/StageEditorStageToolbox.hx @@ -51,7 +51,7 @@ class StageEditorStageToolbox extends StageEditorDefaultToolbox } refresh(); - + this.onDialogClosed = onClose; } diff --git a/source/funkin/ui/freeplay/FreeplayStyle.hx b/source/funkin/ui/freeplay/FreeplayStyle.hx index f2fb8b7fa..037aaf056 100644 --- a/source/funkin/ui/freeplay/FreeplayStyle.hx +++ b/source/funkin/ui/freeplay/FreeplayStyle.hx @@ -21,7 +21,6 @@ class FreeplayStyle implements IRegistryEntry * The full data for a freeplay style. */ // public final _data:FreeplayStyleData; - public function new(id:String) { this.id = id; diff --git a/source/funkin/ui/freeplay/LetterSort.hx b/source/funkin/ui/freeplay/LetterSort.hx index 7ef9084bd..4112f33e2 100644 --- a/source/funkin/ui/freeplay/LetterSort.hx +++ b/source/funkin/ui/freeplay/LetterSort.hx @@ -107,6 +107,7 @@ class LetterSort extends FlxSpriteGroup if (inputEnabled) { + #if FEATURE_TOUCH_CONTROLS if (TouchUtil.pressAction()) { for (index => letter in letterHitboxes) @@ -132,12 +133,15 @@ class LetterSort extends FlxSpriteGroup break; } } + #end @:privateAccess { - if (controls.FREEPLAY_LEFT || (TouchUtil.overlaps(swipeBounds, instance.funnyCam) && SwipeUtil.swipeLeft)) changeSelection(-1); + if (controls.FREEPLAY_LEFT #if FEATURE_TOUCH_CONTROLS + || (TouchUtil.overlaps(swipeBounds, instance.funnyCam) && SwipeUtil.swipeLeft) #end) changeSelection(-1); - if (controls.FREEPLAY_RIGHT || (TouchUtil.overlaps(swipeBounds, instance.funnyCam) && SwipeUtil.swipeRight)) changeSelection(1); + if (controls.FREEPLAY_RIGHT #if FEATURE_TOUCH_CONTROLS + || (TouchUtil.overlaps(swipeBounds, instance.funnyCam) && SwipeUtil.swipeRight) #end) changeSelection(1); } } } diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index 675d86116..3c07a46fe 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -456,8 +456,8 @@ class MainMenuState extends MusicBeatState gyroPan.add(FlxG.gyroscope.pitch * -1.25, FlxG.gyroscope.roll * -1.25); // our pseudo damping - gyroPan.x = MathUtil.smoothLerp(gyroPan.x, 0, elapsed, 2.5); - gyroPan.y = MathUtil.smoothLerp(gyroPan.y, 0, elapsed, 2.5); + gyroPan.x = MathUtil.smoothLerpPrecision(gyroPan.x, 0, elapsed, 2.5); + gyroPan.y = MathUtil.smoothLerpPrecision(gyroPan.y, 0, elapsed, 2.5); // how far away from bg mid do we want to pan via gyroPan camFollow.x = bg.getGraphicMidpoint().x - gyroPan.x; diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 8b7441c9d..f530ebf40 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -391,7 +391,7 @@ class StoryMenuState extends MusicBeatState changeDifficulty(-1); } - if (controls.UI_RIGHT || TouchUtil.overlaps(rightDifficultyArrow)) + if (controls.UI_RIGHT #if FEATURE_TOUCH_CONTROLS || TouchUtil.overlaps(rightDifficultyArrow) #end) { rightDifficultyArrow.animation.play('press'); } @@ -400,7 +400,7 @@ class StoryMenuState extends MusicBeatState rightDifficultyArrow.animation.play('idle'); } - if (controls.UI_LEFT || TouchUtil.overlaps(leftDifficultyArrow)) + if (controls.UI_LEFT #if FEATURE_TOUCH_CONTROLS || TouchUtil.overlaps(leftDifficultyArrow) #end) { leftDifficultyArrow.animation.play('press'); } @@ -415,6 +415,7 @@ class StoryMenuState extends MusicBeatState selectLevel(); } + #if FEATURE_TOUCH_CONTROLS if (TouchUtil.justReleased && !TouchUtil.overlaps(leftDifficultyArrow) && !SwipeUtil.justSwipedAny) { for (i in 0...levelTitles.members.length) @@ -427,6 +428,7 @@ class StoryMenuState extends MusicBeatState (item == selectedItem) ? selectLevel() : changeLevel(i - levelList.indexOf(currentLevelId)); } } + #end } if (controls.BACK) goBack(); diff --git a/source/funkin/util/TouchUtil.hx b/source/funkin/util/TouchUtil.hx index f33e5cb10..81940812b 100644 --- a/source/funkin/util/TouchUtil.hx +++ b/source/funkin/util/TouchUtil.hx @@ -68,11 +68,9 @@ class TouchUtil */ public static function overlaps(?object:FlxBasic, ?camera:FlxCamera):Bool { - #if FEATURE_TOUCH_CONTROLS if (object == null || touch == null) return false; return touch.overlaps(object, camera ?? object.camera); - #end return false; } @@ -87,14 +85,12 @@ class TouchUtil */ public static function overlapsComplex(?object:FlxObject, ?camera:FlxCamera):Bool { - #if FEATURE_TOUCH_CONTROLS if (object == null || touch == null) return false; if (camera == null) camera = object.cameras[0]; @:privateAccess return object.overlapsPoint(touch.getWorldPosition(camera, object._point), true, camera); - #end return false; } @@ -111,7 +107,6 @@ class TouchUtil */ public static function overlapsComplexPoint(?object:FlxObject, point:FlxPoint, ?inScreenSpace:Bool = false, ?camera:FlxCamera):Bool { - #if FEATURE_TOUCH_CONTROLS if (object == null || point == null) return false; if (camera == null) camera = object.cameras[0]; @@ -123,7 +118,6 @@ class TouchUtil } point.putWeak(); - #end return false; } @@ -137,7 +131,6 @@ class TouchUtil */ public static function pressAction(?object:FlxBasic, ?camera:FlxCamera, useOverlapsComplex:Bool = true):Bool { - #if FEATURE_TOUCH_CONTROLS if (TouchUtil.touch == null || (TouchUtil.touch != null && TouchUtil.touch.ticksDeltaSincePress > 200)) return false; if (object == null && camera == null) @@ -149,7 +142,6 @@ class TouchUtil final overlapsObject:Bool = useOverlapsComplex ? overlapsComplex(cast(object, FlxObject), camera) : overlaps(object, camera); return justReleased && overlapsObject; } - #end return false; } @@ -189,13 +181,10 @@ class TouchUtil } #else @:noCompletion - inline static function get_touch():FlxMouse + static function get_touch():FlxMouse { - #if FEATURE_TOUCH_CONTROLS FlxG.mouse.visible = true; return FlxG.mouse; - #end - return null; } #end }