diff --git a/assets/data/alphabet/bold.xml b/assets/data/alphabet/bold.xml
new file mode 100644
index 00000000..b39cc25f
--- /dev/null
+++ b/assets/data/alphabet/bold.xml
@@ -0,0 +1,6 @@
+
+
+ menus/alphabet-bold
+
+ LETTER bold
+
\ No newline at end of file
diff --git a/assets/data/alphabet/normal.xml b/assets/data/alphabet/normal.xml
new file mode 100644
index 00000000..7b7bb076
--- /dev/null
+++ b/assets/data/alphabet/normal.xml
@@ -0,0 +1,23 @@
+
+
+ menus/alphabet
+
+ LETTER capital
+ LETTER lowercase
+ LETTER
+
+ dollarsign
+ hashtag
+ amp
+ right arrow
+ left arrow
+ down arrow
+ up arrow
+ comma
+ period
+ question mark
+ start parentheses
+ ^
+ ~
+ =
+
\ No newline at end of file
diff --git a/project.xml b/project.xml
index 9c69253d..25ad3308 100644
--- a/project.xml
+++ b/project.xml
@@ -153,6 +153,9 @@
+
+
+
diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx
index ff3e9f56..51f87e40 100644
--- a/source/funkin/backend/system/MainState.hx
+++ b/source/funkin/backend/system/MainState.hx
@@ -61,12 +61,16 @@ class MainState extends FlxState {
EventsData.reloadEvents();
TitleState.initialized = false;
+ #if (ALPHABET_TESTING)
+ FlxG.switchState(new funkin.menus.ui.NewAlphabetTest());
+ #else
if (betaWarningShown)
FlxG.switchState(new TitleState());
else {
FlxG.switchState(new BetaWarningState());
betaWarningShown = true;
}
+ #end
CoolUtil.safeSetAttribute('./.temp/', HIDDEN);
}
diff --git a/source/funkin/menus/ui/NewAlphabet.hx b/source/funkin/menus/ui/NewAlphabet.hx
new file mode 100644
index 00000000..5e735ffe
--- /dev/null
+++ b/source/funkin/menus/ui/NewAlphabet.hx
@@ -0,0 +1,190 @@
+package funkin.menus.ui;
+
+import haxe.xml.Access;
+
+using StringTools;
+
+enum abstract AlphabetAlignment(String) from String to String {
+ var LEFT = "left";
+ var CENTER = "center";
+ var RIGHT = "right";
+
+ public function getMultiplier():Float {
+ return switch(this) {
+ case CENTER: 0.5;
+ case RIGHT: 1.0;
+ default: 0.0;
+ }
+ }
+}
+
+class NewAlphabet extends FlxSprite {
+ public var text(default, set):String = "";
+ @:isVar public var textWidth(get, set):Float;
+ public var textHeight(get, null):Float;
+ public var alignment:AlphabetAlignment = LEFT;
+ var __laneWidths:Array = []; // TODO: add alignment.
+ var __forceWidth:Float = 0; // TODO: implement functionality for this.
+ var __queueResize:Bool = false;
+ var __animTime:Float = 0;
+
+ public var font(default, set):String;
+ var defaultAdvance:Float = 40;
+ var forceCase:String = "none";
+ var lineGap:Float = 75;
+ var fps:Int = 24;
+ var defaults:Map = [];
+ var anims:Map = [];
+ var failedLetters:Array = [];
+ var yOffset:Map = [];
+
+ public function new(?x:Float, ?y:Float, ?font:String = "bold") {
+ super(x, y);
+ this.font = font;
+ }
+
+ override function draw() {
+ __animTime += FlxG.elapsed;
+ var curLine = 0;
+ var daText = switch (forceCase) {
+ case "upper": text.toUpperCase();
+ case "lower": text.toLowerCase();
+ default: text;
+ }
+
+ var ogOffX = frameOffset.x;
+ var ogOffY = frameOffset.y;
+ frameOffset.addPoint(origin);
+ frameOffset.x -= (textWidth - __laneWidths[0]) * alignment.getMultiplier();
+ offset.subtractPoint(origin);
+
+ for (i in 0...daText.length) {
+ var letter = daText.charAt(i);
+ if (letter == "\n") {
+ curLine++;
+ frameOffset.x = textWidth * 0.5 - (textWidth - __laneWidths[curLine]) * alignment.getMultiplier();
+ frameOffset.y -= lineGap;
+ continue;
+ }
+
+ var anim = getLetterAnim(letter);
+ if (anim == null) {
+ frameOffset.x -= defaultAdvance;
+ } else {
+ var frameToGet = Math.floor(__animTime * fps) % anim.numFrames;
+ frame = frames.frames[anim.frames[frameToGet]];
+ var offsetY = frame.sourceSize.y - lineGap;
+ frameOffset.y += offsetY + (yOffset.exists(letter) ? yOffset.get(letter) : 0);
+ super.draw();
+ frameOffset.y -= offsetY + (yOffset.exists(letter) ? yOffset.get(letter) : 0);
+ frameOffset.x -= frame.sourceSize.x;
+ }
+ }
+
+ frameOffset.set(ogOffX, ogOffY);
+ offset.addPoint(origin);
+ }
+
+ override function updateHitbox() {
+ width = Math.abs(scale.x) * textWidth;
+ height = Math.abs(scale.y) * textHeight;
+ offset.set(-0.5 * (width - textWidth), -0.5 * (height - textHeight));
+ origin.set(textWidth * 0.5, textHeight * 0.5);
+ }
+
+ function recalcSizes():Void {
+ __queueResize = false;
+ var curLine = 0;
+ var daText = switch (forceCase) {
+ case "upper": text.toUpperCase();
+ case "lower": text.toLowerCase();
+ default: text;
+ }
+ @:bypassAccessor textWidth = 0;
+ textHeight = lineGap;
+ __laneWidths = [0];
+ for (i in 0...daText.length) {
+ var letter = daText.charAt(i);
+ if (letter == "\n") {
+ textHeight += lineGap;
+ __laneWidths.push(0);
+ curLine++;
+ continue;
+ }
+
+ var anim = getLetterAnim(letter);
+ __laneWidths[curLine] += (anim != null) ? frames.frames[anim.frames[0]].sourceSize.x : defaultAdvance;
+ @:bypassAccessor textWidth = Math.max(textWidth, __laneWidths[curLine]);
+ }
+
+ origin.set(textWidth * 0.5, textHeight * 0.5);
+ }
+
+ function getLetterAnim(char:String) {
+ if (failedLetters.contains(char)) return null;
+ else if (animation.exists(char)) return animation.getByName(char);
+
+ var charCode:Int = char.charCodeAt(0);
+
+ var anim:String = defaults.get("").replace("LETTER", char);
+ if (charCode >= 65 && charCode <= 90 && defaults.exists("UPPER"))
+ anim = defaults.get("UPPER").replace("LETTER", char);
+ else if (charCode >= 97 && charCode <= 122 && defaults.exists("LOWER"))
+ anim = defaults.get("LOWER").replace("LETTER", char);
+ if (anims.exists(char))
+ anim = anims.get(char);
+
+ animation.addByPrefix(char, anim, fps);
+ if (!animation.exists(char)) {
+ failedLetters.push(char);
+ return null;
+ }
+ return animation.getByName(char);
+ }
+
+ function get_textWidth():Float {
+ if (__queueResize)
+ recalcSizes();
+ return textWidth;
+ }
+ function set_textWidth(value:Float):Float {
+ __queueResize = __queueResize || (__forceWidth != value);
+ return __forceWidth = value;
+ }
+
+ function get_textHeight():Float {
+ if (__queueResize)
+ recalcSizes();
+ return textHeight;
+ }
+
+ function set_text(value:String):String {
+ __queueResize = __queueResize || (text != value);
+ return text = value;
+ }
+
+ function set_font(value:String):String {
+ if (font != value) {
+ __queueResize = true;
+ trace(Paths.xml("alphabet/" + value));
+ var xml:Xml = Xml.parse(openfl.Assets.getText(Paths.xml("alphabet/" + value))).firstElement();
+ defaultAdvance = Std.parseFloat(xml.get("advance")).getDefault(40);
+ lineGap = Std.parseFloat(xml.get("lineGap")).getDefault(75);
+ fps = Std.parseInt(xml.get("fps")).getDefault(24);
+ forceCase = xml.get("forceCasing").getDefault("none").toLowerCase();
+ for (node in xml.elements()) {
+ switch (node.nodeName) {
+ case "spritesheet":
+ frames = Paths.getFrames(node.firstChild().nodeValue);
+ case "defaultAnim":
+ defaults.set(node.get("casing").getDefault("").toUpperCase(), node.firstChild().nodeValue);
+ case "anim":
+ anims.set(node.get("char"), node.firstChild().nodeValue);
+ if (node.exists("y"))
+ yOffset.set(node.get("char"), Std.parseFloat(node.get("y")).getDefault(0));
+ }
+ }
+ }
+ return font = value;
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/menus/ui/NewAlphabetTest.hx b/source/funkin/menus/ui/NewAlphabetTest.hx
new file mode 100644
index 00000000..ead653a4
--- /dev/null
+++ b/source/funkin/menus/ui/NewAlphabetTest.hx
@@ -0,0 +1,38 @@
+package funkin.menus.ui;
+
+#if (ALPHABET_TESTING)
+/**
+ * TEMPORARY CLASS.
+ */
+class NewAlphabetTest extends MusicBeatState {
+ var alphabet:NewAlphabet;
+
+ override function create() {
+ super.create();
+ bgColor = 0xFF808080;
+
+ alphabet = new NewAlphabet(100, 360, "bold");
+ alphabet.text = "hi tarr\ninsert smiley face";
+ alphabet.alignment = CENTER;
+ add(alphabet);
+ }
+
+ override function update(elapsed:Float) {
+ super.update(elapsed);
+
+ if (controls.ACCEPT) {
+ switch (alphabet.alignment) {
+ case LEFT:
+ alphabet.text = "hey dont put me\non center.";
+ alphabet.alignment = CENTER;
+ case CENTER:
+ alphabet.text = "hey put me\nback! >:(";
+ alphabet.alignment = RIGHT;
+ case RIGHT:
+ alphabet.text = "Thank you.\nJeez some people.";
+ alphabet.alignment = LEFT;
+ }
+ }
+ }
+}
+#end
\ No newline at end of file