diff --git a/assets/data/alphabet.xml b/assets/data/alphabet.xml index 8ae172cd..9eef31ca 100644 --- a/assets/data/alphabet.xml +++ b/assets/data/alphabet.xml @@ -2,33 +2,77 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/data/alphabet/bold.xml b/assets/data/alphabet/bold.xml index b39cc25f..266036d5 100644 --- a/assets/data/alphabet/bold.xml +++ b/assets/data/alphabet/bold.xml @@ -1,6 +1,50 @@ - + menus/alphabet-bold - LETTER bold + character-LETTER0 + character-ampersand + character-anglebracket-left + character-anglebracket-right + character-asterisk + character-at + character-backslash + character-backtick + character-caret + character-colon + character-comma + character-curlybracket-left + character-curlybracket-right + character-dollar + character-doublequote + character-equal + character-exclamationmark + character-hyphen + character-percent + character-period + character-plus + character-pound + character-questionmark + character-roundbracket-left + character-roundbracket-right + character-semicolon + character-singlequote + character-slash + character-squarebracket-left + character-squarebracket-right + character-tilde + character-underscore + character-verticalbar + character-asterisk + + character-zero + character-one + character-two + character-three + character-four + character-five + character-six + character-seven + character-eight + character-nine \ No newline at end of file diff --git a/assets/data/alphabet/normal.xml b/assets/data/alphabet/normal.xml index 02dbc2e5..a82323fe 100644 --- a/assets/data/alphabet/normal.xml +++ b/assets/data/alphabet/normal.xml @@ -12,7 +12,6 @@ q lowercase y lowercase dollarsign - hashtag amp right arrow left arrow diff --git a/assets/images/menus/alphabet-bold.png b/assets/images/menus/alphabet-bold.png index 012721ce..004a99f5 100644 Binary files a/assets/images/menus/alphabet-bold.png and b/assets/images/menus/alphabet-bold.png differ diff --git a/assets/images/menus/alphabet-bold.xml b/assets/images/menus/alphabet-bold.xml index 26ce48db..7ffb4267 100644 --- a/assets/images/menus/alphabet-bold.xml +++ b/assets/images/menus/alphabet-bold.xml @@ -1,153 +1,514 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/languages/en/Options.xml b/assets/languages/en/Options.xml index f7debfaf..51e317eb 100644 --- a/assets/languages/en/Options.xml +++ b/assets/languages/en/Options.xml @@ -42,8 +42,7 @@ Engine - - Change Mod or Mode + Mod/Mode diff --git a/source/funkin/editors/EditorPicker.hx b/source/funkin/editors/EditorPicker.hx index 3fb4c5d9..dbc30e60 100644 --- a/source/funkin/editors/EditorPicker.hx +++ b/source/funkin/editors/EditorPicker.hx @@ -196,7 +196,7 @@ class EditorPickerOption extends FlxTypedSpriteGroup { iconSpr.x = 25 + ((height - iconSpr.width) / 2); iconSpr.y = (height - iconSpr.height) / 2; - label = new Alphabet(25 + iconSpr.width + 25, 0, name, true); + label = new Alphabet(25 + iconSpr.width + 25, 0, name, "bold"); label.y = (height - label.height) / 2; selectionBG = new FlxSprite().makeGraphic(1, 1, -1); diff --git a/source/funkin/menus/BetaWarningState.hx b/source/funkin/menus/BetaWarningState.hx index ebe6605d..68147a00 100644 --- a/source/funkin/menus/BetaWarningState.hx +++ b/source/funkin/menus/BetaWarningState.hx @@ -13,7 +13,7 @@ class BetaWarningState extends MusicBeatState { public override function create() { super.create(); - titleAlphabet = new Alphabet(0, 0, TU.translate("betaWarning.title"), true); + titleAlphabet = new Alphabet(0, 0, TU.translate("betaWarning.title"), "bold"); titleAlphabet.screenCenter(X); add(titleAlphabet); diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx index c055a405..bee0eef7 100644 --- a/source/funkin/menus/FreeplayState.hx +++ b/source/funkin/menus/FreeplayState.hx @@ -130,7 +130,7 @@ class FreeplayState extends MusicBeatState for (i in 0...songs.length) { - var songText:Alphabet = new Alphabet(0, (70 * i) + 30, songs[i].displayName, true, false); + var songText:Alphabet = new Alphabet(0, (70 * i) + 30, songs[i].displayName, "bold"); songText.isMenuItem = true; songText.targetY = i; grpSongs.add(songText); diff --git a/source/funkin/menus/ModSwitchMenu.hx b/source/funkin/menus/ModSwitchMenu.hx index 79277ff3..309afdad 100644 --- a/source/funkin/menus/ModSwitchMenu.hx +++ b/source/funkin/menus/ModSwitchMenu.hx @@ -28,7 +28,7 @@ class ModSwitchMenu extends MusicBeatSubstate { alphabets = new FlxTypedGroup(); for(mod in mods) { - var a = new Alphabet(0, 0, mod == null ? TU.translate("mods.disableMods") : mod, true); + var a = new Alphabet(0, 0, mod == null ? TU.translate("mods.disableMods") : mod, "bold"); if(mod == ModsFolder.currentModFolder) a.color = FlxColor.LIME; a.isMenuItem = true; diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx index fad1a15b..82a6173e 100644 --- a/source/funkin/menus/PauseSubState.hx +++ b/source/funkin/menus/PauseSubState.hx @@ -107,7 +107,7 @@ class PauseSubState extends MusicBeatSubstate for (i in 0...menuItems.length) { var pauseId = "pause." + TU.raw2Id(menuItems[i]); - var songText:Alphabet = new Alphabet(0, (70 * i) + 30, TU.translate(pauseId), true, false); + var songText:Alphabet = new Alphabet(0, (70 * i) + 30, TU.translate(pauseId), "bold"); songText.isMenuItem = true; songText.targetY = i; grpMenuShit.add(songText); diff --git a/source/funkin/menus/PlaytestingWarningSubstate.hx b/source/funkin/menus/PlaytestingWarningSubstate.hx index 1bd5a00f..de455d83 100644 --- a/source/funkin/menus/PlaytestingWarningSubstate.hx +++ b/source/funkin/menus/PlaytestingWarningSubstate.hx @@ -40,7 +40,7 @@ class PlaytestingWarningSubstate extends MusicBeatSubstate bg.scrollFactor.set(); add(bg); - titleAlphabet = new Alphabet(0, 140, TU.translate("playtesting.warningTitle"), true); + titleAlphabet = new Alphabet(0, 140, TU.translate("playtesting.warningTitle"), "bold"); titleAlphabet.screenCenter(X); add(titleAlphabet); diff --git a/source/funkin/menus/TitleState.hx b/source/funkin/menus/TitleState.hx index bf9f1061..ab190b62 100644 --- a/source/funkin/menus/TitleState.hx +++ b/source/funkin/menus/TitleState.hx @@ -175,7 +175,7 @@ class TitleState extends MusicBeatState for (i=>text in textArray) { if (text == "" || text == null) continue; - var money:Alphabet = new Alphabet(0, (i * 60) + 200, text, true, false); + var money:Alphabet = new Alphabet(0, (i * 60) + 200, text, "bold"); money.screenCenter(X); textGroup.add(money); } @@ -183,7 +183,7 @@ class TitleState extends MusicBeatState public function addMoreText(text:String) { - var coolText:Alphabet = new Alphabet(0, (textGroup.length * 60) + 200, text, true, false); + var coolText:Alphabet = new Alphabet(0, (textGroup.length * 60) + 200, text, "bold"); coolText.screenCenter(X); textGroup.add(coolText); } diff --git a/source/funkin/menus/ui/Alphabet.hx b/source/funkin/menus/ui/Alphabet.hx index 00302a53..4298be57 100644 --- a/source/funkin/menus/ui/Alphabet.hx +++ b/source/funkin/menus/ui/Alphabet.hx @@ -1,284 +1,648 @@ package funkin.menus.ui; -import flixel.group.FlxSpriteGroup; -import flixel.util.FlxColor; -import flixel.util.FlxTimer; -import funkin.backend.assets.AssetsLibraryList; -import funkin.backend.assets.IModsAssetLibrary; -import funkin.backend.assets.LimeLibrarySymbol; +import flixel.util.typeLimit.OneOfTwo; +import haxe.ds.Vector; +import funkin.menus.ui.effects.RegionEffect; + +import flixel.animation.FlxAnimation; +import flixel.util.FlxDirectionFlags; +import flixel.math.FlxPoint; +import flixel.math.FlxAngle; +import flixel.FlxTypes; + import haxe.xml.Access; -import openfl.utils.AssetLibrary; using StringTools; +using flixel.util.FlxColorTransformUtil; -/** - * Loosely based on FlxTypeText lolol - */ -class Alphabet extends FlxSpriteGroup -{ - public var delay:Float = 0.05; - public var paused:Bool = false; +@:structInit +final class AlphabetComponent { + public var anim:String; + public var x:Float; + public var y:Float; + + // Precalculated values for angle + public var shouldRotate:Bool; + public var sin:Float; + public var cos:Float; + public var scaleX:Float; + public var scaleY:Float; + + public var hasColorMode:Bool; + public var colorMode:ColorMode; +} + +@:structInit +final class AlphabetLetterData { + @:optional public var isDefault:Bool = false; + public var advance:Float; + public var advanceEmpty:Bool; + public var components:Array; +} + +enum abstract AlphabetAlignment(ByteUInt) from ByteUInt to ByteUInt { + var LEFT; + var CENTER; + var RIGHT; + + public function getMultiplier():Float { + return switch(this) { + case CENTER: 0.5; + case RIGHT: 1.0; + default: 0.0; + } + } + + @:from + public static function fromString(value:String):AlphabetAlignment + return switch(value) { + case "left": LEFT; + case "center": CENTER; + case "right": RIGHT; + default: LEFT; + } +} + +enum abstract CaseMode(ByteUInt) from ByteUInt to ByteUInt { + var NONE; + var UPPER; + var LOWER; + + @:from + public static function fromString(value:String):CaseMode + return switch(value) { + case "none": NONE; + case "upper": UPPER; + case "lower": LOWER; + default: NONE; + } +} + +enum abstract ColorMode(ByteInt) from ByteInt to ByteInt { + var TINT = 0; + var OFFSET = 1; + var NONE = 2; + + public function setColorTransform(color:openfl.geom.ColorTransform, red:Float, green:Float, blue:Float, alpha:Float) { + var moduloWorkaround:Int = this; + var tintMult = Math.max(-moduloWorkaround + 1, 0); + var tintOffset = Math.min(moduloWorkaround, 1); + var offsetMult = (moduloWorkaround % 2) * 255; + + color.redMultiplier = red * tintMult + tintOffset; + color.greenMultiplier = green * tintMult + tintOffset; + color.blueMultiplier = blue * tintMult + tintOffset; + color.redOffset = red * offsetMult; + color.greenOffset = green * offsetMult; + color.blueOffset = blue * offsetMult; + color.alphaMultiplier = alpha; + } +} + +class Alphabet extends FlxSprite { + public var effects:Array = []; + var __renderData:AlphabetRenderData; + var __component:AlphabetComponent; + + public var text(default, set):String = ""; + @:isVar public var textWidth(get, set):Float; + public var textHeight(get, null):Float; + + public var alignment:AlphabetAlignment = LEFT; + public var forceCase:CaseMode = NONE; + + var __laneWidths:Array = []; // TODO: add alignment. + var __forceWidth:Float = 0.0; // TODO: implement functionality for this. + var __queueResize:Bool = false; + var __animTime:Float = 0.0; + var __ogForceScreen:Bool = false; + + public var originOffset:FlxPoint = FlxPoint.get(); + + public var font(default, set):String; + var colorMode:ColorMode = TINT; + var defaultAdvance:Float = 40.0; + var lineGap:Float = 75.0; + var fps:Float = 24.0; + var letterData:Map = []; + var defaults:Vector = { + var v = new Vector(3); + v[0] = null; + v[1] = null; + v[2] = null; + v; + }; + var loaded:Vector> = { + var v = new Vector(3); + v[0] = []; + v[1] = []; + v[2] = []; + v; + }; + var failedLetters:Array = []; // for menu shit public var targetY:Float = 0; public var isMenuItem:Bool = false; - public var text:String = ""; - - var _finalText:String = ""; - var _curText:String = ""; - - var yMulti:Float = 1; - - // custom shit - // amp, backslash, question mark, apostrophy, comma, angry faic, period - var lastSprite:AlphaCharacter; - var xPosResetted:Bool = false; - - var splitWords:Array = []; - - var isBold:Bool = false; - - private override function set_color(c:Int):Int { - for(e in group.members) { - if (e is AlphaCharacter) { - var char:AlphaCharacter = cast e; - char.setColor(c, isBold); - } - } - return super.set_color(c); - } - - // TODO: fix this shit refreshing - public function refreshAlphabetXML(path:String) { - AlphaCharacter.__alphaPath = Paths.getAssetsRoot() + path; - try { - var xml = new Access(Xml.parse(Assets.getText(path)).firstElement()); - AlphaCharacter.boldAnims = []; - AlphaCharacter.letterAnims = []; - AlphaCharacter.boldAlphabetPath = AlphaCharacter.letterAlphabetPath = 'ui/alphabet'; - - for(e in xml.elements) { - var bold = e.name == "bold"; - var list = bold ? AlphaCharacter.boldAnims : AlphaCharacter.letterAnims; - if (e.has.spritesheet) { - if (bold) - AlphaCharacter.boldAlphabetPath = e.att.spritesheet; - else - AlphaCharacter.letterAlphabetPath = e.att.spritesheet; - } - for(e in e.nodes.letter) { - if (!e.has.char || !e.has.anim) continue; - var name = e.att.char; - var anim = e.att.anim; - list[name] = anim; - } - } - } catch(e) { - trace(e.details()); - } - } - public function new(x:Float, y:Float, text:String = "", ?bold:Bool = false, typed:Bool = false) - { + public function new(?x:Float, ?y:Float, ?text:String = "", ?font:OneOfTwo = "normal") { super(x, y); + this.text = text; + this.font = (font is String) ? font : ((font == true) ? "bold" : "normal"); // == true just in case + this.__renderData = new AlphabetRenderData(this); + } - _finalText = this.text = text; - isBold = bold; - - var alphabetPath = Paths.xml("alphabet"); - if (Paths.getAssetsRoot() + alphabetPath != AlphaCharacter.__alphaPath) { - refreshAlphabetXML(alphabetPath); - } - #if MOD_SUPPORT else { - var libThing = new LimeLibrarySymbol(alphabetPath); - if (libThing.library is AssetLibrary) { - var library = AssetsLibraryList.getCleanLibrary(libThing.library); - if (library is IModsAssetLibrary) { - var modLib = cast(library, IModsAssetLibrary); - @:privateAccess - if (!modLib.__isCacheValid(library.cachedBytes, libThing.symbolName)) { - refreshAlphabetXML(alphabetPath); - } - } - } - } + override function update(elapsed:Float):Void { + //super.update(elapsed); + // FLXOBJECT UPDATE + #if FLX_DEBUG + @:privateAccess FlxBasic.activeCount += 1; #end - if (text != "") - { - if (typed) - { - startTypedText(); - } - else - { - addText(); - } - } - } + last.set(x, y); - public function addText() - { - doSplitWords(); + if (path != null && path.active) + path.update(elapsed); - var xPos:Float = 0; - var curRow:Int = 0; - for (i=>character in splitWords) - { - if (lastSprite != null) - xPos = lastSprite.x + lastSprite.width - x; + if (moves) + updateMotion(elapsed); - if (xPosResetted && !(xPosResetted = false)) xPos = 0; + wasTouching = touching; + touching = FlxDirectionFlags.NONE; + // FLXOBJECT UPDATE END - var letter:AlphaCharacter = new AlphaCharacter(xPos, 0); - letter.row = curRow; - if (isBold) - letter.createBold(character); - else - letter.createLetter(character); + __animTime += elapsed; + for (effect in effects) + effect.effectTime += elapsed * effect.speed; - // anim not found - if (!letter.visible) - xPos += 40; - - letter.setColor(color, isBold); - add(letter); - - lastSprite = letter; - if (_finalText.fastCodeAt(i) == "\n".code) - { - xPosResetted = true; - curRow += 1; - } - } - } - - function doSplitWords():Void - { - splitWords = _finalText.split(""); - } - - //public var personTalking:String = 'gf'; - - public function startTypedText():Void - { - _finalText = text; - doSplitWords(); - - // trace(arrayShit); - - var loopNum:Int = 0; - - var xPos:Float = 0; - var curRow:Int = 0; - - new FlxTimer().start(0.05, function(tmr:FlxTimer) - { - // trace(_finalText.fastCodeAt(loopNum) + " " + _finalText.charAt(loopNum)); - if (_finalText.fastCodeAt(loopNum) == "\n".code) - { - yMulti += 1; - xPosResetted = true; - xPos = 0; - curRow += 1; - } - loopNum += 1; - - tmr.time = FlxG.random.float(0.04, 0.09); - }, splitWords.length); - } - - override function update(elapsed:Float) - { - super.update(elapsed); - - if (isMenuItem) - { - var scaledY = FlxMath.remapToRange(targetY, 0, 1, 0, 1.3); + if (isMenuItem) { + var scaledY = targetY * 1.3; y = CoolUtil.fpsLerp(y, (scaledY * 120) + (FlxG.height * 0.48), 0.16); x = CoolUtil.fpsLerp(x, (targetY * 20) + 90, 0.16); } - - if (text != _finalText) { - _finalText = text; - for(e in members) - e.destroy(); - members.clear(); - lastSprite = null; - addText(); - } } - override function draw() { + override function draw():Void { + __ogForceScreen = forceIsOnScreen; + forceIsOnScreen = true; super.draw(); } -} -class AlphaCharacter extends FlxSprite -{ - @:dox(hide) @:noCompletion public static var __alphaPath:String = null; + override function isSimpleRender(?camera:FlxCamera):Bool { + return false; // maybe ill get simple render working another time??? not right now tho. + } - public static var letterAlphabetPath:String; - public static var boldAlphabetPath:String; + override function drawComplex(camera:FlxCamera):Void { + forceIsOnScreen = __ogForceScreen; - public static var boldAnims:Map = []; - public static var letterAnims:Map = []; + if (__queueResize) + recalcSizes(); + var curLine = 0; + var lastLine = 0; + var daText = switch (forceCase) { + case UPPER: text.toUpperCase(); + case LOWER: text.toLowerCase(); + case NONE: text; + } - public var row:Int = 0; + var alignmentMultiplier = alignment.getMultiplier(); - public function setColor(c:FlxColor, isBold:Bool) { - if (isBold) { - colorTransform.redMultiplier = c.redFloat; - colorTransform.greenMultiplier = c.greenFloat; - colorTransform.blueMultiplier = c.blueFloat; - } else { - colorTransform.redOffset = c.red; - colorTransform.greenOffset = c.green; - colorTransform.blueOffset = c.blue; + var ogOffX = frameOffset.x; + var ogOffY = frameOffset.y; + frameOffset.x -= (textWidth - __laneWidths[0]) * alignmentMultiplier; + + var isGraphicsShader = shader != null && shader is flixel.graphics.tile.FlxGraphicsShader; + var ogRed:Float = colorTransform.redMultiplier; + var ogGreen:Float = colorTransform.greenMultiplier; + var ogBlue:Float = colorTransform.blueMultiplier; + var ogAlpha:Float = colorTransform.alphaMultiplier; + + var frameTime = Math.floor(__animTime * fps); + + for (i in 0...daText.length) { + __renderData.reset(this, ogRed, ogGreen, ogBlue, ogAlpha, daText.charAt(i)); + for (effect in effects) { + if (effect.willModify(i, i - lastLine, __renderData)) + effect.modify(i, i - lastLine, __renderData); + } + + var letter = __renderData.letter; + if (letter == "\n") { + curLine++; + lastLine = i + 1; + frameOffset.x = ogOffX - (textWidth - __laneWidths[curLine]) * alignmentMultiplier; + frameOffset.y -= lineGap; + continue; + } + + var data = getData(letter); + + if (data == null) { + frameOffset.x -= defaultAdvance; + continue; + } + + var advance:Float = Math.NaN; + + for (i in 0...data.components.length) { + __component = data.components[i]; + var anim = getLetterAnim(letter, data, __component, i); + advance = (Math.isNaN(advance)) ? getAdvance(letter, anim, data) : advance; + + if (anim == null || __renderData.alpha <= 0.0) + break; // shouldnt this be like continue? + + var frameToGet = frameTime % anim.numFrames; + frame = frames.frames[anim.frames[frameToGet]]; + + var offsetX = __component.x + __renderData.offsetX; + var offsetY = frame.sourceSize.y - lineGap + __component.y + __renderData.offsetY; + frameOffset.x += offsetX; + frameOffset.y += offsetY; + if (!isOnScreen(camera)) { + frameOffset.y -= offsetY; + frameOffset.x -= offsetX; + break; + } + if (isGraphicsShader) + shader.setCamSize(_frame.frame.x, _frame.frame.y, _frame.frame.width, _frame.frame.height); + + if (__component.hasColorMode) + __component.colorMode.setColorTransform(colorTransform, __renderData.red, __renderData.green, __renderData.blue, __renderData.alpha); + else + colorMode.setColorTransform(colorTransform, __renderData.red, __renderData.green, __renderData.blue, __renderData.alpha); + + drawLetter(camera); + frameOffset.y -= offsetY; + frameOffset.x -= offsetX; + } + frameOffset.x -= advance; + } + __component = null; + + setColorTransform(ogRed, ogGreen, ogBlue, ogAlpha, 0, 0, 0, 0); + frameOffset.set(ogOffX, ogOffY); + } + + function drawLetter(camera) { + _frame.prepareMatrix(_matrix, ANGLE_0, checkFlipX() != camera.flipX, checkFlipY() != camera.flipY); + + _matrix.translate(_frame.frame.width * -0.5, _frame.frame.height * -0.5); + _matrix.scale(__component.scaleX, __component.scaleY); + if(__component.shouldRotate) _matrix.rotateWithTrig(__component.cos, __component.sin); + _matrix.translate(_frame.frame.width * 0.5, _frame.frame.height * 0.5); + + _matrix.translate(-origin.x, -origin.y); + + if (frameOffsetAngle != null && frameOffsetAngle != angle) + { + var angleOff = (frameOffsetAngle - angle) * FlxAngle.TO_RAD; + var cos = Math.cos(angleOff); + var sin = Math.sin(angleOff); + // cos doesnt need to be negated + _matrix.rotateWithTrig(cos, -sin); + _matrix.translate(-frameOffset.x, -frameOffset.y); + _matrix.rotateWithTrig(cos, sin); + } + else + _matrix.translate(-frameOffset.x, -frameOffset.y); + + _matrix.scale(scale.x, scale.y); + + if (bakedRotationAngle <= 0) + { + updateTrig(); + + if (angle != 0) + _matrix.rotateWithTrig(_cosAngle, _sinAngle); + } + + getScreenPosition(_point, camera).subtractPoint(offset); + _point.add(origin.x, origin.y); + _matrix.translate(_point.x, _point.y); + + if (isPixelPerfectRender(camera)) + { + _matrix.tx = Math.floor(_matrix.tx); + _matrix.ty = Math.floor(_matrix.ty); + } + + doAdditionalMatrixStuff(_matrix, camera); + + if (layer != null) + layer.drawPixels(this, camera, _frame, framePixels, _matrix, colorTransform, blend, antialiasing, shaderEnabled ? shader : null); + else + camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shaderEnabled ? shader : null); + } + + override function updateHitbox():Void { + offset.set(-0.5 * (width - textWidth), -0.5 * (height - textHeight)); + origin.set(textWidth * 0.5 + originOffset.x, textHeight * 0.5 + originOffset.y); + } + + function recalcSizes():Void { + __queueResize = false; + var curLine = 0; + var daText = switch (forceCase) { + case UPPER: text.toUpperCase(); + case LOWER: text.toLowerCase(); + case NONE: 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 data = getData(letter); + __laneWidths[curLine] += (data != null) ? getAdvance(letter, getLetterAnim(letter, data, data.components[0], 0), data) : defaultAdvance; + @:bypassAccessor textWidth = Math.max(textWidth, __laneWidths[curLine]); + } + + origin.set(textWidth * 0.5 + originOffset.x, textHeight * 0.5 + originOffset.y); + } + + function getAdvance(letter:String, anim:FlxAnimation, data:AlphabetLetterData):Float { + if (anim == null) + return defaultAdvance; + + if (data.advanceEmpty && !data.isDefault) { + data.advanceEmpty = false; + data.advance = frames.frames[anim.frames[0]].sourceSize.x; + } + return (data.isDefault) ? frames.frames[anim.frames[0]].sourceSize.x : data.advance; + } + + function getData(char:String):AlphabetLetterData { + if (failedLetters.contains(char)) return null; + for (i in 0...3) { // this feels wrong + if (loaded[i].contains(char)) + return defaults[i]; + } + + var data:AlphabetLetterData = null; + if (letterData.exists(char)) + data = letterData.get(char); + + if (data == null) { + var charCode:Int = char.charCodeAt(0); + if (charCode >= 'A'.code && charCode <= 'Z'.code && defaults[CaseMode.UPPER] != null) + data = defaults[CaseMode.UPPER]; + else if (charCode >= 'a'.code && charCode <= 'z'.code && defaults[CaseMode.LOWER] != null) + data = defaults[CaseMode.LOWER]; + else + data = defaults[CaseMode.NONE]; + } + + if(data == null) { + failedLetters.push(char); + return null; + } else if (data.isDefault) + loaded[defaultsIndexOf(data)].push(char); + return data; + } + + final function defaultsIndexOf(data:AlphabetLetterData):Int { + //while(i < defaults.length) { + for(i in 0...3) { + if(defaults[i] == data) + return i; + } + return -1; + } + + function getLetterAnim(char:String, data:AlphabetLetterData, component:AlphabetComponent, index:Int):FlxAnimation { + if (data == null) return null; + var name = char + Std.string(index); + if (animation.exists(name)) return animation.getByName(name); + + var anim = (data.isDefault) ? + component.anim.replace("LETTER", char) : + component.anim; + animation.addByPrefix(name, anim, fps); + if (!animation.exists(name)) { + failedLetters.push(char); + return null; + } + return animation.getByName(name); + } + + function checkNode(node:Xml):Void { + switch (node.nodeName) { + case "spritesheet": + if (frames == null) + frames = Paths.getFrames(node.firstChild().nodeValue); + else { + for (frame in Paths.getFrames(node.firstChild().nodeValue).frames) + frames.pushFrame(frame); + } + case "defaultAnim": + var idx = ["UPPER", "LOWER"].indexOf(node.get("casing").toUpperCase()) + 1; + + var rad:Float = Std.parseFloat(node.get("angle")).getDefaultFloat(0.0) * FlxAngle.TO_RAD; + var angleCos = Math.cos(rad); + var angleSin = Math.sin(rad); + + var res:AlphabetLetterData = { + isDefault: true, + advance: 0.0, + advanceEmpty: true, + components: [{ + anim: node.firstChild().nodeValue, + + x: 0.0, + y: 0.0, + scaleX: Std.parseFloat(node.get("scaleX")).getDefaultFloat(1.0), + scaleY: Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0), + + shouldRotate: rad != 0, + cos: angleCos, + sin: angleSin, + + hasColorMode: false, + colorMode: 0 + }] + } + + defaults[idx] = res; + case "composite": + if(!node.exists("char")) { + Logs.error(" must have a char attribute", "Alphabet"); + return; + } + var char = node.get("char"); + var advance:Float = Std.parseFloat(node.get("advance")); + var components:Array = []; + for (component in node.elementsNamed("component")) { + if(!component.exists("anim")) { + Logs.error(" must have a anim attribute", "Alphabet"); + return; + } + + var rad:Float = Std.parseFloat(component.get("angle")).getDefaultFloat(0.0) * FlxAngle.TO_RAD; + var angleCos = Math.cos(rad); + var angleSin = Math.sin(rad); + + var xOff:Float = -Std.parseFloat(component.get("x")).getDefaultFloat(0.0); + var yOff:Float = Std.parseFloat(component.get("y")).getDefaultFloat(0.0); + + var colorMode = ["tint", "offsets", "none"].indexOf(component.get("colorMode")); + + components.push({ + anim: component.get("anim"), + + x: xOff, + y: yOff, + scaleX: Std.parseFloat(component.get("scaleX")).getDefaultFloat(1.0), + scaleY: Std.parseFloat(component.get("scaleY")).getDefaultFloat(1.0), + + shouldRotate: rad != 0, + cos: angleCos, + sin: angleSin, + + hasColorMode: colorMode >= 0, + colorMode: colorMode + }); + } + letterData.set(char, { + isDefault: false, + advance: advance, + advanceEmpty: Math.isNaN(advance), + components: components + }); + case "anim": + if(!node.exists("char")) { + Logs.error(" must have a char attribute", "Alphabet"); + return; + } + var char = node.get("char"); + + var rad:Float = Std.parseFloat(node.get("angle")).getDefaultFloat(0.0) * FlxAngle.TO_RAD; + var angleCos = Math.cos(rad); + var angleSin = Math.sin(rad); + + var xOff:Float = -Std.parseFloat(node.get("x")).getDefaultFloat(0.0); + var yOff:Float = Std.parseFloat(node.get("y")).getDefaultFloat(0.0); + var advance:Float = Std.parseFloat(node.get("advance")); + + var colorMode = ["tint", "offsets", "none"].indexOf(node.get("colorMode")); + + letterData.set(char, { + isDefault: false, + advance: advance, + advanceEmpty: Math.isNaN(advance), + components: [{ + anim: node.firstChild().nodeValue, + + x: xOff, + y: yOff, + scaleX: Std.parseFloat(node.get("scaleX")).getDefaultFloat(1.0), + scaleY: Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0), + + shouldRotate: rad != 0, + cos: angleCos, + sin: angleSin, + + hasColorMode: colorMode >= 0, + colorMode: colorMode + }] + }); + case "languageSection": // used to only parse characters if a specific language is selected + if(!node.exists("langs")) { + Logs.error(" must have a langs attribute", "Alphabet"); + return; + } + var langs = [for (lang in node.get("langs").split(",")) lang.trim()]; + // maybe add a way to toggle this off? like force it to add all + if (langs.contains(Options.language)) { + for (langNode in node.elements()) + checkNode(langNode); + } } } - public function new(x:Float, y:Float) - { - super(x, y); - antialiasing = true; - } + function loadFont(value:String):Void { + __queueResize = true; - public function createBold(letter:String) - { - if(!boldAnims.exists(letter)) - letter = letter.toUpperCase(); - if (!boldAnims.exists(letter)) { - visible = false; - active = false; - scale.set(); - width = 40; - return; + var xml:Xml = Xml.parse(Assets.getText(Paths.xml("alphabet/" + value))).firstElement(); + + // reset old values + letterData = new Map(); + defaults = { + var v = new Vector(3); + v[0] = null; + v[1] = null; + v[2] = null; + v; } - frames = Paths.getFrames(boldAlphabetPath); - animation.addByPrefix(letter, boldAnims[letter], 24); - animation.play(letter); - updateHitbox(); - - y += row * height; - } - - public function createLetter(letter:String):Void - { - if (!letterAnims.exists(letter)) { - visible = false; - active = false; - scale.set(); - width = 40; - return; + loaded = { + var v = new Vector(3); + v[0] = []; + v[1] = []; + v[2] = []; + v; } - frames = Paths.getFrames(letterAlphabetPath); - animation.addByPrefix(letter, letterAnims[letter], 24); - animation.play(letter); - updateHitbox(); + failedLetters = []; - y = (110 - height); - y += row * 60; + defaultAdvance = Std.parseFloat(xml.get("advance")).getDefaultFloat(40.0); + lineGap = Std.parseFloat(xml.get("lineGap")).getDefaultFloat(75.0); + fps = Std.parseFloat(xml.get("fps")).getDefaultFloat(24.0); + forceCase = xml.get("forceCasing").getDefault("none").toLowerCase(); + colorMode = (["offsets", "none"].indexOf(xml.get("colorMode")) + 1); + + frames = null; + + for (node in xml.elements()) + checkNode(node); } -} + + override function destroy():Void { + originOffset = FlxDestroyUtil.destroy(originOffset); + failedLetters = []; + super.destroy(); + } + + function get_textWidth():Float { + if (__queueResize) + recalcSizes(); + return textWidth; + } + function set_textWidth(value:Float):Float { + if(!__queueResize) + __queueResize = __forceWidth != value; + return __forceWidth = value; + } + + override function get_width():Float { + return textWidth * scale.x; + } + override function get_height():Float { + return textHeight * scale.y; + } + + function get_textHeight():Float { + if (__queueResize) + recalcSizes(); + return textHeight; + } + + function set_text(value:String):String { + if(!__queueResize) + __queueResize = text != value; + return text = value; + } + + function set_font(value:String):String { + if (font != value) { + loadFont(font = value); + } + return value; + } +} \ No newline at end of file diff --git a/source/funkin/menus/ui/ClassicAlphabet.hx b/source/funkin/menus/ui/ClassicAlphabet.hx new file mode 100644 index 00000000..4ece8633 --- /dev/null +++ b/source/funkin/menus/ui/ClassicAlphabet.hx @@ -0,0 +1,284 @@ +package funkin.menus.ui; + +import flixel.group.FlxSpriteGroup; +import flixel.util.FlxColor; +import flixel.util.FlxTimer; +import funkin.backend.assets.AssetsLibraryList; +import funkin.backend.assets.IModsAssetLibrary; +import funkin.backend.assets.LimeLibrarySymbol; +import haxe.xml.Access; +import openfl.utils.AssetLibrary; + +using StringTools; + +/** + * Loosely based on FlxTypeText lolol + */ +class ClassicAlphabet extends FlxSpriteGroup +{ + public var delay:Float = 0.05; + public var paused:Bool = false; + + // for menu shit + public var targetY:Float = 0; + public var isMenuItem:Bool = false; + + public var text:String = ""; + + var _finalText:String = ""; + var _curText:String = ""; + + var yMulti:Float = 1; + + // custom shit + // amp, backslash, question mark, apostrophy, comma, angry faic, period + var lastSprite:AlphaCharacter; + var xPosResetted:Bool = false; + + var splitWords:Array = []; + + var isBold:Bool = false; + + private override function set_color(c:Int):Int { + for(e in group.members) { + if (e is AlphaCharacter) { + var char:AlphaCharacter = cast e; + char.setColor(c, isBold); + } + } + return super.set_color(c); + } + + // TODO: fix this shit refreshing + public function refreshAlphabetXML(path:String) { + AlphaCharacter.__alphaPath = Paths.getAssetsRoot() + path; + try { + var xml = new Access(Xml.parse(Assets.getText(path)).firstElement()); + AlphaCharacter.boldAnims = []; + AlphaCharacter.letterAnims = []; + AlphaCharacter.boldAlphabetPath = AlphaCharacter.letterAlphabetPath = 'ui/alphabet'; + + for(e in xml.elements) { + var bold = e.name == "bold"; + var list = bold ? AlphaCharacter.boldAnims : AlphaCharacter.letterAnims; + if (e.has.spritesheet) { + if (bold) + AlphaCharacter.boldAlphabetPath = e.att.spritesheet; + else + AlphaCharacter.letterAlphabetPath = e.att.spritesheet; + } + for(e in e.nodes.letter) { + if (!e.has.char || !e.has.anim) continue; + var name = e.att.char; + var anim = e.att.anim; + list[name] = anim; + } + } + } catch(e) { + trace(e.details()); + } + } + public function new(x:Float, y:Float, text:String = "", ?bold:Bool = false, typed:Bool = false) + { + super(x, y); + + _finalText = this.text = text; + isBold = bold; + + var alphabetPath = Paths.xml("alphabet"); + if (Paths.getAssetsRoot() + alphabetPath != AlphaCharacter.__alphaPath) { + refreshAlphabetXML(alphabetPath); + } + #if MOD_SUPPORT else { + var libThing = new LimeLibrarySymbol(alphabetPath); + if (libThing.library is AssetLibrary) { + var library = AssetsLibraryList.getCleanLibrary(libThing.library); + if (library is IModsAssetLibrary) { + var modLib = cast(library, IModsAssetLibrary); + @:privateAccess + if (!modLib.__isCacheValid(library.cachedBytes, libThing.symbolName)) { + refreshAlphabetXML(alphabetPath); + } + } + } + } + #end + + if (text != "") + { + if (typed) + { + startTypedText(); + } + else + { + addText(); + } + } + } + + public function addText() + { + doSplitWords(); + + var xPos:Float = 0; + var curRow:Int = 0; + for (i=>character in splitWords) + { + if (lastSprite != null) + xPos = lastSprite.x + lastSprite.width - x; + + if (xPosResetted && !(xPosResetted = false)) xPos = 0; + + var letter:AlphaCharacter = new AlphaCharacter(xPos, 0); + letter.row = curRow; + if (isBold) + letter.createBold(character); + else + letter.createLetter(character); + + // anim not found + if (!letter.visible) + xPos += 40; + + letter.setColor(color, isBold); + add(letter); + + lastSprite = letter; + if (_finalText.fastCodeAt(i) == "\n".code) + { + xPosResetted = true; + curRow += 1; + } + } + } + + function doSplitWords():Void + { + splitWords = _finalText.split(""); + } + + //public var personTalking:String = 'gf'; + + public function startTypedText():Void + { + _finalText = text; + doSplitWords(); + + // trace(arrayShit); + + var loopNum:Int = 0; + + var xPos:Float = 0; + var curRow:Int = 0; + + new FlxTimer().start(0.05, function(tmr:FlxTimer) + { + // trace(_finalText.fastCodeAt(loopNum) + " " + _finalText.charAt(loopNum)); + if (_finalText.fastCodeAt(loopNum) == "\n".code) + { + yMulti += 1; + xPosResetted = true; + xPos = 0; + curRow += 1; + } + loopNum += 1; + + tmr.time = FlxG.random.float(0.04, 0.09); + }, splitWords.length); + } + + override function update(elapsed:Float) + { + super.update(elapsed); + + if (isMenuItem) + { + var scaledY = FlxMath.remapToRange(targetY, 0, 1, 0, 1.3); + + y = CoolUtil.fpsLerp(y, (scaledY * 120) + (FlxG.height * 0.48), 0.16); + x = CoolUtil.fpsLerp(x, (targetY * 20) + 90, 0.16); + } + + if (text != _finalText) { + _finalText = text; + for(e in members) + e.destroy(); + members.clear(); + lastSprite = null; + addText(); + } + } + + override function draw() { + super.draw(); + } +} + +class AlphaCharacter extends FlxSprite +{ + @:dox(hide) @:noCompletion public static var __alphaPath:String = null; + + public static var letterAlphabetPath:String; + public static var boldAlphabetPath:String; + + public static var boldAnims:Map = []; + public static var letterAnims:Map = []; + + public var row:Int = 0; + + public function setColor(c:FlxColor, isBold:Bool) { + if (isBold) { + colorTransform.redMultiplier = c.redFloat; + colorTransform.greenMultiplier = c.greenFloat; + colorTransform.blueMultiplier = c.blueFloat; + } else { + colorTransform.redOffset = c.red; + colorTransform.greenOffset = c.green; + colorTransform.blueOffset = c.blue; + } + } + public function new(x:Float, y:Float) + { + super(x, y); + + antialiasing = true; + } + + public function createBold(letter:String) + { + if(!boldAnims.exists(letter)) + letter = letter.toUpperCase(); + if (!boldAnims.exists(letter)) { + visible = false; + active = false; + scale.set(); + width = 40; + return; + } + frames = Paths.getFrames(boldAlphabetPath); + animation.addByPrefix(letter, boldAnims[letter], 24); + animation.play(letter); + updateHitbox(); + + y += row * height; + } + + public function createLetter(letter:String):Void + { + if (!letterAnims.exists(letter)) { + visible = false; + active = false; + scale.set(); + width = 40; + return; + } + frames = Paths.getFrames(letterAlphabetPath); + animation.addByPrefix(letter, letterAnims[letter], 24); + animation.play(letter); + updateHitbox(); + + y = (110 - height); + y += row * 60; + } +} diff --git a/source/funkin/menus/ui/NewAlphabet.hx b/source/funkin/menus/ui/NewAlphabet.hx deleted file mode 100644 index e6f41619..00000000 --- a/source/funkin/menus/ui/NewAlphabet.hx +++ /dev/null @@ -1,630 +0,0 @@ -package funkin.menus.ui; - -import haxe.ds.Vector; -import funkin.menus.ui.effects.RegionEffect; - -import flixel.animation.FlxAnimation; -import flixel.util.FlxDirectionFlags; -import flixel.math.FlxPoint; -import flixel.math.FlxAngle; -import flixel.FlxTypes; - -import haxe.xml.Access; - -using StringTools; -using flixel.util.FlxColorTransformUtil; - -@:structInit -final class AlphabetComponent { - public var anim:String; - public var x:Float; - public var y:Float; - - // Precalculated values for angle - public var shouldRotate:Bool; - public var sin:Float; - public var cos:Float; - public var scaleX:Float; - public var scaleY:Float; - - public var hasColorMode:Bool; - public var colorMode:ColorMode; -} - -@:structInit -final class AlphabetLetterData { - @:optional public var isDefault:Bool = false; - public var advance:Float; - public var advanceEmpty:Bool; - public var components:Array; -} - -enum abstract AlphabetAlignment(ByteUInt) from ByteUInt to ByteUInt { - var LEFT; - var CENTER; - var RIGHT; - - public function getMultiplier():Float { - return switch(this) { - case CENTER: 0.5; - case RIGHT: 1.0; - default: 0.0; - } - } - - @:from - public static function fromString(value:String):AlphabetAlignment - return switch(value) { - case "left": LEFT; - case "center": CENTER; - case "right": RIGHT; - default: LEFT; - } -} - -enum abstract CaseMode(ByteUInt) from ByteUInt to ByteUInt { - var NONE; - var UPPER; - var LOWER; - - @:from - public static function fromString(value:String):CaseMode - return switch(value) { - case "none": NONE; - case "upper": UPPER; - case "lower": LOWER; - default: NONE; - } -} - -enum abstract ColorMode(ByteInt) from ByteInt to ByteInt { - var TINT = 0; - var OFFSET = 1; - var NONE = 2; - - public function setColorTransform(color:openfl.geom.ColorTransform, red:Float, green:Float, blue:Float, alpha:Float) { - var moduloWorkaround:Int = this; - var tintMult = Math.max(-moduloWorkaround + 1, 0); - var tintOffset = Math.min(moduloWorkaround, 1); - var offsetMult = (moduloWorkaround % 2) * 255; - - color.redMultiplier = red * tintMult + tintOffset; - color.greenMultiplier = green * tintMult + tintOffset; - color.blueMultiplier = blue * tintMult + tintOffset; - color.redOffset = red * offsetMult; - color.greenOffset = green * offsetMult; - color.blueOffset = blue * offsetMult; - color.alphaMultiplier = alpha; - } -} - -class NewAlphabet extends FlxSprite { - public var effects:Array = []; - var __renderData:AlphabetRenderData; - var __component:AlphabetComponent; - - public var text(default, set):String = ""; - @:isVar public var textWidth(get, set):Float; - public var textHeight(get, null):Float; - - public var alignment:AlphabetAlignment = LEFT; - public var forceCase:CaseMode = NONE; - - var __laneWidths:Array = []; // TODO: add alignment. - var __forceWidth:Float = 0.0; // TODO: implement functionality for this. - var __queueResize:Bool = false; - var __animTime:Float = 0.0; - var __ogForceScreen:Bool = false; - - public var originOffset:FlxPoint = FlxPoint.get(); - - public var font(default, set):String; - var colorMode:ColorMode = TINT; - var defaultAdvance:Float = 40.0; - var lineGap:Float = 75.0; - var fps:Float = 24.0; - var letterData:Map = []; - var defaults:Vector = { - var v = new Vector(3); - v[0] = null; - v[1] = null; - v[2] = null; - v; - }; - var loaded:Vector> = { - var v = new Vector(3); - v[0] = []; - v[1] = []; - v[2] = []; - v; - }; - var failedLetters:Array = []; - - public function new(?x:Float, ?y:Float, ?font:String = "bold") { - super(x, y); - this.font = font; - this.__renderData = new AlphabetRenderData(this); - } - - override function update(elapsed:Float):Void { - //super.update(elapsed); - // FLXOBJECT UPDATE - #if FLX_DEBUG - @:privateAccess FlxBasic.activeCount += 1; - #end - - last.set(x, y); - - if (path != null && path.active) - path.update(elapsed); - - if (moves) - updateMotion(elapsed); - - wasTouching = touching; - touching = FlxDirectionFlags.NONE; - // FLXOBJECT UPDATE END - - __animTime += elapsed; - for (effect in effects) - effect.effectTime += elapsed * effect.speed; - } - - override function draw():Void { - __ogForceScreen = forceIsOnScreen; - forceIsOnScreen = true; - super.draw(); - } - - override function isSimpleRender(?camera:FlxCamera):Bool { - return false; // maybe ill get simple render working another time??? not right now tho. - } - - override function drawComplex(camera:FlxCamera):Void { - forceIsOnScreen = __ogForceScreen; - - if (__queueResize) - recalcSizes(); - var curLine = 0; - var lastLine = 0; - var daText = switch (forceCase) { - case UPPER: text.toUpperCase(); - case LOWER: text.toLowerCase(); - case NONE: text; - } - - var alignmentMultiplier = alignment.getMultiplier(); - - var ogOffX = frameOffset.x; - var ogOffY = frameOffset.y; - frameOffset.x -= (textWidth - __laneWidths[0]) * alignmentMultiplier; - - var isGraphicsShader = shader != null && shader is flixel.graphics.tile.FlxGraphicsShader; - var ogRed:Float = colorTransform.redMultiplier; - var ogGreen:Float = colorTransform.greenMultiplier; - var ogBlue:Float = colorTransform.blueMultiplier; - var ogAlpha:Float = colorTransform.alphaMultiplier; - - var frameTime = Math.floor(__animTime * fps); - - for (i in 0...daText.length) { - __renderData.reset(this, ogRed, ogGreen, ogBlue, ogAlpha, daText.charAt(i)); - for (effect in effects) { - if (effect.willModify(i, i - lastLine, __renderData)) - effect.modify(i, i - lastLine, __renderData); - } - - var letter = __renderData.letter; - if (letter == "\n") { - curLine++; - lastLine = i + 1; - frameOffset.x = ogOffX - (textWidth - __laneWidths[curLine]) * alignmentMultiplier; - frameOffset.y -= lineGap; - continue; - } - - var data = getData(letter); - - if (data == null) { - frameOffset.x -= defaultAdvance; - continue; - } - - var advance:Float = Math.NaN; - - for (i in 0...data.components.length) { - __component = data.components[i]; - var anim = getLetterAnim(letter, data, __component, i); - advance = (Math.isNaN(advance)) ? getAdvance(letter, anim, data) : advance; - - if (anim == null || __renderData.alpha <= 0.0) - break; // shouldnt this be like continue? - - var frameToGet = frameTime % anim.numFrames; - frame = frames.frames[anim.frames[frameToGet]]; - - var offsetX = __component.x + __renderData.offsetX; - var offsetY = frame.sourceSize.y - lineGap + __component.y + __renderData.offsetY; - frameOffset.x += offsetX; - frameOffset.y += offsetY; - if (!isOnScreen(camera)) { - frameOffset.y -= offsetY; - frameOffset.x -= offsetX; - break; - } - if (isGraphicsShader) - shader.setCamSize(_frame.frame.x, _frame.frame.y, _frame.frame.width, _frame.frame.height); - - if (__component.hasColorMode) - __component.colorMode.setColorTransform(colorTransform, __renderData.red, __renderData.green, __renderData.blue, __renderData.alpha); - else - colorMode.setColorTransform(colorTransform, __renderData.red, __renderData.green, __renderData.blue, __renderData.alpha); - - drawLetter(camera); - frameOffset.y -= offsetY; - frameOffset.x -= offsetX; - } - frameOffset.x -= advance; - } - __component = null; - - setColorTransform(ogRed, ogGreen, ogBlue, ogAlpha, 0, 0, 0, 0); - frameOffset.set(ogOffX, ogOffY); - } - - function drawLetter(camera) { - _frame.prepareMatrix(_matrix, ANGLE_0, checkFlipX() != camera.flipX, checkFlipY() != camera.flipY); - - _matrix.translate(_frame.frame.width * -0.5, _frame.frame.height * -0.5); - _matrix.scale(__component.scaleX, __component.scaleY); - if(__component.shouldRotate) _matrix.rotateWithTrig(__component.cos, __component.sin); - _matrix.translate(_frame.frame.width * 0.5, _frame.frame.height * 0.5); - - _matrix.translate(-origin.x, -origin.y); - - if (frameOffsetAngle != null && frameOffsetAngle != angle) - { - var angleOff = (frameOffsetAngle - angle) * FlxAngle.TO_RAD; - var cos = Math.cos(angleOff); - var sin = Math.sin(angleOff); - // cos doesnt need to be negated - _matrix.rotateWithTrig(cos, -sin); - _matrix.translate(-frameOffset.x, -frameOffset.y); - _matrix.rotateWithTrig(cos, sin); - } - else - _matrix.translate(-frameOffset.x, -frameOffset.y); - - _matrix.scale(scale.x, scale.y); - - if (bakedRotationAngle <= 0) - { - updateTrig(); - - if (angle != 0) - _matrix.rotateWithTrig(_cosAngle, _sinAngle); - } - - getScreenPosition(_point, camera).subtractPoint(offset); - _point.add(origin.x, origin.y); - _matrix.translate(_point.x, _point.y); - - if (isPixelPerfectRender(camera)) - { - _matrix.tx = Math.floor(_matrix.tx); - _matrix.ty = Math.floor(_matrix.ty); - } - - doAdditionalMatrixStuff(_matrix, camera); - - if (layer != null) - layer.drawPixels(this, camera, _frame, framePixels, _matrix, colorTransform, blend, antialiasing, shaderEnabled ? shader : null); - else - camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shaderEnabled ? shader : null); - } - - override function updateHitbox():Void { - 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 + originOffset.x, textHeight * 0.5 + originOffset.y); - } - - function recalcSizes():Void { - __queueResize = false; - var curLine = 0; - var daText = switch (forceCase) { - case UPPER: text.toUpperCase(); - case LOWER: text.toLowerCase(); - case NONE: 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 data = getData(letter); - __laneWidths[curLine] += (data != null) ? getAdvance(letter, getLetterAnim(letter, data, data.components[0], 0), data) : defaultAdvance; - @:bypassAccessor textWidth = Math.max(textWidth, __laneWidths[curLine]); - } - - origin.set(textWidth * 0.5 + originOffset.x, textHeight * 0.5 + originOffset.y); - } - - function getAdvance(letter:String, anim:FlxAnimation, data:AlphabetLetterData):Float { - if (anim == null) - return defaultAdvance; - - if (data.advanceEmpty && !data.isDefault) { - data.advanceEmpty = false; - data.advance = frames.frames[anim.frames[0]].sourceSize.x; - } - return (data.isDefault) ? frames.frames[anim.frames[0]].sourceSize.x : data.advance; - } - - function getData(char:String):AlphabetLetterData { - if (failedLetters.contains(char)) return null; - for (i in 0...3) { // this feels wrong - if (loaded[i].contains(char)) - return defaults[i]; - } - - var data:AlphabetLetterData = null; - if (letterData.exists(char)) - data = letterData.get(char); - - if (data == null) { - var charCode:Int = char.charCodeAt(0); - if (charCode >= 'A'.code && charCode <= 'Z'.code && defaults[CaseMode.UPPER] != null) - data = defaults[CaseMode.UPPER]; - else if (charCode >= 'a'.code && charCode <= 'z'.code && defaults[CaseMode.LOWER] != null) - data = defaults[CaseMode.LOWER]; - else - data = defaults[CaseMode.NONE]; - } - - if(data == null) { - failedLetters.push(char); - return null; - } else if (data.isDefault) - loaded[defaultsIndexOf(data)].push(char); - return data; - } - - final function defaultsIndexOf(data:AlphabetLetterData):Int { - //while(i < defaults.length) { - for(i in 0...3) { - if(defaults[i] == data) - return i; - } - return -1; - } - - function getLetterAnim(char:String, data:AlphabetLetterData, component:AlphabetComponent, index:Int):FlxAnimation { - if (data == null) return null; - var name = char + Std.string(index); - if (animation.exists(name)) return animation.getByName(name); - - var anim = (data.isDefault) ? - component.anim.replace("LETTER", char) : - component.anim; - animation.addByPrefix(name, anim, fps); - if (!animation.exists(name)) { - failedLetters.push(char); - return null; - } - return animation.getByName(name); - } - - function checkNode(node:Xml):Void { - switch (node.nodeName) { - case "spritesheet": - if (frames == null) - frames = Paths.getFrames(node.firstChild().nodeValue); - else { - for (frame in Paths.getFrames(node.firstChild().nodeValue).frames) - frames.pushFrame(frame); - } - case "defaultAnim": - var idx = ["UPPER", "LOWER"].indexOf(node.get("casing").toUpperCase()) + 1; - - var rad:Float = Std.parseFloat(node.get("angle")).getDefaultFloat(0.0) * FlxAngle.TO_RAD; - var angleCos = Math.cos(rad); - var angleSin = Math.sin(rad); - - var res:AlphabetLetterData = { - isDefault: true, - advance: 0.0, - advanceEmpty: true, - components: [{ - anim: node.firstChild().nodeValue, - - x: 0.0, - y: 0.0, - scaleX: Std.parseFloat(node.get("scaleX")).getDefaultFloat(1.0), - scaleY: Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0), - - shouldRotate: rad != 0, - cos: angleCos, - sin: angleSin, - - hasColorMode: false, - colorMode: 0 - }] - } - - defaults[idx] = res; - case "composite": - if(!node.exists("char")) { - Logs.error(" must have a char attribute", "Alphabet"); - return; - } - var char = node.get("char"); - var advance:Float = Std.parseFloat(node.get("advance")); - var components:Array = []; - for (component in node.elementsNamed("component")) { - if(!component.exists("anim")) { - Logs.error(" must have a anim attribute", "Alphabet"); - return; - } - - var rad:Float = Std.parseFloat(component.get("angle")).getDefaultFloat(0.0) * FlxAngle.TO_RAD; - var angleCos = Math.cos(rad); - var angleSin = Math.sin(rad); - - var xOff:Float = -Std.parseFloat(component.get("x")).getDefaultFloat(0.0); - var yOff:Float = Std.parseFloat(component.get("y")).getDefaultFloat(0.0); - - var colorMode = ["tint", "offsets", "none"].indexOf(component.get("colorMode")); - - components.push({ - anim: component.get("anim"), - - x: xOff, - y: yOff, - scaleX: Std.parseFloat(component.get("scaleX")).getDefaultFloat(1.0), - scaleY: Std.parseFloat(component.get("scaleY")).getDefaultFloat(1.0), - - shouldRotate: rad != 0, - cos: angleCos, - sin: angleSin, - - hasColorMode: colorMode >= 0, - colorMode: colorMode - }); - } - letterData.set(char, { - isDefault: false, - advance: advance, - advanceEmpty: Math.isNaN(advance), - components: components - }); - case "anim": - if(!node.exists("char")) { - Logs.error(" must have a char attribute", "Alphabet"); - return; - } - var char = node.get("char"); - - var rad:Float = Std.parseFloat(node.get("angle")).getDefaultFloat(0.0) * FlxAngle.TO_RAD; - var angleCos = Math.cos(rad); - var angleSin = Math.sin(rad); - - var xOff:Float = -Std.parseFloat(node.get("x")).getDefaultFloat(0.0); - var yOff:Float = Std.parseFloat(node.get("y")).getDefaultFloat(0.0); - var advance:Float = Std.parseFloat(node.get("advance")); - - var colorMode = ["tint", "offsets", "none"].indexOf(node.get("colorMode")); - - letterData.set(char, { - isDefault: false, - advance: advance, - advanceEmpty: Math.isNaN(advance), - components: [{ - anim: node.firstChild().nodeValue, - - x: xOff, - y: yOff, - scaleX: Std.parseFloat(node.get("scaleX")).getDefaultFloat(1.0), - scaleY: Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0), - - shouldRotate: rad != 0, - cos: angleCos, - sin: angleSin, - - hasColorMode: colorMode >= 0, - colorMode: colorMode - }] - }); - case "languageSection": // used to only parse characters if a specific language is selected - if(!node.exists("langs")) { - Logs.error(" must have a langs attribute", "Alphabet"); - return; - } - var langs = [for (lang in node.get("langs").split(",")) lang.trim()]; - // maybe add a way to toggle this off? like force it to add all - if (langs.contains(Options.language)) { - for (langNode in node.elements()) - checkNode(langNode); - } - } - } - - function loadFont(value:String):Void { - __queueResize = true; - - var xml:Xml = Xml.parse(Assets.getText(Paths.xml("alphabet/" + value))).firstElement(); - - // reset old values - letterData = new Map(); - defaults = { - var v = new Vector(3); - v[0] = null; - v[1] = null; - v[2] = null; - v; - } - loaded = { - var v = new Vector(3); - v[0] = []; - v[1] = []; - v[2] = []; - v; - } - failedLetters = []; - - defaultAdvance = Std.parseFloat(xml.get("advance")).getDefaultFloat(40.0); - lineGap = Std.parseFloat(xml.get("lineGap")).getDefaultFloat(75.0); - fps = Std.parseFloat(xml.get("fps")).getDefaultFloat(24.0); - forceCase = xml.get("forceCasing").getDefault("none").toLowerCase(); - colorMode = (["offsets", "none"].indexOf(xml.get("colorMode")) + 1); - - frames = null; - - for (node in xml.elements()) - checkNode(node); - } - - override function destroy():Void { - originOffset = FlxDestroyUtil.destroy(originOffset); - failedLetters = []; - super.destroy(); - } - - function get_textWidth():Float { - if (__queueResize) - recalcSizes(); - return textWidth; - } - function set_textWidth(value:Float):Float { - if(!__queueResize) - __queueResize = __forceWidth != value; - return __forceWidth = value; - } - - function get_textHeight():Float { - if (__queueResize) - recalcSizes(); - return textHeight; - } - - function set_text(value:String):String { - if(!__queueResize) - __queueResize = text != value; - return text = value; - } - - function set_font(value:String):String { - if (font != value) { - loadFont(font = value); - } - return value; - } -} \ No newline at end of file diff --git a/source/funkin/menus/ui/effects/RegionEffect.hx b/source/funkin/menus/ui/effects/RegionEffect.hx index 1eac1025..7f99b166 100644 --- a/source/funkin/menus/ui/effects/RegionEffect.hx +++ b/source/funkin/menus/ui/effects/RegionEffect.hx @@ -3,7 +3,7 @@ package funkin.menus.ui.effects; import flixel.util.FlxColor; class AlphabetRenderData { - public var parent:NewAlphabet; + public var parent:Alphabet; public var letter:String; public var offsetX:Float = 0; public var offsetY:Float = 0; @@ -13,11 +13,11 @@ class AlphabetRenderData { public var blue:Float = 0; public var alpha:Float = 1; - public function new(parent:NewAlphabet) { + public function new(parent:Alphabet) { this.parent = parent; } - public function reset(parent:NewAlphabet, red:Float, green:Float, blue:Float, alpha:Float, letter:String) { + public function reset(parent:Alphabet, red:Float, green:Float, blue:Float, alpha:Float, letter:String) { this.parent = parent; this.letter = letter; this.offsetX = 0; diff --git a/source/funkin/options/keybinds/KeybindSetting.hx b/source/funkin/options/keybinds/KeybindSetting.hx index 191c3233..c793e35a 100644 --- a/source/funkin/options/keybinds/KeybindSetting.hx +++ b/source/funkin/options/keybinds/KeybindSetting.hx @@ -20,7 +20,7 @@ class KeybindSetting extends FlxTypedSpriteGroup { public function new(x:Float, y:Float, name:String, value:String, ?sparrowIcon:String, ?sparrowAnim:String) { super(); this.value = value; - title = new Alphabet(0, 0, name, true); + title = new Alphabet(0, 0, name, "bold"); add(title); var controlArrayP1:Array = Reflect.field(Options, 'P1_${value}'); @@ -31,12 +31,13 @@ class KeybindSetting extends FlxTypedSpriteGroup { for(i in 1...3) { var b = null; + var bx = FlxG.width * (0.25 * (i+1)) - x; if (i == 1) - b = bind1 = new Alphabet(0, 0, "", false); + b = bind1 = new Alphabet(bx, 0, "", "normal"); else - b = bind2 = new Alphabet(0, 0, "", false); + b = bind2 = new Alphabet(bx, 0, "", "normal"); - b.setPosition(FlxG.width * (0.25 * (i+1)) - x, -60); + //b.setPosition(FlxG.width * (0.25 * (i+1)) - x, -60); add(b); } updateText(); diff --git a/source/funkin/options/keybinds/KeybindsOptions.hx b/source/funkin/options/keybinds/KeybindsOptions.hx index e8a0d95e..56e8f0f5 100644 --- a/source/funkin/options/keybinds/KeybindsOptions.hx +++ b/source/funkin/options/keybinds/KeybindsOptions.hx @@ -148,7 +148,7 @@ class KeybindsOptions extends MusicBeatSubstate { var k:Int = 0; for(category in categories) { k++; - var title = new Alphabet(0, k * 75, category.name, true); + var title = new Alphabet(0, k * 75, category.name, "bold"); title.screenCenter(X); add(title); diff --git a/source/funkin/options/type/ArrayOption.hx b/source/funkin/options/type/ArrayOption.hx index 5a1496ec..cede15dc 100644 --- a/source/funkin/options/type/ArrayOption.hx +++ b/source/funkin/options/type/ArrayOption.hx @@ -37,8 +37,8 @@ class ArrayOption extends OptionType { this.optionName = optionName; - add(__text = new Alphabet(100, 20, text, true)); - add(__selectiontext = new Alphabet(__text.width + 120, -30, formatTextOption(), false)); + add(__text = new Alphabet(100, 20, text, "bold")); + add(__selectiontext = new Alphabet(__text.width + 120, 20, formatTextOption(), "normal")); rawText = text; } diff --git a/source/funkin/options/type/NumOption.hx b/source/funkin/options/type/NumOption.hx index 17c7ff80..10608350 100644 --- a/source/funkin/options/type/NumOption.hx +++ b/source/funkin/options/type/NumOption.hx @@ -39,8 +39,8 @@ class NumOption extends OptionType { this.changeVal = changeVal; this.optionName = optionName; - add(__text = new Alphabet(100, 20, text, true)); - add(__number = new Alphabet(__text.width + 120, -30, ': $currentSelection', false)); + add(__text = new Alphabet(100, 20, text, "bold")); + add(__number = new Alphabet(__text.width + 120, 20, ': $currentSelection', "normal")); rawText = text; } diff --git a/source/funkin/options/type/TextOption.hx b/source/funkin/options/type/TextOption.hx index b16d285d..8f7ec08d 100644 --- a/source/funkin/options/type/TextOption.hx +++ b/source/funkin/options/type/TextOption.hx @@ -20,7 +20,7 @@ class TextOption extends OptionType { public function new(text:String, desc:String, ?suffix:String = "", ?selectCallback:Void->Void = null) { super(desc); this.selectCallback = selectCallback; - add(__text = new Alphabet(100, 20, text, true)); + add(__text = new Alphabet(100, 20, text, "bold")); this.suffix = suffix; rawText = text; }