From 48a1f5bf77ac0a3f41ea8e8202d37308d57aa841 Mon Sep 17 00:00:00 2001 From: lunarcleint <88809950+lunarcleint@users.noreply.github.com> Date: Sun, 9 Jul 2023 15:32:39 -0700 Subject: [PATCH] Character editor work in progress i think pls test --- .../editors/character/delete-button.png | Bin 0 -> 193 bytes .../images/editors/character/edit-button.png | Bin 0 -> 246 bytes .../images/editors/character/ghost-button.png | Bin 0 -> 287 bytes source/funkin/backend/utils/XMLUtil.hx | 4 +- .../funkin/editors/character/AddAnimScreen.hx | 113 -- .../editors/character/CharacterAnimScreen.hx | 57 + .../editors/character/CharacterAnimsWindow.hx | 159 +++ .../editors/character/CharacterConfig.hx | 169 --- .../editors/character/CharacterEditor.hx | 1066 ++++++----------- .../editors/character/CharacterInfoScreen.hx | 125 ++ .../editors/character/CharacterProperties.hx | 123 -- .../character/CharacterPropertiesWindow.hx | 5 + .../editors/character/CharacterSelection.hx | 70 +- .../editors/character/DeleteAnimScreen.hx | 73 -- .../funkin/editors/character/HelpControls.hx | 71 -- .../funkin/editors/character/NewCharacter.hx | 172 --- .../editors/character/SelectAnimUpdate.hx | 62 - .../editors/character/UpdateAnimScreen.hx | 125 -- source/funkin/editors/charter/Charter.hx | 1 - source/funkin/editors/ui/UIWindow.hx | 12 + source/funkin/game/Character.hx | 184 ++- 21 files changed, 843 insertions(+), 1748 deletions(-) create mode 100644 assets/images/editors/character/delete-button.png create mode 100644 assets/images/editors/character/edit-button.png create mode 100644 assets/images/editors/character/ghost-button.png delete mode 100644 source/funkin/editors/character/AddAnimScreen.hx create mode 100644 source/funkin/editors/character/CharacterAnimScreen.hx create mode 100644 source/funkin/editors/character/CharacterAnimsWindow.hx delete mode 100644 source/funkin/editors/character/CharacterConfig.hx create mode 100644 source/funkin/editors/character/CharacterInfoScreen.hx delete mode 100644 source/funkin/editors/character/CharacterProperties.hx create mode 100644 source/funkin/editors/character/CharacterPropertiesWindow.hx delete mode 100644 source/funkin/editors/character/DeleteAnimScreen.hx delete mode 100644 source/funkin/editors/character/HelpControls.hx delete mode 100644 source/funkin/editors/character/NewCharacter.hx delete mode 100644 source/funkin/editors/character/SelectAnimUpdate.hx delete mode 100644 source/funkin/editors/character/UpdateAnimScreen.hx create mode 100644 source/funkin/editors/ui/UIWindow.hx diff --git a/assets/images/editors/character/delete-button.png b/assets/images/editors/character/delete-button.png new file mode 100644 index 0000000000000000000000000000000000000000..0bf75235e56dff79b9bae603b2a4fcb4bd9113d2 GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^{6H+g!3HExhN-duDaPU;cPEB*=VV?2In|ynjv*Qo z-%j4h*&Y%O$A!&1tKT8JcXr|9YyvAeF*WK$$UAeY(+R|da$%lmwWpB7YedcEeWyT9(@8=|ciPLJZbc_6cEA_ATe8&c_yIU{t r*Nf|&|8g$fQ<5!PKEAl`V}jY6=}Ic4FRHwN&S3C#^>bP0l+XkK!CXd? literal 0 HcmV?d00001 diff --git a/assets/images/editors/character/edit-button.png b/assets/images/editors/character/edit-button.png new file mode 100644 index 0000000000000000000000000000000000000000..849dae298935723da0f4c4898b691ae271c3721f GIT binary patch literal 246 zcmVPx#ut`KgR5*>Dk}(d#Fc1VcQ1}HX4+=%{FKR0O;uN7g3AwS9hKefU1NH-s>MSR+ ziJdJ^JL|iZfkI7MbE!88JLmXN$fPxkszYKpR#Aw+v$gdh+n1|Ca%;yS*)F8*oMvl} z!@FxAh0iklE(i!cgn2z#|gaKc0x_g_;>9{Kb53`>06uTCjbBd07*qoM6N<$f{kcp-2eap literal 0 HcmV?d00001 diff --git a/assets/images/editors/character/ghost-button.png b/assets/images/editors/character/ghost-button.png new file mode 100644 index 0000000000000000000000000000000000000000..65e136bcc57de276d84a9da945b335cf6df9d177 GIT binary patch literal 287 zcmV+)0pR|LP)Px#*-1n}R7i=fm0=EpFbIXgc%oiq*W6X4M`Ay=Hk3Y;>Rw_@jUT)MR;ECzs_1#G zby$~D_z4fetVCo=vdV86*IC`+hw`lkVCGAnnMqaq-Sc}}pmO>kO{k9|q9_?kZu!jz za+~TwywLEWWY!eocXG;hp;gQpi7J5N4iy)XmEj%qm`5; + public var offset:FlxPoint; + public var flxanim:FlxAnimation; + + public var saveButton:UIButton; + public var closeButton:UIButton; + + private var onSave:(animName:Null, offset:FlxPoint, flxanim:FlxAnimation) -> Void = null; + + public function new(animName:Null, ?offset:FlxPoint, ?flxanim:FlxAnimation, ?onSave:(animName:Null, offset:FlxPoint, flxanim:FlxAnimation) -> Void) { + this.animName = animName; + this.offset = offset; + this.flxanim = flxanim; + this.onSave = onSave; + super(); + } + + public override function create() { + var creatingAnim:Bool = animName == null; + + if (creatingAnim) { + animName = ""; + offset = FlxPoint.get(0,0); + flxanim = new FlxAnimation(CharacterEditor.instance.character.animation, animName, []); + } + + winTitle = creatingAnim ? 'Creating Animation' : 'Animation $animName properties'; + winWidth = 690; winHeight = 334; + + super.create(); + + function addLabelOn(ui:UISprite, text:String) + add(new UIText(ui.x, ui.y - 24, 0, text)); + + var title:UIText; + add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, creatingAnim ? "Create New Animation" : "Edit Animation Properties", 28)); + + saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, "Save & Close", function() { + // saveStrumline(); + close(); + }, 125); + add(saveButton); + + closeButton = new UIButton(saveButton.x - 20, saveButton.y, creatingAnim ? "Cancel" : "Close", function() { + if (creatingAnim) onSave(null, null, null); + close(); + }, 125); + add(closeButton); + closeButton.x -= closeButton.bWidth; + } +} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterAnimsWindow.hx b/source/funkin/editors/character/CharacterAnimsWindow.hx new file mode 100644 index 00000000..824c0e2a --- /dev/null +++ b/source/funkin/editors/character/CharacterAnimsWindow.hx @@ -0,0 +1,159 @@ +package funkin.editors.character; + +import flixel.group.FlxGroup; +import flixel.util.FlxColor; +import flixel.math.FlxPoint; +import funkin.game.Character; + +class CharacterAnimsWindow extends UIWindow { + public var character:Character; + + public var buttons:FlxGroup = new FlxGroup(); + public var animButtons:Map = []; + + public var addButton:UIButton; + public var addIcon:FlxSprite; + + public function new(character:Character) { + super(800-23,140+23+46,450+23,511-23, "Character Animations"); + this.character = character; + + createButtons(); + buttons.cameras = [CharacterEditor.instance.animsCamera]; + members.push(buttons); + } + + public function createButtons() { + for (i=>anim in character.animation.getNameList()) + createNewButton(anim, character.getAnimOffset(anim), false, -1, false); + + addButton = new UIButton(25, 16+((32+16)*(character.animation.getNameList().length)), "", null, 429); + addButton.cameras = [CharacterEditor.instance.animsCamera]; + addButton.color = FlxColor.GREEN; + + addIcon = new FlxSprite(addButton.x + (440/2) - 8, addButton.y + (32/2) - 8).loadGraphic(Paths.image('editors/charter/add-button')); + addIcon.cameras = [CharacterEditor.instance.animsCamera]; + addIcon.antialiasing = false; + addButton.members.push(addIcon); + + members.push(addButton); + updateButtonsPos(); + } + + public function createNewButton(anim:String, offset:FlxPoint, ghost:Bool = false, id:Int = -1, updatePos:Bool = true) { + var button = new CharacterAnimButtons(0,0, anim, offset); + if (id == -1) buttons.add(button); + else buttons.members.insert(id, button); + animButtons.set(anim,button); + + if (updatePos) + updateButtonsPos(); + } + + public function removeButton(anim:String) { + var button:CharacterAnimButtons = animButtons.get(anim); + buttons.members.remove(button); + button.destroy(); + + updateButtonsPos(); + } + + public function updateButtonsPos() { + for (i => button in buttons.members) + cast(button, CharacterAnimButtons).updatePos(23,16+((32+16)*i)); + + addButton.y = 16+((32+16)*(character.animation.getNameList().length)); + addIcon.x = addButton.x + (440/2) - 8; addIcon.y = addButton.y + (32/2) - 8; + } + + public inline function changeCurAnim(anim:String) { + for (_anim => button in animButtons) + button.alpha = _anim == anim ? 1 : 0.25; + } + + var scrollY:Float = 0; + + public override function update(elapsed:Float) { + super.update(elapsed); + + __rect.x = x; __rect.y = y; + __rect.width = bWidth; __rect.height = bHeight; + if(UIState.state.isOverlapping(this, __rect)) { + var nextscrollY = scrollY - FlxG.mouse.wheel * 12; + if (nextscrollY >= 0 && nextscrollY + CharacterEditor.instance.animsCamera.height <= (addButton.y +32 +26)) + scrollY = nextscrollY; + hovered = true; + } else + hovered = false; + + CharacterEditor.instance.animsCamera.scroll.y = FlxMath.lerp(CharacterEditor.instance.animsCamera.scroll.y, scrollY, 1/3); + + //addButtonIndicator.y = CharacterEditor.instance.animsCamera.height - addButtonIndicator.width + CharacterEditor.instance.animsCamera.scroll.y; + } +} + +class CharacterAnimButtons extends UIButton { + public var anim:String = ""; + + public var editButton:UIButton; + public var editIcon:FlxSprite; + + public var ghostButton:UIButton; + public var ghostIcon:FlxSprite; + + public var deleteButton:UIButton; + public var deleteIcon:FlxSprite; + + public function new(x:Float,y:Float,anim:String, offset:FlxPoint) { + this.anim = anim; + super(x,y, '$anim (${offset.x}, ${offset.y})', function () { + CharacterEditor.instance.playAnimation(this.anim); + }, 282); + + ghostButton = new UIButton(x+282+17, y, "", null, 32); + members.push(ghostButton); + + ghostIcon = new FlxSprite(ghostButton.x + 8, ghostButton.y + 8).loadGraphic(Paths.image('editors/character/ghost-button'), true, 16, 16); + ghostIcon.animation.add("alive", [0]); + ghostIcon.animation.add("dead", [1]); + ghostIcon.animation.play("dead"); ghostIcon.alpha = 0.5; + ghostIcon.antialiasing = false; + ghostIcon.updateHitbox(); + members.push(ghostIcon); + + editButton = new UIButton(ghostButton.x+32+17, y, "", null, 32); + members.push(editButton); + + editIcon = new FlxSprite(editButton.x + 8, editButton.y + 8).loadGraphic(Paths.image('editors/character/edit-button')); + editIcon.antialiasing = false; + members.push(editIcon); + + deleteButton = new UIButton(editButton.x+32+17, y, "", function () { + CharacterEditor.instance.deleteAnim(this.anim); + }, 32); + deleteButton.color = FlxColor.RED; + members.push(deleteButton); + + deleteIcon = new FlxSprite(deleteButton.x + (15/2), deleteButton.y + 8).loadGraphic(Paths.image('editors/character/delete-button')); + deleteIcon.antialiasing = false; + members.push(deleteIcon); + } + + public function updateInfo(anim:String, offset:FlxPoint, ghost:Bool) { + this.anim = anim; + + field.text = '$anim (${offset.x}, ${offset.y})'; + ghostIcon.animation.play(ghost ? "alive" : "dead"); + ghostIcon.alpha = ghost ? 1 : 0.5; + } + + public inline function updatePos(x:Float, y:Float) { + // buttons + this.x = x; this.y = ghostButton.y = editButton.y = deleteButton.y = y; + deleteButton.x = (editButton.x = (ghostButton.x = (x+282+17))+32+17)+32+17; + // icons + ghostIcon.x = ghostButton.x + 8; ghostIcon.y = ghostButton.y + 8; + editIcon.x = editButton.x + 8; editIcon.y = editButton.y + 8; + deleteIcon.x = deleteButton.x + (15/2); deleteIcon.y = deleteButton.y + 8; + } +} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterConfig.hx b/source/funkin/editors/character/CharacterConfig.hx deleted file mode 100644 index 0c93959e..00000000 --- a/source/funkin/editors/character/CharacterConfig.hx +++ /dev/null @@ -1,169 +0,0 @@ -package funkin.editors.character; - -import haxe.xml.Printer; -import sys.io.File; -import sys.FileSystem; -import flixel.group.FlxGroup; -import haxe.xml.Access; -import haxe.Exception; -import flixel.util.FlxColor; -import funkin.game.Character.CharacterData; - -class CharacterConfig extends FlxTypedGroup -{ - public static var DEFAULT_CHARACTER = "bf"; - - public static function loadCharacterData(character:String):CharacterData - { - var xml:Access; - - var charData:CharacterData = { - offsetY: 0, - offsetX: 0, - camOffsetY: 0, - camOffsetX: 0, - quickDancer: false, - icon: character, - iconColor: FlxColor.fromRGB(94, 94, 94), - scale: 1, - isPlayer: false, - isGF: false, - holdTime: 4, - flipX: false, - antialiasing: true, - sprite: "bf", - name: character, - animations: [] - } - - var xmlPath = Paths.xml('characters/$character'); - - var plainXML = Assets.getText(xmlPath); - try - { - var charXML = Xml.parse(plainXML).firstElement(); - if (charXML == null) - throw new Exception("Missing \"character\" node in XML."); - xml = new Access(charXML); - } - - charData = xmlToData(charData, xml); - - return charData; - } - - public static function xmlToData(charData:CharacterData, xml:Access):CharacterData - { - if (xml.has.isPlayer) - charData.isPlayer = (xml.att.isPlayer == "true"); - if (xml.has.isGF) - charData.isGF = (xml.att.isGF == "true"); - if (xml.has.x) - charData.offsetX = Std.parseFloat(xml.att.x); - if (xml.has.y) - charData.offsetY = Std.parseFloat(xml.att.y); - if (xml.has.gameOverChar) - charData.gameOverChar = xml.att.gameOverChar; - if (xml.has.camx) - charData.camOffsetX = Std.parseFloat(xml.att.camx); - if (xml.has.camy) - charData.camOffsetY = Std.parseFloat(xml.att.camy); - if (xml.has.holdTime) - charData.holdTime = CoolUtil.getDefault(Std.parseFloat(xml.att.holdTime), 4); - if (xml.has.flipX) - charData.flipX = (xml.att.flipX == "true"); - if (xml.has.icon) - charData.icon = xml.att.icon; - if (xml.has.iconColor) - charData.iconColor = stringToRGB(xml.att.iconColor); - if (xml.has.scale) - { - var scale = Std.parseFloat(xml.att.scale).getDefault(1); - charData.scale = scale; - } - if (xml.has.antialiasing) - charData.antialiasing = (xml.att.antialiasing == "true"); - if (xml.has.sprite) - charData.sprite = xml.att.sprite; - else - charData.sprite = charData.name; - - for (anim in xml.nodes.anim) - { - var animType:XMLAnimType = NONE; - var animloop = false; - if (anim.has.loop) - animloop = (anim.att.loop == "true"); - charData.animations.push(XMLUtil.extractAnimFromXML(anim, animType, animloop)); - } - return charData; - } - - public static function dataToXml(charData:CharacterData):Access - { - var root = Xml.createElement("character"); - root.set("x", Std.string(Math.floor(charData.offsetX))); - root.set("y", Std.string(Math.floor(charData.offsetY))); - root.set("gameOverChar", Std.string(charData.gameOverChar)); - root.set("camx", Std.string(Math.floor(charData.camOffsetX))); - root.set("camy", Std.string(Math.floor(charData.camOffsetY))); - root.set("holdTime", Std.string(charData.holdTime)); - root.set("flipX", Std.string(charData.flipX)); - root.set("icon", charData.icon); - root.set("iconColor", Std.string(charData.iconColor)); - root.set("scale", Std.string(charData.scale)); - root.set("antialiasing", Std.string(charData.antialiasing)); - root.set("sprite", Std.string(charData.sprite)); - root.set("isPlayer", Std.string(charData.isPlayer)); - root.set("isGF", Std.string(charData.isGF)); - - for (anim in charData.animations) - { - var child:Xml = Xml.createElement('anim'); - child.set("name", Std.string(anim.name)); - child.set("anim", Std.string(anim.anim)); - child.set("loop", Std.string(anim.loop)); - child.set("fps", Std.string(anim.fps)); - child.set("x", Std.string(anim.x)); - child.set("y", Std.string(anim.y)); - child.set("indices", Std.string(anim.indices)); - root.addChild(child); - } - var xml = new Access(Xml.parse('${root.toString()}')); - - return xml; - } - - public static function stringToRGB(rgbColor:String):FlxColor - { - var colorSplit = rgbColor.split(","); - if (colorSplit[0] != null && colorSplit[1] != null && colorSplit[2] != null) - return FlxColor.fromRGB(Std.parseInt(colorSplit[0]), Std.parseInt(colorSplit[1]), Std.parseInt(colorSplit[2])); - else - return FlxColor.fromRGB(90,90,90); - } - - public static function rgbToString(color:FlxColor):String - return '${color.red}, ${color.green}, ${color.blue}'; - /** - * @return Saves the character to the specific song folder path. - */ - public static function save(character:String, xml:String):String - { - var charPath = ''; - var data = '$xml'; - #if sys - var charPath = '${character}'; - - File.saveContent(charPath, data); - - // idk how null reacts to it so better be sure - #end - return xml; - } - - public static function generateCharXML(xml:Xml):String - { - return '\n${Printer.print(xml, true)}'; - } -} diff --git a/source/funkin/editors/character/CharacterEditor.hx b/source/funkin/editors/character/CharacterEditor.hx index 2fbaab43..530e5c15 100644 --- a/source/funkin/editors/character/CharacterEditor.hx +++ b/source/funkin/editors/character/CharacterEditor.hx @@ -1,318 +1,57 @@ package funkin.editors.character; -import funkin.backend.system.framerate.Framerate; -import funkin.backend.FlxAnimate; -import flixel.group.FlxSpriteGroup; -import flixel.graphics.FlxGraphic; import funkin.backend.utils.XMLUtil.AnimData; import flixel.math.FlxPoint; +import flixel.animation.FlxAnimation; import funkin.editors.ui.UIContextMenu.UIContextMenuOption; +import flixel.input.keyboard.FlxKey; import funkin.game.Character; -import flixel.system.debug.interaction.tools.Pointer.GraphicCursorCross; -import flixel.util.FlxColor; -class CharacterEditor extends UIState -{ - public var blockedInputs = []; - public var blockedStuff = []; +class CharacterEditor extends UIState { + var __character:String; + public var character:Character; - public var curChar:String; + public static var instance(get, null):CharacterEditor; - public var char:Character; - public var ghostChar:Character; - public var charLayer:FlxTypedSpriteGroup = new FlxTypedSpriteGroup(0, 0); - public var animAtlas:FlxAnimate; - - public var cameraPointer:FlxSprite; - - public var camFollow:FlxObject; - public var editorCamera:FlxCamera; - public var uiCamera:FlxCamera; + private static inline function get_instance() + return FlxG.state is CharacterEditor ? cast FlxG.state : null; + /** + * CHARACTER UI STUFF + */ public var topMenu:Array; public var topMenuSpr:UITopMenu; public var uiGroup:FlxTypedGroup = new FlxTypedGroup(); - public var stage:FlxTypedSpriteGroup = new FlxTypedSpriteGroup(0, 0); - public var curAnim:Int = 0; + // WINDOWS + public var characterPropertiresWindow:CharacterPropertiesWindow; + public var characterAnimsWindow:CharacterAnimsWindow; + + // camera for the character itself so that it can be unzoomed/zoomed in again + public var charCamera:FlxCamera; + // camera for the animations list + public var animsCamera:FlxCamera; + // camera for the ui + public var uiCamera:FlxCamera; - public var playingAnim:UIText; - public var currentChar:UIText; - public var movingCamText:UIText; + public var undoList:Array = []; + public var redoList:Array = []; - //Healthbar - public var healthBarBG:FlxSprite; - public var icon:FlxSprite; - - public var camZoom(default, set):Float = 1; - - public function new(charName:String) - { + public function new(character:String) { super(); - this.curChar = charName; + __character = character; } - override function create() - { + public override function create() { super.create(); - - // Camera setup - camFollow = new FlxObject(0, 0, 2, 2); - camFollow.screenCenter(); - add(camFollow); - editorCamera = FlxG.camera; - uiCamera = new FlxCamera(); - uiCamera.bgColor = 0; - FlxG.cameras.add(uiCamera, false); - FlxG.camera.follow(camFollow); - - // Add Stage and Centered Cross - add(stage); - add(ofs_cross); - loadCross(); - - // Indicators - var pointer:FlxGraphic = FlxGraphic.fromClass(GraphicCursorCross); - cameraPointer = new FlxSprite().loadGraphic(pointer); - cameraPointer.setGraphicSize(40, 40); - cameraPointer.updateHitbox(); - cameraPointer.color = FlxColor.WHITE; - - // Character - add(charLayer); - add(cameraPointer); - loadChar(!CharacterConfig.loadCharacterData(curChar).isPlayer); - - // Reload Stage - reloadStage(); - - // UI - loadUI(); - - playingAnim = new UIText(846.62, 64.23, 0, 'Animation: ${char.characterData.animations[curAnim].name}', 24); - playingAnim.alignment = LEFT; - playingAnim.cameras = [uiCamera]; - - currentChar = new UIText(846.62, 30.23, 0, 'Character: ${char.curCharacter}', 24); - currentChar.alignment = LEFT; - currentChar.cameras = [uiCamera]; - - movingCamText = new UIText((FlxG.width / 4) - 310, (FlxG.height / 2), -1, - "Press the left mouse button anywhere on this stage to set the character's camera position.", 24); - movingCamText.cameras = [uiCamera]; - movingCamText.visible = false; - - //Health Bar - healthBarBG = new FlxSprite(747, 102).loadGraphic(Paths.image('game/healthBar')); - healthBarBG.setGraphicSize(399,13); - healthBarBG.scrollFactor.set(); - healthBarBG.cameras = [uiCamera]; - - - var charData:CharacterData; - charData = char.characterData; - - var path = Paths.image('icons/${charData.icon}'); - if (!Assets.exists(path)) path = Paths.image('icons/face'); - - //Show both Losing Normal Icons - icon = new FlxSprite(754, 86).loadGraphic(path); - icon.scale.set(0.5,0.5); - icon.scrollFactor.set(); - icon.cameras = [uiCamera]; - - - - - - healthBarBG.color = charData.iconColor; - - // Top Menu - add(currentChar); - add(playingAnim); - add(icon); - add(healthBarBG); - add(movingCamText); - add(topMenuSpr); - add(uiGroup); - } - - override function update(elapsed:Float) - { - // Control - controlCamera(elapsed); - controlCharacter(); - changeAnim(); - moveCharPos(); - detectCamClick(); - mouseControl(); - - ghostChar.setPosition(char.x, char.y); - super.update(elapsed); - } - - public function reloadIcon() - { - var charData:CharacterData; - charData = char.characterData; - - var path = Paths.image('icons/${charData.icon}'); - if (!Assets.exists(path)) path = Paths.image('icons/face'); - icon.loadGraphic(path); - } - - public function searchAnim(name:String, ?array:Array):AnimData - { - if (array == null) - array = char.characterData.animations; - for (i in 0...array.length - 1) - if (array[i].name == name) - return array[i]; - return array[0]; - } - - public function getAnimIndex(name:String, ?array:Array):Int - { - if (array == null) - array = char.characterData.animations; - for (i in 0...array.length - 1) - if (array[i].name == name) - return i; - return 0; - } - - function resortAnims(animations:Array) - { - // TODO: add support for danceLeft danceRight - if (getAnimIndex("idle") != 0) - { - var oldanims = animations.copy(); - animations.clear(); - animations.push(searchAnim("idle", oldanims)); - for (anim in oldanims) - if (anim != searchAnim("idle", oldanims)) - animations.push(anim); - } - } - - public function reloadChar(isOpponent:Bool, charData:CharacterData) - { - var i:Int = charLayer.members.length - 1; - while (i >= 0) - { - var memb:Character = charLayer.members[i]; - if (memb != null) - { - memb.kill(); - charLayer.remove(memb); - memb.destroy(); - } - --i; - } - charLayer.clear(); - ghostChar = new Character(0, 0, curChar, !isOpponent); - ghostChar.characterData = charData; - ghostChar.debugMode = true; - ghostChar.alpha = 0.6; - - char = new Character(0, 0, curChar, !isOpponent); - char.characterData = charData; - - var charData = char.characterData; - resortAnims(charData.animations); - var availableAnim = searchAnim("idle"); - curAnim = getAnimIndex("idle"); - ghostChar.characterData.animations = char.characterData.animations; - char.playAnim(availableAnim.name, true); - ghostChar.playAnim(availableAnim.name, true); - - char.debugMode = true; - char.setPosition(char.characterData.offsetX + OFFSET_X + 100, char.characterData.offsetY); - - charLayer.add(ghostChar); - charLayer.add(char); - - animAtlas = (char.animateAtlas != null ? char.animateAtlas : null); - - currentChar.text = 'Character: ${char.curCharacter}'; - healthBarBG.color = charData.iconColor; - reloadIcon(); - - trace(charData.iconColor); - - updateCharPos(charData.offsetX, charData.offsetY); - updatePointerPos(); - mixDataToChar(char, charData); - mixDataToChar(ghostChar, charData); - } - - function mixDataToChar(target:Character, data:CharacterData) - { - var sprite = (target.xml.has.sprite ? target.xml.att.sprite : target.curCharacter); - if (data.sprite != sprite) - target.loadSprite(Paths.image('characters/${data.sprite}')); - target.icon = data.icon; - target.flipX = !!data.flipX; - target.antialiasing = data.antialiasing; - target.isPlayer = data.isPlayer; - target.playerOffsets = data.isPlayer; - target.isGF = data.isGF; - target.scale.set(data.scale, data.scale); - target.holdTime = data.holdTime; - target.fixChar(true); - target.updateHitbox(); - } - - function loadChar(isOpponent:Bool) - { - var i:Int = charLayer.members.length - 1; - while (i >= 0) - { - var memb:Character = charLayer.members[i]; - if (memb != null) - { - memb.kill(); - charLayer.remove(memb); - memb.destroy(); - } - --i; - } - charLayer.clear(); - ghostChar = new Character(0, 0, curChar, !isOpponent); - ghostChar.debugMode = true; - ghostChar.alpha = 0.6; - - char = new Character(0, 0, curChar, !isOpponent); - char.debugMode = true; - char.setPosition(char.characterData.offsetX + OFFSET_X + 100, char.characterData.offsetY); - - charLayer.add(ghostChar); - charLayer.add(char); - - var charData = char.characterData; - animAtlas = (char.animateAtlas != null ? char.animateAtlas : null); - updateCharPos(charData.offsetX, charData.offsetY); - resortAnims(char.characterData.animations); - curAnim = getAnimIndex("idle"); - char.playAnim(charData.animations[getAnimIndex("idle")].name, true); - ghostChar.characterData.animations = char.characterData.animations; - - char.updateHitbox(); - ghostChar.updateHitbox(); - updatePointerPos(); - } - - function loadUI() - { topMenu = [ { label: "File", childs: [ { - label: "New", - onSelect: new_character + label: "New" }, null, { @@ -328,7 +67,6 @@ class CharacterEditor extends UIState null, { label: "Exit", - keybind: [ESCAPE], onSelect: _file_exit } ] @@ -337,438 +75,416 @@ class CharacterEditor extends UIState label: "Edit", childs: [ { - label: "Camera Position", - onSelect: moveCameraPos + label: "Undo", + keybind: [CONTROL, Z], + onSelect: _edit_undo }, { - label: "Properties", - onSelect: edit_properties_window, - } + label: "Redo", + keybinds: [[CONTROL, Y], [CONTROL, SHIFT, Z]], + onSelect: _edit_redo + }, + null ] }, { - label: "Animation", + label: "Character", childs: [ { label: "New Animation", keybind: [CONTROL, N], - onSelect: add_anim_window, + onSelect: _char_add_anim, + }, + { + label: "Edit Animation", + onSelect: _char_update_anim, + }, + { + label: "Delete Animation", + keybind: [DELETE], + onSelect: _char_remove_anim, }, null, { - label: "Update Animation", - keybind: [CONTROL, U], - onSelect: update_anim_window, + label: "Edit Info", + onSelect: _char_edit_info, + } + ] + }, + { + label: "Playback", + childs: [ + { + label: "Play Animation", + keybind: [SPACE], + onSelect: _playback_play_anim, + }, + { + label: "Stop Animation", + onSelect: _playback_stop_anim }, null, { - label: "Remove Animation", - keybind: [CONTROL, D], - onSelect: remove_anim_window, + label: "Go to frame", }, ] }, { - label: "Help", + label: "Offsets", childs: [ { - label: "Controls", - onSelect: help_controls_window + label: "Move Left", + keybind: [LEFT], + onSelect: _offsets_left, + }, + { + label: "Move Up", + keybind: [UP], + onSelect: _offsets_up, + }, + { + label: "Move Down", + keybind: [DOWN], + onSelect: _offsets_down, + }, + { + label: "Move Right", + keybind: [RIGHT], + onSelect: _offsets_right, + }, + null, + { + label: "Move Extra Left", + keybind: [SHIFT, LEFT], + onSelect: _offsets_extra_left, + }, + { + label: "Move Extra Up", + keybind: [SHIFT, UP], + onSelect: _offsets_extra_up, + }, + { + label: "Move Extra Down", + keybind: [SHIFT, DOWN], + onSelect: _offsets_extra_down, + }, + { + label: "Move Extra Right", + keybind: [SHIFT, RIGHT], + onSelect: _offsets_extra_right, + }, + null, + { + label: "Clear Offsets", + keybind: [CONTROL, R], + onSelect: _offsets_clear, } ] - } + }, + { + label: "View", + childs: [ + { + label: "Zoom in", + keybind: [CONTROL, NUMPADPLUS], + onSelect: _view_zoomin + }, + { + label: "Zoom out", + keybind: [CONTROL, NUMPADMINUS], + onSelect: _view_zoomout + }, + { + label: "Reset zoom", + keybind: [CONTROL, NUMPADZERO], + onSelect: _view_zoomreset + }, + + ] + }, ]; + charCamera = FlxG.camera; + + uiCamera = new FlxCamera(); + uiCamera.bgColor = 0; + animsCamera = new FlxCamera(800-23,140+23+30+46,450+23,511-24-30); + animsCamera.bgColor = 0; + + FlxG.cameras.add(uiCamera); + FlxG.cameras.add(animsCamera); + + character = new Character(0,0,__character); + character.debugMode = true; + character.cameras = [charCamera]; + add(character); + topMenuSpr = new UITopMenu(topMenu); topMenuSpr.cameras = uiGroup.cameras = [uiCamera]; - Framerate.fpsCounter.alpha = 0.4; - Framerate.memoryCounter.alpha = 0.4; - Framerate.codenameBuildField.alpha = 0.4; + characterPropertiresWindow = new CharacterPropertiesWindow(800-23,23 + 23, 450 + 23, 140, "Character Properties"); + characterAnimsWindow = new CharacterAnimsWindow(character); + uiGroup.add(characterPropertiresWindow); + uiGroup.add(characterAnimsWindow); + + playAnimation(character.animation.getNameList()[0]); + + add(topMenuSpr); + add(uiGroup); } - var ofs_cross:FlxSpriteGroup = new FlxSpriteGroup(0, 0); + private var movingCam:Bool = false; + private var camDrag:FlxPoint = FlxPoint.get(0,0); - public function loadCross() - { - var i:Int = ofs_cross.members.length - 1; - while (i >= 0) - { - var memb:FlxSprite = ofs_cross.members[i]; - if (memb != null) - { - memb.kill(); - ofs_cross.remove(memb); - memb.destroy(); - } - --i; - } - ofs_cross.clear(); + private var nextScroll:FlxPoint = FlxPoint.get(0,0); + public override function update(elapsed:Float) { + super.update(elapsed); - var x = -800; - - var horizontal_cross = new FlxSprite(x, -70).makeSolid(2000, 5); - var vertical_cross = new FlxSprite(x, 0).makeSolid(5, 2000); - horizontal_cross.y += (vertical_cross.height / 2); - vertical_cross.x += (horizontal_cross.width / 2); - ofs_cross.add(horizontal_cross); - ofs_cross.add(vertical_cross); - horizontal_cross.scrollFactor.set(1, 1); - vertical_cross.scrollFactor.set(1, 1); - // ofs_cross.screenCenter(); - ofs_cross.y = -200; - ofs_cross.scrollFactor.set(1, 1); - ofs_cross.alpha = 0.6; - } - - var OFFSET_X:Float = 150; - - public function reloadStage() - { - var i:Int = stage.members.length - 1; - while (i >= 0) - { - var memb:FunkinSprite = stage.members[i]; - if (memb != null) - { - memb.kill(); - stage.remove(memb); - memb.destroy(); - } - --i; - } - stage.clear(); - var playerXDifference = 0; - if (char.isPlayer) - playerXDifference = 670; - - var bg = new FunkinSprite(-600 + OFFSET_X - playerXDifference, -200, Paths.image("stages/default/stageback")); - stage.add(bg); - bg.scrollFactor.set(0.9, 0.9); - - var stageFront = new FunkinSprite(-600 + OFFSET_X - playerXDifference, 600, Paths.image("stages/default/stagefront")); - stageFront.setGraphicSize(Std.int(stageFront.width * 1.1)); - stageFront.updateHitbox(); - stage.add(stageFront); - stageFront.scrollFactor.set(0.9, 0.9); - - stage.setPosition(0, 0); - } - - function hasAtlas():Bool - return (animAtlas != null); - - var movingCam:Bool = false; - var movingCamDefaultPos:FlxPoint = null; - var movingCamDefaultPosCam:FlxPoint = null; - - public function moveCameraPos(_) - { - movingCam = true; - movingCamText.visible = true; - updatePointerPos(); - } - - public function detectCamClick() - { - var charData = char.characterData; - - if (movingCam) - { - if (FlxG.mouse.justPressed) - { - var midx = char.getMidpoint().x; - var midy = char.getMidpoint().y; - - var pos = FlxG.mouse.getWorldPosition(editorCamera); - var placedPos:FlxPoint = new FlxPoint((!char.isPlayer ? pos.x + -(midx) : pos.x + -(midx)), -(pos.y - midy)); - - movingCam = false; - movingCamText.visible = false; - charData.camOffsetX = placedPos.x; - charData.camOffsetY = placedPos.y; - updatePointerPos(); - } - } - } - - function updatePointerPos() - { - var x:Float = char.getMidpoint().x; - var y:Float = char.getMidpoint().y; - /*if (!char.isPlayer) - { */ - x += char.characterData.camOffsetX; - /*} - else - { - x -= char.characterData.camOffsetX; - }*/ - y -= char.characterData.camOffsetY; - - x -= cameraPointer.width / 2; - y -= cameraPointer.height / 2; - cameraPointer.setPosition(x, y); - } - - var movingOffset:Bool = false; - var dragX:Float = -1; - var dragY:Float = -1; - - public function moveCharPos() - { - var charSprite = (hasAtlas() ? char.animateAtlas : char); - var charData = char.characterData; - if (FlxG.mouse.justPressed && FlxG.mouse.overlaps(charSprite)) - { - var pos = FlxG.mouse.getWorldPosition(FlxG.camera); - dragX = charData.offsetX - pos.x; - dragY = charData.offsetY - pos.y; - movingOffset = true; - } - else if (!FlxG.mouse.overlaps(charSprite)) - movingOffset = false; - - if (movingOffset && FlxG.mouse.pressed) - { - var pos = FlxG.mouse.getWorldPosition(editorCamera); - updateCharPos((pos.x + dragX), (pos.y + dragY)); - } - } - - public function updateCharPos(x:Float, y:Float) - { - char.characterData.offsetX = x; - char.characterData.offsetY = y; - char.x = x; - char.y = y; - updatePointerPos(); - } - - public function reloadGhost() - { - ghostChar.loadSprite(char.characterData.sprite); - for (i in 0...char.characterData.animations.length - 1) - { - XMLUtil.addAnimToSprite(ghostChar, char.characterData.animations[i]); + if (true) { + if(FlxG.keys.justPressed.ANY) + UIUtil.processShortcuts(topMenu); } - char.alpha = 0.85; - ghostChar.visible = true; - /*if (ghostDropDown.selectedLabel == '') - { - ghostChar.visible = false; - char.alpha = 1; - }*/ - ghostChar.color = 0xFF666688; - ghostChar.antialiasing = char.antialiasing; - ghostChar.playAnim(char.characterData.animations[curAnim].name, true); - } - - public function mouseControl() - { - if (FlxG.mouse.justReleasedRight) { - closeCurrentContextMenu(); - openContextMenu(topMenu[1].childs); - } - - if (FlxG.mouse.wheel != 0) { - camZoom += (FlxG.mouse.wheel / 10); - } + if (FlxG.mouse.wheel != 0 && !(characterPropertiresWindow.hovered || characterAnimsWindow.hovered)) { + zoom += 0.25 * FlxG.mouse.wheel; + __camZoom = Math.pow(2, zoom); } - function controlCharacter() - { - var controlArray:Array = [ - FlxG.keys.justPressed.LEFT, - FlxG.keys.justPressed.RIGHT, - FlxG.keys.justPressed.UP, - FlxG.keys.justPressed.DOWN - ]; - var holdShift = FlxG.keys.pressed.SHIFT; - var holdAlt = FlxG.keys.pressed.ALT; - - for (i in 0...controlArray.length) - { - if (controlArray[i]) - { - var multiplier:Float = 1; - if (holdShift) - multiplier = 10; - if (holdAlt) - multiplier = 0.1; - - var negaMult:Int = 1; - if (i % 2 == 1) - negaMult = -1; - - // For Player Offsets - if (i == 1 && char.playerOffsets) - negaMult = -1; - if (i == 0 && char.playerOffsets) - negaMult = 1; - - if (i > 1) - { - char.characterData.animations[curAnim].y += negaMult * multiplier; - ghostChar.characterData.animations[curAnim].y += negaMult * multiplier; - } - else - { - char.characterData.animations[curAnim].x += negaMult * multiplier; - ghostChar.characterData.animations[curAnim].x += negaMult * multiplier; - } - - char.animOffsets.set(char.characterData.animations[curAnim].name, - new FlxPoint(char.characterData.animations[curAnim].x, char.characterData.animations[curAnim].y)); - ghostChar.animOffsets.set(char.characterData.animations[curAnim].name, - new FlxPoint(char.characterData.animations[curAnim].x, char.characterData.animations[curAnim].y)); - - char.addOffset(char.characterData.animations[curAnim].anim, char.characterData.animations[curAnim].x, - char.characterData.animations[curAnim].y); - ghostChar.addOffset(char.characterData.animations[curAnim].anim, char.characterData.animations[curAnim].x, - char.characterData.animations[curAnim].y); - /*ghostChar.animOffsets.set(char.animation.curAnim.name, - new FlxPoint(char.characterData.animations[curAnim].x, char.characterData.animations[curAnim].y)); */ - - char.playAnim(char.characterData.animations[curAnim].name, false); - if (char.getAnimName() != null && char.getAnimName() == ghostChar.getAnimName()) - { - ghostChar.playAnim(char.getAnimName(), false); - } - } - } - } - - function controlCamera(elapsed:Float) - { - var ofs:Float = 500 * elapsed; - var shift:Bool = (FlxG.keys.pressed.SHIFT); - var cam_up:Bool = (FlxG.keys.pressed.W); - var cam_down:Bool = (FlxG.keys.pressed.S); - var cam_left:Bool = (FlxG.keys.pressed.A); - var cam_right:Bool = (FlxG.keys.pressed.D); - var cam_zoomers:Array = [FlxG.keys.pressed.I, FlxG.keys.pressed.O]; - - if (shift) - ofs *= 4; - - if (cam_up) - camFollow.y -= ofs; - if (cam_down) - camFollow.y += ofs; - if (cam_left) - camFollow.x -= ofs; - if (cam_right) - camFollow.x += ofs; - - var zoom_ofs = FlxG.camera.zoom * elapsed; - if (cam_zoomers[0] && camZoom < 3) - { - camZoom += zoom_ofs; - if (camZoom > 3) - camZoom = 3; - } - if (cam_zoomers[1] && camZoom > 0.1) - { - camZoom -= zoom_ofs; - if (camZoom < 0.1) - camZoom = 0.1; + if (FlxG.mouse.justPressedMiddle) { + FlxG.mouse.getScreenPosition(charCamera, camDrag); + camDrag *= 1.2; + camDrag += charCamera.scroll; } - - - - } - - function changeAnim() - { - var space = (FlxG.keys.justPressed.SPACE); - var forward = (FlxG.keys.justPressed.L); - var backwards = (FlxG.keys.justPressed.K); - - if (forward || backwards) - { - var changeLimit = char.characterData.animations.length; - curAnim = FlxMath.wrap(curAnim + (forward ? 1 : -1), 0, changeLimit-1); - - trace(curAnim); - trace(char.characterData.animations[curAnim]); - - char.playAnim(char.characterData.animations[curAnim].name, false); - playingAnim.text = 'Animation: ${char.characterData.animations[curAnim].name}'; + if (FlxG.mouse.pressedMiddle) { + var pos = FlxG.mouse.getScreenPosition(charCamera); + nextScroll.set((camDrag.x - (pos.x*1.2)), (camDrag.y - (pos.y*1.2))); } - if (space) - char.playAnim(char.characterData.animations[curAnim].name, true); + charCamera.scroll.set( + lerp(charCamera.scroll.x, nextScroll.x, 0.5), + lerp(charCamera.scroll.y, nextScroll.y, 0.5) + ); - char.updateHitbox(); - ghostChar.updateHitbox(); + charCamera.zoom = lerp(charCamera.zoom, __camZoom, 0.125); } - // Animation Windows - function add_anim_window(_) - openSubState(new AddAnimScreen(char, ghostChar, this)); - - function update_anim_window(_) - openSubState(new SelectAnimUpdate(char, ghostChar, this)); - - function remove_anim_window(_) - openSubState(new DeleteAnimScreen(char, ghostChar, this)); - - function help_controls_window(_) - openSubState(new HelpControls()); - - function _file_exit(_) - { - FlxG.switchState(new CharacterSelection()); - } - - override function destroy() { - Framerate.fpsCounter.alpha = 1; - Framerate.memoryCounter.alpha = 1; - Framerate.codenameBuildField.alpha = 1; - super.destroy(); - } - - function new_character(_) - FlxG.state.openSubState(new NewCharacter(char, ghostChar, this)); - - - function edit_properties_window(_) - FlxG.state.openSubState(new CharacterProperties(char, ghostChar, this)); - - function _file_save(_) - { - #if sys - var assetPath = Paths.character(char.curCharacter); - var path = Assets.getPath(assetPath); - saveTo(path); - return; - #end - _file_saveas(_); - } - - function _file_saveas(_) - { - var xml = CharacterConfig.dataToXml(char.characterData); - openSubState(new SaveSubstate(CharacterConfig.generateCharXML(xml.x), { - defaultSaveFile: '${char.curCharacter}.xml' - })); - } - - #if sys - function saveTo(path:String) - { - var xml = CharacterConfig.dataToXml(char.characterData); - CharacterConfig.save(path, CharacterConfig.generateCharXML(xml.x)); + // UNDO/REDO LOGIC + #if REGION + public inline function addToUndo(c:CharacterChange) { + redoList = []; + undoList.insert(0, c); + while(undoList.length > Options.maxUndos) + undoList.pop(); } #end - function set_camZoom(value:Float):Float - { - editorCamera.zoom = value; - return camZoom = value; + // TOP MENU OPTIONS + #if REGION + function _file_exit(_) { + FlxG.switchState(new CharacterSelection()); } + + function _file_save(_) { + } + + function _file_saveas(_) { + } + + function _edit_delete(_) { + } + + function _edit_undo(_) { + var v = undoList.shift(); + switch (v) { + case null: + // do nothing + case CCreateAnim(animID, animData): + deleteAnim(animData.name, false); + case CDeleteAnim(animID, animData): + createAnim(animData, animID, false); + case CChangeOffset(name, change): + changeOffset(name, change * -1, false); + case CResetOffsets(oldOffsets): + for (anim => offsets in oldOffsets) + character.animOffsets.set(anim, offsets.clone()); + + for (anim in character.animation.getNameList()) + characterAnimsWindow.animButtons[anim].updateInfo(anim, character.getAnimOffset(anim), false); + + changeOffset(character.animation.name, FlxPoint.get(0, 0), false); // apply da new offsets + } + if (v != null) + redoList.insert(0, v); + } + + function _edit_redo(_) { + var v = redoList.shift(); + switch (v) { + case null: + // do nothing + case CCreateAnim(animID, animData): + createAnim(animData, animID, false); + case CDeleteAnim(animID, animData): + deleteAnim(animData.name, false); + case CChangeOffset(name, change): + changeOffset(name, change, false); + case CResetOffsets(oldOffsets): + clearOffsets(false); + } + if (v != null) + undoList.insert(0, v); + } + + function _char_add_anim(_) { + FlxG.state.openSubState(new CharacterAnimScreen(null)); + } + + function _char_update_anim(_) { + } + + function _char_remove_anim(_) { + } + + function _char_edit_info(_) { + FlxG.state.openSubState(new CharacterInfoScreen(character)); + } + + public function createAnim(animData:AnimData, animID:Int = -1, addtoUndo:Bool = true) { + XMLUtil.addAnimToSprite(character, animData); + characterAnimsWindow.createNewButton(animData.name, FlxPoint.get(animData.x,animData.y), false, animID); + + playAnimation(animData.name); + + if (addtoUndo) + addToUndo(CCreateAnim(character.animation.getNameList().length, animData)); + } + + public function deleteAnim(name:String, addtoUndo:Bool = true) { + playAnimation(character.animation.getNameList()[character.animation.getNameList().indexOf(name)-1]); + + @:privateAccess var flxanim:FlxAnimation = character.animation._animations.get(name); + var oldID:Int = character.animation.getNameList().indexOf(name); + var oldAnimData:AnimData = { + name: name, + anim: flxanim.prefix, + fps: flxanim.frameRate, + loop: flxanim.looped, + x: character.animOffsets.get(name).x, + y: character.animOffsets.get(name).y, + indices: flxanim.usesIndicies ? flxanim.frames : [], + animType: NONE + }; + @:privateAccess character.animation._animations.remove(name); + flxanim.destroy(); + + character.animOffsets.remove(name); + characterAnimsWindow.removeButton(name); + + if (addtoUndo) + addToUndo(CDeleteAnim(oldID, oldAnimData)); + } + + function _playback_play_anim(_) { + playAnimation(character.animation.name); + } + + function _playback_stop_anim(_) { + } + + public function playAnimation(anim:String) { + character.playAnim(anim, true); + characterAnimsWindow.changeCurAnim(anim); + } + + function _offsets_left(_) { + changeOffset(character.animation.name, FlxPoint.get(1, 0)); + } + + function _offsets_up(_) { + changeOffset(character.animation.name, FlxPoint.get(0, 1)); + } + + function _offsets_down(_) { + changeOffset(character.animation.name, FlxPoint.get(0, -1)); + } + + function _offsets_right(_) { + changeOffset(character.animation.name, FlxPoint.get(-1, 0)); + } + + function _offsets_extra_left(_) { + changeOffset(character.animation.name, FlxPoint.get(5, 0)); + } + + function _offsets_extra_up(_) { + changeOffset(character.animation.name, FlxPoint.get(0, 5)); + } + + function _offsets_extra_down(_) { + changeOffset(character.animation.name, FlxPoint.get(0, -5)); + } + + function _offsets_extra_right(_) { + changeOffset(character.animation.name, FlxPoint.get(-5, 0)); + } + + function _offsets_clear(_) { + clearOffsets(); + } + + function changeOffset(anim:String, change:FlxPoint, addtoUndo:Bool = true) { + character.animOffsets.set(anim, character.getAnimOffset(anim) + change); + characterAnimsWindow.animButtons[anim].updateInfo(anim, character.getAnimOffset(anim), false); + character.frameOffset.set(character.getAnimOffset(anim).x, character.getAnimOffset(anim).y); + + if (addtoUndo) + addToUndo(CChangeOffset(anim, change)); + } + + function clearOffsets(addtoUndo:Bool = true) { + var oldOffsets:Map = [ + for (anim => offsets in character.animOffsets) + anim => offsets.clone() + ]; + + for (anim in character.animation.getNameList()) { + character.animOffsets[anim].zero(); + characterAnimsWindow.animButtons[anim].updateInfo(anim, character.getAnimOffset(anim), false); + } + + if (addtoUndo) + addToUndo(CResetOffsets(oldOffsets)); + } + + var zoom(default, set):Float = 0; + var __camZoom(default, set):Float = 1; + function set_zoom(val:Float) { + return zoom = FlxMath.bound(val, -3.5, 1.75); // makes zooming not lag behind when continuing scrolling + } + function set___camZoom(val:Float) { + return __camZoom = FlxMath.bound(val, 0.1, 3); + } + + function _view_zoomin(_) { + zoom += 0.25; + __camZoom = Math.pow(2, zoom); + } + function _view_zoomout(_) { + zoom -= 0.25; + __camZoom = Math.pow(2, zoom); + } + function _view_zoomreset(_) { + zoom = 0; + __camZoom = Math.pow(2, zoom); + } + #end } + +enum CharacterChange { + CCreateAnim(animID:Int, animData:AnimData); + CDeleteAnim(animID:Int, animData:AnimData); + CChangeOffset(name:String, change:FlxPoint); + CResetOffsets(oldOffsets:Map); +} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterInfoScreen.hx b/source/funkin/editors/character/CharacterInfoScreen.hx new file mode 100644 index 00000000..336e67ca --- /dev/null +++ b/source/funkin/editors/character/CharacterInfoScreen.hx @@ -0,0 +1,125 @@ +package funkin.editors.character; + +import funkin.game.Character; + +class CharacterInfoScreen extends UISubstateWindow { + public var character:Character; + + public var spriteTextBox:UITextBox; + public var iconTextBox:UITextBox; + public var iconSprite:FlxSprite; + public var gameOverCharTextBox:UITextBox; + public var positionXStepper:UINumericStepper; + public var positionYStepper:UINumericStepper; + public var cameraXStepper:UINumericStepper; + public var cameraYStepper:UINumericStepper; + public var scaleStepper:UINumericStepper; + public var singTimeStepper:UINumericStepper; + public var isPlayerCheckbox:UICheckbox; + public var isGFCheckbox:UICheckbox; + public var flipXCheckbox:UICheckbox; + public var antialiasingCheckbox:UICheckbox; + + public var saveButton:UIButton; + public var closeButton:UIButton; + + public function new(character:Character) { + super(); + this.character = character; + } + + public override function create() { + winTitle = "Editing Character"; + winWidth = 960; + + super.create(); + + function addLabelOn(ui:UISprite, text:String) + add(new UIText(ui.x, ui.y - 24, 0, text)); + + var title:UIText; + add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Sprite Data", 28)); + + spriteTextBox = new UITextBox(title.x, title.y + title.height + 38, character.sprite, 200); + add(spriteTextBox); + addLabelOn(spriteTextBox, "Sprite"); + + iconTextBox = new UITextBox(spriteTextBox.x + 200 + 26, spriteTextBox.y, character.getIcon(), 150); + iconTextBox.onChange = (newIcon:String) -> {updateIcon(newIcon);} + add(iconTextBox); + addLabelOn(iconTextBox, "Icon"); + + updateIcon(character.getIcon()); + + gameOverCharTextBox = new UITextBox(iconTextBox.x + 150 + (75 + 16), iconTextBox.y, character.gameOverCharacter, 200); + add(gameOverCharTextBox); + addLabelOn(gameOverCharTextBox, "Game Over Character"); + + add(title = new UIText(spriteTextBox.x, spriteTextBox.y + 10 + 46, 0, "Character Data", 28)); + + positionXStepper = new UINumericStepper(title.x, title.y + title.height + 36, character.globalOffset.x, 0.001, 2, null, null, 84); + add(positionXStepper); + addLabelOn(positionXStepper, "Position (X,Y)"); + + add(new UIText(positionXStepper.x + 84 - 32 + 0, positionXStepper.y + 9, 0, ",", 22)); + + positionYStepper = new UINumericStepper(positionXStepper.x + 84 - 32 + 26, positionXStepper.y, character.globalOffset.y, 0.001, 2, null, null, 84); + add(positionYStepper); + + cameraXStepper = new UINumericStepper(positionYStepper.x + 36 + 84 - 32, positionYStepper.y, character.cameraOffset.x, 0.001, 2, null, null, 84); + add(cameraXStepper); + addLabelOn(cameraXStepper, "Camera Position (X,Y)"); + + add(new UIText(cameraXStepper.x + 84 - 32 + 0, cameraXStepper.y + 9, 0, ",", 22)); + + cameraYStepper = new UINumericStepper(cameraXStepper.x + 84 - 32 + 26, cameraXStepper.y, character.cameraOffset.y, 0.001, 2, null, null, 84); + add(cameraYStepper); + + scaleStepper = new UINumericStepper(cameraYStepper.x + 84 - 32 + 90, cameraYStepper.y, character.scale.x, 0.001, 2, null, null, 74); + add(scaleStepper); + addLabelOn(scaleStepper, "Scale"); + + singTimeStepper = new UINumericStepper(scaleStepper.x + 74 - 32 + 36, scaleStepper.y, character.holdTime, 0.001, 2, null, null, 74); + add(singTimeStepper); + addLabelOn(singTimeStepper, "Sing Time"); + + isPlayerCheckbox = new UICheckbox(positionXStepper.x, positionXStepper.y + 10 + 32 + 26, "isPlayer", character.playerOffsets); + add(isPlayerCheckbox); + addLabelOn(isPlayerCheckbox, "Is Player"); + + for (checkbox in [isPlayerCheckbox]) + {checkbox.y += 6; checkbox.x += 4;} + + saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight- 20, "Save & Close", function() { + close(); + }, 125); + saveButton.x -= saveButton.bWidth; + saveButton.y -= saveButton.bHeight; + + closeButton = new UIButton(saveButton.x - 20, saveButton.y, "Close", function() { + close(); + }, 125); + closeButton.x -= closeButton.bWidth; + add(closeButton); + add(saveButton); + } + + function updateIcon(icon:String) { + if (iconSprite == null) add(iconSprite = new FlxSprite()); + + if (iconSprite.animation.exists(icon)) return; + @:privateAccess iconSprite.animation.clearAnimations(); + + var path:String = Paths.image('icons/$icon'); + if (!Assets.exists(path)) path = Paths.image('icons/face'); + + iconSprite.loadGraphic(path, true, 150, 150); + iconSprite.animation.add(icon, [0], 0, false); + iconSprite.antialiasing = true; + iconSprite.animation.play(icon); + + iconSprite.scale.set(0.5, 0.5); + iconSprite.updateHitbox(); + iconSprite.setPosition(iconTextBox.x + 150 + 8, (iconTextBox.y + 16) - (iconSprite.height/2)); + } +} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterProperties.hx b/source/funkin/editors/character/CharacterProperties.hx deleted file mode 100644 index 2c167c9c..00000000 --- a/source/funkin/editors/character/CharacterProperties.hx +++ /dev/null @@ -1,123 +0,0 @@ -package funkin.editors.character; - -import flixel.util.FlxColor; -import flixel.math.FlxPoint; -import funkin.backend.utils.XMLUtil.AnimData; -import funkin.game.Character; - -class CharacterProperties extends UISubstateWindow -{ - public var char:Character; - public var ghostChar:Character; - public var charData:CharacterData; - - public var applyButton:UIButton; - public var cancelButton:UIButton; - public var editor:CharacterEditor; - - public var curAnimData:AnimData; - - public var spriteFile:UITextBox; - public var gameOverChar:UITextBox; - public var healthIcon:UITextBox; - public var iconColor:UITextBox; - public var flipX:UICheckbox; - public var isPlayer:UICheckbox; - public var isGF:UICheckbox; - public var antialiasing:UICheckbox; - public var scale:UITextBox; - public var holdTime:UITextBox; - - public function new(char:Character, ghostChar, editor:CharacterEditor) - { - super(); - this.char = char; - this.ghostChar = ghostChar; - this.charData = char.characterData; - this.editor = editor; - } - - public override function create() - { - winTitle = 'Character Properties'; - winWidth = 960; - - super.create(); - - // Offsets - var y_ofs = 60; - var x_ofs = 60; - var y_text_ofs = 20; - - // Character Properties - spriteFile = new UITextBox(windowSpr.x + 50, windowSpr.y + 10 + y_ofs, charData.sprite); - add(spriteFile); - add(new UIText(spriteFile.x, spriteFile.y - y_text_ofs, -1, "Image/Atlas file: ")); - - gameOverChar = new UITextBox(spriteFile.x + spriteFile.bWidth + x_ofs, spriteFile.y, charData.gameOverChar); - add(gameOverChar); - add(new UIText(gameOverChar.x, gameOverChar.y - y_text_ofs, -1, "Game Over character: ")); - - healthIcon = new UITextBox(spriteFile.x, spriteFile.y + y_ofs, charData.icon); - add(healthIcon); - add(new UIText(healthIcon.x, healthIcon.y - y_text_ofs, -1, "Health Icon: ")); - - iconColor = new UITextBox(healthIcon.x + healthIcon.bWidth + x_ofs, healthIcon.y, CharacterConfig.rgbToString(charData.iconColor)); - add(iconColor); - add(new UIText(iconColor.x, iconColor.y - y_text_ofs, -1, "Icon color (RGB): ")); - - flipX = new UICheckbox(healthIcon.x, healthIcon.y + y_ofs, "Flip X", charData.flipX); - add(flipX); - - antialiasing = new UICheckbox(flipX.x + flipX.field.width + x_ofs, flipX.y, "Antialiasing", charData.antialiasing); - add(antialiasing); - - isPlayer = new UICheckbox(flipX.x, flipX.y + y_ofs, "Playable Character", charData.isPlayer); - add(isPlayer); - - isGF = new UICheckbox(isPlayer.x + isPlayer.field.width + x_ofs, isPlayer.y, "GF Character", charData.isGF); - add(isGF); - - scale = new UITextBox(isPlayer.x, isPlayer.y + y_ofs, Std.string(charData.scale)); - add(scale); - add(new UIText(scale.x, scale.y - y_text_ofs, -1, "Scale: ")); - - holdTime = new UITextBox(scale.x + scale.bWidth + y_ofs, scale.y, Std.string(charData.holdTime)); - add(holdTime); - add(new UIText(holdTime.x, holdTime.y - y_text_ofs, -1, "Hold time: ")); - - // Apply / Cancel buttons - applyButton = new UIButton(windowSpr.x - x_ofs - 70, windowSpr.y, "Apply", function() - { - updateProperties(spriteFile.label.text, healthIcon.label.text, CharacterConfig.stringToRGB(healthIcon.label.text), flipX.checked, - antialiasing.checked, isPlayer.checked, isGF.checked, Std.parseFloat(scale.label.text), Std.parseFloat(holdTime.label.text), gameOverChar.label.text); - }); - add(applyButton); - - cancelButton = new UIButton(applyButton.x, applyButton.y + y_ofs, "Cancel", function() - { - close(); - }); - add(cancelButton); - } - - public function updateProperties(sprite:String, healthIcon:String, iconColor:FlxColor, flipX:Bool, antialiasing:Bool, isPlayer:Bool, isGF:Bool, - scale:Float, holdTime:Float, gameOverChar:String) - { - // Character Data - charData.sprite = sprite; - charData.icon = healthIcon; - charData.iconColor = iconColor; - charData.flipX = flipX; - charData.antialiasing = antialiasing; - charData.isPlayer = isPlayer; - charData.isGF = isGF; - charData.scale = scale; - charData.holdTime = holdTime; - charData.gameOverChar = gameOverChar; - - editor.reloadChar(!char.isPlayer, charData); - editor.reloadStage(); - editor.loadCross(); - } -} diff --git a/source/funkin/editors/character/CharacterPropertiesWindow.hx b/source/funkin/editors/character/CharacterPropertiesWindow.hx new file mode 100644 index 00000000..9f36bd9d --- /dev/null +++ b/source/funkin/editors/character/CharacterPropertiesWindow.hx @@ -0,0 +1,5 @@ +package funkin.editors.character; + +class CharacterPropertiesWindow extends UIWindow { + +} \ No newline at end of file diff --git a/source/funkin/editors/character/CharacterSelection.hx b/source/funkin/editors/character/CharacterSelection.hx index 1d26f6a6..050df0b2 100644 --- a/source/funkin/editors/character/CharacterSelection.hx +++ b/source/funkin/editors/character/CharacterSelection.hx @@ -3,7 +3,6 @@ package funkin.editors.character; import flixel.util.FlxColor; import funkin.game.Character; import funkin.backend.chart.Chart; -import funkin.game.Character.CharacterData; import funkin.options.type.TextOption; import funkin.options.type.IconOption; import funkin.options.OptionsScreen; @@ -11,77 +10,18 @@ import sys.FileSystem; class CharacterSelection extends EditorTreeMenu { - public var characterList:CharactersList; - public override function create() { bgType = "charter"; super.create(); - characterList = CharactersList.get(); - main = new OptionsScreen("Character Editor", "Select a character to edit", [ - for (s in characterList.characters) - new IconOption(s.name, "Press ACCEPT to edit this character.", s.icon, function() - { - FlxG.switchState(new CharacterEditor(s.name)); + for (char in Character.getList()) + new IconOption(char, "Press ACCEPT to edit this character.", Character.getIconFromCharName(char), + function() { + FlxG.switchState(new CharacterEditor(char)); }) ]); } - - public override function update(elapsed:Float) - { - super.update(elapsed); - } - - public override function onMenuChange() { - super.onMenuChange(); - if (optionsTree.members.length > 1) { // selected a song - // small flashbang - var color:FlxColor = characterList.characters[main.curSelected].iconColor.getDefault(0xFFFFFFFF); - - bg.colorTransform.redOffset = 0.25 * color.red; - bg.colorTransform.greenOffset = 0.25 * color.green; - bg.colorTransform.blueOffset = 0.25 * color.blue; - bg.colorTransform.redMultiplier = FlxMath.lerp(1, color.redFloat, 0.25); - bg.colorTransform.greenMultiplier = FlxMath.lerp(1, color.greenFloat, 0.25); - bg.colorTransform.blueMultiplier = FlxMath.lerp(1, color.blueFloat, 0.25); - } - } -} - -class CharactersList -{ - public var characters:Array = []; - - public function new() - { - } - - public function getCharactersFromSource(source:funkin.backend.assets.AssetsLibraryList.AssetSource) - { - var charactersFound:Array = []; - - charactersFound = Paths.getFolderContent('data/characters', false, source); - - if (charactersFound.length > 0) - { - for (s in charactersFound) - if (s.endsWith(".xml")) - characters.push(CharacterConfig.loadCharacterData(s.replace(".xml", ""))); - return true; - } - return false; - } - - public static function get() - { - var characterList = new CharactersList(); - - if (!characterList.getCharactersFromSource(MODS)) - characterList.getCharactersFromSource(SOURCE); - - return characterList; - } -} +} \ No newline at end of file diff --git a/source/funkin/editors/character/DeleteAnimScreen.hx b/source/funkin/editors/character/DeleteAnimScreen.hx deleted file mode 100644 index ec5d6299..00000000 --- a/source/funkin/editors/character/DeleteAnimScreen.hx +++ /dev/null @@ -1,73 +0,0 @@ -package funkin.editors.character; - -import flixel.math.FlxRandom; -import cpp.Random; -import flixel.math.FlxPoint; -import funkin.backend.utils.XMLUtil.AnimData; -import funkin.game.Character; - -class DeleteAnimScreen extends UISubstateWindow -{ - public var char:Character; - public var ghostChar:Character; - - public var editor:CharacterEditor; - - public var animButtons:FlxTypedSpriteGroup = new FlxTypedSpriteGroup(365, -10); - - public var closeButton:UIButton; - - public function new(char:Character, ghostChar:Character, editor:CharacterEditor) - { - super(); - this.char = char; - this.ghostChar = ghostChar; - this.editor = editor; - } - - public override function create() - { - winTitle = "Delete Animation"; - winWidth = 480; - winHeight = 700; - - super.create(); - - add(animButtons); - - var y_adder = 60; - for (anim in char.characterData.animations) - { - var newbutton = new UIButton(animButtons.x - 550, animButtons.y + y_adder, anim.name, function() - { - removeAnimation(anim); - close(); - }); - animButtons.add(newbutton); - y_adder += 45; - } - - closeButton = new UIButton(windowSpr.x - 120, windowSpr.y + 20, "Close", function() { - close(); - }); - add(closeButton); - } - - public function removeAnimation(anim:AnimData) - { - var refreshAnim:Bool = false; - if (char.animation.curAnim != null && anim.name == char.animation.curAnim.name) - refreshAnim = true; - - if (char.animation.getByName(anim.name) != null) - char.animation.remove(anim.name); - if (char.animOffsets.exists(anim.name)) - char.animOffsets.remove(anim.name); - char.characterData.animations.remove(anim); - - if (refreshAnim && char.characterData.animations.length > 0) - char.playAnim(char.characterData.animations[0].name, true); - - editor.reloadGhost(); - } -} diff --git a/source/funkin/editors/character/HelpControls.hx b/source/funkin/editors/character/HelpControls.hx deleted file mode 100644 index 66fe3e78..00000000 --- a/source/funkin/editors/character/HelpControls.hx +++ /dev/null @@ -1,71 +0,0 @@ -package funkin.editors.character; - -class HelpControls extends UISubstateWindow -{ - - var helpTextTitles:Array = [ - "Camera:", - "Offset:", - "Animation:" - ]; - var helpTexts:Array = [ - "\n\n- WASD: Move camera\n\n- I & O: Zoom In & Out.", - "\n\n- Arrows keys: Move the offset of the current\nanimation.\n\n- Left Click: Move the offset of the character", - "\n\n- K & L: Switch to previous & next animation\n\n- SPACE: Play current animation.\n\n" - ]; - - var curPage:Int = 0; - public function new() - { - super(); - } - - public override function create() - { - winTitle = 'Help: Controls'; - winWidth = 960; - - super.create(); - - var helpTextTitle = new UIText(windowSpr.x + 25, windowSpr.y + 45, -1, helpTextTitles[0], 36); - add(helpTextTitle); - - var helpText = new UIText(windowSpr.x + 25, windowSpr.y + 50, -1, helpTexts[0], 24); - add(helpText); - - var pageText = new UIText(855, windowSpr.y + 460, -1, '${curPage+1}/${helpTexts.length}', 24); - add(pageText); - - var rightButton = new UIButton(885, windowSpr.y + 500, ">", function() - { - if(curPage == helpTexts.length-1) - curPage = 0; - else - curPage++; - - pageText.text = '${curPage+1}/${helpTexts.length}'; - helpText.text = helpTexts[curPage]; - helpTextTitle.text = helpTextTitles[curPage]; - }, 32); - - var leftButton = new UIButton(845, windowSpr.y + 500, "<", function() - { - if(curPage == 0) - curPage = helpTexts.length-1; - else - curPage--; - - pageText.text = '${curPage+1}/${helpTexts.length}'; - helpText.text = helpTexts[curPage]; - helpTextTitle.text = helpTextTitles[curPage]; - }, 32); - - var closeButton = new UIButton(helpText.x, windowSpr.y + 500, "Close", function() - { - close(); - }); - add(closeButton); - add(leftButton); - add(rightButton); - } -} diff --git a/source/funkin/editors/character/NewCharacter.hx b/source/funkin/editors/character/NewCharacter.hx deleted file mode 100644 index 1a729a12..00000000 --- a/source/funkin/editors/character/NewCharacter.hx +++ /dev/null @@ -1,172 +0,0 @@ -package funkin.editors.character; - -import flixel.util.FlxColor; -import flixel.math.FlxPoint; -import funkin.backend.utils.XMLUtil.AnimData; -import funkin.game.Character; -import funkin.editors.character.DeleteAnimScreen; - -class NewCharacter extends UISubstateWindow -{ - public var char:Character; - public var ghostChar:Character; - public var charData:CharacterData; - - public var applyButton:UIButton; - public var nextButton:UIButton; - public var cancelButton:UIButton; - public var editor:CharacterEditor; - - public var curAnimData:AnimData; - - public var spriteFile:UITextBox; - public var gameOverChar:UITextBox; - public var healthIcon:UITextBox; - public var iconColor:UITextBox; - public var flipX:UICheckbox; - public var isPlayer:UICheckbox; - public var isGF:UICheckbox; - public var antialiasing:UICheckbox; - public var scale:UITextBox; - public var holdTime:UITextBox; - - public function new(char:Character, ghostChar, editor:CharacterEditor) - { - super(); - this.char = char; - this.ghostChar = ghostChar; - this.charData = char.characterData; - this.editor = editor; - - //TO DO: Generate and Save - //---- - - - //Delete old Animaitons - } - - public override function create() - { - winTitle = 'Create New Character'; - winWidth = 960; - - super.create(); - - // Offsets - var y_ofs = 60; - var x_ofs = 60; - var y_text_ofs = 20; - - // Character Properties - spriteFile = new UITextBox(windowSpr.x + 50, windowSpr.y + 10 + y_ofs, charData.sprite); - add(spriteFile); - add(new UIText(spriteFile.x, spriteFile.y - y_text_ofs, -1, "Image/Atlas file: ")); - - gameOverChar = new UITextBox(spriteFile.x + spriteFile.bWidth + x_ofs, spriteFile.y, charData.gameOverChar); - add(gameOverChar); - add(new UIText(gameOverChar.x, gameOverChar.y - y_text_ofs, -1, "Game Over character: ")); - - healthIcon = new UITextBox(spriteFile.x, spriteFile.y + y_ofs, charData.icon); - add(healthIcon); - add(new UIText(healthIcon.x, healthIcon.y - y_text_ofs, -1, "Health Icon: ")); - - iconColor = new UITextBox(healthIcon.x + healthIcon.bWidth + x_ofs, healthIcon.y, CharacterConfig.rgbToString(charData.iconColor)); - add(iconColor); - add(new UIText(iconColor.x, iconColor.y - y_text_ofs, -1, "Icon color (RGB): ")); - - flipX = new UICheckbox(healthIcon.x, healthIcon.y + y_ofs, "Flip X", charData.flipX); - add(flipX); - - antialiasing = new UICheckbox(flipX.x + flipX.field.width + x_ofs, flipX.y, "Antialiasing", charData.antialiasing); - add(antialiasing); - - isPlayer = new UICheckbox(flipX.x, flipX.y + y_ofs, "Playable Character", charData.isPlayer); - add(isPlayer); - - isGF = new UICheckbox(isPlayer.x + isPlayer.field.width + x_ofs, isPlayer.y, "GF Character", charData.isGF); - add(isGF); - - scale = new UITextBox(isPlayer.x, isPlayer.y + y_ofs, Std.string(charData.scale)); - add(scale); - add(new UIText(scale.x, scale.y - y_text_ofs, -1, "Scale: ")); - - holdTime = new UITextBox(scale.x + scale.bWidth + y_ofs, scale.y, Std.string(charData.holdTime)); - add(holdTime); - add(new UIText(holdTime.x, holdTime.y - y_text_ofs, -1, "Hold time: ")); - - // Apply / Cancel buttons - - - applyButton = new UIButton(windowSpr.x - x_ofs - 70, windowSpr.y, "Apply", function() - { - - updateProperties(spriteFile.label.text, healthIcon.label.text, CharacterConfig.stringToRGB(healthIcon.label.text), flipX.checked, - antialiasing.checked, isPlayer.checked, isGF.checked, Std.parseFloat(scale.label.text), Std.parseFloat(holdTime.label.text), gameOverChar.label.text); - - //TO DO: Add a Animation - }); - add(applyButton); - - cancelButton = new UIButton(applyButton.x, applyButton.y + y_ofs, "Cancel", function() - { - close(); - }); - add(cancelButton); - } - - public function updateProperties(sprite:String, healthIcon:String, iconColor:FlxColor, flipX:Bool, antialiasing:Bool, isPlayer:Bool, isGF:Bool, - scale:Float, holdTime:Float, gameOverChar:String) - { - // Character Data - charData.sprite = sprite; - charData.icon = healthIcon; - charData.iconColor = iconColor; - charData.flipX = flipX; - charData.antialiasing = antialiasing; - charData.isPlayer = isPlayer; - charData.isGF = isGF; - charData.scale = scale; - charData.holdTime = holdTime; - charData.gameOverChar = gameOverChar; - - //Bring Player Char to Default Position for User - if(isPlayer) - { - charData.offsetX = 770; - charData.offsetY = 410; - } - else if(isGF) - { - charData.offsetX = 400; - charData.offsetY = 330; - } - else - { - charData.offsetX = 100; - charData.offsetY = 410; - } - - - - - editor.reloadChar(!char.isPlayer, charData); - editor.reloadStage(); - editor.loadCross(); - - - - } - - public function removeAnimation(anim:AnimData) - { - var refreshAnim:Bool = false; - if (char.animation.curAnim != null && anim.name == char.animation.curAnim.name) - refreshAnim = true; - - if (char.animation.getByName(anim.name) != null) - char.animation.remove(anim.name); - if (char.animOffsets.exists(anim.name)) - char.animOffsets.remove(anim.name); - char.characterData.animations.remove(anim); - } -} diff --git a/source/funkin/editors/character/SelectAnimUpdate.hx b/source/funkin/editors/character/SelectAnimUpdate.hx deleted file mode 100644 index 576f0189..00000000 --- a/source/funkin/editors/character/SelectAnimUpdate.hx +++ /dev/null @@ -1,62 +0,0 @@ -package funkin.editors.character; - -import flixel.math.FlxRandom; -import cpp.Random; -import flixel.math.FlxPoint; -import funkin.backend.utils.XMLUtil.AnimData; -import funkin.game.Character; - -class SelectAnimUpdate extends UISubstateWindow -{ - public var cancelButton:UIButton; - - public var char:Character; - public var ghostChar:Character; - - public var editor:CharacterEditor; - - public var animButtons:FlxTypedSpriteGroup = new FlxTypedSpriteGroup(365, -10); - - public function new(char:Character, ghostChar:Character, editor:CharacterEditor) - { - super(); - this.char = char; - this.ghostChar = ghostChar; - this.editor = editor; - } - - public override function create() - { - winTitle = "Select an animation to update:"; - winWidth = 480; - winHeight = 700; - - super.create(); - - add(animButtons); - - var y_ofs = 60; - - for (anim in char.characterData.animations) - { - var newbutton = new UIButton(animButtons.x - 550, animButtons.y + y_ofs, anim.name, function() - { - openUpdateWindow(anim); - }); - animButtons.add(newbutton); - y_ofs += 45; - } - - cancelButton = new UIButton(windowSpr.x - 10, windowSpr.y + 20, "Cancel", function() - { - close(); - }); - cancelButton.x -= cancelButton.bWidth; - add(cancelButton); - } - - public function openUpdateWindow(anim:AnimData) - { - openSubState(new UpdateAnimScreen(this.char, this.ghostChar, anim, this.editor)); - } -} diff --git a/source/funkin/editors/character/UpdateAnimScreen.hx b/source/funkin/editors/character/UpdateAnimScreen.hx deleted file mode 100644 index e04ccd87..00000000 --- a/source/funkin/editors/character/UpdateAnimScreen.hx +++ /dev/null @@ -1,125 +0,0 @@ -package funkin.editors.character; - -import flixel.math.FlxPoint; -import funkin.backend.utils.XMLUtil.AnimData; -import funkin.game.Character; - -class UpdateAnimScreen extends UISubstateWindow -{ - public var char:Character; - public var ghostChar:Character; - - public var updateButton:UIButton; - public var cancelButton:UIButton; - public var editor:CharacterEditor; - - public var curAnimData:AnimData; - - public var animName:UITextBox; - public var animPrefix:UITextBox; - public var fps:UITextBox; - public var loop:UICheckbox; - public var indices:UITextBox; - - public function new(char:Character, ghostChar, animData:AnimData, editor:CharacterEditor) - { - super(); - this.char = char; - this.ghostChar = ghostChar; - this.curAnimData = animData; - this.editor = editor; - } - - public override function create() - { - winTitle = 'Update Animation: ${curAnimData.name}'; - winWidth = 960; - - super.create(); - - // Animation Properties - var y_ofs = 60; - var y_text_ofs = 20; - - animName = new UITextBox(windowSpr.x + 50, windowSpr.y + y_ofs, curAnimData.name); - add(animName); - add(new UIText(animName.x, animName.y - y_text_ofs, -1, "Animation Name: ")); - - animPrefix = new UITextBox(animName.x, animName.y + y_ofs, curAnimData.anim); - add(animPrefix); - add(new UIText(animPrefix.x, animPrefix.y - y_text_ofs, -1, "Animation on XML/Atlas file: ")); - - fps = new UITextBox(animPrefix.x, animPrefix.y + y_ofs, Std.string(curAnimData.fps)); - add(fps); - add(new UIText(fps.x, fps.y - y_text_ofs, -1, "Framerate: ")); - - loop = new UICheckbox(fps.x, fps.y + y_ofs, "Looped", curAnimData.loop); - add(loop); - - indices = new UITextBox(loop.x, loop.y + y_ofs, Std.string(curAnimData.indices).replace("[", "").replace("]", "")); - add(indices); - add(new UIText(indices.x, indices.y - y_text_ofs, -1, "Indices (0, 1, 2, 3): ")); - - // Add / Cancel Buttons - updateButton = new UIButton(windowSpr.x - 10, windowSpr.y + 31, "Update", function() - { - updateAnimation(animName.label.text, animPrefix.label.text, Std.parseInt(fps.label.text), loop.checked, indices.label.text); - close(); - }); - updateButton.x -= updateButton.bWidth; - add(updateButton); - - cancelButton = new UIButton(updateButton.x, updateButton.y + y_ofs, "Cancel", function() - { - close(); - }); - add(cancelButton); - } - - public function updateAnimation(name:String, ?anim:String, ?fps:Int = 24, ?looped:Bool = false, ?indices:String) - { - var animType:XMLAnimType = NONE; - - var selectedAnim:AnimData = null; - var selectedAnimIndex = 0; - - for (i in 0...char.characterData.animations.length) - { - if (char.characterData.animations[i].name == name) - { - selectedAnim = char.characterData.animations[i]; - selectedAnimIndex = i; - } - } - - if (selectedAnim == null) - return; - - var animData:AnimData = { - name: name, - anim: anim, - fps: fps, - loop: looped, - animType: animType, - indices: [], - x: selectedAnim.x, - y: selectedAnim.y - }; - - if (indices != "" && indices != null) - { - var indicesSplit = indices.split(","); - - for (indice in indicesSplit) - { - var i = Std.parseInt(indice.trim()); - if (i != null) - animData.indices.push(i); - } - } - - XMLUtil.addAnimToSprite(char, animData); - char.characterData.animations[selectedAnimIndex] = animData; - char.playAnim(name, true); - } -} diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 43d2cb9b..c0a0dc62 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -317,7 +317,6 @@ class Charter extends UIState { } ]; - hitsound = FlxG.sound.load(Paths.sound('editors/charter/hitsound')); metronome = FlxG.sound.load(Paths.sound('editors/charter/metronome')); diff --git a/source/funkin/editors/ui/UIWindow.hx b/source/funkin/editors/ui/UIWindow.hx new file mode 100644 index 00000000..c1320599 --- /dev/null +++ b/source/funkin/editors/ui/UIWindow.hx @@ -0,0 +1,12 @@ +package funkin.editors.ui; + +class UIWindow extends UISliceSprite { + public var titleSpr:UIText; + + public override function new(x:Float, y:Float, w:Int, h:Int, title:String) { + super(x, y, w, h, "editors/ui/warning-popup"); + + members.push(titleSpr = new UIText(x + 25, y, bWidth - 50, title, 15, -1)); + titleSpr.y = y + ((30 - titleSpr.height) / 2); + } +} \ No newline at end of file diff --git a/source/funkin/game/Character.hx b/source/funkin/game/Character.hx index fb5c939a..e6f4d5a4 100644 --- a/source/funkin/game/Character.hx +++ b/source/funkin/game/Character.hx @@ -1,6 +1,5 @@ package funkin.game; -import funkin.editors.character.CharacterConfig; import flixel.util.FlxColor; import funkin.backend.FunkinSprite; import flixel.graphics.frames.FlxFrame; @@ -23,27 +22,6 @@ import funkin.backend.scripting.events.PlayAnimEvent.PlayAnimContext; using StringTools; -typedef CharacterData = -{ - var offsetX:Float; - var offsetY:Float; - var camOffsetX:Float; - var camOffsetY:Float; - var quickDancer:Bool; - var holdTime:Float; - var isPlayer:Bool; - var flipX:Bool; - var antialiasing:Bool; - var sprite:String; - var name:String; - var animations:Array; - @:optional var isGF:Bool; - @:optional var icon:String; - @:optional var iconColor:FlxColor; - @:optional var scale:Float; - @:optional var gameOverChar:String; -} - @:allow(funkin.desktop.editors.CharacterEditor) @:allow(funkin.game.StrumLine) @:allow(funkin.game.PlayState) @@ -67,6 +45,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset public var isPlayer:Bool = false; public var isGF:Bool = false; public var curCharacter:String = 'bf'; + public var sprite:String = 'bf'; public var lastHit:Float = Math.NEGATIVE_INFINITY; public var holdTime:Float = 4; @@ -82,21 +61,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset public var script:Script; public var xml:Access; - public var characterData:CharacterData; - public var shadowFrame:CharacterShadowFrame; public inline function getCameraPosition() { var midpoint = getMidpoint(); - var event = EventManager.get(PointEvent) - .recycle(midpoint.x - + (isPlayer ? -100 : 150) - + globalOffset.x - + cameraOffset.x, midpoint.y - - 100 - + globalOffset.y - + cameraOffset.y); + var event = EventManager.get(PointEvent).recycle( + midpoint.x + (isPlayer ? -100 : 150) + globalOffset.x + cameraOffset.x, + midpoint.y - 100 + globalOffset.y + cameraOffset.y); script.call("onGetCamPos", [event]); midpoint.put(); // this event cannot be cancelled @@ -141,59 +113,16 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset try { var charXML = Xml.parse(plainXML).firstElement(); - if (charXML == null) - throw new Exception("Missing \"character\" node in XML."); + if (charXML == null) throw new Exception("Missing \"character\" node in XML."); xml = new Access(charXML); - } - catch (e) - { + } catch (e) { Logs.trace('Error while loading character ${curCharacter}: ${e}', ERROR); curCharacter = "bf"; continue; } - var sprite:String = curCharacter; + applyXML(xml); - if (xml.has.isPlayer) - playerOffsets = (xml.att.isPlayer == "true"); - if (xml.has.isGF) - isGF = (xml.att.isGF == "true"); - if (xml.has.x) - globalOffset.x = Std.parseFloat(xml.att.x); - if (xml.has.y) - globalOffset.y = Std.parseFloat(xml.att.y); - if (xml.has.gameOverChar) - gameOverCharacter = xml.att.gameOverChar; - if (xml.has.camx) - cameraOffset.x = Std.parseFloat(xml.att.camx); - if (xml.has.camy) - cameraOffset.y = Std.parseFloat(xml.att.camy); - if (xml.has.holdTime) - holdTime = CoolUtil.getDefault(Std.parseFloat(xml.att.holdTime), 4); - if (xml.has.flipX) - flipX = (xml.att.flipX == "true"); - if (xml.has.icon) - icon = xml.att.icon; - if (xml.has.scale) - { - var scale = Std.parseFloat(xml.att.scale).getDefault(1); - this.scale.set(scale, scale); - updateHitbox(); - } - if (xml.has.antialiasing) - antialiasing = (xml.att.antialiasing == "true"); - if (xml.has.sprite) - sprite = xml.att.sprite; - - loadSprite(Paths.image('characters/$sprite')); - - for (anim in xml.nodes.anim) - { - XMLUtil.addXMLAnimation(this, anim); - } - - characterData = CharacterConfig.loadCharacterData(curCharacter); - - // Loads the script and calls it's "create" function + // Loads the script and calls it's "create" functions script = Script.create(Paths.script(Path.withoutExtension(xmlPath), null, true)); script.setParent(this); script.load(); @@ -226,7 +155,6 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset // flipX = true; fixChar(true); - dance(); script.call("postCreate"); } @@ -242,6 +170,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset // [ true ][ false ][ true ] // [ false ][ true ][ true ] // [ true ][ true ][ false ] + // bros provided evedince :skull: if (isPlayer != playerOffsets && switchAnims) { // character is flipped @@ -302,8 +231,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset public function tryDance() { - switch (lastAnimContext) - { + switch (lastAnimContext) { case SING | MISS: if (lastHit + (Conductor.stepCrochet * holdTime) < Conductor.songPosition) dance(); @@ -328,8 +256,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset */ public var danceInterval:Int = 1; - public override function beatHit(curBeat:Int) - { + public override function beatHit(curBeat:Int) { script.call("beatHit", [curBeat]); if (danceInterval < 1) danceInterval = 1; @@ -340,16 +267,14 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset } } - public override function stepHit(curStep:Int) - { + public override function stepHit(curStep:Int) { script.call("stepHit", [curStep]); // nothing } var __reverseDrawProcedure:Bool = false; - public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect - { + public override function getScreenBounds(?newRect:FlxRect, ?camera:FlxCamera):FlxRect { if (__reverseDrawProcedure) { scale.x *= -1; @@ -363,10 +288,8 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset var __drawingShadowFrame = false; var __oldColorTransform = new ColorTransform(); - public override function draw() - { - if (!__drawingShadowFrame && shadowFrame != null) - { + public override function draw() { + if (!__drawingShadowFrame && shadowFrame != null) { __drawingShadowFrame = true; var oldFrame = _frame; @@ -436,17 +359,77 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset globalOffset.put(); } - public inline function getIcon() - { - return (icon != null) ? icon : curCharacter; + public function applyXML(xml:Access) { + sprite = curCharacter; + + if (xml.has.isPlayer) playerOffsets = (xml.att.isPlayer == "true"); + if (xml.has.isGF) isGF = (xml.att.isGF == "true"); + if (xml.has.x) globalOffset.x = Std.parseFloat(xml.att.x); // + if (xml.has.y) globalOffset.y = Std.parseFloat(xml.att.y); // + if (xml.has.gameOverChar) gameOverCharacter = xml.att.gameOverChar; // + if (xml.has.camx) cameraOffset.x = Std.parseFloat(xml.att.camx); // + if (xml.has.camy) cameraOffset.y = Std.parseFloat(xml.att.camy); // + if (xml.has.holdTime) holdTime = CoolUtil.getDefault(Std.parseFloat(xml.att.holdTime), 4); // + if (xml.has.flipX) flipX = (xml.att.flipX == "true"); + if (xml.has.icon) icon = xml.att.icon; // + if (xml.has.scale) { // + var scale = Std.parseFloat(xml.att.scale).getDefault(1); + this.scale.set(scale, scale); + updateHitbox(); + } + if (xml.has.antialiasing) antialiasing = (xml.att.antialiasing == "true"); + if (xml.has.sprite) sprite = xml.att.sprite; // + + loadSprite(Paths.image('characters/$sprite')); + + animation.destroyAnimations(); + for (anim in xml.nodes.anim) + { + XMLUtil.addXMLAnimation(this, anim); + } } + public function buildXML():Xml { + var xml = Xml.createElement("character"); + xml.set("isPlayer", playerOffsets == true ? "true" : "false"); + xml.set("isGF", isGF == true ? "true" : "false"); + xml.set("x", Std.string(globalOffset.x)); + xml.set("y", Std.string(globalOffset.y)); + xml.set("gameOverChar", gameOverCharacter); + xml.set("camx", Std.string(cameraOffset.x)); + xml.set("camy", Std.string(cameraOffset.y)); + xml.set("holdTime", Std.string(holdTime)); + xml.set("flipX", Std.string(flipX)); + xml.set("icon", icon); + xml.set("scale", Std.string(scale.x)); + xml.set("antialiasing", antialiasing == true ? "true" : "false"); + xml.set("sprite", sprite); + + for (anim in animation.getAnimationList()) + { + var animXml:Xml = Xml.createElement('anim'); + animXml.set("name", anim.name); + animXml.set("anim", anim.prefix); + animXml.set("loop", Std.string(anim.looped)); + animXml.set("fps", Std.string(anim.frameRate)); + var offset:FlxPoint = getAnimOffset(anim.name); + animXml.set("x", Std.string(offset.x)); + animXml.set("y", Std.string(offset.y)); + if (anim.usesIndicies) + animXml.set("indices", anim.frames.join(",")); + xml.addChild(animXml); + } + return xml; + } + + public inline function getIcon() + return (icon != null) ? icon : curCharacter; + public static function getIconFromCharName(?curCharacter:String) { if(curCharacter == null) return "face"; var icon = curCharacter; while(true) { - switch (curCharacter) - { + switch (curCharacter) { // case 'your-char': // To hardcode characters icons default: // load xml @@ -474,6 +457,13 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset } return icon; } + + public static function getList():Array { + return [ + for (path in Paths.getFolderContent('data/characters/', true, BOTH)) + if (Path.extension(path) == "xml") Path.withoutDirectory(Path.withoutExtension(path)) + ]; + } } typedef CharacterShadowFrame =