diff --git a/source/funkin/editors/alphabet/AlphabetEditor.hx b/source/funkin/editors/alphabet/AlphabetEditor.hx index 52f65f35..630fada1 100644 --- a/source/funkin/editors/alphabet/AlphabetEditor.hx +++ b/source/funkin/editors/alphabet/AlphabetEditor.hx @@ -34,17 +34,22 @@ class AlphabetEditor extends UIState { inline function translate(id:String, ?args:Array) return TU.translate("editor.alphabet." + id, args); + public var brokenWarning:UIText; public var tape:Alphabet; public var bigLetter:Alphabet; public var curLetter:Int = 0; public var targetX:Float = 0; + public var queueReorder:Bool = false; public var componentList:UIButtonList; + public var infoWindow:GlyphInfoWindow; public var curSelectedComponent:AlphabetComponent = null; + public var curSelectedData:AlphabetLetterData = null; + public var outlineIdx:Int = -1; public var defaultTmr:Float = 0.0; - public var charsForDefault:Array = []; + public var charsForDefault:Array> = []; public override function create() { super.create(); @@ -167,7 +172,7 @@ class AlphabetEditor extends UIState { tape.renderMode = MONOSPACE; add(tape); - bigLetter = new Alphabet(0, 0, charsForDefault[0], ""); + bigLetter = new Alphabet(0, 0, "", ""); bigLetter.copyData(tape); bigLetter.alignment = CENTER; bigLetter.scale.set(4, 4); @@ -176,27 +181,46 @@ class AlphabetEditor extends UIState { bigLetter.screenCenter(); add(bigLetter); - updateTape(); tape.x = targetX; - var infoWindow = new GlyphInfoWindow(); - infoWindow.info.text = [ - //"Char: " + allChars.charAt(randomIndex), - //"Anim Name: " + allChars.charAt(randomIndex) + " bold0", - "X Offset: " + 0, - "Y Offset: " + 0, - "ScaleX: " + 1, - "ScaleY: " + 1, - "Angle: " + 0, - ].join('\n'); + brokenWarning = new UIText(0, 550, FlxG.width, "", 32); + brokenWarning.alignment = CENTER; + brokenWarning.color = 0xFFFF6969; + add(brokenWarning); + + infoWindow = new GlyphInfoWindow(); uiGroup.add(infoWindow); componentList = new UIButtonList(0, 720 - 170 - 30, 230, 170, "Components:", FlxPoint.get(230, 50), FlxPoint.get(0, 0), 0); componentList.dragCallback = (button, oldID, newID) -> { + queueReorder = true; // not do it for every button reordered } componentList.addButton.callback = () -> { + curSelectedComponent = { + anim: "", + x: 0, + y: 0, + + shouldRotate: false, + angle: 0, + sin: 0, + cos: 1, + + scaleX: 1, + scaleY: 1, + + hasColorMode: false, + colorMode: bigLetter.colorMode + }; + curSelectedData.components.push(curSelectedComponent); + var newButton = new ComponentButton(curSelectedComponent); + componentList.add(newButton); + newButton.ID = componentList.buttons.members.length - 1; // readjust it because UIButtonList doesnt generate the id properly + findOutline(); + infoWindow.updateInfo(); } + updateTape(); uiGroup.add(componentList); add(topMenuSpr); @@ -222,36 +246,65 @@ class AlphabetEditor extends UIState { public override function update(elapsed:Float) { super.update(elapsed); + if (queueReorder) { + queueReorder = false; + var outlines = []; + var newList = []; + for (button in componentList.buttons.members) { + var com = button.component; + if (com.outIndex != null) { + var out = curSelectedData.components[com.outIndex]; + out.refIndex = newList.length; + com.outIndex = outlines.length; + outlines.push(out); + } + newList.push(com); + } + curSelectedData.components = outlines.concat(newList); - if (true) { + findOutline(); + for (i in 0...curSelectedData.components.length) { + var anim = bigLetter.text + i; + bigLetter.animation.remove(anim); + tape.animation.remove(anim); + } + } + + if (currentFocus == null) { if(FlxG.keys.justPressed.ANY) UIUtil.processShortcuts(topMenu); + + if(curSelectedComponent != null) { + if(FlxG.keys.pressed.K) { + curSelectedComponent.y -= 100 * elapsed; + } + if(FlxG.keys.pressed.I) { + curSelectedComponent.y += 100 * elapsed; + } + if(FlxG.keys.pressed.L) { + curSelectedComponent.x -= 100 * elapsed; + } + if(FlxG.keys.pressed.J) { + curSelectedComponent.x += 100 * elapsed; + } + } } - if(curSelectedComponent != null) { - if(FlxG.keys.pressed.I) { - curSelectedComponent.y -= 100 * elapsed; - } - if(FlxG.keys.pressed.K) { - curSelectedComponent.y += 100 * elapsed; - } - if(FlxG.keys.pressed.J) { - curSelectedComponent.x -= 100 * elapsed; - } - if(FlxG.keys.pressed.L) { - curSelectedComponent.x += 100 * elapsed; - } - } var lastIdx = Math.floor(defaultTmr); defaultTmr += elapsed * 0.5; - if (lastIdx != (lastIdx = Math.floor(defaultTmr))) - tape.text = charsForDefault[Math.floor(defaultTmr)] + " " + tape.manualLetters.join(" "); + if (lastIdx != (lastIdx = Math.floor(defaultTmr))) { + tape.text = ""; + for (def in charsForDefault) + tape.text += def[Std.int(lastIdx % def.length)] + " "; + tape.text += tape.manualLetters.join(" "); + } tape.x = lerp(tape.x, targetX, 0.25); } function updateTape() { - charsForDefault = []; + charsForDefault = [[], [], []]; + for (i in 33...127) { var letter = String.fromCharCode(i); if (tape.manualLetters.contains(letter)) continue; @@ -261,19 +314,76 @@ class AlphabetEditor extends UIState { if (tape.fastGetLetterAnim(letter, data, com, i) == null) continue; } - charsForDefault.push(letter); + + if (i >= 'A'.code && i <= 'Z'.code && tape.defaults[CaseMode.UPPER] != null) + charsForDefault[CaseMode.UPPER].push(letter); + else if (i >= 'a'.code && i <= 'z'.code && tape.defaults[CaseMode.LOWER] != null) + charsForDefault[CaseMode.LOWER].push(letter); + else if (tape.defaults[CaseMode.NONE] != null) + charsForDefault[CaseMode.NONE].push(letter); } - defaultTmr %= charsForDefault.length; - tape.text = charsForDefault[Math.floor(defaultTmr)] + " " + tape.manualLetters.join(" "); + + var i = charsForDefault.length - 1; + while (i >= 0) { + if (charsForDefault[i].length <= 0) + charsForDefault.splice(i, 1); + --i; + } + + tape.text = ""; + for (def in charsForDefault) + tape.text += def[Math.floor(defaultTmr % def.length)] + " "; + tape.text += tape.manualLetters.join(" "); changeLetter(0); } function changeLetter(inc:Int) { - curLetter = CoolUtil.positiveModuloInt(curLetter + inc, tape.manualLetters.length + 1); + curLetter = CoolUtil.positiveModuloInt(curLetter + inc, tape.manualLetters.length + charsForDefault.length); targetX = FlxG.width * 0.5 - tape.defaultAdvance * (0.5 + curLetter * 2); - bigLetter.text = (curLetter == 0) ? charsForDefault[0] : tape.manualLetters[curLetter - 1]; + bigLetter.text = (curLetter < charsForDefault.length) ? charsForDefault[curLetter][0] : tape.manualLetters[curLetter - charsForDefault.length]; bigLetter.updateHitbox(); bigLetter.screenCenter(); + + checkForFailed(); + + while (componentList.buttons.members.length > 0) + componentList.remove(componentList.buttons.members[0]); + for (i in curSelectedData.startIndex...curSelectedData.components.length) { + var newButton = new ComponentButton(curSelectedData.components[i]); + componentList.add(newButton); + newButton.ID = componentList.buttons.members.length - 1; // readjust it because UIButtonList doesnt generate the id properly + } + curSelectedComponent = (curSelectedData.components.length > 0) ? curSelectedData.components[curSelectedData.startIndex] : null; + findOutline(); + infoWindow.updateInfo(); + } + + public function findOutline() { + outlineIdx = -1; + for (i in curSelectedData.startIndex...curSelectedData.components.length) { + var com = curSelectedData.components[i]; + if (com.outIndex != null) + outlineIdx = com.outIndex; + if (com == curSelectedComponent) + break; + } + ++outlineIdx; + } + + public function checkForFailed() { + bigLetter.failedLetters.remove(bigLetter.text); + curSelectedData = bigLetter.fastGetData(bigLetter.text); + brokenWarning.text = ""; + bigLetter.draw(); + if (!bigLetter.failedLetters.contains(bigLetter.text)) + return false; + + for (i in 0...curSelectedData.components.length) { + var com = curSelectedData.components[i]; + if (!bigLetter.animation.exists(bigLetter.text + Std.string(i))) + brokenWarning.text += " Unable to find: " + com.anim + "\n"; + } + return true; } function _tape_left(_) { @@ -331,32 +441,59 @@ class ComponentButton extends UIButton { public var selected:Bool = false; public var deleteButton:UIButton; + public var deleteIcon:FlxSprite; public function new(component:AlphabetComponent) { super(0, 0, component.anim, function() { AlphabetEditor.instance.curSelectedComponent = component; + AlphabetEditor.instance.findOutline(); + AlphabetEditor.instance.infoWindow.updateInfo(); }, 230, 50); this.component = component; - deleteButton = new UIButton(bWidth - 32, 0, "Delete", function() { + deleteButton = new UIButton(bWidth - 32 - 10, 0, "", function() { + var data = AlphabetEditor.instance.curSelectedData; + var outlineDrop = (component.outIndex != null) ? 1 : 0; + var componentIndex = data.components.indexOf(component); + for (i in 0...data.components.length) { + var nextCompon = data.components[i]; + var anim = AlphabetEditor.instance.bigLetter.text + i; + AlphabetEditor.instance.bigLetter.animation.remove(anim); + AlphabetEditor.instance.tape.animation.remove(anim); + + if (i > componentIndex && nextCompon.outIndex != null) { + --data.components[nextCompon.outIndex].refIndex; + nextCompon.outIndex -= outlineDrop; + } + } + + if (component.outIndex != null) { + data.components.splice(component.outIndex, 1); + --data.startIndex; + } + + data.components.remove(component); + AlphabetEditor.instance.curSelectedComponent = (AlphabetEditor.instance.curSelectedComponent == component) ? null : AlphabetEditor.instance.curSelectedComponent; + AlphabetEditor.instance.findOutline(); + AlphabetEditor.instance.infoWindow.updateInfo(); + + AlphabetEditor.instance.componentList.remove(this); }, 32); deleteButton.color = FlxColor.RED; deleteButton.autoAlpha = false; members.push(deleteButton); + + deleteIcon = new FlxSprite(deleteButton.x + (15/2), deleteButton.y + 8).loadGraphic(Paths.image('editors/delete-button')); + deleteIcon.antialiasing = false; + members.push(deleteIcon); } -} -class GlyphInfoWindow extends UIWindow { - public var info:UIText; - public function new() { - var width = 230; - var height = 170; - var margin = 30; - super(FlxG.width - width - margin, FlxG.height - height - margin, width, height, "Glyph Info"); + override function update(elapsed:Float) { + super.update(elapsed); - info = new UIText(x + 28, y + 46, 400, ""); - info.alignment = LEFT; - members.push(info); + deleteButton.y = y + 10; + deleteIcon.x = deleteButton.x + (15/2); + deleteIcon.y = deleteButton.y + 8; } } \ No newline at end of file diff --git a/source/funkin/editors/alphabet/GlyphInfoWindow.hx b/source/funkin/editors/alphabet/GlyphInfoWindow.hx new file mode 100644 index 00000000..766c0bca --- /dev/null +++ b/source/funkin/editors/alphabet/GlyphInfoWindow.hx @@ -0,0 +1,274 @@ +package funkin.editors.alphabet; + +import flixel.math.FlxAngle; + +class GlyphInfoWindow extends UIWindow { + public var prefixBox:UITextBox; + public var xBox:UINumericStepper; // LIVE + public var yBox:UINumericStepper; // MJWF + public var scaleXBox:UINumericStepper; + public var scaleYBox:UINumericStepper; + public var angleBox:UINumericStepper; + public var colorModeDrop:UIDropDown; + + public var outlineTitle:UIText; + public var outlineSep:UISprite; + public var outlineCheck:UICheckbox; + public var outlineBoxTitle:UIText; + public var outlineBox:UITextBox; + public var outlineOffTitle:UIText; + public var outlineXBox:UINumericStepper; + public var outlineYBox:UINumericStepper; + + public var targetPercent:Float = 0; + public var outlinePercent:Float = 0; + public var outlineAlphaMults:Array; + public var outlineItems:Array; + public var initialY:Array = []; + + var compon(get, never):AlphabetComponent; + var data(get, never):AlphabetLetterData; + + public function new() { + var width = 360; + var height = 255; + var margin = 30; + var itemMargin = 15; // there was already a buncha variables so i thought why not follow + var labelOffset = 24; + super(FlxG.width - width - margin, FlxG.height - height - margin, width, height, "Glyph Info"); + + function addLabelOn(ui:UISprite, text:String) { + var title = new UIText(ui.x, ui.y - labelOffset, 0, text); + members.push(title); + return title; + } + + prefixBox = new UITextBox(x + itemMargin, y + itemMargin + labelOffset * 2, "", 330); + prefixBox.onChange = function(val) { + var index = data.components.indexOf(compon); + compon.anim = val; + var anim = AlphabetEditor.instance.bigLetter.text + Std.string(index); + AlphabetEditor.instance.bigLetter.animation.remove(anim); + AlphabetEditor.instance.tape.animation.remove(anim); + AlphabetEditor.instance.checkForFailed(); + } + members.push(prefixBox); + addLabelOn(prefixBox, "Animation Prefix"); + + xBox = new UINumericStepper(prefixBox.x, prefixBox.y + prefixBox.bHeight + itemMargin + labelOffset, 0, 1, 2, null, null, 80); + xBox.onChange = valueSet.bind(xBox, function(val) { // kinda dumb but blame cne ui + compon.x = val; + }); + members.push(xBox); + + members.push(new UIText(xBox.x + xBox.bWidth - 2, xBox.y + 9, 0, ",", 22)); + + yBox = new UINumericStepper(xBox.x + xBox.bWidth + itemMargin, xBox.y, 0, 1, 2, null, null, 80); + yBox.onChange = valueSet.bind(yBox, function(val) { // kinda dumb but blame cne ui + compon.y = val; + }); + members.push(yBox); + addLabelOn(xBox, "Offset (x,y)"); + + scaleXBox = new UINumericStepper(yBox.x + yBox.bWidth + itemMargin * 2, yBox.y, 1, 0.1, 2, null, null, 80); + scaleXBox.onChange = valueSet.bind(scaleXBox, function(val) { // kinda dumb but blame cne ui + compon.scaleX = val; + if (outlineCheck.checked) + data.components[compon.outIndex].scaleX = val; + }); + members.push(scaleXBox); + + members.push(new UIText(scaleXBox.x + scaleXBox.bWidth - 2, scaleXBox.y + 9, 0, ",", 22)); + + scaleYBox = new UINumericStepper(scaleXBox.x + scaleXBox.bWidth + itemMargin, scaleXBox.y, 1, 0.1, 2, null, null, 80); + scaleYBox.onChange = valueSet.bind(scaleYBox, function(val) { // kinda dumb but blame cne ui + compon.scaleY = val; + if (outlineCheck.checked) + data.components[compon.outIndex].scaleY = val; + }); + members.push(scaleYBox); + addLabelOn(scaleXBox, "Scale (x,y)"); + + angleBox = new UINumericStepper(scaleYBox.x + scaleYBox.bWidth + itemMargin * 2, scaleYBox.y, 0, 1, 2, null, null, 80); + angleBox.onChange = valueSet.bind(angleBox, function(val) { // kinda dumb but blame cne ui + compon.shouldRotate = val != 0; + compon.angle = val; + compon.cos = Math.cos(val * FlxAngle.TO_RAD); + compon.sin = Math.sin(val * FlxAngle.TO_RAD); + + if (outlineCheck.checked) { + data.components[compon.outIndex].shouldRotate = compon.shouldRotate; + data.components[compon.outIndex].angle = compon.angle; + data.components[compon.outIndex].cos = compon.cos; + data.components[compon.outIndex].sin = compon.sin; + } + }); + members.push(angleBox); + addLabelOn(angleBox, "Angle"); + + colorModeDrop = new UIDropDown(xBox.x, xBox.y + xBox.bHeight + itemMargin + labelOffset, 145, 32, ["TINT", "OFFSETS", "NONE"]); + colorModeDrop.onChange = function(mode) { + compon.colorMode = mode; + @:privateAccess compon.hasColorMode = mode != AlphabetEditor.instance.tape.colorMode; + if (outlineCheck.checked) { + data.components[compon.outIndex].colorMode = mode; + @:privateAccess data.components[compon.outIndex].hasColorMode = compon.hasColorMode; + } + }; + members.push(colorModeDrop); + addLabelOn(colorModeDrop, "Color Mode"); + + outlineCheck = new UICheckbox(colorModeDrop.x + colorModeDrop.bWidth + itemMargin * 2, colorModeDrop.y + 4, "Use Outline?"); + outlineCheck.onChecked = function(val) { + targetPercent = val ? 1 : 0; + + for (i in 0...data.components.length) { + var anim = AlphabetEditor.instance.bigLetter.text + i; + AlphabetEditor.instance.bigLetter.animation.remove(anim); + AlphabetEditor.instance.tape.animation.remove(anim); + } + + if (val) { + compon.outIndex = AlphabetEditor.instance.outlineIdx; + + var newOutline:AlphabetComponent = { + refIndex: data.components.indexOf(compon), + anim: outlineBox.label.text, + + x: outlineXBox.value, + y: outlineYBox.value, + scaleX: compon.scaleX, + scaleY: compon.scaleY, + + shouldRotate: compon.shouldRotate, + angle: compon.angle, + cos: compon.cos, + sin: compon.sin, + + hasColorMode: compon.hasColorMode, + colorMode: compon.colorMode + }; + + data.components.insert(AlphabetEditor.instance.outlineIdx, newOutline); + ++data.startIndex; + } else { + compon.outIndex = null; + data.components.splice(AlphabetEditor.instance.outlineIdx - 1, 1); + --data.startIndex; + } + } + members.push(outlineCheck); + + outlineTitle = new UIText(colorModeDrop.x, colorModeDrop.y + colorModeDrop.bHeight + itemMargin, 0, "Outline Data"); + members.push(outlineTitle); + outlineSep = new UISprite(x + itemMargin, colorModeDrop.y + colorModeDrop.bHeight + itemMargin + 19); + outlineSep.makeSolid(width - itemMargin * 2, 1, 0x40FFFFFF); + members.push(outlineSep); + + outlineBox = new UITextBox(colorModeDrop.x, colorModeDrop.y + colorModeDrop.bHeight + itemMargin + labelOffset * 2 + 3, "", 190); + outlineBox.onChange = function(val) { + data.components[compon.outIndex].anim = val; + var anim = AlphabetEditor.instance.bigLetter.text + Std.string(compon.outIndex); + AlphabetEditor.instance.bigLetter.animation.remove(anim); + AlphabetEditor.instance.tape.animation.remove(anim); + AlphabetEditor.instance.checkForFailed(); + } + members.push(outlineBox); + outlineBoxTitle = addLabelOn(outlineBox, "Prefix"); + + outlineXBox = new UINumericStepper(outlineBox.x + outlineBox.bWidth + itemMargin * 2, outlineBox.y, 0, 1, 2, null, null, 80); + outlineXBox.onChange = valueSet.bind(outlineXBox, function(val) { // kinda dumb but blame cne ui + if (outlineCheck.checked) + data.components[compon.outIndex].x = val; + }); + members.push(outlineXBox); + + members.push(new UIText(outlineXBox.x + outlineXBox.bWidth - 2, outlineXBox.y + 9, 0, ",", 22)); + + outlineYBox = new UINumericStepper(outlineXBox.x + outlineXBox.bWidth + itemMargin, outlineXBox.y, 0, 1, 2, null, null, 80); + outlineYBox.onChange = valueSet.bind(outlineYBox, function(val) { // kinda dumb but blame cne ui + if (outlineCheck.checked) + data.components[compon.outIndex].y = val; + }); + members.push(outlineYBox); + outlineOffTitle = addLabelOn(outlineXBox, "Offset (x,y)"); + + for (mem in members) { + if (mem is FlxObject) + initialY.push(cast (mem, FlxObject).y); + } + outlineAlphaMults = [1.0, 0.9, 0.5, 0.15, 0.15, 0.5, 0.15, 0.15, 0.15, 0.15]; + outlineItems = [outlineTitle, outlineSep, outlineBoxTitle, outlineBox, outlineBox.label, outlineOffTitle, outlineXBox, outlineXBox.label, outlineYBox, outlineYBox.label]; + } + + override public function update(elapsed:Float) { + super.update(elapsed); + + outlinePercent = FlxMath.lerp(outlinePercent, targetPercent, elapsed * 10); + bHeight = Std.int(255 + 105 * outlinePercent); + y = FlxG.height - bHeight - 30; + + for (i in 0...outlineItems.length) + outlineItems[i].alpha = outlinePercent * FlxMath.lerp(outlineAlphaMults[i], 1, outlinePercent * outlinePercent) * alpha; + + var yIdxOff = 0; + for (i in 0...members.length) { + if (members[i] is FlxObject) + cast (members[i], FlxObject).y = initialY[i - yIdxOff] - (bHeight - 255); + else + ++yIdxOff; + } + } + + public function updateInfo() { + var com = compon; + if (com == null) { + for (item in members) { + if (item is UISprite) + cast(item, UISprite).selectable = false; + } + colorModeDrop.dropButton.selectable = false; + return; + } + + prefixBox.label.text = com.anim; + xBox.value = com.x; + yBox.value = com.y; + scaleXBox.value = com.scaleX; + scaleYBox.value = com.scaleY; + angleBox.value = com.angle; + @:privateAccess colorModeDrop.index = com.hasColorMode ? com.colorMode : AlphabetEditor.instance.tape.colorMode; + @:privateAccess colorModeDrop.label.text = colorModeDrop.options[colorModeDrop.index]; + + outlineCheck.checked = com.outIndex != null; + outlineBox.label.text = ""; + outlineXBox.value = 0; + outlineYBox.value = 0; + targetPercent = 0; + if (com.outIndex != null) { + var out = data.components[com.outIndex]; + outlineBox.label.text = out.anim; + outlineXBox.value = out.x; + outlineYBox.value = out.y; + targetPercent = 1; + } + + for (item in members) { + if (item is UISprite) + cast(item, UISprite).selectable = true; + } + colorModeDrop.dropButton.selectable = true; + } + + function valueSet(item:UINumericStepper, func:Dynamic, text:String) { + @:privateAccess item.__onChange(text); + func(item.value); + } + + function get_compon() { + return AlphabetEditor.instance.curSelectedComponent; + } + function get_data() { + return AlphabetEditor.instance.curSelectedData; + } +} \ No newline at end of file diff --git a/source/funkin/menus/ui/Alphabet.hx b/source/funkin/menus/ui/Alphabet.hx index 9aa2ef0a..261e8450 100644 --- a/source/funkin/menus/ui/Alphabet.hx +++ b/source/funkin/menus/ui/Alphabet.hx @@ -24,6 +24,7 @@ final class AlphabetOutline { @:structInit final class AlphabetComponent { @:optional public var refIndex:Null; + @:optional public var outIndex:Null; public var anim:String; public var x:Float; public var y:Float; @@ -279,7 +280,7 @@ class Alphabet extends FlxSprite { advance = (Math.isNaN(advance)) ? getAdvance(letter, anim, data) : advance; if (anim == null || __renderData.alpha <= 0.0) - break; // shouldnt this be like continue? + continue; var frameToGet = frameTime % anim.numFrames; frame = frames.frames[anim.frames[frameToGet]]; @@ -420,7 +421,7 @@ class Alphabet extends FlxSprite { } var data = getData(letter); - __laneWidths[curLine] += (data != null) ? getAdvance(letter, getLetterAnim(letter, data, data.components[data.startIndex], data.startIndex), data) : defaultAdvance; + __laneWidths[curLine] += (data != null && data.components.length > 0) ? getAdvance(letter, getLetterAnim(letter, data, data.components[data.startIndex], data.startIndex), data) : defaultAdvance; @:bypassAccessor textWidth = Math.max(textWidth, __laneWidths[curLine]); } @@ -613,6 +614,7 @@ class Alphabet extends FlxSprite { } components.push({ + outIndex: (component.get("hasOutline") == "true") ? startIndex - 1 : null, anim: component.get("anim"), x: xOff, @@ -680,6 +682,7 @@ class Alphabet extends FlxSprite { } components.push({ + outIndex: (node.get("hasOutline") == "true") ? 0 : null, anim: node.firstChild().nodeValue, x: xOff,