Skip to content

Commit 2e4caef

Browse files
committed
Merge remote-tracking branch 'origin/main' into internal-merge
2 parents 8b60c24 + fb54e50 commit 2e4caef

29 files changed

Lines changed: 804 additions & 166 deletions
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<window width="660" height="320" title="${translate('win-title', null, [button.animData.name])}">
2+
<set name="MARGIN" value="20" />
3+
<set name="XOFFSET" value="35" />
4+
<set name="YOFFSET" value="40" />
5+
<set name="TO" value="64" />
6+
7+
<exec>
8+
function col(c:Int) {
9+
return MARGIN + (200 + XOFFSET) * c;
10+
}
11+
</exec>
12+
13+
<title name="title" x="MARGIN" y="YOFFSET + 6" text="${translate('anim-title')}"/>
14+
15+
<!-- Alot of terms have already been translated in other places, so they get reused for consistency -->
16+
<section>
17+
<set name="curY" value="title.y + TO"/>
18+
<textbox name="animID" label="${translate('characterAnim.name', 'characterEditor.')}" text="${button.animData.name}" x="col(0)" y="curY" width="120"/>
19+
<stepper name="animFPS" label="${translate('characterAnim.fps', 'characterEditor.')}" value="button.animData.fps" x="last.x + last.bWidth + XOFFSET - 20" y="curY" width="100" precision="2" min="1"/>
20+
<textbox name="animPrefix" label="${translate('characterAnim.anim', 'characterEditor.')}" text="${button.animData.anim}" x="col(1)" y="curY" width="190"/>
21+
<checkbox name="useFrameLabel" value="button.animData.label" x="last.x + last.bWidth + MARGIN" y="curY + 6"/>
22+
<label name="labelText" text="${translate('label')}" x="last.x + last.width + MARGIN - 15" size="15"/>
23+
</section>
24+
25+
<section>
26+
<set name="curY" value="animID.y + animID.bHeight + YOFFSET"/>
27+
<dropdown name="animType" label="${translate('animType', 'stageElementEditScreen.')}" value="button.animData.animType" x="col(0)" y="curY" width="180">
28+
<item label="${translate('animType.none', 'stageElementEditScreen.')}" value="0" />
29+
<item label="${translate('animType.beat', 'stageElementEditScreen.')}" value="1" />
30+
<item label="${translate('animType.loop', 'stageElementEditScreen.')}" value="2" />
31+
</dropdown>
32+
<textbox name="animIndices" label="${translate('characterAnim.indices', 'characterEditor.')}" text="${CoolUtil.formatNumberRange(button.animData.indices, ', ')}" x="col(1)" y="curY" width="200"/>
33+
<stepper name="offsetX" value="button.animData.x" x="col(2)" y="curY" width="100" precision="1"/>
34+
<stepper name="offsetY" value="button.animData.y" x="last.x + last.bWidth + XOFFSET - 20" y="curY" width="100" precision="1"/>
35+
<label name="offsets" text="${translate('offsets')}" x="offsetX.x" y="offsetX.y" size="15"/>
36+
</section>
37+
38+
<section>
39+
<set name="curY" value="self.winHeight - YOFFSET"/>
40+
<checkbox name="loop" text="${translate('characterAnim.looping', 'characterEditor.')}" value="button.animData.loop" x="col(0)" y="curY"/>
41+
<checkbox name="forcedPlay" text="${translate('forcePlay')}" value="CoolUtil.getDefault(button.animData.forced, false)" x="last.x + last.field.width + XOFFSET" y="curY"/>
42+
</section>
43+
44+
<exec>
45+
var prevText:String = animID.label.text;
46+
function onUpdate() {
47+
if (animID.label.text != prevText)
48+
self.winTitle = self.titleSpr.text = translate('win-title', null, [animID.label.text]);
49+
prevText = animID.label.text;
50+
51+
labelText.y = (useFrameLabel.y + (useFrameLabel.height / 2)) - (labelText.height / 2);
52+
}
53+
54+
function onSave() {
55+
button.animData.name = animID.label.text;
56+
button.animData.animType = animType.value;
57+
button.animData.anim = animPrefix.label.text;
58+
button.animData.fps = animFPS.value;
59+
button.animData.x = offsetX.value; button.animData.y = offsetY.value;
60+
button.animData.loop = loop.checked;
61+
button.animData.forced = forcedPlay.checked;
62+
button.animData.indices = CoolUtil.parseNumberRange(animIndices.label.text);
63+
button.animData.label = useFrameLabel.checked;
64+
button.updateDisplay();
65+
}
66+
</exec>
67+
</window>

