diff --git a/.vscode/launch.json b/.vscode/launch.json index 74f72b8261..afc0ab8dbf 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,6 +7,12 @@ "type": "lime", "request": "launch" }, + { + "name": "Debug", + "type": "lime", + "request": "launch", + "preLaunchTask": null + }, { // Launch in browser "name": "HTML5 Debug", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 16a77646f7..be4414ea0a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,13 +1,13 @@ { - "version": "2.0.0", - "tasks": [ - { - "type": "lime", - "command": "test", - "group": { - "kind": "build", - "isDefault": true - } - } - ] + "version": "2.0.0", + "tasks": [ + { + "type": "lime", + "command": "test", + "group": { + "kind": "build", + "isDefault": true + } + } + ] } diff --git a/source/funkin/Conductor.hx b/source/funkin/Conductor.hx index 104682ffdd..0d84172a0b 100644 --- a/source/funkin/Conductor.hx +++ b/source/funkin/Conductor.hx @@ -5,6 +5,9 @@ import flixel.util.FlxSignal; import flixel.math.FlxMath; import funkin.data.song.SongData.SongTimeChange; import funkin.data.song.SongDataUtils; +import funkin.save.Save; +import haxe.Timer; +import flixel.sound.FlxSound; /** * A core class which handles musical timing throughout the game, @@ -89,6 +92,9 @@ class Conductor */ public var songPosition(default, null):Float = 0; + var prevTimestamp:Float = 0; + var prevTime:Float = 0; + /** * Beats per minute of the current song at the current time. */ @@ -233,8 +239,41 @@ class Conductor /** * An offset set by the user to compensate for input lag. + * No matter if you're using a local conductor or not, this always loads + * to/from the save file */ - public var inputOffset:Float = 0; + public var inputOffset(get, set):Int; + + /** + * An offset set by the user to compensate for audio/visual lag + * No matter if you're using a local conductor or not, this always loads + * to/from the save file + */ + public var audioVisualOffset(get, set):Int; + + function get_inputOffset():Int + { + return Save.instance.options.inputOffset; + } + + function set_inputOffset(value:Int):Int + { + Save.instance.options.inputOffset = value; + Save.instance.flush(); + return Save.instance.options.inputOffset; + } + + function get_audioVisualOffset():Int + { + return Save.instance.options.audioVisualOffset; + } + + function set_audioVisualOffset(value:Int):Int + { + Save.instance.options.audioVisualOffset = value; + Save.instance.flush(); + return Save.instance.options.audioVisualOffset; + } /** * The number of beats in a measure. May be fractional depending on the time signature. @@ -353,16 +392,19 @@ class Conductor * BPM, current step, etc. will be re-calculated based on the song position. * * @param songPosition The current position in the song in milliseconds. - * Leave blank to use the `FlxG.sound.music` position. + * Leave blank to use the FlxG.sound.music position. + * @param applyOffsets If it should apply the instrumentalOffset + formatOffset + audioVisualOffset */ - public function update(?songPos:Float):Void + public function update(?songPos:Float, applyOffsets:Bool = true, forceDispatch:Bool = false) { if (songPos == null) { - // Take into account instrumental and file format song offsets. - songPos = (FlxG.sound.music != null) ? (FlxG.sound.music.time + instrumentalOffset + formatOffset) : 0.0; + songPos = (FlxG.sound.music != null) ? FlxG.sound.music.time : 0.0; } + // Take into account instrumental and file format song offsets. + songPos += applyOffsets ? (instrumentalOffset + formatOffset + audioVisualOffset) : 0; + var oldMeasure:Float = this.currentMeasure; var oldBeat:Float = this.currentBeat; var oldStep:Float = this.currentStep; @@ -421,6 +463,35 @@ class Conductor { this.onMeasureHit.dispatch(); } + + // only update the timestamp if songPosition actually changed + // which it doesn't do every frame! + if (prevTime != this.songPosition) + { + // Update the timestamp for use in-between frames + prevTime = this.songPosition; + prevTimestamp = Std.int(Timer.stamp() * 1000); + } + } + + /** + * Can be called in-between frames, usually for input related things + * that can potentially get processed on exact milliseconds/timestmaps. + * If you need song position, use `Conductor.instance.songPosition` instead + * for use in update() related functions. + * @param soundToCheck Which FlxSound object to check, defaults to FlxG.sound.music if no input + * @return Float + */ + public function getTimeWithDiff(?soundToCheck:FlxSound):Float + { + if (soundToCheck == null) soundToCheck = FlxG.sound.music; + // trace(this.songPosition); + + @:privateAccess + this.songPosition = soundToCheck._channel.position; + // return this.songPosition + (Std.int(Timer.stamp() * 1000) - prevTimestamp); + // trace("\t--> " + this.songPosition); + return this.songPosition; } /** @@ -468,7 +539,7 @@ class Conductor } // Update currentStepTime - this.update(Conductor.instance.songPosition); + this.update(this.songPosition, false); } /** @@ -587,7 +658,8 @@ class Conductor } /** - * Add variables of the current Conductor instance to the Flixel debugger. + * Adds Conductor fields to the Flixel debugger variable display. + * @param conductorToUse The conductor to use. Defaults to `Conductor.instance`. */ public static function watchQuick(?target:Conductor):Void { diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 9b842bc13c..2377343c34 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -16,14 +16,14 @@ import funkin.util.macro.MacroUtil; import funkin.util.WindowUtil; import funkin.play.PlayStatePlaylist; import openfl.display.BitmapData; -import funkin.data.level.LevelRegistry; +import funkin.data.story.level.LevelRegistry; import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.event.SongEventRegistry; import funkin.data.stage.StageRegistry; -import funkin.data.dialogue.ConversationRegistry; -import funkin.data.dialogue.DialogueBoxRegistry; -import funkin.data.dialogue.SpeakerRegistry; -import funkin.data.freeplay.AlbumRegistry; +import funkin.data.dialogue.conversation.ConversationRegistry; +import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry; +import funkin.data.dialogue.speaker.SpeakerRegistry; +import funkin.data.freeplay.album.AlbumRegistry; import funkin.data.song.SongRegistry; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.modding.module.ModuleHandler; @@ -304,7 +304,7 @@ class InitState extends FlxState */ function startLevel(levelId:String, difficultyId:String = 'normal'):Void { - var currentLevel:funkin.ui.story.Level = funkin.data.level.LevelRegistry.instance.fetchEntry(levelId); + var currentLevel:funkin.ui.story.Level = funkin.data.story.level.LevelRegistry.instance.fetchEntry(levelId); if (currentLevel == null) { diff --git a/source/funkin/data/DataParse.hx b/source/funkin/data/DataParse.hx index 244d411322..472ffd6e75 100644 --- a/source/funkin/data/DataParse.hx +++ b/source/funkin/data/DataParse.hx @@ -120,7 +120,7 @@ class DataParse } } - public static function backdropData(json:Json, name:String):funkin.data.dialogue.ConversationData.BackdropData + public static function backdropData(json:Json, name:String):funkin.data.dialogue.conversation.ConversationData.BackdropData { switch (json.value) { @@ -152,7 +152,7 @@ class DataParse } } - public static function outroData(json:Json, name:String):Null + public static function outroData(json:Json, name:String):Null { switch (json.value) { diff --git a/source/funkin/data/conversation/TODO.md b/source/funkin/data/conversation/TODO.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/source/funkin/data/dialogue/TODO.md b/source/funkin/data/dialogue/TODO.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/source/funkin/data/dialogue/conversation/CHANGELOG.md b/source/funkin/data/dialogue/conversation/CHANGELOG.md new file mode 100644 index 0000000000..f9ab99fab1 --- /dev/null +++ b/source/funkin/data/dialogue/conversation/CHANGELOG.md @@ -0,0 +1,9 @@ +# Dialogue Conversation Data Schema Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] +Initial release. diff --git a/source/funkin/data/dialogue/ConversationData.hx b/source/funkin/data/dialogue/conversation/ConversationData.hx similarity index 98% rename from source/funkin/data/dialogue/ConversationData.hx rename to source/funkin/data/dialogue/conversation/ConversationData.hx index 795ddae9a4..30e3f451be 100644 --- a/source/funkin/data/dialogue/ConversationData.hx +++ b/source/funkin/data/dialogue/conversation/ConversationData.hx @@ -1,4 +1,4 @@ -package funkin.data.dialogue; +package funkin.data.dialogue.conversation; import funkin.data.animation.AnimationData; diff --git a/source/funkin/data/dialogue/ConversationRegistry.hx b/source/funkin/data/dialogue/conversation/ConversationRegistry.hx similarity index 96% rename from source/funkin/data/dialogue/ConversationRegistry.hx rename to source/funkin/data/dialogue/conversation/ConversationRegistry.hx index 4a8af21628..ca072897fd 100644 --- a/source/funkin/data/dialogue/ConversationRegistry.hx +++ b/source/funkin/data/dialogue/conversation/ConversationRegistry.hx @@ -1,7 +1,7 @@ -package funkin.data.dialogue; +package funkin.data.dialogue.conversation; import funkin.play.cutscene.dialogue.Conversation; -import funkin.data.dialogue.ConversationData; +import funkin.data.dialogue.conversation.ConversationData; import funkin.play.cutscene.dialogue.ScriptedConversation; class ConversationRegistry extends BaseRegistry diff --git a/source/funkin/data/dialogue/dialoguebox/CHANGELOG.md b/source/funkin/data/dialogue/dialoguebox/CHANGELOG.md new file mode 100644 index 0000000000..06604734a0 --- /dev/null +++ b/source/funkin/data/dialogue/dialoguebox/CHANGELOG.md @@ -0,0 +1,13 @@ +# Dialogue Box Data Schema Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.1.0] +### Added +- Added an option to specify the font used by the dialogue box. Defaults to `Arial` if unspecified. + +## [1.0.0] +Initial release. diff --git a/source/funkin/data/dialogue/DialogueBoxData.hx b/source/funkin/data/dialogue/dialoguebox/DialogueBoxData.hx similarity index 98% rename from source/funkin/data/dialogue/DialogueBoxData.hx rename to source/funkin/data/dialogue/dialoguebox/DialogueBoxData.hx index a75a5595ab..228d391c8f 100644 --- a/source/funkin/data/dialogue/DialogueBoxData.hx +++ b/source/funkin/data/dialogue/dialoguebox/DialogueBoxData.hx @@ -1,4 +1,4 @@ -package funkin.data.dialogue; +package funkin.data.dialogue.dialoguebox; import funkin.data.animation.AnimationData; diff --git a/source/funkin/data/dialogue/DialogueBoxRegistry.hx b/source/funkin/data/dialogue/dialoguebox/DialogueBoxRegistry.hx similarity index 96% rename from source/funkin/data/dialogue/DialogueBoxRegistry.hx rename to source/funkin/data/dialogue/dialoguebox/DialogueBoxRegistry.hx index d07ea6da2a..18f1e640a4 100644 --- a/source/funkin/data/dialogue/DialogueBoxRegistry.hx +++ b/source/funkin/data/dialogue/dialoguebox/DialogueBoxRegistry.hx @@ -1,7 +1,7 @@ -package funkin.data.dialogue; +package funkin.data.dialogue.dialoguebox; import funkin.play.cutscene.dialogue.DialogueBox; -import funkin.data.dialogue.DialogueBoxData; +import funkin.data.dialogue.dialoguebox.DialogueBoxData; import funkin.play.cutscene.dialogue.ScriptedDialogueBox; class DialogueBoxRegistry extends BaseRegistry diff --git a/source/funkin/data/dialogue/speaker/CHANGELOG.md b/source/funkin/data/dialogue/speaker/CHANGELOG.md new file mode 100644 index 0000000000..284e690e1d --- /dev/null +++ b/source/funkin/data/dialogue/speaker/CHANGELOG.md @@ -0,0 +1,9 @@ +# Dialogue Speaker Data Schema Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] +Initial release. diff --git a/source/funkin/data/dialogue/SpeakerData.hx b/source/funkin/data/dialogue/speaker/SpeakerData.hx similarity index 97% rename from source/funkin/data/dialogue/SpeakerData.hx rename to source/funkin/data/dialogue/speaker/SpeakerData.hx index e8a2eacf0a..458f91caba 100644 --- a/source/funkin/data/dialogue/SpeakerData.hx +++ b/source/funkin/data/dialogue/speaker/SpeakerData.hx @@ -1,4 +1,4 @@ -package funkin.data.dialogue; +package funkin.data.dialogue.speaker; import funkin.data.animation.AnimationData; diff --git a/source/funkin/data/dialogue/SpeakerRegistry.hx b/source/funkin/data/dialogue/speaker/SpeakerRegistry.hx similarity index 96% rename from source/funkin/data/dialogue/SpeakerRegistry.hx rename to source/funkin/data/dialogue/speaker/SpeakerRegistry.hx index c76c6d7661..89144f8430 100644 --- a/source/funkin/data/dialogue/SpeakerRegistry.hx +++ b/source/funkin/data/dialogue/speaker/SpeakerRegistry.hx @@ -1,7 +1,7 @@ -package funkin.data.dialogue; +package funkin.data.dialogue.speaker; import funkin.play.cutscene.dialogue.Speaker; -import funkin.data.dialogue.SpeakerData; +import funkin.data.dialogue.speaker.SpeakerData; import funkin.play.cutscene.dialogue.ScriptedSpeaker; class SpeakerRegistry extends BaseRegistry diff --git a/source/funkin/data/freeplay/AlbumData.hx b/source/funkin/data/freeplay/album/AlbumData.hx similarity index 96% rename from source/funkin/data/freeplay/AlbumData.hx rename to source/funkin/data/freeplay/album/AlbumData.hx index ca851376da..9dea443b17 100644 --- a/source/funkin/data/freeplay/AlbumData.hx +++ b/source/funkin/data/freeplay/album/AlbumData.hx @@ -1,4 +1,4 @@ -package funkin.data.freeplay; +package funkin.data.freeplay.album; import funkin.data.animation.AnimationData; diff --git a/source/funkin/data/freeplay/AlbumRegistry.hx b/source/funkin/data/freeplay/album/AlbumRegistry.hx similarity index 96% rename from source/funkin/data/freeplay/AlbumRegistry.hx rename to source/funkin/data/freeplay/album/AlbumRegistry.hx index 78fba451bc..c04464fd26 100644 --- a/source/funkin/data/freeplay/AlbumRegistry.hx +++ b/source/funkin/data/freeplay/album/AlbumRegistry.hx @@ -1,7 +1,7 @@ -package funkin.data.freeplay; +package funkin.data.freeplay.album; import funkin.ui.freeplay.Album; -import funkin.data.freeplay.AlbumData; +import funkin.data.freeplay.album.AlbumData; import funkin.ui.freeplay.ScriptedAlbum; class AlbumRegistry extends BaseRegistry diff --git a/source/funkin/data/freeplay/album/CHANGELOG.md b/source/funkin/data/freeplay/album/CHANGELOG.md new file mode 100644 index 0000000000..ae307465ea --- /dev/null +++ b/source/funkin/data/freeplay/album/CHANGELOG.md @@ -0,0 +1,9 @@ +# Freeplay Album Data Schema Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] +Initial release. diff --git a/source/funkin/data/song/CHANGELOG.md b/source/funkin/data/song/CHANGELOG.md new file mode 100644 index 0000000000..3cd3af0700 --- /dev/null +++ b/source/funkin/data/song/CHANGELOG.md @@ -0,0 +1,36 @@ +# Song Chart Data Schema Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [2.2.2] +### Added +- Added `playData.previewStart` and `playData.previewEnd` fields to specify when in the song should the song's audio should be played as a preview in Freeplay. + +## [2.2.1] +### Added +- Added `playData.offsets` field to specify instrumental and vocal offsets. + +## [2.2.0] +### Added +- Added `playData.album` to specify the album art to display in Freeplay. +- Added `playData.ratings` for difficulty ratings displayed in Freeplay. +### Changed +- Renamed `playData.noteSkin` to `playData.noteStyle`. + +## [2.1.0] +### Changed +- Rearranged the `playData` field. + - Refactored the `playableChars` +### Removed +- Removed the `variation` field. + +## [2.0.0] +Full refactor of the chart format for improved structure. +### Added +- Added a semantic version field for migration tracking. + +## [1.0.0] +Initial version from 2020. diff --git a/source/funkin/data/speaker/TODO.md b/source/funkin/data/speaker/TODO.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/source/funkin/data/stage/CHANGELOG.md b/source/funkin/data/stage/CHANGELOG.md new file mode 100644 index 0000000000..879139db55 --- /dev/null +++ b/source/funkin/data/stage/CHANGELOG.md @@ -0,0 +1,14 @@ +# Story Mode Level Data Schema Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.1] +### Added +- Added the ability to specify a hexadecimal color in the `assetPath` field instead of a texture key. + - In this case, the `scale` property will be used to determine the size of the rectangle in pixels. + +## [1.0.0] +Initial release. diff --git a/source/funkin/data/stage/StageRegistry.hx b/source/funkin/data/stage/StageRegistry.hx index 13a5afb8da..a033712960 100644 --- a/source/funkin/data/stage/StageRegistry.hx +++ b/source/funkin/data/stage/StageRegistry.hx @@ -11,7 +11,7 @@ class StageRegistry extends BaseRegistry * Handle breaking changes by incrementing this value * and adding migration to the `migrateStageData()` function. */ - public static final STAGE_DATA_VERSION:thx.semver.Version = "1.0.1"; + public static final STAGE_DATA_VERSION:thx.semver.Version = "1.0.0"; public static final STAGE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x"; diff --git a/source/funkin/data/story/level/CHANGELOG.md b/source/funkin/data/story/level/CHANGELOG.md new file mode 100644 index 0000000000..75d1fe4c0b --- /dev/null +++ b/source/funkin/data/story/level/CHANGELOG.md @@ -0,0 +1,9 @@ +# Story Mode Level Data Schema Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] +Initial release. diff --git a/source/funkin/data/level/LevelData.hx b/source/funkin/data/story/level/LevelData.hx similarity index 95% rename from source/funkin/data/level/LevelData.hx rename to source/funkin/data/story/level/LevelData.hx index f5e58ae165..957a9f66f3 100644 --- a/source/funkin/data/level/LevelData.hx +++ b/source/funkin/data/story/level/LevelData.hx @@ -1,4 +1,4 @@ -package funkin.data.level; +package funkin.data.story.level; import funkin.data.animation.AnimationData; @@ -13,7 +13,7 @@ typedef LevelData = * When making changes to the level data format, this should be incremented, * and a migration function should be added to LevelDataParser to handle old versions. */ - @:default(funkin.data.level.LevelRegistry.LEVEL_DATA_VERSION) + @:default(funkin.data.story.level.LevelRegistry.LEVEL_DATA_VERSION) var version:String; /** diff --git a/source/funkin/data/level/LevelRegistry.hx b/source/funkin/data/story/level/LevelRegistry.hx similarity index 97% rename from source/funkin/data/level/LevelRegistry.hx rename to source/funkin/data/story/level/LevelRegistry.hx index e37e78d8cc..e94b5870b9 100644 --- a/source/funkin/data/level/LevelRegistry.hx +++ b/source/funkin/data/story/level/LevelRegistry.hx @@ -1,7 +1,7 @@ -package funkin.data.level; +package funkin.data.story.level; import funkin.ui.story.Level; -import funkin.data.level.LevelData; +import funkin.data.story.level.LevelData; import funkin.ui.story.ScriptedLevel; class LevelRegistry extends BaseRegistry diff --git a/source/funkin/graphics/shaders/HSVShader.hx b/source/funkin/graphics/shaders/HSVShader.hx index b6d079612f..587008ce2a 100644 --- a/source/funkin/graphics/shaders/HSVShader.hx +++ b/source/funkin/graphics/shaders/HSVShader.hx @@ -29,7 +29,7 @@ class HSVShader extends FlxRuntimeShader function set_saturation(value:Float):Float { - this.setFloat('sat', value); + this.setFloat('_sat', value); this.saturation = value; return this.saturation; @@ -37,7 +37,7 @@ class HSVShader extends FlxRuntimeShader function set_value(value:Float):Float { - this.setFloat('val', value); + this.setFloat('_val', value); this.value = value; return this.value; diff --git a/source/funkin/input/PreciseInputManager.hx b/source/funkin/input/PreciseInputManager.hx index 59e6610a5e..1dfa7ac950 100644 --- a/source/funkin/input/PreciseInputManager.hx +++ b/source/funkin/input/PreciseInputManager.hx @@ -293,8 +293,9 @@ class PreciseInputManager extends FlxKeyManager // TODO: Remove this line with SDL3 when timestamps change meaning. // This is because SDL3's timestamps are measured in nanoseconds, not milliseconds. - timestamp *= Constants.NS_PER_MS; - + timestamp *= Constants.NS_PER_MS; // 18126000000 38367000000 + timestamp -= Conductor.instance.inputOffset * Constants.NS_PER_MS; + // trace(timestamp); updateKeyStates(key, true); if (getInputByKey(key)?.justPressed ?? false) diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx index 62860ee0ff..32be3c8e0e 100644 --- a/source/funkin/modding/PolymodHandler.hx +++ b/source/funkin/modding/PolymodHandler.hx @@ -1,14 +1,14 @@ package funkin.modding; -import funkin.data.dialogue.ConversationRegistry; -import funkin.data.dialogue.DialogueBoxRegistry; -import funkin.data.dialogue.SpeakerRegistry; +import funkin.data.dialogue.conversation.ConversationRegistry; +import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry; +import funkin.data.dialogue.speaker.SpeakerRegistry; import funkin.data.event.SongEventRegistry; -import funkin.data.level.LevelRegistry; +import funkin.data.story.level.LevelRegistry; import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.song.SongRegistry; import funkin.data.stage.StageRegistry; -import funkin.data.freeplay.AlbumRegistry; +import funkin.data.freeplay.album.AlbumRegistry; import funkin.modding.module.ModuleHandler; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.save.Save; diff --git a/source/funkin/play/PauseSubState.hx b/source/funkin/play/PauseSubState.hx index fc1d013771..fb9d9b4e29 100644 --- a/source/funkin/play/PauseSubState.hx +++ b/source/funkin/play/PauseSubState.hx @@ -2,14 +2,16 @@ package funkin.play; import flixel.addons.transition.FlxTransitionableState; import flixel.FlxG; -import flixel.util.FlxTimer; import flixel.FlxSprite; +import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxSpriteGroup; import flixel.math.FlxMath; +import flixel.sound.FlxSound; import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; import flixel.util.FlxColor; +import flixel.util.FlxTimer; import funkin.audio.FunkinSound; import funkin.data.song.SongRegistry; import funkin.ui.freeplay.FreeplayState; @@ -17,6 +19,7 @@ import funkin.graphics.FunkinSprite; import funkin.play.cutscene.VideoCutscene; import funkin.play.PlayState; import funkin.ui.AtlasText; +import funkin.ui.debug.latency.LatencyState; import funkin.ui.MusicBeatSubState; import funkin.ui.transition.StickerSubState; diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index bfe9a7bf95..775f3c25bb 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -19,7 +19,7 @@ import flixel.util.FlxTimer; import funkin.api.newgrounds.NGio; import funkin.audio.FunkinSound; import funkin.audio.VoicesGroup; -import funkin.data.dialogue.ConversationRegistry; +import funkin.data.dialogue.conversation.ConversationRegistry; import funkin.data.event.SongEventRegistry; import funkin.data.notestyle.NoteStyleData; import funkin.data.notestyle.NoteStyleRegistry; @@ -890,7 +890,8 @@ class PlayState extends MusicBeatSubState { if (isInCountdown) { - Conductor.instance.update(Conductor.instance.songPosition + elapsed * 1000); + // Do NOT apply offsets at this point, because they already got applied the previous frame! + Conductor.instance.update(Conductor.instance.songPosition + elapsed * 1000, false); if (Conductor.instance.songPosition >= (startTimestamp)) startSong(); } } @@ -2049,10 +2050,10 @@ class PlayState extends MusicBeatSubState { if (note == null) continue; - // TODO: Does this properly account for offsets? - var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS; - var hitWindowCenter = note.strumTime; - var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS; + // TODO: Are offsets being accounted for in the correct direction? + var hitWindowStart = note.strumTime + Conductor.instance.inputOffset - Constants.HIT_WINDOW_MS; + var hitWindowCenter = note.strumTime + Conductor.instance.inputOffset; + var hitWindowEnd = note.strumTime + Conductor.instance.inputOffset + Constants.HIT_WINDOW_MS; if (Conductor.instance.songPosition > hitWindowEnd) { diff --git a/source/funkin/play/components/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx index b062b22cfb..b7e206e977 100644 --- a/source/funkin/play/components/PopUpStuff.hx +++ b/source/funkin/play/components/PopUpStuff.hx @@ -42,7 +42,7 @@ class PopUpStuff extends FlxTypedGroup if (PlayState.instance.currentStageId.startsWith('school')) { - rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.65)); + rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.7)); rating.antialiasing = false; } else @@ -133,7 +133,7 @@ class PopUpStuff extends FlxTypedGroup if (PlayState.instance.currentStageId.startsWith('school')) { - numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE)); + numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 0.7)); numScore.antialiasing = false; } else diff --git a/source/funkin/play/cutscene/dialogue/Conversation.hx b/source/funkin/play/cutscene/dialogue/Conversation.hx index 2c59eaba0d..9c217948d1 100644 --- a/source/funkin/play/cutscene/dialogue/Conversation.hx +++ b/source/funkin/play/cutscene/dialogue/Conversation.hx @@ -8,13 +8,13 @@ import flixel.tweens.FlxTween; import flixel.util.FlxColor; import flixel.util.FlxSort; import funkin.audio.FunkinSound; -import funkin.data.dialogue.ConversationData; -import funkin.data.dialogue.ConversationData.DialogueEntryData; -import funkin.data.dialogue.ConversationRegistry; -import funkin.data.dialogue.DialogueBoxData; -import funkin.data.dialogue.DialogueBoxRegistry; -import funkin.data.dialogue.SpeakerData; -import funkin.data.dialogue.SpeakerRegistry; +import funkin.data.dialogue.conversation.ConversationData; +import funkin.data.dialogue.conversation.ConversationData.DialogueEntryData; +import funkin.data.dialogue.conversation.ConversationRegistry; +import funkin.data.dialogue.dialoguebox.DialogueBoxData; +import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry; +import funkin.data.dialogue.speaker.SpeakerData; +import funkin.data.dialogue.speaker.SpeakerRegistry; import funkin.data.IRegistryEntry; import funkin.graphics.FunkinSprite; import funkin.modding.events.ScriptEvent; diff --git a/source/funkin/play/cutscene/dialogue/DialogueBox.hx b/source/funkin/play/cutscene/dialogue/DialogueBox.hx index fe4f13be01..6d3c0b8bc5 100644 --- a/source/funkin/play/cutscene/dialogue/DialogueBox.hx +++ b/source/funkin/play/cutscene/dialogue/DialogueBox.hx @@ -11,8 +11,8 @@ import funkin.modding.events.ScriptEvent; import funkin.audio.FunkinSound; import funkin.modding.IScriptedClass.IDialogueScriptedClass; import flixel.util.FlxColor; -import funkin.data.dialogue.DialogueBoxData; -import funkin.data.dialogue.DialogueBoxRegistry; +import funkin.data.dialogue.dialoguebox.DialogueBoxData; +import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry; class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass implements IRegistryEntry { diff --git a/source/funkin/play/cutscene/dialogue/Speaker.hx b/source/funkin/play/cutscene/dialogue/Speaker.hx index 0d29481c49..7203ddb287 100644 --- a/source/funkin/play/cutscene/dialogue/Speaker.hx +++ b/source/funkin/play/cutscene/dialogue/Speaker.hx @@ -6,8 +6,8 @@ import funkin.modding.events.ScriptEvent; import flixel.graphics.frames.FlxFramesCollection; import funkin.util.assets.FlxAnimationUtil; import funkin.modding.IScriptedClass.IDialogueScriptedClass; -import funkin.data.dialogue.SpeakerData; -import funkin.data.dialogue.SpeakerRegistry; +import funkin.data.dialogue.speaker.SpeakerData; +import funkin.data.dialogue.speaker.SpeakerRegistry; /** * The character sprite which displays during dialogue. diff --git a/source/funkin/play/event/FocusCameraSongEvent.hx b/source/funkin/play/event/FocusCameraSongEvent.hx index 569dcb87a7..074fafccf1 100644 --- a/source/funkin/play/event/FocusCameraSongEvent.hx +++ b/source/funkin/play/event/FocusCameraSongEvent.hx @@ -127,7 +127,7 @@ class FocusCameraSongEvent extends SongEvent switch (ease) { case 'CLASSIC': // Old-school. No ease. Just set follow point. - PlayState.instance.resetCamera(); + PlayState.instance.resetCamera(false, true); PlayState.instance.cameraFollowPoint.setPosition(targetX, targetY); case 'INSTANT': // Instant ease. Duration is automatically 0. PlayState.instance.tweenCameraToPosition(targetX, targetY, 0); diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index 2b4e093709..6a18f17d5b 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -15,6 +15,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. @@ -45,6 +46,25 @@ class Strumline extends FlxSpriteGroup */ public var isPlayer:Bool; + /** + * Usually you want to keep this as is, but if you are using a Strumline and + * playing a sound that has it's own conductor, set this (LatencyState for example) + */ + public var conductorInUse(get, set):Conductor; + + var _conductorInUse:Null; + + function get_conductorInUse():Conductor + { + if (_conductorInUse == null) return Conductor.instance; + return _conductorInUse; + } + + function set_conductorInUse(value:Conductor):Conductor + { + return _conductorInUse = value; + } + /** * The notes currently being rendered on the strumline. * This group iterates over this every frame to update note positions. @@ -151,24 +171,10 @@ class Strumline extends FlxSpriteGroup updateNotes(); } - var frameMax:Int; - var animFinishedEver:Bool; - /** - * Get a list of notes within + or - the given strumtime. - * @param strumTime The current time. - * @param hitWindow The hit window to check. + * Return notes that are within `Constants.HIT_WINDOW` ms of the strumline. + * @return An array of `NoteSprite` objects. */ - public function getNotesInRange(strumTime:Float, hitWindow:Float):Array - { - var hitWindowStart:Float = strumTime - hitWindow; - var hitWindowEnd:Float = strumTime + hitWindow; - - return notes.members.filter(function(note:NoteSprite) { - return note != null && note.alive && !note.hasBeenHit && note.strumTime >= hitWindowStart && note.strumTime <= hitWindowEnd; - }); - } - public function getNotesMayHit():Array { return notes.members.filter(function(note:NoteSprite) { @@ -176,6 +182,10 @@ class Strumline extends FlxSpriteGroup }); } + /** + * Return hold notes that are within `Constants.HIT_WINDOW` ms of the strumline. + * @return An array of `SustainTrail` objects. + */ public function getHoldNotesHitOrMissed():Array { return holdNotes.members.filter(function(holdNote:SustainTrail) { @@ -183,19 +193,6 @@ class Strumline extends FlxSpriteGroup }); } - public function getHoldNotesInRange(strumTime:Float, hitWindow:Float):Array - { - var hitWindowStart:Float = strumTime - hitWindow; - var hitWindowEnd:Float = strumTime + hitWindow; - - return holdNotes.members.filter(function(note:SustainTrail) { - return note != null - && note.alive - && note.strumTime >= hitWindowStart - && (note.strumTime + note.fullSustainLength) <= hitWindowEnd; - }); - } - public function getNoteSprite(noteData:SongNoteData):NoteSprite { if (noteData == null) return null; @@ -280,7 +277,7 @@ class Strumline extends FlxSpriteGroup * @param strumTime * @return Float */ - static function calculateNoteYPos(strumTime:Float, vwoosh:Bool = true):Float + public function calculateNoteYPos(strumTime:Float, vwoosh:Bool = true):Float { // Make the note move faster visually as it moves offscreen. // var vwoosh:Float = (strumTime < Conductor.songPosition) && vwoosh ? 2.0 : 1.0; @@ -288,7 +285,8 @@ class Strumline extends FlxSpriteGroup var vwoosh:Float = 1.0; var scrollSpeed:Float = PlayState.instance?.currentChart?.scrollSpeed ?? 1.0; - return Constants.PIXELS_PER_MS * (Conductor.instance.songPosition - strumTime) * scrollSpeed * vwoosh * (Preferences.downscroll ? 1 : -1); + return + Constants.PIXELS_PER_MS * (conductorInUse.songPosition - strumTime - Conductor.instance.inputOffset) * scrollSpeed * vwoosh * (Preferences.downscroll ? 1 : -1); } function updateNotes():Void @@ -301,8 +299,8 @@ class Strumline extends FlxSpriteGroup // if (conductorInUse.currentStep == 0) nextNoteIndex = 0; var songStart:Float = PlayState.instance?.startTimestamp ?? 0.0; - var hitWindowStart:Float = Conductor.instance.songPosition - Constants.HIT_WINDOW_MS; - var renderWindowStart:Float = Conductor.instance.songPosition + RENDER_DISTANCE_MS; + var hitWindowStart:Float = conductorInUse.songPosition - Constants.HIT_WINDOW_MS; + var renderWindowStart:Float = conductorInUse.songPosition + RENDER_DISTANCE_MS; for (noteIndex in nextNoteIndex...noteData.length) { @@ -351,7 +349,7 @@ class Strumline extends FlxSpriteGroup { if (holdNote == null || !holdNote.alive) continue; - if (Conductor.instance.songPosition > holdNote.strumTime && holdNote.hitNote && !holdNote.missedNote) + if (conductorInUse.songPosition > holdNote.strumTime && holdNote.hitNote && !holdNote.missedNote) { if (isPlayer && !isKeyHeld(holdNote.noteDirection)) { @@ -365,7 +363,7 @@ class Strumline extends FlxSpriteGroup var renderWindowEnd = holdNote.strumTime + holdNote.fullSustainLength + Constants.HIT_WINDOW_MS + RENDER_DISTANCE_MS / 8; - if (holdNote.missedNote && Conductor.instance.songPosition >= renderWindowEnd) + if (holdNote.missedNote && conductorInUse.songPosition >= renderWindowEnd) { // Hold note is offscreen, kill it. holdNote.visible = false; @@ -422,13 +420,13 @@ class Strumline extends FlxSpriteGroup holdNote.cover.kill(); } } - else if (Conductor.instance.songPosition > holdNote.strumTime && holdNote.hitNote) + else if (conductorInUse.songPosition > holdNote.strumTime && holdNote.hitNote) { // Hold note is currently being hit, clip it off. holdConfirm(holdNote.noteDirection); holdNote.visible = true; - holdNote.sustainLength = (holdNote.strumTime + holdNote.fullSustainLength) - Conductor.instance.songPosition; + holdNote.sustainLength = (holdNote.strumTime + holdNote.fullSustainLength) - conductorInUse.songPosition; if (holdNote.sustainLength <= 10) { diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index bfbda2a022..fbec836bbe 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -87,6 +87,8 @@ class Save zoomCamera: true, debugDisplay: false, autoPause: true, + inputOffset: 0, + audioVisualOffset: 0, controls: { @@ -866,6 +868,18 @@ typedef SaveDataOptions = */ var autoPause:Bool; + /** + * Offset the users inputs by this many ms. + * @default `0` + */ + var inputOffset:Int; + + /** + * Affects the delay between the audio and the visuals during gameplay + * @default `0` + */ + var audioVisualOffset:Int; + var controls: { var p1: diff --git a/source/funkin/save/changelog.md b/source/funkin/save/changelog.md new file mode 100644 index 0000000000..3fa9839d1c --- /dev/null +++ b/source/funkin/save/changelog.md @@ -0,0 +1,12 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + +## [2.0.3] - 2024-01-09 +### Added +- `inputOffset:Float` to `SongDataOptions` +- `audioVisualOffset:Float` to `SongDataOptions` diff --git a/source/funkin/ui/MusicBeatState.hx b/source/funkin/ui/MusicBeatState.hx index 2cdc747efb..92169df751 100644 --- a/source/funkin/ui/MusicBeatState.hx +++ b/source/funkin/ui/MusicBeatState.hx @@ -29,6 +29,21 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler public var leftWatermarkText:FlxText = null; public var rightWatermarkText:FlxText = null; + public var conductorInUse(get, set):Conductor; + + var _conductorInUse:Null; + + function get_conductorInUse():Conductor + { + if (_conductorInUse == null) return Conductor.instance; + return _conductorInUse; + } + + function set_conductorInUse(value:Conductor):Conductor + { + return _conductorInUse = value; + } + public function new() { super(); @@ -111,7 +126,7 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler public function stepHit():Bool { - var event = new SongTimeScriptEvent(SONG_STEP_HIT, Conductor.instance.currentBeat, Conductor.instance.currentStep); + var event = new SongTimeScriptEvent(SONG_STEP_HIT, conductorInUse.currentBeat, conductorInUse.currentStep); dispatchEvent(event); @@ -122,7 +137,7 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler public function beatHit():Bool { - var event = new SongTimeScriptEvent(SONG_BEAT_HIT, Conductor.instance.currentBeat, Conductor.instance.currentStep); + var event = new SongTimeScriptEvent(SONG_BEAT_HIT, conductorInUse.currentBeat, conductorInUse.currentStep); dispatchEvent(event); diff --git a/source/funkin/ui/MusicBeatSubState.hx b/source/funkin/ui/MusicBeatSubState.hx index e9effdbeec..15c564db0c 100644 --- a/source/funkin/ui/MusicBeatSubState.hx +++ b/source/funkin/ui/MusicBeatSubState.hx @@ -21,6 +21,21 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler public var leftWatermarkText:FlxText = null; public var rightWatermarkText:FlxText = null; + public var conductorInUse(get, set):Conductor; + + var _conductorInUse:Null; + + function get_conductorInUse():Conductor + { + if (_conductorInUse == null) return Conductor.instance; + return _conductorInUse; + } + + function set_conductorInUse(value:Conductor):Conductor + { + return _conductorInUse = value; + } + public function new(bgColor:FlxColor = FlxColor.TRANSPARENT) { super(); @@ -51,7 +66,6 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler override function update(elapsed:Float):Void { - // 3.59% CPU Usage (100% is FlxTypedGroup#update() and most of that is updating each member.) super.update(elapsed); // Emergency exit button. @@ -62,11 +76,8 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler // Display Conductor info in the watch window. FlxG.watch.addQuick("musicTime", FlxG.sound.music?.time ?? 0.0); + Conductor.watchQuick(conductorInUse); - // 0.09% CPU Usage? - Conductor.watchQuick(); - - // 4.31% CPU Usage dispatchEvent(new UpdateScriptEvent(elapsed)); } @@ -94,7 +105,7 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler */ public function stepHit():Bool { - var event:ScriptEvent = new SongTimeScriptEvent(SONG_STEP_HIT, Conductor.instance.currentBeat, Conductor.instance.currentStep); + var event:ScriptEvent = new SongTimeScriptEvent(SONG_STEP_HIT, conductorInUse.currentBeat, conductorInUse.currentStep); dispatchEvent(event); @@ -110,7 +121,7 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler */ public function beatHit():Bool { - var event:ScriptEvent = new SongTimeScriptEvent(SONG_BEAT_HIT, Conductor.instance.currentBeat, Conductor.instance.currentStep); + var event:ScriptEvent = new SongTimeScriptEvent(SONG_BEAT_HIT, conductorInUse.currentBeat, conductorInUse.currentStep); dispatchEvent(event); diff --git a/source/funkin/ui/debug/DebugMenuSubState.hx b/source/funkin/ui/debug/DebugMenuSubState.hx index 56a05eb862..0804a83fda 100644 --- a/source/funkin/ui/debug/DebugMenuSubState.hx +++ b/source/funkin/ui/debug/DebugMenuSubState.hx @@ -23,7 +23,9 @@ class DebugMenuSubState extends MusicBeatSubState override function create():Void { + FlxTransitionableState.skipNextTransIn = true; super.create(); + bgColor = 0x00000000; // Create an object for the camera to track. @@ -48,9 +50,12 @@ 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)); + createItem("Input Offset Testing", openInputOffsetTesting); createItem("ANIMATION EDITOR", openAnimationEditor); createItem("STAGE EDITOR", openStageEditor); createItem("TEST STICKERS", testStickers); @@ -92,6 +97,12 @@ class DebugMenuSubState extends MusicBeatSubState FlxG.switchState(() -> new ChartEditorState()); } + function openInputOffsetTesting() + { + openSubState(new funkin.ui.debug.latency.LatencyState()); + trace('Input Offset Testing'); + } + function openAnimationEditor() { FlxG.switchState(() -> new funkin.ui.debug.anim.DebugBoundingState()); diff --git a/source/funkin/ui/debug/dialogue/ConversationDebugState.hx b/source/funkin/ui/debug/dialogue/ConversationDebugState.hx index c2edcca5a1..db7b35d3e5 100644 --- a/source/funkin/ui/debug/dialogue/ConversationDebugState.hx +++ b/source/funkin/ui/debug/dialogue/ConversationDebugState.hx @@ -5,13 +5,13 @@ import funkin.modding.events.ScriptEventDispatcher; import funkin.modding.events.ScriptEvent; import flixel.util.FlxColor; import funkin.ui.MusicBeatState; -import funkin.data.dialogue.ConversationData; -import funkin.data.dialogue.ConversationRegistry; -import funkin.data.dialogue.DialogueBoxData; -import funkin.data.dialogue.DialogueBoxRegistry; -import funkin.data.dialogue.SpeakerData; -import funkin.data.dialogue.SpeakerRegistry; -import funkin.data.freeplay.AlbumRegistry; +import funkin.data.dialogue.conversation.ConversationData; +import funkin.data.dialogue.conversation.ConversationRegistry; +import funkin.data.dialogue.dialoguebox.DialogueBoxData; +import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry; +import funkin.data.dialogue.speaker.SpeakerData; +import funkin.data.dialogue.speaker.SpeakerRegistry; +import funkin.data.freeplay.album.AlbumRegistry; import funkin.play.cutscene.dialogue.Conversation; import funkin.play.cutscene.dialogue.DialogueBox; import funkin.play.cutscene.dialogue.Speaker; diff --git a/source/funkin/ui/debug/latency/CoolStatsGraph.hx b/source/funkin/ui/debug/latency/CoolStatsGraph.hx index 01689f494f..fc733ec28f 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 620f0edd75..9624381544 100644 --- a/source/funkin/ui/debug/latency/LatencyState.hx +++ b/source/funkin/ui/debug/latency/LatencyState.hx @@ -8,20 +8,27 @@ 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; 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; +import funkin.data.song.SongData.SongNoteData; +import haxe.Timer; +import flixel.FlxCamera; class LatencyState extends MusicBeatSubState { + var visualOffsetText:FlxText; var offsetText:FlxText; - var noteGrp:FlxTypedGroup; - var strumLine:FlxSprite; + var noteGrp:Array = []; + var strumLine:Strumline; var blocks:FlxTypedGroup; @@ -31,76 +38,81 @@ class LatencyState extends MusicBeatSubState var beatTrail:FlxSprite; var diffGrp:FlxTypedGroup; - var offsetsPerBeat:Array = []; + var offsetsPerBeat:Array> = []; var swagSong:HomemadeMusic; - #if FLX_DEBUG - var funnyStatsGraph:CoolStatsGraph; - var realStats:CoolStatsGraph; - #end + 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() { - swagSong = new HomemadeMusic(); - swagSong.loadEmbedded(Paths.sound('soundTest'), true); + super.create(); - FlxG.sound.music = swagSong; - FlxG.sound.music.play(); + prevPersistentDraw = FlxG.state.persistentDraw; + prevPersistentUpdate = FlxG.state.persistentUpdate; - #if FLX_DEBUG - funnyStatsGraph = new CoolStatsGraph(0, Std.int(FlxG.height / 2), FlxG.width, Std.int(FlxG.height / 2), FlxColor.PINK, "time"); - FlxG.addChildBelowMouse(funnyStatsGraph); + FlxG.state.persistentDraw = false; + FlxG.state.persistentUpdate = false; - realStats = new CoolStatsGraph(0, Std.int(FlxG.height / 2), FlxG.width, Std.int(FlxG.height / 2), FlxColor.YELLOW, "REAL"); - FlxG.addChildBelowMouse(realStats); - #end + localConductor = new Conductor(); + conductorInUse = localConductor; - FlxG.stage.addEventListener(KeyboardEvent.KEY_DOWN, key -> { - trace(key.charCode); + stateCamera = new FlxCamera(0, 0, FlxG.width, FlxG.height); + stateCamera.bgColor = FlxColor.BLACK; + FlxG.cameras.add(stateCamera); - if (key.charCode == 120) generateBeatStuff(); + var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK); + add(bg); - 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()); + 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 - // trace("\tREDDIT: \t" + swagSong.frfrTime + " " + Timer.stamp()); - @:privateAccess - trace("\tREDDIT: \t" + FlxG.sound.music._channel.position + " " + Timer.stamp()); - // trace("EVENT LISTENER: " + key); - }); + swagSong = new HomemadeMusic(); + swagSong.loadEmbedded(Paths.sound('soundTest'), true); + swagSong.looped = true; + swagSong.play(); + FlxG.sound.list.add(swagSong); - // funnyStatsGraph.hi + PreciseInputManager.instance.onInputPressed.add(preciseInputPressed); - Conductor.instance.forceBPM(60); + PreciseInputManager.instance.onInputReleased.add(preciseInputReleased); - noteGrp = new FlxTypedGroup(); - add(noteGrp); + localConductor.forceBPM(60); + + Conductor.instance.forceBPM(60); diffGrp = new FlxTypedGroup(); add(diffGrp); - // var musSpec:PolygonSpectogram = new PolygonSpectogram(FlxG.sound.music, FlxColor.RED, FlxG.height, Math.floor(FlxG.height / 2)); - // musSpec.x += 170; - // musSpec.scrollFactor.set(); - // musSpec.waveAmplitude = 100; - // musSpec.realtimeVisLenght = 0.45; - // // musSpec.visType = FREQUENCIES; - // add(musSpec); - - for (beat in 0...Math.floor(FlxG.sound.music.length / Conductor.instance.beatLengthMs)) + for (beat in 0...Math.floor(swagSong.length / (localConductor.stepLengthMs * 2))) { - var beatTick:FlxSprite = new FlxSprite(songPosToX(beat * Conductor.instance.beatLengthMs), FlxG.height - 15); + 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 * Conductor.instance.beatLengthMs), FlxG.height - 26, 0, "swag"); + var offsetTxt:FlxText = new FlxText(songPosToX(beat * (localConductor.stepLengthMs * 2)), FlxG.height - 26, 0, ""); offsetTxt.alpha = 0.5; diffGrp.add(offsetTxt); - offsetsPerBeat.push(0); + offsetsPerBeat.push(null); } songVisFollowAudio = new FlxSprite(0, FlxG.height - 20).makeGraphic(2, 20, FlxColor.YELLOW); @@ -121,32 +133,92 @@ class LatencyState extends MusicBeatSubState for (i in 0...8) { - var block = new FlxSprite(2, 50 * i).makeGraphic(48, 48); - block.alpha = 0; + 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); } - for (i in 0...32) - { - var note:NoteSprite = new NoteSprite(NoteStyleRegistry.instance.fetchDefault()); - noteGrp.add(note); - } + 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); offsetText = new FlxText(); - offsetText.screenCenter(); + 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); - strumLine = new FlxSprite(FlxG.width / 2, 100).makeGraphic(FlxG.width, 5); - add(strumLine); + var helpText:FlxText = new FlxText(); + helpText.setFormat(Paths.font("vcr.ttf"), 20); + helpText.text = "Press BACK to return to main menu"; + helpText.x = FlxG.width - helpText.width; + helpText.y = FlxG.height - (helpText.height * 2) - 2; + add(helpText); - super.create(); + 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 close():Void + { + PreciseInputManager.instance.onInputPressed.remove(preciseInputPressed); + PreciseInputManager.instance.onInputReleased.remove(preciseInputReleased); + + FlxG.sound.music.volume = previousVolume; + swagSong.stop(); + FlxG.sound.list.remove(swagSong); + + FlxG.cameras.remove(stateCamera); + + FlxG.state.persistentDraw = prevPersistentDraw; + FlxG.state.persistentUpdate = prevPersistentUpdate; + super.close(); + } + + 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 (Conductor.instance.currentStep % 4 == 2) + if (localConductor.currentStep % 4 == 2) { - blocks.members[((Conductor.instance.currentBeat % 8) + 1) % 8].alpha = 0.5; + blocks.members[((localConductor.currentBeat % 8) + 1) % 8].alpha = 0.5; } return super.stepHit(); @@ -154,11 +226,11 @@ class LatencyState extends MusicBeatSubState override function beatHit():Bool { - if (Conductor.instance.currentBeat % 8 == 0) blocks.forEach(blok -> { - blok.alpha = 0; + if (localConductor.currentBeat % 8 == 0) blocks.forEach(blok -> { + blok.alpha = 0.1; }); - blocks.members[Conductor.instance.currentBeat % 8].alpha = 1; + blocks.members[localConductor.currentBeat % 8].alpha = 1; // block.visible = !block.visible; return super.beatHit(); @@ -171,117 +243,114 @@ class LatencyState extends MusicBeatSubState trace(FlxG.sound.music._channel.position); */ - // localConductor.update(swagSong.time, false); - - if (FlxG.keys.justPressed.S) - { - trace("\tUPDATE PRESS: \t" + FlxG.sound.music.time + " " + Timer.stamp()); - } - - if (FlxG.keys.justPressed.SPACE) - { - if (FlxG.sound.music.playing) FlxG.sound.music.pause(); - else - FlxG.sound.music.resume(); - } + localConductor.update(swagSong.time, false); - if (FlxG.keys.pressed.D) FlxG.sound.music.time += 1000 * FlxG.elapsed; + // localConductor.songPosition += (Timer.stamp() * 1000) - FlxG.sound.music.prevTimestamp; - Conductor.instance.update(swagSong.getTimeWithDiff() - Conductor.instance.inputOffset); - // Conductor.instance.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); - songPosVis.x = songPosToX(Conductor.instance.songPosition); - songVisFollowAudio.x = songPosToX(Conductor.instance.songPosition - Conductor.instance.instrumentalOffset); - songVisFollowVideo.x = songPosToX(Conductor.instance.songPosition - Conductor.instance.inputOffset); + visualOffsetText.text = "Visual Offset: " + localConductor.audioVisualOffset + "ms"; + 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"; - offsetText.text = "INST Offset: " + Conductor.instance.instrumentalOffset + "ms"; - offsetText.text += "\nINPUT Offset: " + Conductor.instance.inputOffset + "ms"; - offsetText.text += "\ncurrentStep: " + Conductor.instance.currentStep; - offsetText.text += "\ncurrentBeat: " + Conductor.instance.currentBeat; + offsetText.text = "INPUT Offset (Left/Right to change): " + localConductor.inputOffset + "ms"; + offsetText.text += "\n\nYou can hold SHIFT to step 1ms at a time"; var avgOffsetInput:Float = 0; + var loopInd:Int = 0; for (offsetThing in offsetsPerBeat) + { + if (offsetThing == null) continue; avgOffsetInput += offsetThing; + loopInd++; + } - avgOffsetInput /= offsetsPerBeat.length; + avgOffsetInput /= loopInd; - offsetText.text += "\naverage input offset needed: " + avgOffsetInput; + offsetText.text += "\n\nEstimated average input offset needed: " + avgOffsetInput; - var multiply:Float = 10; + var multiply:Int = 10; if (FlxG.keys.pressed.SHIFT) multiply = 1; - if (FlxG.keys.pressed.CONTROL) + if (FlxG.keys.pressed.CONTROL || FlxG.keys.pressed.SPACE) { if (FlxG.keys.justPressed.RIGHT) { - Conductor.instance.instrumentalOffset += 1.0 * multiply; + localConductor.audioVisualOffset += 1 * multiply; } if (FlxG.keys.justPressed.LEFT) { - Conductor.instance.instrumentalOffset -= 1.0 * multiply; + localConductor.audioVisualOffset -= 1 * multiply; } } else { - if (FlxG.keys.justPressed.RIGHT) - { - Conductor.instance.inputOffset += 1.0 * multiply; - } - - if (FlxG.keys.justPressed.LEFT) + if (FlxG.keys.anyJustPressed([LEFT, RIGHT])) { - Conductor.instance.inputOffset -= 1.0 * multiply; + 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 = ""); } } - noteGrp.forEach(function(daNote:NoteSprite) { - daNote.y = (strumLine.y - ((Conductor.instance.songPosition - Conductor.instance.instrumentalOffset) - daNote.noteData.time) * 0.45); - daNote.x = strumLine.x + 30; - - if (daNote.y < strumLine.y) daNote.alpha = 0.5; - - if (daNote.y < 0 - daNote.height) - { - daNote.alpha = 1; - // daNote.data.strumTime += Conductor.instance.beatLengthMs * 8; - } - }); + if (controls.BACK) + { + close(); + } super.update(elapsed); } - function generateBeatStuff() + function generateBeatStuff(event:PreciseInputEvent) { - Conductor.instance.update(swagSong.getTimeWithDiff()); + // 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(Conductor.instance.songPosition / Conductor.instance.beatLengthMs) % diffGrp.members.length; - var getDiff:Float = Conductor.instance.songPosition - (closestBeat * Conductor.instance.beatLengthMs); - getDiff -= Conductor.instance.inputOffset; + 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 >= Conductor.instance.beatLengthMs * 2) getDiff -= FlxG.sound.music.length; + if (closestBeat == 0 && getDiff >= localConductor.stepLengthMs * 2) getDiff -= swagSong.length; - trace("\tDISTANCE TO CLOSEST BEAT: " + getDiff + "ms"); - trace("\tCLOSEST BEAT: " + closestBeat); beatTrail.x = songPosVis.x; diffGrp.members[closestBeat].text = getDiff + "ms"; - offsetsPerBeat[closestBeat] = Std.int(getDiff); + offsetsPerBeat[closestBeat] = Math.round(getDiff); } function songPosToX(pos:Float):Float { - return FlxMath.remapToRange(pos, 0, FlxG.sound.music.length, 0, FlxG.width); + return FlxMath.remapToRange(pos, 0, swagSong.length, 0, FlxG.width); } } class HomemadeMusic extends FlxSound { public var prevTimestamp:Int = 0; - public var timeWithDiff:Float = 0; public function new() { diff --git a/source/funkin/ui/freeplay/Album.hx b/source/funkin/ui/freeplay/Album.hx index 3060d3eb83..9bae88de00 100644 --- a/source/funkin/ui/freeplay/Album.hx +++ b/source/funkin/ui/freeplay/Album.hx @@ -1,8 +1,8 @@ package funkin.ui.freeplay; -import funkin.data.freeplay.AlbumData; +import funkin.data.freeplay.album.AlbumData; +import funkin.data.freeplay.album.AlbumRegistry; import funkin.data.animation.AnimationData; -import funkin.data.freeplay.AlbumRegistry; import funkin.data.IRegistryEntry; import flixel.graphics.FlxGraphic; diff --git a/source/funkin/ui/freeplay/AlbumRoll.hx b/source/funkin/ui/freeplay/AlbumRoll.hx index 6b963a2425..35facf131f 100644 --- a/source/funkin/ui/freeplay/AlbumRoll.hx +++ b/source/funkin/ui/freeplay/AlbumRoll.hx @@ -7,7 +7,7 @@ import flixel.util.FlxSort; import flixel.tweens.FlxTween; import flixel.util.FlxTimer; import flixel.tweens.FlxEase; -import funkin.data.freeplay.AlbumRegistry; +import funkin.data.freeplay.album.AlbumRegistry; import funkin.util.assets.FlxAnimationUtil; import funkin.graphics.FunkinSprite; import funkin.util.SortUtil; diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 44f9a99677..b16cbe63a4 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -18,7 +18,7 @@ import flixel.util.FlxColor; import flixel.util.FlxSpriteUtil; import flixel.util.FlxTimer; import funkin.audio.FunkinSound; -import funkin.data.level.LevelRegistry; +import funkin.data.story.level.LevelRegistry; import funkin.data.song.SongRegistry; import funkin.graphics.FunkinCamera; import funkin.graphics.FunkinSprite; diff --git a/source/funkin/ui/options/FunkinSoundTray.hx b/source/funkin/ui/options/FunkinSoundTray.hx index 4af94569b6..b8c408508a 100644 --- a/source/funkin/ui/options/FunkinSoundTray.hx +++ b/source/funkin/ui/options/FunkinSoundTray.hx @@ -20,6 +20,7 @@ class FunkinSoundTray extends FlxSoundTray { var graphicScale:Float = 0.30; var lerpYPos:Float = 0; + var alphaTarget:Float = 0; var volumeMaxSound:String; @@ -40,7 +41,7 @@ class FunkinSoundTray extends FlxSoundTray // makes an alpha'd version of all the bars (bar_10.png) var backingBar:Bitmap = new Bitmap(Assets.getBitmapData(Paths.image("soundtray/bars_10"))); - backingBar.x = 10; + backingBar.x = 9; backingBar.y = 5; backingBar.scaleX = graphicScale; backingBar.scaleY = graphicScale; @@ -56,7 +57,7 @@ class FunkinSoundTray extends FlxSoundTray for (i in 1...11) { var bar:Bitmap = new Bitmap(Assets.getBitmapData(Paths.image("soundtray/bars_" + i))); - bar.x = 10; + bar.x = 9; bar.y = 5; bar.scaleX = graphicScale; bar.scaleY = graphicScale; @@ -77,15 +78,18 @@ class FunkinSoundTray extends FlxSoundTray override public function update(MS:Float):Void { y = MathUtil.coolLerp(y, lerpYPos, 0.1); + alpha = MathUtil.coolLerp(alpha, alphaTarget, 0.25); // Animate sound tray thing if (_timer > 0) { _timer -= (MS / 1000); + alphaTarget = 1; } else if (y > -height) { lerpYPos = -height - 10; + alphaTarget = 0; if (y <= -height) { diff --git a/source/funkin/ui/options/OptionsState.hx b/source/funkin/ui/options/OptionsState.hx index da375706bf..81331b266f 100644 --- a/source/funkin/ui/options/OptionsState.hx +++ b/source/funkin/ui/options/OptionsState.hx @@ -1,5 +1,6 @@ package funkin.ui.options; +import funkin.ui.debug.latency.LatencyState; import flixel.FlxSprite; import flixel.FlxSubState; import flixel.addons.transition.FlxTransitionableState; @@ -190,6 +191,9 @@ class OptionsMenu extends Page add(items = new TextMenuList()); createItem("PREFERENCES", function() switchPage(Preferences)); createItem("CONTROLS", function() switchPage(Controls)); + createItem("INPUT OFFSETS", function() { + FlxG.state.openSubState(new LatencyState()); + }); #if newgrounds if (NGio.isLoggedIn) createItem("LOGOUT", selectLogout); diff --git a/source/funkin/ui/story/Level.hx b/source/funkin/ui/story/Level.hx index 8f454aa1a6..e56fdc7dc0 100644 --- a/source/funkin/ui/story/Level.hx +++ b/source/funkin/ui/story/Level.hx @@ -6,8 +6,8 @@ import flixel.util.FlxColor; import funkin.play.song.Song; import funkin.data.IRegistryEntry; import funkin.data.song.SongRegistry; -import funkin.data.level.LevelRegistry; -import funkin.data.level.LevelData; +import funkin.data.story.level.LevelRegistry; +import funkin.data.story.level.LevelData; /** * An object used to retrieve data about a story mode level (also known as "weeks"). diff --git a/source/funkin/ui/story/LevelProp.hx b/source/funkin/ui/story/LevelProp.hx index d8eae9c776..ffc756e1cb 100644 --- a/source/funkin/ui/story/LevelProp.hx +++ b/source/funkin/ui/story/LevelProp.hx @@ -2,7 +2,7 @@ package funkin.ui.story; import funkin.play.stage.Bopper; import funkin.util.assets.FlxAnimationUtil; -import funkin.data.level.LevelData; +import funkin.data.story.level.LevelData; class LevelProp extends Bopper { diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index d4ffa961dd..d8bfd59fb4 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -9,7 +9,7 @@ import flixel.tweens.FlxTween; import flixel.util.FlxColor; import flixel.util.FlxTimer; import funkin.audio.FunkinSound; -import funkin.data.level.LevelRegistry; +import funkin.data.story.level.LevelRegistry; import funkin.data.song.SongRegistry; import funkin.graphics.FunkinSprite; import funkin.modding.events.ScriptEvent; diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index 6c05e19e85..568f9ec5aa 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -295,6 +295,8 @@ class Constants /** * Constant for the number of seconds in a minute. + * + * sex per min */ public static final SECS_PER_MIN:Int = 60; diff --git a/tests/unit/source/funkin/data/level/LevelRegistryTest.hx b/tests/unit/source/funkin/data/level/LevelRegistryTest.hx index 691d8bedc5..44cfb909c6 100644 --- a/tests/unit/source/funkin/data/level/LevelRegistryTest.hx +++ b/tests/unit/source/funkin/data/level/LevelRegistryTest.hx @@ -1,6 +1,6 @@ -package funkin.data.level; +package funkin.data.story.level; -import funkin.data.level.LevelRegistry; +import funkin.data.story.level.LevelRegistry; import funkin.ui.story.Level; import massive.munit.Assert; import massive.munit.async.AsyncFactory; @@ -8,7 +8,7 @@ import massive.munit.util.Timer; @:nullSafety @:access(funkin.ui.story.Level) -@:access(funkin.data.level.LevelRegistry) +@:access(funkin.data.story.level.LevelRegistry) class LevelRegistryTest extends FunkinTest { public function new()