diff --git a/source/funkin/assets/Paths.hx b/source/funkin/assets/Paths.hx index b4552a29..15eb5d76 100644 --- a/source/funkin/assets/Paths.hx +++ b/source/funkin/assets/Paths.hx @@ -87,12 +87,14 @@ class Paths return OpenFlAssets.exists(diff) ? diff : getPath('songs/${song.toLowerCase()}/song/Inst.$SOUND_EXT', null); } - inline static public function image(key:String, ?library:String, checkForAtlas:Bool = false) + static public function image(key:String, ?library:String, checkForAtlas:Bool = false) { - var atlasPath = checkForAtlas ? getPath('images/$key/spritemap.png', library) : null; - var multiplePath = checkForAtlas ? getPath('images/$key/1.png', library) : null; - if (atlasPath != null && OpenFlAssets.exists(atlasPath)) return atlasPath.substr(0, atlasPath.length - 14); - if (multiplePath != null && OpenFlAssets.exists(multiplePath)) return multiplePath.substr(0, multiplePath.length - 6); + if (checkForAtlas) { + var atlasPath = getPath('images/$key/spritemap.png', library); + var multiplePath = getPath('images/$key/1.png', library); + if (atlasPath != null && OpenFlAssets.exists(atlasPath)) return atlasPath.substr(0, atlasPath.length - 14); + if (multiplePath != null && OpenFlAssets.exists(multiplePath)) return multiplePath.substr(0, multiplePath.length - 6); + } return getPath('images/$key.png', library); } @@ -115,17 +117,7 @@ class Paths difficulty = difficulty.toLowerCase(); song = song.toLowerCase(); - //var difficultyEnd = (difficulty == "normal") ? "" : '-$difficulty'; - - // songs/your-song/charts/hard.json - var p = getPath('songs/$song/charts/$difficulty.json', null); - if (OpenFlAssets.exists(p)) return p; - - // data/charts/your-song/hard.json - var p = json('charts/$song/$difficulty'); - if (OpenFlAssets.exists(p)) return p; - - return p; // returns the normal one so that it shows the correct path in the error message. + return getPath('songs/$song/charts/$difficulty.json', null); } inline static public function font(key:String) @@ -155,7 +147,7 @@ class Paths inline static public function getSparrowAtlas(key:String, ?library:String) return FlxAtlasFrames.fromSparrow(image(key, library), file('images/$key.xml', library)); - + inline static public function getSparrowAtlasAlt(key:String) return FlxAtlasFrames.fromSparrow('$key.png', '$key.xml'); @@ -181,7 +173,7 @@ class Paths } return tempFramesCache[key] = loadFrames(assetsPath ? key : Paths.image(key, library, true)); } - + /** * Loads frames from a specific image path. Supports Sparrow Atlases, Packer Atlases, and multiple spritesheets. diff --git a/source/funkin/chart/Chart.hx b/source/funkin/chart/Chart.hx index 7c898d0c..dbe842c4 100644 --- a/source/funkin/chart/Chart.hx +++ b/source/funkin/chart/Chart.hx @@ -158,8 +158,6 @@ class Chart { var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart); var meta = filteredChart.meta; - // idk how null reacts to it so better be sure - #if sys if (!FileSystem.exists('${songFolderPath}\\charts\\')) FileSystem.createDirectory('${songFolderPath}\\charts\\'); @@ -169,6 +167,7 @@ class Chart { File.saveContent(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? "\t" : null)); + // idk how null reacts to it so better be sure if (saveSettings.overrideExistingMeta == true || !FileSystem.exists(metaPath)) File.saveContent(metaPath, Json.stringify(meta, null, saveSettings.prettyPrint == true ? "\t" : null)); #end diff --git a/source/funkin/editors/SaveSubstate.hx b/source/funkin/editors/SaveSubstate.hx new file mode 100644 index 00000000..5a3fe81b --- /dev/null +++ b/source/funkin/editors/SaveSubstate.hx @@ -0,0 +1,64 @@ +package funkin.editors; + +#if desktop +import sys.io.File; +#end +import haxe.io.Path; +import lime.ui.FileDialog; +import openfl.net.FileReference; +import openfl.events.Event; +import openfl.events.IOErrorEvent; + +class SaveSubstate extends MusicBeatSubstate { + public var saveOptions:Map; + public var options:SaveSubstateData; + + public var data:String; + + public var cam:FlxCamera; + + public function new(data:String, ?options:SaveSubstateData, ?saveOptions:Map) { + super(); + this.data = data; + + if (saveOptions == null) + saveOptions = []; + this.saveOptions = saveOptions; + + if (options != null) + this.options = options; + } + + public override function create() { + super.create(); + + var fileDialog = new FileDialog(); + fileDialog.onCancel.add(function() { + trace("h"); + close(); + }); + fileDialog.onSelect.add(function(str) { + #if desktop + File.saveContent(str, data); + #end + trace("h"); + close(); + }); + fileDialog.browse(SAVE, options.saveExt.getDefault(Path.extension(options.defaultSaveFile)), options.defaultSaveFile); + } + + public override function update(elapsed:Float) { + super.update(elapsed); + parent.persistentUpdate = false; + } + + private function onError(_) { + // TODO: error handling + close(); + } +} + +typedef SaveSubstateData = { + var ?defaultSaveFile:String; + var ?saveExt:String; +} \ No newline at end of file diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 6de44212..5b4c3347 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -1,5 +1,6 @@ package funkin.editors.charter; +import haxe.Json; import flixel.input.keyboard.FlxKey; import flixel.sound.FlxSound; import flixel.util.FlxSort; @@ -95,6 +96,17 @@ class Charter extends UIState { label: "New" }, null, + { + label: "Save", + keybind: [CONTROL, S], + onSelect: _file_save, + }, + { + label: "Save As...", + keybind: [CONTROL, SHIFT, S], + onSelect: _file_saveas, + }, + null, { label: "Exit", onSelect: _file_exit @@ -356,13 +368,14 @@ class Charter extends UIState { loadSong(); } + var instPath:String; public function loadSong() { if (__reload) PlayState.loadSong(__song, __diff, false, false); Conductor.setupSong(PlayState.SONG); - FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff))); + FlxG.sound.setMusic(FlxG.sound.load(instPath = Paths.inst(__song, __diff))); vocals = FlxG.sound.load(Paths.voices(__song, __diff)); vocals.group = FlxG.sound.defaultMusicGroup; @@ -469,20 +482,31 @@ class Charter extends UIState { if (!FlxG.keys.pressed.SHIFT) verticalChange = CoolUtil.floorInt(verticalChange); var horizontalChange:Int = CoolUtil.floorInt((mousePos.x - dragStartPos.x) / 40); - addToUndo(CNoteDrag([for(s in selection) { + var drags = []; + var deletes = []; + for(s in selection) { var oldStep = s.step; var oldID = s.id; - s.updatePos(s.step + verticalChange, s.id + horizontalChange, s.susLength, s.type); + var newID = s.id + horizontalChange; + var newStep = s.step + verticalChange; + if (newStep < 0 || newID < 0 || newID >= strumLines.length * 4) { + s.updatePos(s.step, s.id, s.susLength, s.type); + deletes.push(s); + } else { + s.updatePos(newStep, newID, s.susLength, s.type); - { - note: s, - oldID: oldID, - oldStep: oldStep, - newID: s.id, - newStep: s.step - }; - }])); + drags.push({ + note: s, + oldID: oldID, + oldStep: oldStep, + newID: s.id, + newStep: s.step + }); + } + } + deleteNotes(deletes, false); + addToUndo(CNoteDrag(drags, deletes)); gridActionType = NONE; } case NONE: @@ -527,23 +551,25 @@ class Charter extends UIState { } } - public function deleteNote(note:CharterNote):CharterNote { + public function deleteNote(note:CharterNote, addToUndo:Bool = true):CharterNote { if (note == null) return note; notesGroup.remove(note, true); note.kill(); - addToUndo(CDeleteNotes([note])); + if (addToUndo) + this.addToUndo(CDeleteNotes([note])); return null; } - public function deleteNotes(notes:Array) { + public function deleteNotes(notes:Array, addToUndo:Bool = true) { if (notes.length <= 0) return []; for(note in notes) { notesGroup.remove(note, true); note.kill(); } - addToUndo(CDeleteNotes(notes)); + if (addToUndo) + this.addToUndo(CDeleteNotes(notes)); return []; } @@ -638,6 +664,32 @@ class Charter extends UIState { function _file_exit(_) { FlxG.switchState(new CharterSelection()); } + function _file_save(_) { + #if sys + for(assetPath in [Paths.chart(__song, __diff.toLowerCase()), instPath]) { + var path = Assets.getPath(assetPath); + var filteredPath = path.substr(0, path.lastIndexOf(assetPath == instPath ? '/song/' : '/charts/')); + trace(filteredPath); + saveTo(filteredPath); + return; + } + #end + _file_saveas(_); + } + + function _file_saveas(_) { + openSubState(new SaveSubstate(Json.stringify(Chart.filterChartForSaving(PlayState.SONG, true)), { + defaultSaveFile: '${__diff.toLowerCase()}.json' + })); + } + + #if sys + function saveTo(path:String) { + buildChart(); + Chart.save(path, PlayState.SONG, __diff.toLowerCase()); + } + #end + function _edit_copy(_) { var minStep:Float = selection[0].step; for(s in selection) @@ -694,9 +746,14 @@ class Charter extends UIState { case CSustainChange(changes): for(n in changes) n.note.updatePos(n.note.step, n.note.id, n.before, n.note.type); - case CNoteDrag(notes): + case CNoteDrag(notes, deletes): for(n in notes) n.note.updatePos(n.oldStep, n.oldID, n.note.susLength, n.note.type); + for(d in deletes) { + notesGroup.add(d); + d.revive(); + } + selection = [for(n in notes) n.note]; } if (v != null) @@ -736,9 +793,11 @@ class Charter extends UIState { case CSustainChange(changes): for(n in changes) n.note.updatePos(n.note.step, n.note.id, n.after, n.note.type); - case CNoteDrag(notes): + case CNoteDrag(notes, deletes): for(n in notes) n.note.updatePos(n.newStep, n.newID, n.note.susLength, n.note.type); + deleteNotes(deletes, false); + selection = [for(n in notes) n.note]; } if (v != null) undoList.insert(0, v); @@ -862,7 +921,7 @@ enum CharterChange { CSustainChange(notes:Array); CCreateNotes(notes:Array); CDeleteNotes(notes:Array); - CNoteDrag(notes:Array); + CNoteDrag(notes:Array, deletes:Array); } enum CharterCopyboardObject { diff --git a/source/funkin/editors/ui/UIUtil.hx b/source/funkin/editors/ui/UIUtil.hx index 021830ef..7f9de548 100644 --- a/source/funkin/editors/ui/UIUtil.hx +++ b/source/funkin/editors/ui/UIUtil.hx @@ -19,6 +19,9 @@ class UIUtil { * @param topMenuOptions */ public static function processShortcuts(topMenuOptions:Array) { + var maxKeyLength = 0; + var curTopMenuOption = null; + for(o in topMenuOptions) { if (o == null) continue; @@ -56,16 +59,22 @@ class UIUtil { if (!needsShift && FlxG.keys.pressed.SHIFT) continue; if (!pressed || !justPressed) continue; - if (o.onSelect != null) - o.onSelect(o); - - return true; + if (maxKeyLength < o.keybinds.length) { + maxKeyLength = o.keybinds.length; + curTopMenuOption = o; + } } } if (o.childs != null && processShortcuts(o.childs)) return true; } + + if (curTopMenuOption != null) { + if (curTopMenuOption.onSelect != null) + curTopMenuOption.onSelect(curTopMenuOption); + return true; + } return false; }