assets/data/editors/layouts/stage/spriteEditScreen.xml

Lines changed: 103 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,21 @@
66

77
<exec>
88
function col(c:Int) {
9-
return 20 + (200 + XOFFSET) * c;
9+
return MARGIN + (200 + XOFFSET) * c;
1010
}
1111

1212
if(sprite.animateAtlas == null) {
13-
previewSprite = new FunkinSprite(50, self.winHeight - 100);
13+
previewSprite = new FunkinSprite(MARGIN, self.winHeight - (100 + MARGIN));
1414
previewSprite.frame = sprite.frame;
1515

1616
self.add(previewSprite);
1717
}
18+
19+
function animTypeToString(animType:AnimType) {
20+
return ["none", "beat", "loop"][animType];
21+
}
22+
if (getEx('anims') == null)
23+
setEx('anims', sprite.animDatas.copy());
1824
</exec>
1925

2026
<title name="title" x="20" y="30 + 16" text="${translate('sprite-title')}" />
@@ -25,7 +31,7 @@
2531
<textbox name="nameTextBox" label="${translate('nameID')}" value="button.getName()" x="col(0)" y="curY" width="200" height="32" />
2632
<textbox name="spriteTextBox" value="getEx('imageFile')" x="last.x + last.bWidth + XOFFSET" y="curY" width="200" height="32" />
2733
<stepper name="alphaStepper" label="Alpha" value="sprite.alpha" x="spriteTextBox.x + spriteTextBox.bWidth + XOFFSET" y="curY" precision="2" step="0.01" width="100" />
28-
<label name="imageFile" text="${translate('imageFile')}" x="spriteTextBox.x" y="spriteTextBox.y - 14" size="15" />
34+
<label name="imageFile" text="${translate('imageFile', [stage.spritesParentFolder])}" x="spriteTextBox.x" y="spriteTextBox.y - 14" size="15" />
2935
</section>
3036

3137
<section> <!-- ROW 2 -->
@@ -64,8 +70,6 @@
6470
<checkbox name="antialiasingCheckbox" text="${translate('antialiasing')}" value="sprite.antialiasing" x="col(0)" y="curY" />
6571
<radio for="memoryCheck" name="highMemoryRadio" text="${translate('highMemory')}" value="getEx('highMemory')" x="col(1)" y="curY" />
6672
<radio for="memoryCheck" name="lowMemoryRadio" text="${translate('lowMemory')}" value="getEx('lowMemory')" x="col(2)" y="curY" />
67-
68-
<!-- TODO: Animation list -->
6973
</section>
7074

7175
<section> <!-- RIGHT SIDE -->
@@ -77,30 +81,39 @@
7781
<camSpacing value="0" />
7882

7983
<code>
80-
buttonlist.addButton.exists = false;
81-
</code>
82-
83-
<!-- <code>
84-
import funkin.editors.extra.PropertyButton;
84+
//import funkin.backend.utils.XMLUtil.AnimData;
85+
import funkin.editors.stage.elements.StageSpriteAnimButton;
8586

8687
var buttonList = buttonlist; // important for now, since it gets reset after the code is run
87-
8888
var width = buttonList.buttonSize.x;
8989
var height = buttonList.buttonSize.y;
9090

91+
inline function addAnimBtn(animData:AnimData) {
92+
var animButton = new StageSpriteAnimButton(animData, buttonList, width, height, width / 5, width / 3);
93+
animButton.spriteXML = xml;
94+
buttonList.add(animButton);
95+
}
96+
9197
buttonList.addButton.callback = function() {
92-
buttonList.add(new PropertyButton("newProperty", "valueHere", buttonList, width, height, 150, 220, 25));
98+
final dummyAnim = {
99+
name: TranslationUtil.translate('characterEditor.characterAnim.defaultAnimName'),
100+
anim: translate('defaultAnimPrefix'),
101+
fps: 24,
102+
loop: false,
103+
animType: animType.value,
104+
x: 0,
105+
y: 0,
106+
indices: [],
107+
label: false
108+
};
109+
addAnimBtn(dummyAnim);
93110
}
94111

