character editor undos part 1

This commit is contained in:
lunarcleint
2025-03-20 03:08:54 -07:00
parent 3e51453541
commit 4b9d32fb50
2 changed files with 160 additions and 20 deletions
@@ -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<FlxBasic> = [];
public var stagePosition:String = null;
public static var undos:UndoList<CharacterEditorChange>;
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<CharacterEditorChange>();
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<Int>, newIndicies:Array<Int>);
CCharClearOffsets(oldOffsets:Map<String, FlxPoint>);
}
@@ -113,36 +113,58 @@ class CharacterPropertiesWindow extends UISliceSprite {
this.character = character;
}
public function changePosition(newPosX:Null<Float>, newPosY:Null<Float>) {
public function changePosition(newPosX:Null<Float>, newPosY:Null<Float>, 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<Float>, newPosY:Null<Float>) {
public function changeCamPosition(newPosX:Null<Float>, newPosY:Null<Float>, 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() {