diff --git a/assets/editors/layouts/stage/spriteEditScreen.xml b/assets/editors/layouts/stage/spriteEditScreen.xml index 5b10371f..8c1cb089 100644 --- a/assets/editors/layouts/stage/spriteEditScreen.xml +++ b/assets/editors/layouts/stage/spriteEditScreen.xml @@ -1,42 +1,79 @@ - + - + <exec> + previewSprite = new FunkinSprite(50, self.winHeight - 100); + previewSprite.frame = sprite.frame; - <textbox name="nameTextBox" label="Name Identifier" text="${data.getName()}" x="20" y="title.y + TO" width="200" height="32" /> - <textbox name="spriteTextBox" label="Image File (${stage.spritesParentFolder})" text="${getEx('imageFile')}" x="last.x + last.bWidth + XOFFSET" y="title.y + TO" width="200" height="32" /> + self.add(previewSprite); + </exec> - <set name="curY" value="nameTextBox.y + nameTextBox.bHeight + YOFFSET" /> + <title name="title" x="20" y="30 + 16" text="Sprite Data" /> + + <set name="curY" value="title.y + TO" /> <!-- ROW 1 --> + + <textbox name="nameTextBox" label="Name Identifier" text="${data.getName()}" x="20" y="curY" width="200" height="32" /> + <textbox name="spriteTextBox" label="Image File (${stage.spritesParentFolder})" text="${getEx('imageFile')}" x="last.x + last.bWidth + XOFFSET" y="curY" width="200" height="32" /> + + <set name="curY" value="nameTextBox.y + nameTextBox.bHeight + YOFFSET" /> <!-- ROW 2 --> <stepper name="xStepper" label="X" value="sprite.x" x="20" y="curY" step="1" width="100" /> <stepper name="yStepper" label="Y" value="sprite.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" step="1" width="100" /> - <stepper name="scrollXStepper" label="Scroll X" value="sprite.scrollFactor.x" x="spriteTextBox.x" y="curY" step="0.01" width="100" /> - <stepper name="scrollYStepper" label="Scroll Y" value="sprite.scrollFactor.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" step="0.01" width="100" /> + <stepper name="scrollXStepper" label="Scroll X" value="sprite.scrollFactor.x" x="spriteTextBox.x" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="scrollYStepper" label="Scroll Y" value="sprite.scrollFactor.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> - <stepper name="scaleXStepper" label="Scale X" value="sprite.scale.x" x="20 + (200 + XOFFSET) * 2" y="curY" step="0.01" width="100" /> - <stepper name="scaleYStepper" label="Scale Y" value="sprite.scale.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" step="0.01" width="100" /> + <stepper name="scaleXStepper" label="Scale X" value="sprite.scale.x" x="20 + (200 + XOFFSET) * 2" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="scaleYStepper" label="Scale Y" value="sprite.scale.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> - <stepper name="skewXStepper" label="Skew X" value="sprite.skew.x" x="20 + (200 + XOFFSET) * 3" y="curY" step="0.01" width="100" /> - <stepper name="skewYStepper" label="Skew Y" value="sprite.skew.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" step="0.01" width="100" /> + <set name="curY" value="xStepper.y + xStepper.bHeight + YOFFSET" /> <!-- ROW 3 --> - <set name="curY" value="xStepper.y + xStepper.bHeight + YOFFSET" /> + <stepper name="angleStepper" label="Angle" value="sprite.angle" x="20 + (200 + XOFFSET) * 0" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="zoomFactorStepper" label="Zoom Factor" value="sprite.zoomFactor" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> - <stepper name="angleStepper" label="Angle" value="sprite.angle" x="20 + (200 + XOFFSET) * 0" y="curY" step="0.01" width="100" /> - <stepper name="zoomFactorStepper" label="Zoom Factor" value="sprite.zoomFactor" x="20 + (200 + XOFFSET) * 1" y="curY" step="0.01" width="100" /> + <stepper name="skewXStepper" label="Skew X" value="sprite.skew.x" x="20 + (200 + XOFFSET) * 2" y="curY" precision="2" step="0.01" width="100" /> + <stepper name="skewYStepper" label="Skew Y" value="sprite.skew.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" precision="2" step="0.01" width="100" /> - <dropdown name="animType" label="Animation Type" value="['NONE','BEAT','LOOP'][sprite.spriteAnimType]" x="20 + (200 + XOFFSET) * 2" y="curY" width="100" height="32" options="NONE,BEAT,LOOP" /> + <!-- TODO: Simplify this --> + <dropdown name="animType" label="Animation Type" value="sprite.spriteAnimType" x="20 + (200 + XOFFSET) * 1" y="curY" width="150" height="32"> + <item label="NONE" value="0" /> + <item label="BEAT" value="1" /> + <item label="LOOP" value="2" /> + </dropdown> + + <!-- ROW 4 --> + <set name="curY" value="angleStepper.y + angleStepper.bHeight + YOFFSET" /> + + <checkbox name="antialiasingCheckbox" text="Antialiasing" value="sprite.antialiasing" x="20 + (200 + XOFFSET) * 0" y="curY" /> + + <!-- TODO: Animation list --> + + + <!-- Preview Transform --> + + <title name="previewTitle" x="20" y="self.winHeight - 150" text="Preview Transform:" size="14" /> <exec> + function onUpdate() { + previewSprite.color = 0xFFFFFFFF; + previewSprite.skew.x = skewXStepper.value; + previewSprite.skew.y = skewYStepper.value; + previewSprite.angle = angleStepper.value; + var ratio = sprite.width / sprite.height; + previewSprite.setGraphicSize(50 * scaleXStepper.value * ratio, 50 * scaleYStepper.value); + previewSprite.updateHitbox(); + } + function onSave() { sprite.x = xStepper.value; sprite.y = yStepper.value; sprite.zoomFactor = zoomFactorStepper.value; - //sprite.antialiasing = antialiasingCheckbox.checked; + sprite.antialiasing = antialiasingCheckbox.checked; //sprite.color = colorWheel.color; - sprite.spriteAnimType = animType.index; + sprite.spriteAnimType = animType.value; + sprite.angle = angleStepper.value; function saveXY(obj:Dynamic, name:String, x:UINumericStepper, y:UINumericStepper) { data.xml.remove(name + ""); @@ -57,7 +94,9 @@ data.xml.set("x", xStepper.value); data.xml.set("y", yStepper.value); data.xml.set("zoom", zoomFactorStepper.value); - data.xml.set("type", ['NONE','BEAT','LOOP'][animType.index]); + data.xml.set("type", animType.key); + data.xml.set("antialiasing", antialiasingCheckbox.checked); + data.xml.set("angle", angleStepper.value); saveXY(sprite.scrollFactor, "scroll", scrollXStepper, scrollYStepper); saveXY(sprite.scale, "scale", scaleXStepper, scaleYStepper); diff --git a/source/funkin/backend/FunkinSprite.hx b/source/funkin/backend/FunkinSprite.hx index 3b81598a..40b36a02 100644 --- a/source/funkin/backend/FunkinSprite.hx +++ b/source/funkin/backend/FunkinSprite.hx @@ -29,6 +29,15 @@ enum abstract XMLAnimType(Int) default: def; } } + + @:to public function toString():String { + return switch (cast this) + { + case NONE: "none"; + case BEAT: "beat"; + case LOOP: "loop"; + } + } } class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements IOffsetCompatible implements IXMLEvents diff --git a/source/funkin/editors/stage/StageEditor.hx b/source/funkin/editors/stage/StageEditor.hx index 86cf70a1..7fe8556b 100644 --- a/source/funkin/editors/stage/StageEditor.hx +++ b/source/funkin/editors/stage/StageEditor.hx @@ -561,7 +561,7 @@ class StageEditor extends UIState { FlxPoint.get(0.5, 0), // center //FlxPoint.get(0.5, 0.5) - ], sprite.camera, sprite.width, sprite.height); + ], sprite.camera, sprite.frameWidth, sprite.frameHeight); //Logs.trace("Guide at " + corners[0].x + ", " + corners[0].y + " sprite at " + sprite.x + ", " + sprite.y); diff --git a/source/funkin/editors/stage/StageSpriteEditScreen.hx b/source/funkin/editors/stage/StageSpriteEditScreen.hx index 3559044d..82ef54c7 100644 --- a/source/funkin/editors/stage/StageSpriteEditScreen.hx +++ b/source/funkin/editors/stage/StageSpriteEditScreen.hx @@ -1,14 +1,16 @@ package funkin.editors.stage; -import funkin.backend.scripting.HScript; -import hscript.Interp; -import haxe.xml.Access; import flixel.math.FlxPoint; import funkin.backend.chart.ChartData.ChartMetaData; +import funkin.backend.scripting.HScript; +import funkin.backend.scripting.Script; import funkin.editors.extra.PropertyButton; import funkin.editors.stage.elements.StageElementButton; +import funkin.editors.ui.UIDropDown; +import haxe.xml.Access; import hscript.*; import hscript.Expr.Error; +import hscript.Interp; import hscript.Parser; using StringTools; @@ -47,7 +49,11 @@ class StageSpriteEditScreen extends UISubstateWindow { sprite = data.getSprite(); + for(k=>e in Script.getDefaultVariables()) { + set(k, e); + } set("stage", StageEditor.instance.stage); + set("self", this); set("sprite", sprite); set("data", data); set("getEx", function(name:String):Dynamic { @@ -84,24 +90,24 @@ class StageSpriteEditScreen extends UISubstateWindow { function addLabelOn(ui:UISprite, text:String) add(new UIText(ui.x, ui.y - 24, 0, text)); - function execString(code:String):String { - if(code.indexOf("${") == -1) return code; + function execString(input:String):String { + if(input.indexOf("${") == -1) return input; var text = ""; - while(code.length > 0) { - var idx = code.indexOf("${"); + while(input.length > 0) { + var idx = input.indexOf("${"); if(idx == -1) { - text += code; + text += input; break; } - text += code.substring(0, idx); - var ei = code.indexOf("}", idx); + text += input.substring(0, idx); + var ei = input.indexOf("}", idx); if(ei == -1) { - text += code.substring(idx); + text += input.substring(idx); break; } - var block = code.substring(idx+2, ei); + var block = input.substring(idx+2, ei); text += exec(block); - code = code.substring(ei + 1); + input = input.substring(ei + 1); } return text; } @@ -117,7 +123,7 @@ class StageSpriteEditScreen extends UISubstateWindow { for(el in layout.elements) { var type = el.name; //var type:String = el.getAtt("type"); - var name:String = el.getAtt("name"); + var name:String = execString(el.getAtt("name")); var label:String = execString(el.getAtt("label")); var text:String = execString(el.getAtt("text")); var x:Float = execAtt(el, "x", 0); @@ -130,21 +136,28 @@ class StageSpriteEditScreen extends UISubstateWindow { var sprite:FlxSprite = switch(type) { case "set": - set(el.getAtt("name"), exec(el.getAtt("value"))); + set(execString(el.getAtt("name")), exec(el.getAtt("value"))); continue; case "image": var spr = XMLUtil.createSpriteFromXML(el, "", LOOP); cast add(spr); + case "solid": + var spr = new FunkinSprite(x, y).makeSolid( + execAtt(el, "width", 100), + execAtt(el, "height", 100), + execAtt(el, "color", 0xFFFFFFFF) + ); + cast add(spr); case "exec": exec(el.innerData); continue; case "textbox": - var textBox = new UITextBox(x, y, text, width, height, (el.getAtt("multiline").getDefault("false") == "true")); + var textBox = new UITextBox(x, y, text, width, height, execAtt(el, "multiline", false));//(el.getAtt("multiline").getDefault("false") == "true")); add(textBox); addLabelOn(textBox, label); textBox; case "title": - cast add(new UIText(x, y, 0, label, size)); + cast add(new UIText(x, y, 0, text, size)); case "stepper": // x:Float, y:Float, value:Float = 0, step:Float = 1, precision:Int = 0, ?min:Float, ?max:Float, w:Int = 180, h:Int = 32 var stepper = new UINumericStepper( @@ -162,7 +175,7 @@ class StageSpriteEditScreen extends UISubstateWindow { addLabelOn(stepper, label); stepper; case "checkbox": - var checkbox = new UICheckbox(x, y, label, execAtt(el, "value", false)); + var checkbox = new UICheckbox(x, y, text, execAtt(el, "value", false)); add(checkbox); addLabelOn(checkbox, label); checkbox; @@ -172,11 +185,14 @@ class StageSpriteEditScreen extends UISubstateWindow { addLabelOn(slider, label); slider; case "dropdown": - var options = execString(el.getAtt("options")).split(","); + var items:Array<DropDownItem> = []; + for(i=>node in el.nodes.item) + items.push({label: execString(node.getAtt("label")), value: execAtt(node, "value", i)}); var curValue = execAtt(el, "value", null); - var index = options.indexOf(curValue); + var index = UIDropDown.indexOfItemValue(items, curValue); if (index == -1) index = 0; - var dropdown = new UIDropDown(x, y, width, height, options, index); + + var dropdown = new UIDropDown(x, y, width, height, items, index); add(dropdown); addLabelOn(dropdown, label); dropdown; @@ -248,6 +264,11 @@ class StageSpriteEditScreen extends UISubstateWindow { return null; } + override function update(elapsed:Float) { + super.update(elapsed); + callFunc("onUpdate", [elapsed]); + } + public function saveData() { for (el in elementMap) { if(el is UINumericStepper) { diff --git a/source/funkin/editors/ui/UIDropDown.hx b/source/funkin/editors/ui/UIDropDown.hx index 1cb944b8..05fd6859 100644 --- a/source/funkin/editors/ui/UIDropDown.hx +++ b/source/funkin/editors/ui/UIDropDown.hx @@ -1,34 +1,68 @@ package funkin.editors.ui; +import flixel.util.typeLimit.OneOfTwo; + class UIDropDown extends UISliceSprite { public var dropButton:UIButton; public var label:UIText; public var index:Int = 0; public var options:Array<String>; + public var items:Array<DropDownItem>; + public var key(get, never):String; + inline function get_key():String { + return items[index].label; + } + public var value(get, never):Dynamic; + inline function get_value():Dynamic { + return items[index].value; + } public var onChange:Int->Void; var curMenu:UIContextMenu = null; - public function new(x:Float, y:Float, width:Int = 320, height:Int = 32, options:Array<String>, index:Int = 0) { + public static function getItems(options:Array<String>):Array<DropDownItem> { + return [for(i=>o in options) {label: o, value: i}]; + } + + public function new(x:Float, y:Float, width:Int = 320, height:Int = 32, _options:Array<OneOfTwo<DropDownItem, String>>, index:Int = 0) { super(x, y, width - height, height, 'editors/ui/inputbox'); // using same sprite cause fuck you - this.options = options; + var _items:Array<DropDownItem> = []; + + if(_options.length > 0) { + if(_options[0] is String) { + _items = getItems(_options); + } else { + _items = cast _options; + } + } + + this.items = _items; + this.options = [for(o in _items) o.label]; // backwards compatibility this.index = index; cursor = BUTTON; - label = new UIText(0, 0, width - height, options[index]); + label = new UIText(0, 0, width - height, items[index].label); members.push(label); dropButton = new UIButton(0, 0, "V", null, height, height); members.push(dropButton); } + public static function indexOfItemValue(items:Array<DropDownItem>, value:Dynamic):Int { + return [for(o in items) o.value].indexOf(value); + } + + public static function indexOfItemLabel(items:Array<DropDownItem>, str:String):Int { + return [for(o in items) o.label].indexOf(str); + } + public function setOption(newIndex:Int) { if (index != (index = newIndex)) { - label.text = options[index]; + label.text = items[index].label; if (onChange != null) onChange(index); } @@ -53,12 +87,17 @@ class UIDropDown extends UISliceSprite { public function openContextMenu() { var screenPos = getScreenPosition(null, __lastDrawCameras[0] == null ? FlxG.camera : __lastDrawCameras[0]); curMenu = UIState.state.openContextMenu([ - for(k=>o in options) { + for(k=>o in items) { icon: (k == index) ? 1 : 0, - label: o + label: o.label } ], function(_, i, _) { setOption(i); }, __lastDrawCameras[0].x + screenPos.x, __lastDrawCameras[0].y + screenPos.y + bHeight); } +} + +typedef DropDownItem = { + var label:String; + var value:Dynamic; } \ No newline at end of file