95-
//var defaultAttribs = Stage.DEFAULT_ATTRIBUTES;
96-
//for (i in stage.stageXML.attributes()) {
97-
// trace(i, defaultAttribs);
98-
// if (!defaultAttribs.contains(i))
99-
// buttonList.add(new PropertyButton(i, stage.stageXML.get(i), buttonList, width, height, 150, 220, 25));
100-
//}
101-
for(key =&gt; val in stage.extra)
102-
buttonList.add(new PropertyButton(key, val, buttonList, width, height, 150, 220, 25));
103-
</code> -->
112+
for (animData in getEx('anims')) {
113+
if (animData.name == 'idle' &amp;&amp; animData.anim == null) continue; // Skips dummy animation added by XMLUtil.loadSpriteFromXML
114+
addAnimBtn(animData);
115+
}
116+
</code>
104117
</buttonlist>
105118

106119
<text text="${translate('tipCustomProperties')}" x="col(2) - 60" y="self.winHeight - 50" size="15" />
@@ -111,35 +124,61 @@
111124

112125
<section if="previewSprite != null">
113126
<title name="previewTitle" x="20" y="self.winHeight - 150" text="${translate('transformPreview')}" size="15" />
114-
</section>
127+
</section>
115128

116129
<exec>
130+
var prevText = spriteTextBox.label.text;
131+
var prevID = nameTextBox.label.text;
132+
var spriteExists = true;
133+
var ratio = sprite.width / sprite.height;
134+
135+
spriteTextBox.onChange = function(text) {
136+
var newSprite = stage.spritesParentFolder + text;
137+
var animated = Paths.framesExists(newSprite, true);
138+
spriteExists = ( animated || Assets.exists(Paths.image(newSprite)) );
139+
140+
if (text != prevText &amp;&amp; spriteExists) {
141+
if (animated) previewSprite.frames = Paths.getFrames(newSprite, previewSprite.animateSettings);
142+
else previewSprite.loadGraphic(Paths.image(newSprite));
143+
previewSprite.updateHitbox();
144+
145+
ratio = previewSprite.width / previewSprite.height;
146+
previewSprite.colorTransform.__identity();
147+
} else if (!spriteExists) {
148+
previewSprite.colorTransform.color = 0xFFEF0202;
149+
}
150+
prevText = text;
151+
}
152+
117153
function onUpdate() {
154+
if (nameTextBox.label.text != prevID)
155+
self.winTitle = self.titleSpr.text = translate('win-sprite-title', [nameTextBox.label.text]);
156+
prevID = nameTextBox.label.text;
157+
118158
if(previewSprite == null) return;
119159
previewSprite.color = 0xFFFFFFFF; // TODO: Color Wheel
120160
previewSprite.skew.x = skewXStepper.value;
121161
previewSprite.skew.y = skewYStepper.value;
122162
previewSprite.angle = angleStepper.value;
123163
previewSprite.alpha = alphaStepper.value;
124164
previewSprite.antialiasing = antialiasingCheckbox.checked;
125-
var ratio = sprite.width / sprite.height;
126-
previewSprite.setGraphicSize(50 * scaleXStepper.value * ratio, 50 * scaleYStepper.value);
165+
previewSprite.setGraphicSize(85 * scaleXStepper.value * ratio, 85 * scaleYStepper.value);
127166
previewSprite.updateHitbox();
128167
}
129-
168+
130169
function onSave() {
131170
sprite.name = nameTextBox.label.text;
132171
setEx("imageFile", spriteTextBox.label.text);
133172

134173
setEx("highMemory", highMemoryRadio.checked);
135174
setEx("lowMemory", lowMemoryRadio.checked);
136175

137-
//sprite.x = xStepper.value;
138-
//sprite.y = yStepper.value;
139-
//sprite.zoomFactor = zoomFactorStepper.value;
140-
//sprite.antialiasing = antialiasingCheckbox.checked;
176+
sprite.x = xStepper.value;
177+
sprite.y = yStepper.value;
178+
sprite.zoomFactor = zoomFactorStepper.value;
179+
sprite.antialiasing = antialiasingCheckbox.checked;
141180
//sprite.color = colorWheel.color;
142-
//sprite.spriteAnimType = animType.value;
181+
sprite.spriteAnimType = animTypeToString(animType.value);
143182
sprite.angle = angleStepper.value;
144183
sprite.alpha = alphaStepper.value;
145184

@@ -171,15 +210,48 @@
171210
button.xml.set("x", xStepper.value);
172211
button.xml.set("y", yStepper.value);
173212
button.xml.set("zoom", zoomFactorStepper.value);
174-
button.xml.set("type", animType.key);
213+
button.xml.set("type", animTypeToString(animType.value));
175214
button.xml.set("antialiasing", antialiasingCheckbox.checked);
176215
button.xml.set("angle", angleStepper.value);
177216
button.xml.set("alpha", alphaStepper.value);
178217

218+
getEx('anims').clear();
219+
var allowedAnims:Array&lt;String&gt; = [for (b in animations.buttons.members) b.animData.name];
220+
for (node in button.xml.elementsNamed('anim')) if (!allowedAnims.contains( node.get('name') ))
221+
button.xml.removeChild(node);
222+
223+
for (i in 0...animations.buttons.length) {
224+
var actualChildren:Array&lt;Xml&gt; = button.xml.children.filter((f) -&gt; f.nodeType == 0);
225+
if (i &gt; actualChildren.length - 1) {
226+
var newAnim = Xml.createElement('anim');
227+
button.xml.addChild(newAnim);
228+
actualChildren.push(newAnim);
229+
//trace("added new at " + i + ": " + newAnim);
230+
}
231+
232+
final animData:AnimData = animations.buttons.members[i].animData;
233+
final animNode:Xml = actualChildren[i];
234+
//trace(animNode + "(" + i + ") before");
235+
animNode.set("name", animData.name);
236+
animNode.set("type", animTypeToString(animData.animType)); // XMLAnimType is abstract of int, the code is treating it as just int
237+
animNode.set("anim", animData.anim);
238+
animNode.set("fps", animData.fps);
239+
animNode.set("x", animData.x);
240+
animNode.set("y", animData.y);
241+
animNode.set("loop", animData.loop);
242+
animNode.set("forced", CoolUtil.getDefault(animData.forced, false));
243+
animNode.set("indices", CoolUtil.formatNumberRange(animData.indices));
244+
animNode.set("label", animData.label);
245+
//trace(animNode + "(" + i + ")");
246+
getEx('anims').set(animData.name, animData);
247+
}
248+
179249
saveXY(sprite.scrollFactor, "scroll", scrollXStepper, scrollYStepper);
180250
saveXY(sprite.scale, "scale", scaleXStepper, scaleYStepper);
181251
saveXY(sprite.skew, "skew", skewXStepper, skewYStepper);
182252
XMLUtil.loadSpriteFromXML(sprite, button.xml, stage.spritesParentFolder);
253+
button.updateInfo();
254+
setEx('node', button.xml);
183255
}
184256
</exec>
185257
</window>

