diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx index 83311c8d..47a3fc33 100644 --- a/source/funkin/backend/system/MainState.hx +++ b/source/funkin/backend/system/MainState.hx @@ -66,13 +66,16 @@ class MainState extends FlxState { if(Framerate.isLoaded) Framerate.instance.reload(); - if (betaWarningShown) + FlxG.switchState(new funkin.editors.alphabet.AlphabetEditor("bold")); + /*if (betaWarningShown) FlxG.switchState(new TitleState()); else { FlxG.switchState(new BetaWarningState()); betaWarningShown = true; - } + }*/ + #if sys CoolUtil.safeAddAttributes('./.temp/', NativeAPI.FileAttribute.HIDDEN); + #end } } \ No newline at end of file diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx index df8bf94d..36342c67 100644 --- a/source/funkin/backend/utils/CoolUtil.hx +++ b/source/funkin/backend/utils/CoolUtil.hx @@ -78,7 +78,7 @@ class CoolUtil /** * Applies the % operator, but without any negatives. - * + * * @param dividend The intial value. The left side of the equation. * @param divisor What to modulo by. The right side of the equation. * @return A positive reminder of `dividend / divisor`. @@ -87,6 +87,17 @@ class CoolUtil return ((dividend % divisor) + divisor) % divisor; } + /** + * Applies the % operator, but without any negatives. But it's an int. + * + * @param dividend The intial value. The left side of the equation. + * @param divisor What to modulo by. The right side of the equation. + * @return A positive reminder of `dividend / divisor`. + */ + public static inline function positiveModuloInt(dividend:Int, divisor:Int) { + return ((dividend % divisor) + divisor) % divisor; + } + /** * Shortcut to parse JSON from an Asset path * @param assetPath Path to the JSON asset. diff --git a/source/funkin/editors/alphabet/AlphabetEditor.hx b/source/funkin/editors/alphabet/AlphabetEditor.hx index f0b27969..f7138910 100644 --- a/source/funkin/editors/alphabet/AlphabetEditor.hx +++ b/source/funkin/editors/alphabet/AlphabetEditor.hx @@ -8,6 +8,7 @@ import funkin.game.Character; import haxe.xml.Access; import haxe.xml.Printer; +@:access(funkin.menus.ui.Alphabet) class AlphabetEditor extends UIState { static var __typeface:String; @@ -32,6 +33,15 @@ class AlphabetEditor extends UIState { inline function translate(id:String, ?args:Array) return TU.translate("alphabetEditor." + id, args); + public var tape:Alphabet; + public var bigletter:Alphabet; + public var curLetter:Int = 0; + public var targetX:Float = 0; + + public var curSelectedComponent:AlphabetComponent = null; + + public var allChars:Array = []; + public override function create() { super.create(); @@ -98,32 +108,15 @@ class AlphabetEditor extends UIState { }, ] }, - { - label: " ", - childs: [ - { - label: "Tape", - //onSelect: _tape - } - ] - }, { label: "Move Tape Left", - childs: [ - { - label: "Tape", - //onSelect: _tape - } - ] + keybind: [LEFT], + onSelect: _tape_left }, { label: "Move Tape Right", - childs: [ - { - label: "Tape", - //onSelect: _tape - } - ] + keybind: [RIGHT], + onSelect: _tape_right } ]; @@ -132,7 +125,7 @@ class AlphabetEditor extends UIState { uiCamera = new FlxCamera(); uiCamera.bgColor = 0; - FlxG.cameras.add(uiCamera); + FlxG.cameras.add(uiCamera, false); var bg = new FlxSprite(0, 0).makeSolid(Std.int(FlxG.width + 100), Std.int(FlxG.height + 100), 0xFF7f7f7f); bg.cameras = [editorCamera]; @@ -146,79 +139,51 @@ class AlphabetEditor extends UIState { //var alphabet:Alphabet = new Alphabet(0, 0, __typeface); //add(alphabet); - var xml = Xml.parse(Assets.getText(Paths.xml('alphabet/$__typeface'))).firstElement(); - var spritesheet = null; - for(node in xml.elements()) { - if (node.nodeName == "spritesheet") { - spritesheet = node.firstChild().nodeValue; - break; + allChars = []; + + var helperAlphabet = new Alphabet(0, 0, "meow", __typeface); + for(cc in 33...0xffff) { + // todo: unicode + var letter = String.fromCharCode(cc); + var data = helperAlphabet.fastGetData(letter); + if(data == null) continue; + if(data.components.length == 0) continue; + + var hasAnim = true; + var component; + for (i in 0...data.components.length) { + component = data.components[i]; + var anim = helperAlphabet.fastGetLetterAnim(letter, data, component, i); + if(anim == null) { + hasAnim = false; + break; + } } + if(!hasAnim) continue; + + allChars.push(letter); } - var useColorOffsets = xml.get("useColorOffsets").getDefault("false") == "true"; + trace(allChars.join(" ")); + tape = new Alphabet(0, 70, allChars.join(" "), __typeface); + tape.alignment = CENTER; + tape.renderMode = MONOSPACE; + tape.x = targetX = FlxG.width * 0.5 - tape.defaultAdvance * 0.5; + add(tape); - // todo fix crash if invalid spritesheet; - var alphabetFrames = Paths.getFrames(spritesheet); - - var allChars = ""; - for(frame in alphabetFrames.frames) { - var secondChar = frame.name.charAt(1); - if(secondChar != " ") continue; - if(allChars.contains(frame.name.charAt(0))) continue; - allChars += frame.name.charAt(0); - } - - var letterSprites:Array = []; - for(letter in allChars.split("")) { - var spr = new FlxSprite(0, 0); - spr.frames = alphabetFrames; - spr.antialiasing = true; - spr.animation.addByPrefix("letter", letter, 24); - spr.animation.play("letter"); - add(spr); - letterSprites.push(spr); - } - - var length = letterSprites.length; - var letterWidth = 70; - var letterHeight = 80; - //var width = 10; - //var totalWidth = 10 * 70; - //for(i in 0...length) { - // var letter = letterSprites[i]; - // var x = i % width * 70 + (FlxG.width / 2 - totalWidth / 2); - // var y = Std.int(i / width) * 80 + 100; - // letter.setPosition(x, y); - //} - - var randomIndex = FlxG.random.int(0, length-1); - for(i in 0...length) { - var letter = letterSprites[i]; - letter.alpha = 0.2; - } - letterSprites[randomIndex].alpha = 1; - - for(i in 0...length) { - var letter = letterSprites[i]; - letter.x = i * letterWidth - randomIndex * letterWidth + (FlxG.width / 2 - letterWidth / 2); - letter.y = 50; - } - - var bigletter = new FlxSprite(0, 0); - bigletter.frames = alphabetFrames; - bigletter.antialiasing = true; - bigletter.animation.addByPrefix("letter", allChars.charAt(randomIndex), 24); - bigletter.animation.play("letter"); + bigletter = new Alphabet(0, 0, "A", __typeface); + bigletter.alignment = CENTER; + bigletter.scale.set(4, 4); + // TODO: fix the offset issues bigletter.updateHitbox(); bigletter.screenCenter(); - bigletter.scale.set(4, 4); add(bigletter); var infoWindow = new GlyphInfoWindow(); infoWindow.info.text = [ - "Char: " + allChars.charAt(randomIndex), - "Anim Name: " + allChars.charAt(randomIndex) + " bold0", + //"Char: " + allChars.charAt(randomIndex), + //"Anim Name: " + allChars.charAt(randomIndex) + " bold0", "X Offset: " + 0, "Y Offset: " + 0, "ScaleX: " + 1, @@ -230,7 +195,7 @@ class AlphabetEditor extends UIState { var leftWindow = new UIWindow(30, 720 - 170 - 30, 230, 170, "Components:"); leftWindow.members.push({ var info = new UIText(leftWindow.x + 28, leftWindow.y + 46, 400, ""); - info.text = "[0] Letter (" + allChars.charAt(randomIndex) + ")"; + //info.text = "[0] Letter (" + allChars.charAt(randomIndex) + ")"; info.alignment = LEFT; info; }); @@ -266,18 +231,37 @@ class AlphabetEditor extends UIState { UIUtil.processShortcuts(topMenu); } - if(FlxG.keys.pressed.J) { - editorCamera.scroll.x -= 100 * elapsed; - } - if(FlxG.keys.pressed.L) { - editorCamera.scroll.x += 100 * elapsed; - } - if(FlxG.keys.pressed.I) { - editorCamera.scroll.y -= 100 * elapsed; - } - if(FlxG.keys.pressed.K) { - editorCamera.scroll.y += 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; + } } + + tape.x = lerp(tape.x, targetX, 0.25); + } + + function changeLetter(inc:Int) { + curLetter = CoolUtil.positiveModuloInt(curLetter + inc, allChars.length); + targetX = FlxG.width * 0.5 - tape.defaultAdvance * (0.5 + curLetter * 2); + bigletter.text = allChars[curLetter]; + bigletter.updateHitbox(); + bigletter.screenCenter(); + } + + function _tape_left(_) { + changeLetter(-1); + } + function _tape_right(_) { + changeLetter(1); } function _file_save(_) { @@ -303,6 +287,37 @@ class AlphabetEditor extends UIState { } } +/* + +/===============\ +| Components | +|===============| +|[ Component 1 ]| +|[ Component 2 ]| +|[ Component 3 ]| +|[ Component 4 ]| +|[ Component 5 ]| +|[ Add component]| +\===============/ + +*/ + +class ComponentInfoWindow extends UIButtonList { + public function new(x:Float, y:Float) { + super(x, y, ) + } +} + +class ComponentButton extends UIButton { + public var component:AlphabetComponent; + public function new(component:AlphabetComponent) { + super(0, 0, component.name, function() { + AlphabetEditor.instance.curSelectedComponent = component; + }); + this.component = component; + } +} + class GlyphInfoWindow extends UIWindow { public var info:UIText; public function new() { diff --git a/source/funkin/menus/ui/Alphabet.hx b/source/funkin/menus/ui/Alphabet.hx index 4298be57..495fe8f9 100644 --- a/source/funkin/menus/ui/Alphabet.hx +++ b/source/funkin/menus/ui/Alphabet.hx @@ -23,6 +23,7 @@ final class AlphabetComponent { // Precalculated values for angle public var shouldRotate:Bool; + public var angle:Float; public var sin:Float; public var cos:Float; public var scaleX:Float; @@ -99,6 +100,11 @@ enum abstract ColorMode(ByteInt) from ByteInt to ByteInt { } } +enum abstract AlphabetRenderMode(ByteUInt) from ByteUInt to ByteUInt { + var DEFAULT = 0; + var MONOSPACE = 1; +} + class Alphabet extends FlxSprite { public var effects:Array = []; var __renderData:AlphabetRenderData; @@ -141,6 +147,8 @@ class Alphabet extends FlxSprite { }; var failedLetters:Array = []; + public var renderMode:AlphabetRenderMode = DEFAULT; + // for menu shit public var targetY:Float = 0; public var isMenuItem:Bool = false; @@ -220,6 +228,8 @@ class Alphabet extends FlxSprite { var frameTime = Math.floor(__animTime * fps); + var isMonospace = renderMode == MONOSPACE; + for (i in 0...daText.length) { __renderData.reset(this, ogRed, ogGreen, ogBlue, ogAlpha, daText.charAt(i)); for (effect in effects) { @@ -258,6 +268,8 @@ class Alphabet extends FlxSprite { var offsetX = __component.x + __renderData.offsetX; var offsetY = frame.sourceSize.y - lineGap + __component.y + __renderData.offsetY; + if (isMonospace) + offsetX -= (defaultAdvance - advance) * 0.5; frameOffset.x += offsetX; frameOffset.y += offsetY; if (!isOnScreen(camera)) { @@ -277,7 +289,7 @@ class Alphabet extends FlxSprite { frameOffset.y -= offsetY; frameOffset.x -= offsetX; } - frameOffset.x -= advance; + frameOffset.x -= isMonospace ? defaultAdvance : advance; } __component = null; @@ -380,16 +392,8 @@ class Alphabet extends FlxSprite { 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); + private function fastGetData(char:String):AlphabetLetterData { + var data = letterData.get(char); if (data == null) { var charCode:Int = char.charCodeAt(0); @@ -401,7 +405,19 @@ class Alphabet extends FlxSprite { data = defaults[CaseMode.NONE]; } - if(data == null) { + return data; + } + + 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 = fastGetData(char); + + if (data == null) { failedLetters.push(char); return null; } else if (data.isDefault) @@ -418,6 +434,16 @@ class Alphabet extends FlxSprite { return -1; } + function fastGetLetterAnim(char:String, data:AlphabetLetterData, component:AlphabetComponent, index:Int):FlxAnimation { + var name = char + Std.string(index); + + var anim = (data.isDefault) ? + component.anim.replace("LETTER", char) : + component.anim; + animation.addByPrefix(name, anim, fps); + return animation.exists(name) ? animation.getByName(name) : null; + } + function getLetterAnim(char:String, data:AlphabetLetterData, component:AlphabetComponent, index:Int):FlxAnimation { if (data == null) return null; var name = char + Std.string(index); @@ -446,7 +472,8 @@ class Alphabet extends FlxSprite { 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 angle:Float = Std.parseFloat(node.get("angle")).getDefaultFloat(0.0); + var rad = angle * FlxAngle.TO_RAD; var angleCos = Math.cos(rad); var angleSin = Math.sin(rad); @@ -462,7 +489,8 @@ class Alphabet extends FlxSprite { scaleX: Std.parseFloat(node.get("scaleX")).getDefaultFloat(1.0), scaleY: Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0), - shouldRotate: rad != 0, + shouldRotate: angle != 0, + angle: angle, cos: angleCos, sin: angleSin, @@ -486,7 +514,8 @@ class Alphabet extends FlxSprite { return; } - var rad:Float = Std.parseFloat(component.get("angle")).getDefaultFloat(0.0) * FlxAngle.TO_RAD; + var angle:Float = Std.parseFloat(component.get("angle")).getDefaultFloat(0.0); + var rad = angle * FlxAngle.TO_RAD; var angleCos = Math.cos(rad); var angleSin = Math.sin(rad); @@ -503,7 +532,8 @@ class Alphabet extends FlxSprite { scaleX: Std.parseFloat(component.get("scaleX")).getDefaultFloat(1.0), scaleY: Std.parseFloat(component.get("scaleY")).getDefaultFloat(1.0), - shouldRotate: rad != 0, + shouldRotate: angle != 0, + angle: angle, cos: angleCos, sin: angleSin, @@ -524,7 +554,8 @@ class Alphabet extends FlxSprite { } var char = node.get("char"); - var rad:Float = Std.parseFloat(node.get("angle")).getDefaultFloat(0.0) * FlxAngle.TO_RAD; + var angle:Float = Std.parseFloat(node.get("angle")).getDefaultFloat(0.0); + var rad = angle * FlxAngle.TO_RAD; var angleCos = Math.cos(rad); var angleSin = Math.sin(rad); @@ -546,7 +577,8 @@ class Alphabet extends FlxSprite { scaleX: Std.parseFloat(node.get("scaleX")).getDefaultFloat(1.0), scaleY: Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0), - shouldRotate: rad != 0, + shouldRotate: angle != 0, + angle: angle, cos: angleCos, sin: angleSin, @@ -596,6 +628,7 @@ class Alphabet extends FlxSprite { fps = Std.parseFloat(xml.get("fps")).getDefaultFloat(24.0); forceCase = xml.get("forceCasing").getDefault("none").toLowerCase(); colorMode = (["offsets", "none"].indexOf(xml.get("colorMode")) + 1); + antialiasing = xml.get("antialiasing").getDefault("true") == "true"; frames = null; @@ -603,6 +636,38 @@ class Alphabet extends FlxSprite { checkNode(node); } + private static var alphabetProperties:Array = ["fps", "advance", "lineGap", "forceCasing", "useColorOffsets", "antialiasing"]; + private static var componentProperties:Array = ["name", "anim", "x", "y", "scaleX", "scaleY", "angle", "offsetX", "offsetY"]; + + public function buildXML():Xml { + var xml = Xml.createElement("alphabet"); + xml.set("fps", Std.string(fps)); + xml.set("advance", Std.string(defaultAdvance)); + xml.set("lineGap", Std.string(lineGap)); + xml.set("forceCasing", forceCase); + xml.set("useColorOffsets", colorMode == OFFSET ? "true" : "false"); + xml.set("antialiasing", antialiasing ? "true" : "false"); + + xml.attributeOrder = alphabetProperties; + + /*for (component in components) { + var componentXml = Xml.createElement("component"); + componentXml.attributeOrder = componentProperties; + componentXml.set("name", component.name); + componentXml.set("anim", component.anim); + componentXml.set("x", Std.string(component.x)); + componentXml.set("y", Std.string(component.y)); + if(component.scaleX != 1) componentXml.set("scaleX", Std.string(component.scaleX)); + if(component.scaleY != 1) componentXml.set("scaleY", Std.string(component.scaleY)); + if(component.angle != 0) componentXml.set("angle", Std.string(component.angle)); + if(component.offsetX != 0) componentXml.set("offsetX", Std.string(component.offsetX)); + if(component.offsetY != 0) componentXml.set("offsetY", Std.string(component.offsetY)); + xml.addChild(componentXml); + }*/ + + return xml; + } + override function destroy():Void { originOffset = FlxDestroyUtil.destroy(originOffset); failedLetters = [];