diff --git a/assets/data/editors/layouts/stage/animEditScreen.xml b/assets/data/editors/layouts/stage/animEditScreen.xml
new file mode 100644
index 0000000000..39c3303ca5
--- /dev/null
+++ b/assets/data/editors/layouts/stage/animEditScreen.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+ function col(c:Int) {
+ return MARGIN + (200 + XOFFSET) * c;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ var prevText:String = animID.label.text;
+ function onUpdate() {
+ if (animID.label.text != prevText)
+ self.winTitle = self.titleSpr.text = translate('win-title', null, [animID.label.text]);
+ prevText = animID.label.text;
+
+ labelText.y = (useFrameLabel.y + (useFrameLabel.height / 2)) - (labelText.height / 2);
+ }
+
+ function onSave() {
+ button.animData.name = animID.label.text;
+ button.animData.animType = animType.value;
+ button.animData.anim = animPrefix.label.text;
+ button.animData.fps = animFPS.value;
+ button.animData.x = offsetX.value; button.animData.y = offsetY.value;
+ button.animData.loop = loop.checked;
+ button.animData.forced = forcedPlay.checked;
+ button.animData.indices = CoolUtil.parseNumberRange(animIndices.label.text);
+ button.animData.label = useFrameLabel.checked;
+ button.updateDisplay();
+ }
+
+
\ No newline at end of file
diff --git a/assets/data/editors/layouts/stage/spriteEditScreen.xml b/assets/data/editors/layouts/stage/spriteEditScreen.xml
index de6e70bb27..80bb4d26f7 100644
--- a/assets/data/editors/layouts/stage/spriteEditScreen.xml
+++ b/assets/data/editors/layouts/stage/spriteEditScreen.xml
@@ -6,15 +6,21 @@
function col(c:Int) {
- return 20 + (200 + XOFFSET) * c;
+ return MARGIN + (200 + XOFFSET) * c;
}
if(sprite.animateAtlas == null) {
- previewSprite = new FunkinSprite(50, self.winHeight - 100);
+ previewSprite = new FunkinSprite(MARGIN, self.winHeight - (100 + MARGIN));
previewSprite.frame = sprite.frame;
self.add(previewSprite);
}
+
+ function animTypeToString(animType:AnimType) {
+ return ["none", "beat", "loop"][animType];
+ }
+ if (getEx('anims') == null)
+ setEx('anims', sprite.animDatas.copy());
@@ -25,7 +31,7 @@
-
+
@@ -77,30 +81,39 @@
- buttonlist.addButton.exists = false;
-
-
-
+ for (animData in getEx('anims')) {
+ if (animData.name == 'idle' && animData.anim == null) continue; // Skips dummy animation added by XMLUtil.loadSpriteFromXML
+ addAnimBtn(animData);
+ }
+
@@ -111,10 +124,37 @@
+
+ var prevText = spriteTextBox.label.text;
+ var prevID = nameTextBox.label.text;
+ var spriteExists = true;
+ var ratio = sprite.width / sprite.height;
+
+ spriteTextBox.onChange = function(text) {
+ var newSprite = stage.spritesParentFolder + text;
+ var animated = Paths.framesExists(newSprite, true);
+ spriteExists = ( animated || Assets.exists(Paths.image(newSprite)) );
+
+ if (text != prevText && spriteExists) {
+ if (animated) previewSprite.frames = Paths.getFrames(newSprite, previewSprite.animateSettings);
+ else previewSprite.loadGraphic(Paths.image(newSprite));
+ previewSprite.updateHitbox();
+
+ ratio = previewSprite.width / previewSprite.height;
+ previewSprite.colorTransform.__identity();
+ } else if (!spriteExists) {
+ previewSprite.colorTransform.color = 0xFFEF0202;
+ }
+ prevText = text;
+ }
+
function onUpdate() {
+ if (nameTextBox.label.text != prevID)
+ self.winTitle = self.titleSpr.text = translate('win-sprite-title', [nameTextBox.label.text]);
+ prevID = nameTextBox.label.text;
+
if(previewSprite == null) return;
previewSprite.color = 0xFFFFFFFF; // TODO: Color Wheel
previewSprite.skew.x = skewXStepper.value;
@@ -122,11 +162,10 @@
previewSprite.angle = angleStepper.value;
previewSprite.alpha = alphaStepper.value;
previewSprite.antialiasing = antialiasingCheckbox.checked;
- var ratio = sprite.width / sprite.height;
- previewSprite.setGraphicSize(50 * scaleXStepper.value * ratio, 50 * scaleYStepper.value);
+ previewSprite.setGraphicSize(85 * scaleXStepper.value * ratio, 85 * scaleYStepper.value);
previewSprite.updateHitbox();
}
-
+
function onSave() {
sprite.name = nameTextBox.label.text;
setEx("imageFile", spriteTextBox.label.text);
@@ -134,12 +173,12 @@
setEx("highMemory", highMemoryRadio.checked);
setEx("lowMemory", lowMemoryRadio.checked);
- //sprite.x = xStepper.value;
- //sprite.y = yStepper.value;
- //sprite.zoomFactor = zoomFactorStepper.value;
- //sprite.antialiasing = antialiasingCheckbox.checked;
+ sprite.x = xStepper.value;
+ sprite.y = yStepper.value;
+ sprite.zoomFactor = zoomFactorStepper.value;
+ sprite.antialiasing = antialiasingCheckbox.checked;
//sprite.color = colorWheel.color;
- //sprite.spriteAnimType = animType.value;
+ sprite.spriteAnimType = animTypeToString(animType.value);
sprite.angle = angleStepper.value;
sprite.alpha = alphaStepper.value;
@@ -171,15 +210,48 @@
button.xml.set("x", xStepper.value);
button.xml.set("y", yStepper.value);
button.xml.set("zoom", zoomFactorStepper.value);
- button.xml.set("type", animType.key);
+ button.xml.set("type", animTypeToString(animType.value));
button.xml.set("antialiasing", antialiasingCheckbox.checked);
button.xml.set("angle", angleStepper.value);
button.xml.set("alpha", alphaStepper.value);
+ getEx('anims').clear();
+ var allowedAnims:Array<String> = [for (b in animations.buttons.members) b.animData.name];
+ for (node in button.xml.elementsNamed('anim')) if (!allowedAnims.contains( node.get('name') ))
+ button.xml.removeChild(node);
+
+ for (i in 0...animations.buttons.length) {
+ var actualChildren:Array<Xml> = button.xml.children.filter((f) -> f.nodeType == 0);
+ if (i > actualChildren.length - 1) {
+ var newAnim = Xml.createElement('anim');
+ button.xml.addChild(newAnim);
+ actualChildren.push(newAnim);
+ //trace("added new at " + i + ": " + newAnim);
+ }
+
+ final animData:AnimData = animations.buttons.members[i].animData;
+ final animNode:Xml = actualChildren[i];
+ //trace(animNode + "(" + i + ") before");
+ animNode.set("name", animData.name);
+ animNode.set("type", animTypeToString(animData.animType)); // XMLAnimType is abstract of int, the code is treating it as just int
+ animNode.set("anim", animData.anim);
+ animNode.set("fps", animData.fps);
+ animNode.set("x", animData.x);
+ animNode.set("y", animData.y);
+ animNode.set("loop", animData.loop);
+ animNode.set("forced", CoolUtil.getDefault(animData.forced, false));
+ animNode.set("indices", CoolUtil.formatNumberRange(animData.indices));
+ animNode.set("label", animData.label);
+ //trace(animNode + "(" + i + ")");
+ getEx('anims').set(animData.name, animData);
+ }
+
saveXY(sprite.scrollFactor, "scroll", scrollXStepper, scrollYStepper);
saveXY(sprite.scale, "scale", scaleXStepper, scaleYStepper);
saveXY(sprite.skew, "skew", skewXStepper, skewYStepper);
XMLUtil.loadSpriteFromXML(sprite, button.xml, stage.spritesParentFolder);
+ button.updateInfo();
+ setEx('node', button.xml);
}
\ No newline at end of file
diff --git a/assets/languages/en/Editors.xml b/assets/languages/en/Editors.xml
index c36b63b5e1..aa8bf81cf8 100644
--- a/assets/languages/en/Editors.xml
+++ b/assets/languages/en/Editors.xml
@@ -592,8 +592,9 @@
Stage Info
Stage Name
Sprite Path\n(Needs Refresh)
- Start Camera Position (X, Y)
+ Start Camera Position\n(X, Y)
Zoom
+ Custom Attributes
@@ -607,7 +608,7 @@
Element Name
Name Identifier
- Image File (WIP: needs reload)\nIn (${stage.spritesParentFolder})
+ Image File\n(in "{0}")
Scroll {0}
Scale {0}
Camera {0}
@@ -623,6 +624,7 @@
Low Memory
Attributes
Transform Preview:
+ Animations
Animation Type
@@ -631,6 +633,8 @@
LOOP
Tip: To access custom properties,\nHold Shift when clicking edit
+
+ Name on Anim Data
@@ -645,6 +649,7 @@
Scale: {0}, {1}
Scroll: {0}, {1}
+ Unknown Sprite:\n{0}
@@ -654,6 +659,14 @@
Character
Creating a box isnt implemented yet!
+
+
+ Editing "{0}" Anim
+ Anim Data
+ Use Frame Labels?
+ Offset (X, Y)
+ Forced Playing?
+
diff --git a/assets/languages/es/Editors.xml b/assets/languages/es/Editors.xml
index f8f23dd52d..8e4da69a0f 100644
--- a/assets/languages/es/Editors.xml
+++ b/assets/languages/es/Editors.xml
@@ -551,7 +551,7 @@
Info. del Escenario
Nombre del Escenario
Ubicación de Imágenes\n(Necesita Refrescar)
- Posición inicial de la cámara (X, Y)
+ Posición inicial de la cámara\n(X, Y)
Zoom
@@ -566,7 +566,7 @@
Nombre del Elemento
Identificador de Nombre
- Imagen (WIP: necesita refrescar)\nEn (${stage.spritesParentFolder})
+ Imagen\n(en "{0}")
Desplazamiento {0}
Tamaño {0}
Cámara {0}
@@ -580,6 +580,7 @@
Baja Memoria
Atributos
Preview:
+ Animaciónes
Tipo de Animación
@@ -602,6 +603,7 @@
Tamaño: {0}, {1}
Desplazamiento: {0}, {1}
+ Sprite Desconocido:\n{0}
diff --git a/assets/languages/it/Editors.xml b/assets/languages/it/Editors.xml
index 90853b8587..d0968dc16a 100644
--- a/assets/languages/it/Editors.xml
+++ b/assets/languages/it/Editors.xml
@@ -561,7 +561,7 @@
Informazioni Palcoscenico
Nome Palcoscenico
Percorso file Sprite\n(E' necessario ricaricare la pagina)
- Posizioni iniziali della Telecamera (X, Y)
+ Posizioni iniziali della Telecamera\n(X, Y)
Zoom
@@ -576,7 +576,7 @@
Nome Elemento
Nome Identificatore
- File Immagine (WIP: reload necessario)\nIn (${stage.spritesParentFolder})
+ File Immagine\n(in "{0}")
Scorrimento {0}
Scala {0}
Teleamera {0}
diff --git a/assets/languages/pl/Editors.xml b/assets/languages/pl/Editors.xml
index 694e66c3d9..86ff120c14 100644
--- a/assets/languages/pl/Editors.xml
+++ b/assets/languages/pl/Editors.xml
@@ -568,7 +568,7 @@
Info Sceny
Nazwa Sceny
Ścieżka Plikowa Sprite'a\n(Wymaga Odświeżenia)
- Pozycja Startowa Kamery (X, Y)
+ Pozycja Startowa Kamery\n(X, Y)
Przybliżenie
@@ -583,7 +583,7 @@
Nazwa Elementu
Nazwa Identyfikatora
- Plik Obrazu (W TRACKIE PRACY: wymaga odświeżenia)\nIn (${stage.spritesParentFolder})
+ Plik Obrazu\n(in "{0}")
Przewijanie {0}
Skala {0}
Kamera {0}
diff --git a/assets/languages/pt/Editors.xml b/assets/languages/pt/Editors.xml
index aa718df294..2d0a05208d 100644
--- a/assets/languages/pt/Editors.xml
+++ b/assets/languages/pt/Editors.xml
@@ -588,7 +588,7 @@
Info. do Cenário
Nome do Cenário
Caminho do Sprite\n(Requer Recarregamento)
- Posição inicial da Câmara (X, Y)
+ Posição inicial da Câmara\n(X, Y)
Zoom
Atributos Especiais
@@ -604,7 +604,7 @@
Nome do Elemento
Nome Identificador
- Imagem (WIP: precisa recarregar)\nEm (${stage.spritesParentFolder})
+ Imagem\n(em "{0}")
Scroll {0}
Tamanho {0}
Câmara {0}
@@ -629,6 +629,8 @@
REPETIR
Dica: Para aceder a mais dados,\nSegure em Shift quando Editar
+
+ Nome em Dados de Animação
@@ -643,6 +645,7 @@
Tamanho: {0}, {1}
Deslocamento: {0}, {1}
+ Sprite Desconhecido:\n{0}
@@ -652,6 +655,14 @@
Personagem
Criar caixas ainda não foi implementado!
+
+
+ A Editar a Animação "{0}"
+ Dados da Animação
+ Usar Rótulos\nde Quadro?
+ Posições (X, Y)
+ Reprodução Forçada?
+
diff --git a/source/funkin/editors/stage/StageEditor.hx b/source/funkin/editors/stage/StageEditor.hx
index 8b54a2c695..89c2a33473 100644
--- a/source/funkin/editors/stage/StageEditor.hx
+++ b/source/funkin/editors/stage/StageEditor.hx
@@ -260,6 +260,7 @@ class StageEditor extends UIState {
stageSpritesWindow.add(button);
} else if(sprite == null) {
var basic = new FlxBasic(); // prevent awkward layering
+ basic.exists = false;
insert(i, basic);
var button = new StageUnknownButton(0,0, basic, xml);
stageSpritesWindow.add(button);
@@ -308,8 +309,8 @@ class StageEditor extends UIState {
sprite.extra.set(exID("type"), node.name);
sprite.extra.set(exID("imageFile"), '${node.getAtt("sprite").getDefault(sprite.name)}');
sprite.extra.set(exID("parentNode"), parent);
- sprite.extra.set(exID("highMemory"), parent.name == "highMemory");
- sprite.extra.set(exID("lowMemory"), parent.name == "lowMemory");
+ sprite.extra.set(exID("highMemory"), parent.name == "high-memory");
+ sprite.extra.set(exID("lowMemory"), parent.name == "low-memory");
//sprite.active = false;
}
if(sprite is StageCharPos)
@@ -374,6 +375,7 @@ class StageEditor extends UIState {
}
char.name = charName;
char.debugMode = true;
+ char.useRenderTexture = true;
// Play first anim, and make it the last frame
var animToPlay = char.getAnimOrder()[0];
char.playAnim(animToPlay, true, NONE);
@@ -392,8 +394,8 @@ class StageEditor extends UIState {
char.extra.set(exID("camY"), charPos.camyoffset);
char.extra.set(exID("parentNode"), parent);
- char.extra.set(exID("highMemory"), parent.name == "highMemory");
- char.extra.set(exID("lowMemory"), parent.name == "lowMemory");
+ char.extra.set(exID("highMemory"), parent.name == "high-memory");
+ char.extra.set(exID("lowMemory"), parent.name == "low-memory");
chars.push(char);
stage.applyCharStuff(char, charPos.name, 0);
@@ -668,6 +670,8 @@ class StageEditor extends UIState {
saveToXml(spriteXML, "color", sprite.color.toWebString(), "#FFFFFF");
// TODO: save custom parameters
//saveToXml(spriteXML, "flipX", sprite.flipX, false);
+ if (node.hasNode.anim) for (animNode in node.nodes.anim)
+ spriteXML.addChild(animNode.x);
newNode = spriteXML;
} else if(button is StageCharacterButton) {
var button:StageCharacterButton = cast button;
diff --git a/source/funkin/editors/stage/elements/StageSolidButton.hx b/source/funkin/editors/stage/elements/StageSolidButton.hx
index 6ded6d26e8..49ffd1925b 100644
--- a/source/funkin/editors/stage/elements/StageSolidButton.hx
+++ b/source/funkin/editors/stage/elements/StageSolidButton.hx
@@ -8,6 +8,7 @@ class StageSolidButton extends StageSpriteButton {
public function new(x:Float,y:Float, sprite:FunkinSprite, xml:Access) {
super(x,y, sprite, xml);
color = 0xFFD9FF50;
+ hasAdvancedEdit = false;
}
public override function onEdit() {
diff --git a/source/funkin/editors/stage/elements/StageSpriteAnimButton.hx b/source/funkin/editors/stage/elements/StageSpriteAnimButton.hx
new file mode 100644
index 0000000000..651b736da5
--- /dev/null
+++ b/source/funkin/editors/stage/elements/StageSpriteAnimButton.hx
@@ -0,0 +1,99 @@
+package funkin.editors.stage.elements;
+
+import funkin.backend.utils.XMLUtil.AnimData;
+import haxe.xml.Access;
+import funkin.editors.extra.PropertyButton;
+
+class StageSpriteAnimButton extends PropertyButton
+{
+ public var fpsStepper:UINumericStepper;
+ public var editButton:UIButton;
+ public var editIcon:FlxSprite;
+ @:unreflective private var __initialized:Bool = false;
+
+ public var spriteXML:Access;
+ public var animData(default, null):AnimData;
+
+ public function new(anim:AnimData, parent, width:Int = 280, height:Int = 35, nameWidth:Int = 100, valueWidth:Int = 135, inputHeight:Int = 25)
+ {
+ super("", "", parent, width, height, nameWidth, valueWidth, inputHeight);
+ animData = anim;
+ propertyText.onChange = (text) -> animData.name = text;
+ valueText.onChange = (text) -> animData.anim = text;
+
+ var editSize = height - 5 * 2;
+ editButton = new UIButton(deleteButton.x - deleteButton.bWidth - 5, 5, null, openAnimEdit, editSize, editSize);
+ editButton.frames = Paths.getFrames("editors/ui/grayscale-button");
+ editButton.color = 0xFFFF5B0F;
+ editButton.autoAlpha = false;
+ members.insert(members.indexOf(deleteButton), editButton);
+
+ editIcon = new FlxSprite(editButton.x + 8, editButton.y + 10).loadGraphic(Paths.image('editors/stage/edit-button'), true, 16, 16);
+ editIcon.animation.add("advanced", [1]);
+ editIcon.animation.play("advanced");
+ editIcon.antialiasing = false;
+ members.insert(members.indexOf(editButton) + 1, editIcon);
+
+ fpsStepper = new UINumericStepper(valueText.x + valueText.bWidth + 15, 5, 0, 1, 2, 0, null, Math.round(width / 4), 25);
+ fpsStepper.onChange = (text) ->
+ {
+ @:privateAccess fpsStepper.__onChange(text);
+ animData.fps = fpsStepper.value;
+ };
+ members.insert(members.indexOf(deleteButton), fpsStepper);
+
+ __initialized = true;
+ updateDisplay();
+ }
+
+ public function openAnimEdit()
+ {
+ final parent = (FlxG.state.subState is UISoftcodedWindow) ? FlxG.state.subState : FlxG.state;
+ parent.openSubState(new SpriteAnimEditScreen(spriteXML, this, null));
+ }
+
+ public override function updatePos()
+ {
+ super.updatePos();
+ if (!__initialized) return;
+
+ fpsStepper.follow(this, valueText.x + valueText.bWidth, bHeight/2 - (fpsStepper.bHeight/2));
+ editButton.follow(this, deleteButton.x - deleteButton.bWidth - 5, bHeight/2 - (editButton.bHeight/2));
+ editIcon.follow(editButton, editButton.bWidth / 2 - editIcon.width / 2, editButton.bHeight / 2 - editIcon.height / 2);
+ }
+
+ public inline function updateDisplay()
+ {
+ propertyText.label.text = animData.name;
+ valueText.label.text = animData.anim;
+ fpsStepper.value = animData.fps;
+ }
+}
+
+class SpriteAnimEditScreen extends UISoftcodedWindow
+{
+ public var saveCallback:Void->Void;
+ public var parentButton:StageSpriteAnimButton;
+ public var xml:Access;
+
+ inline function translate(id:String, prefix:String = "stageSpriteAnimEditScreen.", ?args:Array)
+ return TU.translate(prefix + id, args);
+
+ public function new(xml:Access, parentButton:StageSpriteAnimButton, saveCallback:Void->Void) {
+ this.saveCallback = saveCallback;
+ this.parentButton = parentButton;
+ this.xml = xml;
+ super("layouts/stage/animEditScreen", [
+ "stage" => StageEditor.instance.stage,
+ "xml" => xml,
+ "exID" => StageEditor.exID,
+ "translate" => translate,
+ "button" => parentButton
+ ]);
+ }
+
+ override function saveData() {
+ super.saveData();
+ if(saveCallback != null) saveCallback();
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/editors/stage/elements/StageUnknownButton.hx b/source/funkin/editors/stage/elements/StageUnknownButton.hx
index a3fb09492a..6c0e5a623c 100644
--- a/source/funkin/editors/stage/elements/StageUnknownButton.hx
+++ b/source/funkin/editors/stage/elements/StageUnknownButton.hx
@@ -8,12 +8,13 @@ class StageUnknownButton extends StageElementButton {
public var lowMemory:Bool = false;
public var highMemory:Bool = false;
public var basic:FlxBasic;
+ var dummy:FunkinSprite; // to allow it to be saved
public function new(x:Float,y:Float, basic:FlxBasic, xml:Access) {
this.basic = basic;
- super(x,y, xml);
+ super(x, y, xml);
- if(xml.x.parent != null) {
+ if (xml.x.parent != null) {
lowMemory = xml.x.parent.nodeName == "low-memory";
highMemory = xml.x.parent.nodeName == "high-memory";
}
@@ -27,8 +28,12 @@ class StageUnknownButton extends StageElementButton {
members.remove(visibilityIcon);
setEditAdvanced();
-
updateInfo();
+
+ dummy = new FunkinSprite();
+ dummy.extra.set(StageEditor.exID("lowMemory"), lowMemory);
+ dummy.extra.set(StageEditor.exID("highMemory"), highMemory);
+ dummy.extra.set(StageEditor.exID("button"), this);
}
public override function update(elapsed:Float) {
@@ -40,11 +45,7 @@ class StageUnknownButton extends StageElementButton {
}
public override function getSprite():FunkinSprite {
- var sprite = new FunkinSprite(); // to allow it to be saved
- sprite.extra.set(StageEditor.exID("lowMemory"), lowMemory);
- sprite.extra.set(StageEditor.exID("highMemory"), highMemory);
- sprite.extra.set(StageEditor.exID("button"), this);
- return sprite;
+ return dummy;
}
public override function canRender() {
@@ -58,10 +59,12 @@ class StageUnknownButton extends StageElementButton {
FlxG.state.openSubState(new StageUnknownEditScreen(this));
}
- //public override function onEdit() {
- // UIState.state.displayNotification(new UIBaseNotification("Editing a character isnt implemented yet!", 2, BOTTOM_LEFT));
- // CoolUtil.playMenuSFX(WARNING, 0.45);
- //}
+ public override function onDelete() {
+ basic.destroy();
+ dummy.destroy();
+ xml.x.parent.removeChild(xml.x);
+ StageEditor.instance.stageSpritesWindow.remove(this);
+ }
public override function onVisiblityToggle() {
}