assets/languages/en/Editors.xml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,8 +594,9 @@
594594
<str id="title">Stage Info</str>
595595
<str id="stageName">Stage Name</str>
596596
<str id="spritePath">Sprite Path\n(Needs Refresh)</str>
597-
<str id="startCamPos">Start Camera Position (X, Y)</str>
597+
<str id="startCamPos">Start Camera Position\n(X, Y)</str>
598598
<str id="zoom">Zoom</str>
599+
<str id="customAttributes">Custom Attributes</str>
599600
</group>
600601

601602
<group name="StageElementEditScreen" prefix="stageElementEditScreen.">
@@ -609,7 +610,7 @@
609610

610611
<str id="elementName">Element Name</str>
611612
<str id="nameID">Name Identifier</str>
612-
<str id="imageFile">Image File (WIP: needs reload)\nIn (${stage.spritesParentFolder})</str>
613+
<str id="imageFile">Image File\n(in "{0}")</str>
613614
<str id="scroll">Scroll {0}</str>
614615
<str id="scale">Scale {0}</str>
615616
<str id="camera">Camera {0}</str>
@@ -625,6 +626,7 @@
625626
<str id="lowMemory">Low Memory</str>
626627
<str id="attributes">Attributes</str>
627628
<str id="transformPreview">Transform Preview:</str>
629+
<str id="animations">Animations</str>
628630

