From 4b9d32fb50482f851f28acad6f4d1db28d0e45e9 Mon Sep 17 00:00:00 2001 From: lunarcleint Date: Thu, 20 Mar 2025 03:08:54 -0700 Subject: [PATCH] character editor undos part 1 --- .../editors/character/CharacterEditor.hx | 117 +++++++++++++++++- .../character/CharacterPropertiesWindow.hx | 63 +++++++--- 2 files changed, 160 insertions(+), 20 deletions(-) diff --git a/source/funkin/editors/character/CharacterEditor.hx b/source/funkin/editors/character/CharacterEditor.hx index 2b573ef8..d4a72376 100644 --- a/source/funkin/editors/character/CharacterEditor.hx +++ b/source/funkin/editors/character/CharacterEditor.hx @@ -1,5 +1,6 @@ package funkin.editors.character; +import funkin.editors.character.CharacterInfoScreen.CharacterExtraInfo; import funkin.editors.extra.AxisGizmo; import flixel.math.FlxRect; import funkin.editors.stage.StageEditor; @@ -54,6 +55,8 @@ class CharacterEditor extends UIState { public var stageSprites:Array = []; public var stagePosition:String = null; + public static var undos:UndoList; + public function new(character:String) { super(); if (character != null) __character = character; @@ -62,6 +65,8 @@ class CharacterEditor extends UIState { public override function create() { super.create(); + undos = new UndoList(); + WindowUtils.suffix = " (Character Editor)"; SaveWarning.selectionClass = CharacterSelection; SaveWarning.saveFunc = () -> {_file_save(null);}; @@ -387,6 +392,9 @@ class CharacterEditor extends UIState { if (Options.characterDragging) handleMouseOffsets(); + WindowUtils.prefix = undos.unsaved ? Flags.UNDO_PREFIX : ""; + SaveWarning.showWarning = undos.unsaved; + super.update(elapsed); } @@ -445,17 +453,19 @@ class CharacterEditor extends UIState { #else _file_saveas(_); #end + undos.save(); } function _file_saveas(_) { openSubState(new SaveSubstate(buildCharacter(), { defaultSaveFile: '${character.curCharacter}.xml' })); + undos.save(); } function _file_exit(_) { - /*if (undos.unsaved) SaveWarning.triggerWarning(); - else*/ FlxG.switchState(new CharacterSelection()); + if (undos.unsaved) SaveWarning.triggerWarning(); + else {undos = null; FlxG.switchState(new CharacterSelection());} } function buildCharacter():String { @@ -477,9 +487,77 @@ class CharacterEditor extends UIState { function _edit_paste_offset(_) { _set_offset(clipboard.x, clipboard.y); } + + function _undo(undo:CharacterEditorChange) { + switch (undo) { + case null: // do nothing + case CCharEditPosition(oldPos, newPos): + characterPropertiesWindow.changePosition(oldPos.x, oldPos.y, false); + case CCharEditCamPosition(oldPos, newPos): + characterPropertiesWindow.changeCamPosition(oldPos.x, oldPos.y, false); + case CCharEditScale(oldScale, newScale): + characterPropertiesWindow.changeScale(oldScale, false); + case CCharEditFlipped(newFlipped): + characterPropertiesWindow.changeFlipX(!newFlipped, false); + case CCharEditAntialiasing(newAntialiasing): + characterPropertiesWindow.changeAntialiasing(!newAntialiasing, false); + case CCharEditDesignedAs(newIsPlayer): + changeCharacterDesginedAs(!newIsPlayer, false); + case CCharEditInfo(oldInfo, newInfo): + characterPropertiesWindow.editCharacterInfo(oldInfo, false); + case CCharEditSprite(oldSprite, newSprite): + case CAnimCreate(animID, animData): + case CAnimDelete(animID, animData): + case CAnimEditOrder(animID, newAnimID): + case CAnimEditName(animID, oldName, newName): + case CAnimEditAnim(animID, oldAnim, newAnim): + case CAnimEditOffset(animID, oldOffset, newOffset): + case CAnimEditFPS(animID, oldFPS, newFPS): + case CAnimEditLooping(animID, newLooping): + case CAnimEditIndices(animID, oldIndicies, newIndicies): + case CCharClearOffsets(oldOffsets): + } + } - function _edit_undo(_) {} - function _edit_redo(_) {} + function _edit_undo(_) { + if (draggingCharacter) return; + _undo(undos.undo()); + } + + function _redo(undo:CharacterEditorChange) { + switch (undo) { + case null: // do nothing + case CCharEditPosition(oldPos, newPos): + characterPropertiesWindow.changePosition(newPos.x, newPos.y, false); + case CCharEditCamPosition(oldPos, newPos): + characterPropertiesWindow.changeCamPosition(newPos.x, newPos.y, false); + case CCharEditScale(oldScale, newScale): + characterPropertiesWindow.changeScale(newScale, false); + case CCharEditFlipped(newFlipped): + characterPropertiesWindow.changeFlipX(newFlipped, false); + case CCharEditAntialiasing(newAntialiasing): + characterPropertiesWindow.changeAntialiasing(newAntialiasing, false); + case CCharEditDesignedAs(newIsPlayer): + changeCharacterDesginedAs(newIsPlayer, false); + case CCharEditInfo(oldInfo, newInfo): + characterPropertiesWindow.editCharacterInfo(newInfo, false); + case CCharEditSprite(oldSprite, newSprite): + case CAnimCreate(animID, animData): + case CAnimDelete(animID, animData): + case CAnimEditOrder(animID, newAnimID): + case CAnimEditName(animID, oldName, newName): + case CAnimEditAnim(animID, oldAnim, newAnim): + case CAnimEditOffset(animID, oldOffset, newOffset): + case CAnimEditFPS(animID, oldFPS, newFPS): + case CAnimEditLooping(animID, newLooping): + case CAnimEditIndices(animID, oldIndicies, newIndicies): + case CCharClearOffsets(oldOffsets): + } + } + + function _edit_redo(_) { + _redo(undos.redo()); + } function _edit_info(_) characterPropertiesWindow.editCharacterInfoUI(); @@ -581,7 +659,7 @@ class CharacterEditor extends UIState { ]); changeStagePosition(stagePosition); - changeCharacterDesginedAs(stagePosition.toUpperCase() == "BOYFRIEND"); + changeCharacterDesginedAs(stagePosition.toUpperCase() == "BOYFRIEND", false); } currentStage = __stage; @@ -615,7 +693,7 @@ class CharacterEditor extends UIState { characterPropertiesWindow.testAsDropDown.label.text = stagePosition.toUpperCase(); } - public function changeCharacterDesginedAs(player:Bool) @:privateAccess { + public function changeCharacterDesginedAs(player:Bool, addToUndo:Bool = true) @:privateAccess { if (stage == null) { changeCharacterIsPlayer(player); return; @@ -630,6 +708,8 @@ class CharacterEditor extends UIState { characterPropertiesWindow.designedAsDropDown.index = characterPropertiesWindow.designedAsDropDown.options.indexOf(player ? "BOYFRIEND" : "DAD"); characterPropertiesWindow.designedAsDropDown.label.text = player ? "BOYFRIEND" : "DAD"; + + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditDesignedAs(player)); } public inline function updateCharacterStagePosition() { @@ -745,4 +825,29 @@ class CharacterEditor extends UIState { i.alpha = i.anim == anim ? 1 : 0.25; } #end +} + +enum CharacterEditorChange { + CCharEditPosition(oldPos:FlxPoint, newPos:FlxPoint); + CCharEditCamPosition(oldPos:FlxPoint, newPos:FlxPoint); + CCharEditScale(oldScale:Float, newScale:Float); + CCharEditFlipped(newFlipped:Bool); + CCharEditAntialiasing(newAntialiasing:Bool); + CCharEditDesignedAs(newIsPlayer:Bool); + + CCharEditInfo(oldInfo:CharacterExtraInfo, newInfo:CharacterExtraInfo); + CCharEditSprite(oldSprite:String, newSprite:String); + + CAnimCreate(animID:Int, animData:AnimData); + CAnimDelete(animID:Int, animData:AnimData); + CAnimEditOrder(animID:Int, newAnimID:Int); + + CAnimEditName(animID:Int, oldName:String, newName:String); + CAnimEditAnim(animID:Int, oldAnim:String, newAnim:String); + CAnimEditOffset(animID:Int, oldOffset:FlxPoint, newOffset:FlxPoint); + CAnimEditFPS(animID:Int, oldFPS:Float, newFPS:Float); + CAnimEditLooping(animID:Int, newLooping:Bool); + CAnimEditIndices(animID:Int, oldIndicies:Array, newIndicies:Array); + + CCharClearOffsets(oldOffsets:Map); } \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterPropertiesWindow.hx b/source/funkin/editors/character/CharacterPropertiesWindow.hx index 991730b2..e32c0d9c 100644 --- a/source/funkin/editors/character/CharacterPropertiesWindow.hx +++ b/source/funkin/editors/character/CharacterPropertiesWindow.hx @@ -113,36 +113,58 @@ class CharacterPropertiesWindow extends UISliceSprite { this.character = character; } - public function changePosition(newPosX:Null, newPosY:Null) { + public function changePosition(newPosX:Null, newPosY:Null, addToUndo:Bool = true) { if (newPosX != null && newPosY != null && newPosX == character.globalOffset.x && newPosY == character.globalOffset.y) return; - else { + else if (addToUndo) { if (newPosX != null && newPosX == character.globalOffset.x) return; if (newPosY != null && newPosY == character.globalOffset.y) return; } + var oldPosition:FlxPoint = character.globalOffset.clone(); if (newPosX != null) character.globalOffset.x = newPosX; if (newPosY != null) character.globalOffset.y = newPosY; CharacterEditor.instance.playAnimation(character.getAnimName()); + + positionXStepper.label.text = Std.string(character.globalOffset.x); + positionYStepper.label.text = Std.string(character.globalOffset.y); + + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditPosition(oldPosition, character.globalOffset.clone())); + else oldPosition.put(); } - public function changeScale(newScale:Float) { + public function changeScale(newScale:Float, addToUndo:Bool = true) { if (character.scale.x == newScale) return; + var oldScale:Float = character.scale.x; character.scale.set(newScale, newScale); character.updateHitbox(); CharacterEditor.instance.playAnimation(character.getAnimName()); + + scaleStepper.label.text = Std.string(newScale); + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditScale(oldScale, newScale)); } public function editCharacterInfoUI() { - CharacterEditor.instance.openSubState(new CharacterInfoScreen(character, (info:CharacterExtraInfo) -> { - character.icon = info.icon; - character.iconColor = info.iconColor; - character.holdTime = info.holdTime; - character.extra = info.customProperties.copy(); - })); + CharacterEditor.instance.openSubState(new CharacterInfoScreen(character, (info:CharacterExtraInfo) -> {editCharacterInfo(info);})); + } + + public function editCharacterInfo(info:CharacterExtraInfo, addToUndo:Bool = true) { + var oldInfo:CharacterExtraInfo = { + icon: character.icon, + iconColor: character.iconColor, + holdTime: character.holdTime, + customProperties: character.extra.copy() + }; + + character.icon = info.icon; + character.iconColor = info.iconColor; + character.holdTime = info.holdTime; + character.extra = info.customProperties.copy(); + + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditInfo(oldInfo, info)); } public function editCharacterSpriteUI() { @@ -191,29 +213,42 @@ class CharacterPropertiesWindow extends UISliceSprite { animsWindow.setAnimAutoComplete(CoolUtil.getAnimsListFromSprite(character)); } - public function changeFlipX(newFlipX:Bool) @:privateAccess { + public function changeFlipX(newFlipX:Bool, addToUndo:Bool = true) @:privateAccess { character.flipX = character.isPlayer ? !newFlipX : newFlipX; character.__baseFlipped = character.flipX; - + CharacterEditor.instance.playAnimation(character.getAnimName()); + + flipXCheckbox.checked = newFlipX; + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditFlipped(newFlipX)); } - public function changeCamPosition(newPosX:Null, newPosY:Null) { + public function changeCamPosition(newPosX:Null, newPosY:Null, addToUndo:Bool = true) { if (newPosX != null && newPosY != null && newPosX == character.cameraOffset.x && newPosY == character.cameraOffset.y) return; - else { + else if (addToUndo) { if (newPosX != null && newPosX == character.cameraOffset.x) return; if (newPosY != null && newPosY == character.cameraOffset.y) return; } + var oldCamPosition:FlxPoint = character.cameraOffset.clone(); if (newPosX != null) character.cameraOffset.x = newPosX; if (newPosY != null) character.cameraOffset.y = newPosY; + + cameraXStepper.label.text = Std.string(character.cameraOffset.x); + cameraYStepper.label.text = Std.string(character.cameraOffset.y); + + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditCamPosition(oldCamPosition, character.cameraOffset.clone())); + else oldCamPosition.put(); } - public function changeAntialiasing(newAntialiasing:Bool) { + public function changeAntialiasing(newAntialiasing:Bool, addToUndo:Bool = true) { if (character.antialiasing == newAntialiasing) return; character.antialiasing = newAntialiasing; animsWindow.displayWindowSprite.antialiasing = newAntialiasing; + + antialiasingCheckbox.checked = newAntialiasing; + if (addToUndo) CharacterEditor.undos.addToUndo(CCharEditAntialiasing(newAntialiasing)); } public function updateButtonsPos() {