From 2d35c1983e6f0c2a8a1038d266b7b3c18f330435 Mon Sep 17 00:00:00 2001 From: lunarcleint Date: Wed, 26 Feb 2025 23:52:54 -0700 Subject: [PATCH] edit sprite (pls test i need alot, kinda buggy) --- source/funkin/backend/utils/CoolUtil.hx | 110 +++++++++++++ .../editors/character/CharacterAnimButton.hx | 32 ++-- .../editors/character/CharacterEditor.hx | 5 +- .../editors/character/CharacterGhost.hx | 6 +- .../character/CharacterPropertiesWindow.hx | 38 ++++- .../character/CharacterSpriteScreen.hx | 27 +++- source/funkin/editors/ui/UIImageExplorer.hx | 150 +++++++++++++----- source/funkin/editors/ui/UIWarningSubstate.hx | 13 +- 8 files changed, 317 insertions(+), 64 deletions(-) diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx index 29dd6272..4033b63c 100644 --- a/source/funkin/backend/utils/CoolUtil.hx +++ b/source/funkin/backend/utils/CoolUtil.hx @@ -1,5 +1,6 @@ package funkin.backend.utils; +import flxanimate.data.AnimationData.AnimAtlas; import flixel.graphics.FlxGraphic; import openfl.display.BitmapData; import flixel.graphics.frames.FlxAtlasFrames; @@ -42,6 +43,8 @@ import haxe.xml.Access; import lime.utils.Assets; import openfl.geom.ColorTransform; import flixel.math.FlxPoint; +import haxe.Constraints.IMap; +import haxe.EnumTools.EnumValueTools; using StringTools; @@ -1168,6 +1171,113 @@ final class CoolUtil return animsList; } + + public static function getAnimsListFromAtlas(atlas:AnimAtlas):Array { + if (atlas == null) return []; + + var animsList:Array = []; + if (atlas.AN.SN != null) animsList.push(atlas.AN.SN); + if (atlas.SD != null) + for (symbol in atlas.SD.S) + if (symbol.SN != null) animsList.push(symbol.SN); + + return animsList; + } + + // TODO: check this for bugs + // Code from https://github.com/elnabo/equals/blob/master/src/equals/Equal.hx, (MIT License), but updated to work with haxe 4 + public static function deepEqual (a:T, b:T) : Bool { + if (a == b) { return true; } // if physical equality + if (isNull(a) || isNull(b)) { + return false; + } + + switch (Type.typeof(a)) { + case TNull, TInt, TBool, TUnknown: + return a == b; + case TFloat: + return Math.isNaN(cast a) && Math.isNaN(cast b); // only valid true result remaining + case TFunction: + return Reflect.compareMethods(a, b); // only physical equality can be tested for function + case TEnum(_): + if (EnumValueTools.getIndex(cast a) != EnumValueTools.getIndex(cast b)) { + return false; + } + var a_args = EnumValueTools.getParameters(cast a); + var b_args = EnumValueTools.getParameters(cast b); + return deepEqual(a_args, b_args); + case TClass(_): + if ((a is String) && (b is String)) { + return a == b; + } + if ((a is Array) && (b is Array)) { + var a = cast(a, Array); + var b = cast(b, Array); + if (a.length != b.length) { return false; } + for (i in 0...a.length) { + if (!deepEqual(a[i], b[i])) { + return false; + } + } + return true; + } + + if ((a is IMap) && (b is IMap)) { + var a = cast(a, IMap); + var b = cast(b, IMap); + var a_keys = [ for (key in a.keys()) key ]; + var b_keys = [ for (key in b.keys()) key ]; + a_keys.sort(Reflect.compare); + b_keys.sort(Reflect.compare); + if (!deepEqual(a_keys, b_keys)) { return false; } + for (key in a_keys) { + if (!deepEqual(a.get(key), b.get(key))) { + return false; + } + } + return true; + } + + if ((a is Date) && (b is Date)) { + return cast(a, Date).getTime() == cast(b, Date).getTime(); + } + + if ((a is haxe.io.Bytes) && (b is haxe.io.Bytes)) { + return deepEqual(cast(a, haxe.io.Bytes).getData(), cast(b, haxe.io.Bytes).getData()); + } + + case TObject: + } + + for (field in Reflect.fields(a)) { + var pa = Reflect.field(a, field); + var pb = Reflect.field(b, field); + if (isFunction(pa)) { + // ignore function as only physical equality can be tested, unless null + if (isNull(pa) != isNull(pb)) { + return false; + } + continue; + } + if (!deepEqual(pa, pb)) { + return false; + } + } + + return true; + } + + static inline function isNull(a:Dynamic):Bool { + return Type.enumEq(Type.typeof(a), TNull); + } + + static inline function isFunction(a:Dynamic):Bool { + return Type.enumEq(Type.typeof(a), TFunction); + } + + public static inline function isMapEmpty(map: Map): Bool { + return !map.keys().hasNext(); + } } /** diff --git a/source/funkin/editors/character/CharacterAnimButton.hx b/source/funkin/editors/character/CharacterAnimButton.hx index 86e79584..79985a30 100644 --- a/source/funkin/editors/character/CharacterAnimButton.hx +++ b/source/funkin/editors/character/CharacterAnimButton.hx @@ -264,18 +264,12 @@ class CharacterAnimButton extends UIButton { if (parent.character.animateAtlas != null) __refreshAnimation(); else { + trace(parent.character.animation._animations); var flxAnimation:FlxAnimation = __getFlxAnimation(); trace(parent.character.animation._animations); flxAnimation.prefix = newAnim; refreshFlxAnimationFrames(flxAnimation, animData); - if (valid) { - parent.buildAnimDisplay(anim, flxAnimation); - animationDisplayBG.alpha = 1; - } else { - parent.removeAnimDisplay(anim); - animationDisplayBG.alpha = 0.4; - } } if (parent.character.getAnimName() == anim) @@ -349,9 +343,12 @@ class CharacterAnimButton extends UIButton { flxAnimation.frames = frameIndices; } else { + trace(flxAnimation.prefix); final animFrames:Array = new Array(); parent.character.animation.findByPrefix(animFrames, flxAnimation.prefix); + trace(animFrames); + final frameIndices:Array = []; parent.character.animation.byPrefixHelper(frameIndices, animFrames, flxAnimation.prefix); @@ -361,9 +358,17 @@ class CharacterAnimButton extends UIButton { if (flxAnimation.frames.length <= 0) invalidate(); else validate(); } catch (e) { - trace('$e'); + trace('$e ${e.details}'); invalidate(); } + + if (valid) { + parent.buildAnimDisplay(anim, flxAnimation); + animationDisplayBG.alpha = 1; + } else { + parent.removeAnimDisplay(anim); + animationDisplayBG.alpha = 0.4; + } } public inline function refreshSymbolKeyFrames(symbol:FlxSymbolAnimation, animData:AnimData) @:privateAccess { @@ -450,13 +455,18 @@ class CharacterAnimButton extends UIButton { validate(false); public inline function __getFlxAnimation():Null @:privateAccess { - if (parent.character.animation._animations[anim] == null) - XMLUtil.addAnimToSprite(parent.character, data); + if (!parent.character.animation._animations.exists(anim)) { + final flxanim = new FlxAnimation(parent.character.animation, anim, [], data.fps, data.loop); + flxanim.prefix = data.anim; // sobbing + + parent.character.animation._animations.set(anim, flxanim); + parent.character.animDatas.set(anim, data); + } return parent.character.animation._animations[anim]; } public inline function __getAnimationSymbol():Null @:privateAccess { - if (parent.character.animateAtlas.anim.animsMap[anim] == null) + if (!parent.character.animateAtlas.anim.animsMap.exists(anim)) XMLUtil.addAnimToSprite(parent.character, data); return parent.character.animateAtlas.anim.animsMap[anim]; } diff --git a/source/funkin/editors/character/CharacterEditor.hx b/source/funkin/editors/character/CharacterEditor.hx index 465a13b0..a1cbefce 100644 --- a/source/funkin/editors/character/CharacterEditor.hx +++ b/source/funkin/editors/character/CharacterEditor.hx @@ -305,7 +305,7 @@ class CharacterEditor extends UIState { uiGroup.add(animationText); characterAnimsWindow = new CharacterAnimsWindow((FlxG.width-(500-16)-16), characterPropertiesWindow.y+224+16, character); - uiGroup.add(characterAnimsWindow); + uiGroup.add(characterPropertiesWindow.animsWindow = characterAnimsWindow); add(uiGroup); @@ -505,6 +505,7 @@ class CharacterEditor extends UIState { } function _change_offset(x:Float, y:Float) { + if (character.getAnimName() == null) return; _set_offset( character.animOffsets[character.getAnimName()].x - x, character.animOffsets[character.getAnimName()].y - y @@ -512,6 +513,8 @@ class CharacterEditor extends UIState { } function _set_offset(x:Float, y:Float) { + trace(character.getAnimName(), characterAnimsWindow.animButtons); + if (character.getAnimName() == null) return; characterAnimsWindow.animButtons.get(character.getAnimName()).changeOffset( FlxMath.roundDecimal(x, 2), FlxMath.roundDecimal(y, 2) ); diff --git a/source/funkin/editors/character/CharacterGhost.hx b/source/funkin/editors/character/CharacterGhost.hx index 1004d014..906f3f13 100644 --- a/source/funkin/editors/character/CharacterGhost.hx +++ b/source/funkin/editors/character/CharacterGhost.hx @@ -60,8 +60,10 @@ class CharacterGhost extends Character { public function setAnimOffset(anim:String) { var daOffset:FlxPoint = animOffsets.get(anim); - frameOffset.set(daOffset.x, daOffset.y); - daOffset.putWeak(); + if (daOffset != null) { + frameOffset.set(daOffset.x, daOffset.y); + daOffset.putWeak(); + } offset.set(globalOffset.x * (isPlayer != playerOffsets ? 1 : -1), -globalOffset.y); } diff --git a/source/funkin/editors/character/CharacterPropertiesWindow.hx b/source/funkin/editors/character/CharacterPropertiesWindow.hx index 26ac7b2f..85fc0f38 100644 --- a/source/funkin/editors/character/CharacterPropertiesWindow.hx +++ b/source/funkin/editors/character/CharacterPropertiesWindow.hx @@ -1,10 +1,12 @@ package funkin.editors.character; +import haxe.io.Path; import funkin.editors.character.CharacterInfoScreen.CharacterExtraInfo; import funkin.game.Character; class CharacterPropertiesWindow extends UISliceSprite { public var character:Character; + public var animsWindow:CharacterAnimsWindow; public var positionXStepper:UINumericStepper; public var positionYStepper:UINumericStepper; @@ -143,11 +145,43 @@ class CharacterPropertiesWindow extends UISliceSprite { } public function editCharacterSpriteUI() { - CharacterEditor.instance.openSubState(new CharacterSpriteScreen('characters/${character.sprite}', (sprite:String) -> { - trace(sprite); + CharacterEditor.instance.openSubState(new CharacterSpriteScreen('characters/${character.sprite}', (sprite:String, isAtlas:Bool) -> { + changeSprite(sprite); })); } + public function changeSprite(sprite:String) @:privateAccess { + var path:String = Paths.image('characters/$sprite'); + var noExt:String = Path.withoutExtension(path); + + character.sprite = sprite; + + if (Assets.exists('$noExt/Animation.json')) { + character.atlasPath = noExt; + character.animateAtlas.loadAtlas(noExt); + + character.animateAtlas.anim.animsMap.clear(); + character.animateAtlas.anim.symbolDictionary.clear(); + + character.animateAtlas.anim.stop(); + } else { + character.frames = Paths.getFrames(path, true); + character.animation.reset(); + + animsWindow.displayWindowSprite.animation.reset(); + if (animsWindow.displayWindowGraphic != null) + animsWindow.displayWindowGraphic.destroy(); + + animsWindow.displayWindowSprite.loadGraphicFromSprite(character); + if (Assets.exists(Paths.image('characters/${character.sprite}'))) + animsWindow.displayWindowGraphic = FlxG.bitmap.add(Assets.getBitmapData(Paths.image('characters/${character.sprite}'), true, false)); + } + + character.animDatas.clear(); + for (anim in animsWindow.buttons) + anim.checkValid(); // Re-add all animations + } + public function changeFlipX(newFlipX:Bool) @:privateAccess { character.flipX = character.isPlayer ? !newFlipX : newFlipX; character.__baseFlipped = character.flipX; diff --git a/source/funkin/editors/character/CharacterSpriteScreen.hx b/source/funkin/editors/character/CharacterSpriteScreen.hx index 5d110e2a..294cfc2e 100644 --- a/source/funkin/editors/character/CharacterSpriteScreen.hx +++ b/source/funkin/editors/character/CharacterSpriteScreen.hx @@ -1,18 +1,21 @@ package funkin.editors.character; +import haxe.io.Bytes; +import flixel.util.typeLimit.OneOfTwo; import flixel.text.FlxText.FlxTextFormat; import flixel.text.FlxText.FlxTextFormatMarkerPair; class CharacterSpriteScreen extends UISubstateWindow { private var imagePath:String = null; - private var onSave:String -> Void = null; + private var onSave:(String, Bool) -> Void = null; - public var imageExplorer:UIFileExplorer; + public var ogImageFiles:Map> = []; + public var imageExplorer:UIImageExplorer; public var saveButton:UIButton; public var closeButton:UIButton; - public function new(imagePath:String, ?onSave:String->Void) { + public function new(imagePath:String, ?onSave:(String, Bool)->Void) { super(); this.imagePath = imagePath; if (onSave != null) this.onSave = onSave; @@ -30,17 +33,23 @@ class CharacterSpriteScreen extends UISubstateWindow { super.create(); - imageExplorer = new UIImageExplorer(20, windowSpr.y + 30 + 16 + 20, imagePath, 320, 58, (_, _) -> {refreshWindowSize();}); + imageExplorer = new UIImageExplorer(20, windowSpr.y + 30 + 16 + 20, imagePath, 320, 58, (_, _) -> {onLoadImage();}); add(imageExplorer); addLabelOn(imageExplorer, "Character Image File").applyMarkup( "Character Image File $* Required$", [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); + ogImageFiles = imageExplorer.imageFiles.copy(); + saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, "Save & Close", function() { - close(); + imageExplorer.saveFiles('${Paths.getAssetsRoot()}/images/characters', () -> { + onSave(imageExplorer.imageName, imageExplorer.isAtlas); + close(); + }); }, 125); saveButton.x -= saveButton.bWidth; saveButton.y -= saveButton.bHeight; + saveButton.selectable = false; closeButton = new UIButton(saveButton.x - 20, saveButton.y, "Cancel", function() { close(); @@ -54,6 +63,14 @@ class CharacterSpriteScreen extends UISubstateWindow { refreshWindowSize(); } + public function onLoadImage() { + refreshWindowSize(); + + if (imageExplorer == null || imageExplorer.imageFiles == null) return; + var filesSame = CoolUtil.deepEqual(ogImageFiles, imageExplorer.imageFiles); + saveButton.selectable = !filesSame && !CoolUtil.isMapEmpty(imageExplorer.imageFiles) && (imageExplorer.animationList.length > 0); + } + public function refreshWindowSize() { if (imageExplorer == null) return; windowSpr.bWidth = 20 + imageExplorer.bWidth + 20; diff --git a/source/funkin/editors/ui/UIImageExplorer.hx b/source/funkin/editors/ui/UIImageExplorer.hx index e309672f..36cd774e 100644 --- a/source/funkin/editors/ui/UIImageExplorer.hx +++ b/source/funkin/editors/ui/UIImageExplorer.hx @@ -1,5 +1,6 @@ package funkin.editors.ui; +import flixel.util.typeLimit.OneOfTwo; import flixel.text.FlxText.FlxTextFormat; import flixel.text.FlxText.FlxTextFormatMarkerPair; import flxanimate.data.SpriteMapData.AnimateAtlas; @@ -15,6 +16,8 @@ using funkin.backend.utils.BitmapUtil; // TODO: make this limited if on web class UIImageExplorer extends UIFileExplorer { + private var allowAtlases:Bool = true; + public function new(x:Float, y:Float, image:String, ?w:Int, ?h:Int, ?onFile:(String, Bytes)->Void) { super(x, y, w, h, "png, jpg", function (filePath, file) { if (filePath != null && file != null) uploadImage(filePath, file); @@ -33,32 +36,37 @@ class UIImageExplorer extends UIFileExplorer { loadFile(fullImagePath); } - private var allowAtlases:Bool = true; - - public var fileText:UIText; + public var isAtlas:Bool = false; static var ANIMATE_ATLAS_REGEX = ~/^(?:Animation\.json|spritemap(?:\d+)?\.json)$/i; // no .zip atlases tho static var SPRITEMAP_JSON_REGEX = ~/^(?:spritemap(?:\d+)?\.json)$/i; static var SPRITEMAP_PNG_REGEX = ~/^(?:spritemap(?:\d+)?\.png)$/i; + public var imageName:String = null; + public var imageFiles:Map> = []; + public var animationList:Array = []; + + public var fileText:UIText; + public function uploadImage(filePath:String, file:Bytes) { + __resetData(); + var imagePath:Dynamic = Path.normalize(filePath); var directoryPath:String = Path.directory(imagePath); var fileName:String = Path.withoutDirectory(imagePath).toLowerCase(); var files:Array = []; - var isAtlas:Bool = false; + // CHECK ATLAS if(allowAtlases && ANIMATE_ATLAS_REGEX.match(fileName)) { // check if directory has the other files files = FileSystem.readDirectory(directoryPath); var hasAnimationJson:Bool = false; var hasSpritemapJson:Bool = false; for(file in files) { - file = file.toLowerCase(); - if(file == "animation.json") + if(file == "Animation.json") hasAnimationJson = true; - else if(SPRITEMAP_JSON_REGEX.match(file)) + else if(SPRITEMAP_JSON_REGEX.match(file.toLowerCase())) hasSpritemapJson = true; if(hasAnimationJson && hasSpritemapJson) { @@ -77,7 +85,7 @@ class UIImageExplorer extends UIFileExplorer { var json:AnimateAtlas = Json.parse(content); if(json.meta.image.toLowerCase() == fileName.toLowerCase()) foundSpritemapJson = true; - } else if(file.toLowerCase() == "animation.json") + } else if(file == "Animation.json") hasAnimationJson = true; if(hasAnimationJson && foundSpritemapJson) { @@ -87,6 +95,7 @@ class UIImageExplorer extends UIFileExplorer { } } + // IF ATLAS FIND SPRITMAPS var spritemaps:Array = []; var spritemapImages:Array = []; if(isAtlas) { @@ -96,7 +105,6 @@ class UIImageExplorer extends UIFileExplorer { spritemaps.push(file); spritemaps.sort(Reflect.compare); - for(spritemap in spritemaps) { var content:String = CoolUtil.removeBOM(File.getContent(Path.join([directoryPath, spritemap]))); var json:AnimateAtlas = Json.parse(content); @@ -115,14 +123,31 @@ class UIImageExplorer extends UIFileExplorer { isAtlas = false; } - var dataPathExt:String = !isAtlas ? CoolUtil.imageHasFrameData(imagePath) : null; - var animationList:Array = []; + // GATHER ANIMATIONS/DATA FILES!!! + if (isAtlas) { + var dataPath:String = '$directoryPath/Animation.json'.replace('/', '\\'); - if (dataPathExt != null) - animationList = CoolUtil.getAnimsListFromFrames(CoolUtil.loadFramesFromData(File.getContent(Path.withExtension(imagePath, dataPathExt)), dataPathExt), dataPathExt); + if (FileSystem.exists(dataPath)) { + var dataPathFile:String = File.getContent(dataPath); + animationList = CoolUtil.getAnimsListFromAtlas(cast haxe.Json.parse(dataPathFile)); - var size = 0; - var image = null; + imageFiles.set(Path.withoutDirectory(dataPath), dataPathFile); + } + } else { + var dataPathExt:String = CoolUtil.imageHasFrameData(imagePath); + var dataPath:String = Path.withExtension(imagePath, dataPathExt); + var dataPathFile:String = !isAtlas ? File.getContent(dataPath) : null; + + if (dataPathExt != null) { + animationList = CoolUtil.getAnimsListFromFrames(CoolUtil.loadFramesFromData(dataPathFile, dataPathExt), dataPathExt); + + imageFiles.set(Path.withoutDirectory(dataPath), dataPathFile); + } + } + + // GATHER INFO!!! + var size:Float = 0; + var image:BitmapData = null; if (isAtlas && spritemapImages.length > 0) { var spritemapPath:String = Path.join([directoryPath, spritemapImages[0]]); @@ -132,25 +157,31 @@ class UIImageExplorer extends UIFileExplorer { for(spritemap in spritemapImages) { var spritemapPath:String = Path.join([directoryPath, spritemap]); + var info = FileSystem.stat(spritemapPath); size += info.size; + + spritemapPath = spritemapPath.replace('/', '\\'); + imageFiles.set(Path.withoutDirectory(spritemapPath), sys.io.File.getBytes(spritemapPath)); } - image = BitmapData.fromFile(spritemapPath).crop(); + file = cast sys.io.File.getBytes(filePath = spritemapPath); + image = BitmapData.fromBytes(file).crop(); + } else { - trace("Loading image"); size = file.length; image = BitmapData.fromBytes(file).crop(); + + imageFiles.set(Path.withoutDirectory(imagePath), file); } + // DISPLAY IMAGE!! uiElement = new FlxSprite().loadGraphic(image); - var imageScale:Float = 1; if (uiElement.width < 300 || uiElement.height < 200) imageScale = Math.max(300/uiElement.width, 200/uiElement.height); else if (uiElement.width > 700 || uiElement.height > 500) imageScale = Math.min(700/uiElement.width, 500/uiElement.height); - uiElement.scale.set(imageScale, imageScale); uiElement.updateHitbox(); @@ -160,31 +191,25 @@ class UIImageExplorer extends UIFileExplorer { uiElement.antialiasing = true; members.push(uiElement); + // GENERATE TEXT!!! imagePath = new Path(imagePath); + imageName = isAtlas ? Path.withoutDirectory(directoryPath) : ${imagePath.file}; var message = new StringBuf(); + if (isAtlas) message.add('${imageName}/'); message.add('${imagePath.file}.${imagePath.ext}'); - message.add(' (${CoolUtil.getSizeString(size)}'); - if(!isAtlas) { - if (animationList.length > 0) { - if(animationList.length == 1) - message.add(', ${animationList.length} Animation Found'); - else - message.add(', ${animationList.length} Animations Found'); - } - else - message.add(', #NO Animations Found#'); - } else { - if(spritemaps.length == 1) - message.add(', ${spritemaps.length} Spritemap Found'); - else - message.add(', ${spritemaps.length} Spritemaps Found'); + if (animationList.length > 0) { + if(animationList.length == 1) message.add(', ${animationList.length} ${isAtlas ? "Symbol" : "Animation"} Found'); + else message.add(', ${animationList.length} ${isAtlas ? "Symbol" : "Animation"}s Found'); + } else message.add(', #NO ${isAtlas ? "Symbol" : "Animation"}s Found#'); + + if (isAtlas) { + if(spritemaps.length == 1) message.add(', ${spritemaps.length} Spritemap Found'); + else message.add(', ${spritemaps.length} Spritemaps Found'); } - message.add(')'); - message.add(isAtlas ? " - Atlas" : " - Spritemap"); fileText = new UIText(x+20, y+16, bWidth-20-deleteButton.bWidth-16, ""); @@ -196,17 +221,54 @@ class UIImageExplorer extends UIFileExplorer { deleteIcon.x = deleteButton.x + deleteButton.bWidth/2 - 8; deleteIcon.y = deleteButton.y + deleteButton.bHeight/2 - 8; + + } - /* - var normalFilePath:String = Path.normalize(filePath); - var gamePath:String = Path.normalize(Sys.getCwd()); + public function saveFiles(directory:String, ?onFinishSaving:Void->Void, ?checkExisting:Bool = true) { + if (isAtlas) directory += '/$imageName'; - if (normalFilePath.contains(gamePath)) - normalFilePath = normalFilePath.replace(gamePath, "."); - */ + var alreadlyExistingFiles:Array = []; + for (name => file in imageFiles) + if (FileSystem.exists('$directory/$name')) + alreadlyExistingFiles.push('$directory/$name'); + + function deleteExistingFiles() { + for (file in alreadlyExistingFiles) + FileSystem.deleteFile(file); + alreadlyExistingFiles = []; + } + + function acuttalySaveFiles() { + for (name => file in imageFiles) + if (!alreadlyExistingFiles.contains('$directory/$name')) { + CoolUtil.safeSaveFile('$directory/$name', file); + trace('SAVED: $directory/$name'); + } + + if (onFinishSaving != null) onFinishSaving(); + } + + if (alreadlyExistingFiles.length > 0) { + (FlxG.state.subState != null ? FlxG.state.subState : FlxG.state).openSubState(new UIWarningSubstate("Alreadly Existing Files!!!", + "The following files alreadly exist: \n\n" + alreadlyExistingFiles.join('\n') + "\n\nIMPORTANT: OVERRIDING CAN NOT BE UNDONE!!!!!!!!", + [ { + label: "Override Files", + color: 0xFFFF0000, + onClick: (_) -> { + deleteExistingFiles(); + acuttalySaveFiles(); + } + }, + { + label: "Use Existing", + onClick: (_) -> {if (onFinishSaving != null) onFinishSaving();} + } + ], false)); + } else acuttalySaveFiles(); } public override function removeFile() { + __resetData(); bWidth = 320; bHeight = 58; members.remove(fileText); @@ -214,4 +276,8 @@ class UIImageExplorer extends UIFileExplorer { super.removeFile(); } + + @:noCompletion inline function __resetData() { + imageFiles.clear(); isAtlas = false; animationList = []; + } } \ No newline at end of file diff --git a/source/funkin/editors/ui/UIWarningSubstate.hx b/source/funkin/editors/ui/UIWarningSubstate.hx index 1440c0bc..952290fb 100644 --- a/source/funkin/editors/ui/UIWarningSubstate.hx +++ b/source/funkin/editors/ui/UIWarningSubstate.hx @@ -69,7 +69,16 @@ class UIWarningSubstate extends MusicBeatSubstate { warnCam.zoom = 0.1; FlxG.cameras.add(warnCam, false); + var spr = new UISliceSprite(0, 0, CoolUtil.maxInt(560, 30 + (170 * buttons.length)), 232, 'editors/ui/${isError ? "normal" : "grayscale"}-popup'); + + var sprIcon:FlxSprite = new FlxSprite(spr.x + 18, spr.y + 28 + 26).loadGraphic(Paths.image('editors/warnings/${isError ? "error" : "warning"}')); + sprIcon.scale.set(1.4, 1.4); + sprIcon.updateHitbox(); + + messageSpr = new UIText(0,0, spr.bWidth - 100 - (26 * 2), message); + spr.bHeight = Std.int(232 + Math.abs(Math.min(sprIcon.height-messageSpr.height, 0))); + spr.x = (FlxG.width - spr.bWidth) / 2; spr.y = (FlxG.height - spr.bHeight) / 2; spr.color = isError ? 0xFFFF0000 : 0xFFFFFF00; @@ -86,7 +95,9 @@ class UIWarningSubstate extends MusicBeatSubstate { sprIcon.antialiasing = true; add(sprIcon); - add(messageSpr = new UIText(sprIcon.x + 70 + 16 + 20, sprIcon.y + 16, spr.bWidth - 100 - (26 * 2), message)); + messageSpr.x = sprIcon.x + 70 + 16 + 20; + messageSpr.y = sprIcon.y + 16; + add(messageSpr); var xPos = (FlxG.width - (30 + (170 * buttons.length))) / 2; for(k=>b in buttons) {