629631
<str id="animType">Animation Type</str>
630632
<group name="AnimTypeDropdown" prefix="animType.">
@@ -633,6 +635,8 @@
633635
<str id="loop">LOOP</str>
634636
</group>
635637
<str id="tipCustomProperties">Tip: To access custom properties,\nHold Shift when clicking edit</str>
638+
639+
<str id="defaultAnimPrefix">Name on Anim Data</str>
636640
</group>
637641

638642
<group name="StageXMLEditScreen" prefix="stageXMLEditScreen.">
@@ -647,6 +651,7 @@
647651
<group name="StageElement" prefix="stageElement.">
648652
<str id="scale">Scale: {0}, {1}</str>
649653
<str id="scroll">Scroll: {0}, {1}</str>
654+
<str id="unknown">Unknown Sprite:\n{0}</str>
650655
</group>
651656

652657
<group name="StageSprites" prefix="stageSprites.">
@@ -656,6 +661,14 @@
656661
<str id="character">Character</str>
657662
<str id="warnings.not-implemented">Creating a box isnt implemented yet!</str>
658663
</group>
664+
665+
<group name="StageAnimEdit" prefix="stageSpriteAnimEditScreen.">
666+
<str id="win-title">Editing "{0}" Anim</str>
667+
<str id="anim-title">Anim Data</str>
668+
<str id="label">Use Frame Labels?</str>
669+
<str id="offsets">Offset (X, Y)</str>
670+
<str id="forcePlay">Forced Playing?</str>
671+
</group>
659672
</group>
660673

661674
<!-- PlayTesting and SaveWarning are almost the same but with some small differences -->

assets/languages/en/Options.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@
177177
<str id="editorSFX-name">Editor SFXs</str>
178178
<str id="editorSFX-desc">If checked, will play sound effects when working on editors (ex: will play sfxs when checking checkboxes...)</str>
179179

180+
<str id="charterSwapEventSides-name">Swap Event Sides</str>
181+
<str id="charterSwapEventSides-desc">If checked, global events will be on the left of the strumlines.</str>
182+
180183
<str id="editorCharterPrettyPrint-name">Chart Pretty Print</str>
181184
<str id="editorCharterPrettyPrint-desc">If checked, the saved files from the chart editor will be formatted to be easily viewable</str>
182185

assets/languages/es/Editors.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@
551551
<str id="title">Info. del Escenario</str>
552552
<str id="stageName">Nombre del Escenario</str>
553553
<str id="spritePath">Ubicación de Imágenes\n(Necesita Refrescar)</str>
554-
<str id="startCamPos">Posición inicial de la cámara (X, Y)</str>
554+
<str id="startCamPos">Posición inicial de la cámara\n(X, Y)</str>
555555
<str id="zoom">Zoom</str>
556556
</group>
557557

@@ -566,7 +566,7 @@
566566

567567
<str id="elementName">Nombre del Elemento</str>
568568
<str id="nameID">Identificador de Nombre</str>
569-
<str id="imageFile">Imagen (WIP: necesita refrescar)\nEn (${stage.spritesParentFolder})</str>
569+
<str id="imageFile">Imagen\n(en "{0}")</str>
570570
<str id="scroll">Desplazamiento {0}</str>
571571
<str id="scale">Tamaño {0}</str>
572572
<str id="camera">Cámara {0}</str>
@@ -580,6 +580,7 @@
580580
<str id="lowMemory">Baja Memoria</str>
581581
<str id="attributes">Atributos</str>
582582
<str id="transformPreview">Preview:</str>
583+
<str id="animations">Animaciónes</str>
583584

584585
<str id="animType">Tipo de Animación</str>
585586
<group name="AnimTypeDropdown" prefix="animType.">
@@ -602,6 +603,7 @@
602603
<group name="StageElement" prefix="stageElement.">
603604
<str id="scale">Tamaño: {0}, {1}</str>
604605
<str id="scroll">Desplazamiento: {0}, {1}</str>
606+
<str id="unknown">Sprite Desconocido:\n{0}</str>
605607
</group>
606608

607609
<group name="StageSprites" prefix="stageSprites.">

0 commit comments

Comments
 (0)