diff --git a/assets/images/editors/autosave-icons.png b/assets/images/editors/autosave-icons.png new file mode 100644 index 00000000..8c61f82e Binary files /dev/null and b/assets/images/editors/autosave-icons.png differ diff --git a/source/funkin/backend/chart/Chart.hx b/source/funkin/backend/chart/Chart.hx index e7a2c2a4..0b5dffab 100644 --- a/source/funkin/backend/chart/Chart.hx +++ b/source/funkin/backend/chart/Chart.hx @@ -200,10 +200,12 @@ class Chart { var meta = filteredChart.meta; #if sys - if (!FileSystem.exists('${songFolderPath}/charts/')) - FileSystem.createDirectory('${songFolderPath}/charts/'); + var saveFolder:String = saveSettings.folder == null ? "charts" : saveSettings.folder; - var chartPath = '${songFolderPath}/charts/${difficulty.trim()}.json'; + if (!FileSystem.exists('${songFolderPath}/$saveFolder/')) + FileSystem.createDirectory('${songFolderPath}/$saveFolder/'); + + var chartPath = '${songFolderPath}/$saveFolder/${difficulty.trim()}.json'; var metaPath = '${songFolderPath}/meta.json'; CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? "\t" : null)); @@ -242,4 +244,5 @@ typedef ChartSaveSettings = { var ?saveMetaInChart:Bool; var ?saveEventsInChart:Bool; var ?prettyPrint:Bool; + var ?folder:String; } diff --git a/source/funkin/editors/DebugOptions.hx b/source/funkin/editors/DebugOptions.hx index 3dc44b22..01df41d0 100644 --- a/source/funkin/editors/DebugOptions.hx +++ b/source/funkin/editors/DebugOptions.hx @@ -39,13 +39,29 @@ class DebugOptionsScreen extends OptionsScreen { "Enable Editor SFXs", "If checked, will play sound effects when working on editors (ex: will play sfxs when checking checkboxes...)", "editorSFX")); - add(new Checkbox( - "Resizable Editors", - "If checked, editors will be resizable and extensible like other programs instead of zooming in upon maximization.", - "resizableEditors")); add(new Checkbox( "Intensive Blur", "If checked, will use more intensive blur that may be laggier but look better.", "intensiveBlur")); + add(new Checkbox( + "Editor Autosaves", + "If checked, this will autosave your files in the editor, with the settings listed below.", + "charterAutoSaves")); + add(new NumOption( + "Autosaving Time", + "This controls how often the editor will autosave your file (in seconds...)", + 60, 60*10, 30, + "charterAutoSaveTime" + )); + add(new NumOption( + "Save Warning Time", + "This controls how long the editor will warn you before it autosaves (in seconds..., 0 to disable)", + 0, 15, 1, + "charterAutoSaveWarningTime" + )); + add(new Checkbox( + "Autosaves Folder", + "If checked, this will autosave your file in a seperate folder with a time stamp instead of overriding your current file. (song/autosaves/)", + "charterAutoSavesSeperateFolder")); } } \ No newline at end of file diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 23bd706e..60ce887e 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -84,6 +84,7 @@ class Charter extends UIState { public var charterCamera:FlxCamera; public var uiCamera:FlxCamera; public var selectionBox:UISliceSprite; + public var autoSaveNotif:CharterAutoSaveUI; public static var selection:Selection; @@ -437,6 +438,9 @@ class Charter extends UIState { strumlineInfoBG.y = 23; strumlineInfoBG.scrollFactor.set(); + autoSaveNotif = new CharterAutoSaveUI(20, strumlineInfoBG.y + strumlineInfoBG.height + 20); + uiGroup.add(autoSaveNotif); + strumlineAddButton = new CharterStrumlineButton("editors/new", "Create New"); strumlineAddButton.onClick = createStrumWithUI; strumlineAddButton.animationOnClick = false; @@ -663,6 +667,34 @@ class Charter extends UIState { for(s in selection) s.selected = true; } + public static var autoSaveTimer:Float = 0; + var __autoSaveLocation:String = null; + public function updateAutoSaving(elapsed:Float) { + if (Options.charterAutoSaves) { + autoSaveTimer -= elapsed; + if (autoSaveTimer < Options.charterAutoSaveWarningTime && !autoSaveNotif.showedAnimation) { + if (Options.charterAutoSavesSeperateFolder) + __autoSaveLocation = DateTools.format(Date.now(), "%Y-%m-%d_%H-%M"); + + autoSaveNotif.startAutoSave(autoSaveTimer, + !Options.charterAutoSavesSeperateFolder ? 'Saved chart at ${__diff.toLowerCase()}.json!' : + 'Saved chart at $__autoSaveLocation.json!' + ); + } + + if (autoSaveTimer <= 0) { + autoSaveTimer = Options.charterAutoSaveTime; buildChart(); + var songPath:String = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}'; + + if (Options.charterAutoSavesSeperateFolder) + Chart.save(songPath, PlayState.SONG, __autoSaveLocation, {saveMetaInChart: false, folder: "autosaves"}); + else + Chart.save(songPath, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: false}); + undos.save(); + } + } + } + var deletedNotes:Selection = new Selection(); public function updateNoteLogic(elapsed:Float) { updateSelectionLogic(); @@ -1093,6 +1125,7 @@ class Charter extends UIState { var __firstFrame:Bool = true; public override function update(elapsed:Float) { updateNoteLogic(elapsed); + updateAutoSaving(elapsed); if (FlxG.sound.music.playing || __firstFrame) { gridBackdrops.conductorSprY = curStepFloat * 40; @@ -1819,11 +1852,12 @@ class Charter extends UIState { undos = new UndoList(); clipboard = []; playtestInfo = null; waveformHandler = new CharterWaveformHandler(); + autoSaveTimer = Options.charterAutoSaveTime; } @:noCompletion public function __clearStatics() { selection = null; undos = null; clipboard = null; playtestInfo = null; - waveformHandler.destroy(); Charter.waveformHandler = null; + waveformHandler.destroy(); Charter.waveformHandler = null; autoSaveTimer = 0; } @:noCompletion public function __updatePlaytestInfo() { diff --git a/source/funkin/editors/charter/CharterAutoSaveUI.hx b/source/funkin/editors/charter/CharterAutoSaveUI.hx new file mode 100644 index 00000000..12cbcf07 --- /dev/null +++ b/source/funkin/editors/charter/CharterAutoSaveUI.hx @@ -0,0 +1,94 @@ +package funkin.editors.charter; + +import flixel.util.FlxTimer; +import flixel.tweens.FlxTween; +import flixel.math.FlxRect; + +using flixel.util.FlxSpriteUtil; + +class CharterAutoSaveUI extends UISliceSprite { + public var icon:FlxSprite; + public var autosavingText:UIText; + public var progressBarBack:FlxSprite; + public var progressBar:FlxSprite; + + public var showedAnimation:Bool = false; + + public function new(x:Float, y:Float) { + super(x, y, 300, 46, "editors/ui/inputbox"); + + icon = new FlxSprite(x+12, y+10).loadGraphic(Paths.image("editors/autosave-icons"), true, 10, 10); + icon.animation.add("icon", [for(i in 0...2) i], 0, true); + icon.animation.play("icon"); icon.animation.curAnim.curFrame = 0; + members.push(icon); + + members.push(autosavingText = new UIText(x+12+10+4, y+8, 0, "Autosaving in 10 seconds...", 12)); + + progressBarBack = new FlxSprite(x + 10, y + bHeight - 20).makeGraphic(Std.int(bWidth-20), 10, 0x00000000, true); + progressBarBack.drawRoundRect(0, 0, progressBarBack.width, progressBarBack.height, 4, 6, 0xFF727272, null, {smoothing: false}); + progressBarBack.drawRoundRect(1, 1, progressBarBack.width-2, progressBarBack.height-2, 4, 6, 0xFF0D0D0D, null, {smoothing: false}); + members.push(progressBarBack); + + progressBar = new FlxSprite(x + 10, y + bHeight - 20).makeGraphic(Std.int(bWidth-20), 10, 0x00000000, true); + progressBar.drawRoundRect(0, 0, progressBar.width, progressBar.height, 4, 6, 0xFF727272, null, {smoothing: false}); + progressBar.drawRoundRect(1, 1, progressBar.width-2, progressBar.height-2, 4, 6, 0xFFFFFFFF, null, {smoothing: false}); + progressBar.antialiasing = false; progressBar.clipRect = new FlxRect(0, 0, progressBar.width, progressBar.height); + members.push(progressBar); + + alpha = 0; + } + + public function startAutoSave(time:Float, sucessText:String) { + appearAnimation(); + + FlxTween.num(0, 1, time, null, (v:Float) -> { + if ((progress = v) < .95) { + autosavingText.text = 'Autosaving in ${Math.min(Math.round(time), 1+Math.floor(Math.abs(time-(progress*time))))} seconds'; + autosavingText.text += [for (i in 0...(Math.floor((progress*time*3)%4))) "."].join(""); + } + }); + (new FlxTimer()).start(time, (_) -> { + autosavingText.text = sucessText; + + icon.animation.curAnim.curFrame = 1; + for (member in [this, progressBar, progressBarBack, autosavingText]) member.color = 0xFFA3EC95; + + (new FlxTimer()).start(1, (_) -> {disappearAnimation();}); + }); + } + + public var progress:Float = 0; + public override function update(elapsed:Float) { + super.update(elapsed); + + icon.follow(this, 12, 9); + icon.alpha = alpha; + + autosavingText.follow(this, 12+10+4, 8); + autosavingText.alpha = alpha; + + for (bar in [progressBar, progressBarBack]) { + bar.follow(this, 10, bHeight-20); + bar.alpha = alpha; + } + progressBar.clipRect = progressBar.clipRect.set(0, 0, progressBar.width*progress,progressBar.height); + } + + public function appearAnimation() { + autosavingText.text = "Autosaving in seconds..."; progress = 0; icon.animation.curAnim.curFrame = 0; + for (member in [this, autosavingText, progressBar, progressBarBack]) member.color = 0xFFFFFFFF; + + x = -(320); alpha=0; FlxTween.cancelTweensOf(this); + FlxTween.tween(this, {x: 20}, .4, {ease: FlxEase.circInOut}); + FlxTween.tween(this, {alpha: 1}, .3, {ease: FlxEase.sineOut, startDelay: .1}); + + showedAnimation = true; + } + + public function disappearAnimation() { + x = 20; alpha = 1; FlxTween.cancelTweensOf(this); + FlxTween.tween(this, {alpha: 0}, .4, {ease: FlxEase.sineOut}); + + showedAnimation = false; + } +} \ No newline at end of file diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx index 5935e623..debe4a13 100644 --- a/source/funkin/options/Options.hx +++ b/source/funkin/options/Options.hx @@ -63,6 +63,10 @@ class Options public static var charterShowBeats:Bool = true; public static var charterEnablePlaytestScripts:Bool = true; public static var charterLowDetailWaveforms:Bool = false; + public static var charterAutoSaves:Bool = true; + public static var charterAutoSaveTime:Float = 60*5; + public static var charterAutoSaveWarningTime:Float = 5; + public static var charterAutoSavesSeperateFolder:Bool = false; /** * PLAYER 1 CONTROLS