From f6891dda524462a32acac7d4b8865f0cb8ff5652 Mon Sep 17 00:00:00 2001 From: Ne_Eo Date: Sun, 19 Jan 2025 18:14:17 +0100 Subject: [PATCH] Work on alphabet editor --- assets/data/alphabet/normal.xml | 85 ++++--- source/funkin/backend/system/MainState.hx | 2 +- .../funkin/editors/alphabet/AlphabetEditor.hx | 110 +++++---- .../alphabet/AlphabetMainDataScreen.hx | 214 ++++++++++++++++++ source/funkin/menus/ui/Alphabet.hx | 47 +++- 5 files changed, 376 insertions(+), 82 deletions(-) create mode 100644 source/funkin/editors/alphabet/AlphabetMainDataScreen.hx diff --git a/assets/data/alphabet/normal.xml b/assets/data/alphabet/normal.xml index ec05dcc4..9180d8df 100644 --- a/assets/data/alphabet/normal.xml +++ b/assets/data/alphabet/normal.xml @@ -1,34 +1,66 @@ - + menus/alphabet - LETTER capital - LETTER lowercase + character-LOWERLETTER-capital + character-LOWERLETTER-lowercase LETTER - g lowercase - j lowercase - p lowercase - q lowercase - y lowercase - right arrow - left arrow - down arrow - up arrow - comma - comma - comma - period - exclamation point - question mark - forward slash - start parentheses - - - ^ - ~ - = + character-g-lowercase + character-j-lowercase + character-p-lowercase + character-q-lowercase + character-y-lowercase - + character-zero + character-one + character-two + character-three + character-four + character-five + character-six + character-seven + character-eight + character-nine + + character-ampersand + character-anglebracket-left + character-anglebracket-right + character-arrow-right + character-arrow-left + character-arrow-down + character-arrow-up + character-asterisk + character-at + character-backslash + character-backtick + character-caret + character-colon + character-comma + character-curlybracket-left + character-curlybracket-right + character-dollar + character-doublequote-start + character-equal + character-exclamationmark + character-minus + 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 + + @@ -83,5 +114,5 @@ - + --> \ No newline at end of file diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx index ccc794eb..4398fc38 100644 --- a/source/funkin/backend/system/MainState.hx +++ b/source/funkin/backend/system/MainState.hx @@ -69,7 +69,7 @@ class MainState extends FlxState { if(Framerate.isLoaded) Framerate.instance.reload(); - FlxG.switchState(new funkin.editors.alphabet.AlphabetEditor("bold")); + FlxG.switchState(new funkin.editors.alphabet.AlphabetEditor("normal")); /* if (Flags.DISABLE_BETA_WARNING_SCREEN || betaWarningShown) FlxG.switchState(new TitleState()); diff --git a/source/funkin/editors/alphabet/AlphabetEditor.hx b/source/funkin/editors/alphabet/AlphabetEditor.hx index 4824807e..52f65f35 100644 --- a/source/funkin/editors/alphabet/AlphabetEditor.hx +++ b/source/funkin/editors/alphabet/AlphabetEditor.hx @@ -35,7 +35,7 @@ class AlphabetEditor extends UIState { return TU.translate("editor.alphabet." + id, args); public var tape:Alphabet; - public var bigletter:Alphabet; + public var bigLetter:Alphabet; public var curLetter:Int = 0; public var targetX:Float = 0; @@ -43,7 +43,8 @@ class AlphabetEditor extends UIState { public var curSelectedComponent:AlphabetComponent = null; - public var allChars:Array = []; + public var defaultTmr:Float = 0.0; + public var charsForDefault:Array = []; public override function create() { super.create(); @@ -74,6 +75,23 @@ class AlphabetEditor extends UIState { ] // TODO: add more options }, + { + label: translate("topBar.edit"), + childs: [ + { + label: translate("edit.undo"), + // TODO: add undo + }, + { + label: translate("edit.redo"), + // TODO: add redo + }, + { + label: "Edit Main Data", // TODO: add translations + onSelect: _edit_main + } + ] + }, { label: translate("topBar.glyph"), childs: [ @@ -143,51 +161,23 @@ class AlphabetEditor extends UIState { topMenuSpr = new UITopMenu(topMenu); topMenuSpr.cameras = uiGroup.cameras = [uiCamera]; - - //var alphabet:Alphabet = new Alphabet(0, 0, __typeface); - //add(alphabet); - - 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); - } - // todo get other characters - - - trace(allChars.join(" ")); - tape = new Alphabet(0, 70, allChars.join(" "), __typeface); + + tape = new Alphabet(0, 70, "", __typeface); tape.alignment = CENTER; tape.renderMode = MONOSPACE; - tape.x = targetX = FlxG.width * 0.5 - tape.defaultAdvance * 0.5; add(tape); - - bigletter = new Alphabet(0, 0, "A", __typeface); - bigletter.alignment = CENTER; - bigletter.scale.set(4, 4); + + bigLetter = new Alphabet(0, 0, charsForDefault[0], ""); + bigLetter.copyData(tape); + bigLetter.alignment = CENTER; + bigLetter.scale.set(4, 4); // TODO: fix the offset issues, when not using updateHitbox - bigletter.updateHitbox(); - bigletter.screenCenter(); - add(bigletter); + bigLetter.updateHitbox(); + bigLetter.screenCenter(); + add(bigLetter); + + updateTape(); + tape.x = targetX; var infoWindow = new GlyphInfoWindow(); infoWindow.info.text = [ @@ -253,15 +243,37 @@ class AlphabetEditor extends UIState { } } + var lastIdx = Math.floor(defaultTmr); + defaultTmr += elapsed * 0.5; + if (lastIdx != (lastIdx = Math.floor(defaultTmr))) + tape.text = charsForDefault[Math.floor(defaultTmr)] + " " + tape.manualLetters.join(" "); tape.x = lerp(tape.x, targetX, 0.25); } + function updateTape() { + charsForDefault = []; + for (i in 33...127) { + var letter = String.fromCharCode(i); + if (tape.manualLetters.contains(letter)) continue; + + var data = tape.fastGetData(letter); + for (i => com in data.components) { + if (tape.fastGetLetterAnim(letter, data, com, i) == null) + continue; + } + charsForDefault.push(letter); + } + defaultTmr %= charsForDefault.length; + tape.text = charsForDefault[Math.floor(defaultTmr)] + " " + tape.manualLetters.join(" "); + changeLetter(0); + } + function changeLetter(inc:Int) { - curLetter = CoolUtil.positiveModuloInt(curLetter + inc, allChars.length); + curLetter = CoolUtil.positiveModuloInt(curLetter + inc, tape.manualLetters.length + 1); targetX = FlxG.width * 0.5 - tape.defaultAdvance * (0.5 + curLetter * 2); - bigletter.text = allChars[curLetter]; - bigletter.updateHitbox(); - bigletter.screenCenter(); + bigLetter.text = (curLetter == 0) ? charsForDefault[0] : tape.manualLetters[curLetter - 1]; + bigLetter.updateHitbox(); + bigLetter.screenCenter(); } function _tape_left(_) { @@ -292,6 +304,10 @@ class AlphabetEditor extends UIState { /*if (undos.unsaved) SaveWarning.triggerWarning(); else */FlxG.switchState(new AlphabetSelection()); } + + function _edit_main(_) { + FlxG.state.openSubState(new AlphabetMainDataScreen()); + } } /* diff --git a/source/funkin/editors/alphabet/AlphabetMainDataScreen.hx b/source/funkin/editors/alphabet/AlphabetMainDataScreen.hx new file mode 100644 index 00000000..2c4b08fa --- /dev/null +++ b/source/funkin/editors/alphabet/AlphabetMainDataScreen.hx @@ -0,0 +1,214 @@ +package funkin.editors.alphabet; + +import flixel.math.FlxRect; +import funkin.editors.alphabet.AlphabetEditor; +import funkin.menus.ui.Alphabet; + +using StringTools; + +// TODO: add translations +class AlphabetMainDataScreen extends UISubstateWindow { + var bigLetter:Alphabet; + var tape:Alphabet; + + public var saveButton:UIButton; + public var closeButton:UIButton; + public var helpButton:UIButton; + + public var fpsStepper:UINumericStepper; + public var advanceStepper:UINumericStepper; + public var lineGapStepper:UINumericStepper; + + public var caseDropdown:UIDropDown; + public var colorDropdown:UIDropDown; + + public var regTextBox:UITextBox; + public var upperTextBox:UITextBox; + public var lowerTextBox:UITextBox; + + public var targetPercent:Float = 0.0; + public var helpBG:UISliceSprite; + public var helpTxt:UIText; + public var helpScroll:UIScrollBar; + + public override function create() { + bigLetter = AlphabetEditor.instance.bigLetter; + tape = AlphabetEditor.instance.tape; + + // temporary until translated + var helpDesc = +"FPS - Frames per second. You get it. +Advance - Actually a backup advance value for missing characters and spaces. +Line Gap - How many pixels to move down when a new line is created. + +Force Casing - Whether to force the text casing to uppercase, lowercase, or neither. +Color Mode - How to color the alphabet. +TINT muliplies the channels, perfect for white letters. +OFFSETS adds to the channels, perfect for black letters. +NONE simply disables coloring. + +Backup Prefixes - What prefix to use if a glyph is not predefined. +Uppercase and Lowercase will default to regular if it is left blank. +LETTER will be substituted with the provided glyph. +UPPERLETTER will be substituted with the uppercase glyph. +LOWERLETTER will be substituted with the lowercase glyph. +UPPERLETTER and LOWERLETTTER can be used in any backup prefix."; + + winTitle = "Alphabet Properties"; + winWidth = 420; winHeight = 520; + + super.create(); + + function addLabelOn(ui:UISprite, text:String) + add(new UIText(ui.x, ui.y - 24, 0, text)); + + var title:UIText; + add(title = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, "Edit Main Alphabet Data", 28)); + + fpsStepper = new UINumericStepper(title.x, title.y + title.height + 38, AlphabetEditor.instance.bigLetter.fps, 1, 2, 0.01, null, 120); + add(fpsStepper); + addLabelOn(fpsStepper, "FPS"); + + advanceStepper = new UINumericStepper(fpsStepper.x + 140, fpsStepper.y, AlphabetEditor.instance.bigLetter.defaultAdvance, 1, 2, 0, null, 120); + add(advanceStepper); + addLabelOn(advanceStepper, "Advance"); + + lineGapStepper = new UINumericStepper(advanceStepper.x + 140, advanceStepper.y, AlphabetEditor.instance.bigLetter.lineGap, 1, 2, 0, null, 120); + add(lineGapStepper); + addLabelOn(lineGapStepper, "Line Gap"); + + caseDropdown = new UIDropDown(fpsStepper.x, fpsStepper.y + fpsStepper.bHeight + 50, 206, 32, ["NONE", "UPPER", "LOWER"], tape.forceCase); + add(caseDropdown); + addLabelOn(caseDropdown, "Force Casing"); + + colorDropdown = new UIDropDown(caseDropdown.x + caseDropdown.bWidth + 20, caseDropdown.y, 206, 32, ["TINT", "OFFSETS", "NONE"], tape.colorMode); + add(colorDropdown); + addLabelOn(colorDropdown, "Color Mode"); + + var regPrefix = (tape.defaults[0] != null) ? tape.defaults[0].components[0].anim : ""; + regTextBox = new UITextBox(caseDropdown.x, caseDropdown.y + caseDropdown.bHeight + 50, regPrefix, 368); + add(regTextBox); + addLabelOn(regTextBox, "Backup Prefix (Regular)"); + + var upperPrefix = (tape.defaults[1] != null) ? tape.defaults[1].components[0].anim : ""; + upperTextBox = new UITextBox(regTextBox.x, regTextBox.y + regTextBox.bHeight + 35, upperPrefix, 368); + add(upperTextBox); + addLabelOn(upperTextBox, "Backup Prefix (Uppercase)"); + + var lowerPrefix = (tape.defaults[2] != null) ? tape.defaults[2].components[0].anim : ""; + lowerTextBox = new UITextBox(upperTextBox.x, upperTextBox.y + upperTextBox.bHeight + 35, lowerPrefix, 368); + add(lowerTextBox); + addLabelOn(lowerTextBox, "Backup Prefix (Lowercase)"); + + saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20 - 125, windowSpr.y + windowSpr.bHeight - 16 - 32, TU.translate("editor.saveClose"), function() { + saveInfo(); + close(); + }, 125); + add(saveButton); + + closeButton = new UIButton(saveButton.x - 20, saveButton.y, TU.translate("editor.close"), function() { + close(); + }, 125); + add(closeButton); + closeButton.color = 0xFFFF0000; + closeButton.x -= closeButton.bWidth; + + helpButton = new UIButton(closeButton.x - 20, closeButton.y, "?", function() { + targetPercent = 1.0 - targetPercent; + }, 32); + add(helpButton); + helpButton.x -= helpButton.bWidth; + + helpBG = new UISliceSprite(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y, 340, windowSpr.bHeight, 'editors/ui/context-bg'); + helpBG.alpha = 0.0; + add(helpBG); + + helpTxt = new UIText(helpBG.x + 20, helpBG.y + 20, helpBG.bWidth - 60, helpDesc); + helpTxt.clipRect = new FlxRect(0, 0, helpTxt.width, helpBG.bHeight - 40); + helpTxt.alpha = 0.0; + add(helpTxt); + + helpScroll = new UIScrollBar(helpBG.x + helpBG.bWidth - 30, helpBG.y + 20, (helpTxt.height - helpTxt.clipRect.height), 0, (helpTxt.clipRect.height / helpTxt.height), 20, helpBG.bHeight - 40); + helpScroll.size *= helpScroll.height; + helpScroll.start -= helpScroll.size * 0.5; + helpScroll.alpha = 0.0; + helpScroll.onChange = function(v) { + helpTxt.clipRect.y = v; + helpTxt.offset.y = v; + helpTxt.clipRect = helpTxt.clipRect; + helpScroll.start = v - helpScroll.size * 0.5; + } + add(helpScroll); + } + + override function update(elapsed:Float) { + super.update(elapsed); + + helpBG.alpha = FlxMath.lerp(helpBG.alpha, targetPercent, elapsed * 15); + helpBG.x = windowSpr.x + windowSpr.bWidth - 20 + 40 * helpBG.alpha; + helpTxt.alpha = helpBG.alpha; + helpTxt.x = helpBG.x + 20; + helpScroll.alpha = helpScroll.thumb.alpha = helpScroll.thumbIcon.alpha = helpBG.alpha; + helpScroll.x = helpBG.x + helpBG.bWidth - 30; + } + + public function saveInfo() { + @:privateAccess fpsStepper.__onChange(fpsStepper.label.text); + @:privateAccess advanceStepper.__onChange(advanceStepper.label.text); + @:privateAccess lineGapStepper.__onChange(lineGapStepper.label.text); + + bigLetter.fps = tape.fps = fpsStepper.value; + bigLetter.defaultAdvance = tape.defaultAdvance = advanceStepper.value; + bigLetter.lineGap = tape.lineGap = lineGapStepper.value; + + bigLetter.forceCase = tape.forceCase = caseDropdown.index; + bigLetter.colorMode = tape.colorMode = colorDropdown.index; + + // give the letter loading another chance + tape.animation.destroyAnimations(); + bigLetter.animation.destroyAnimations(); + tape.failedLetters.splice(1, tape.failedLetters.length - 1); + + setDefault(0, regTextBox.label.text); + setDefault(1, upperTextBox.label.text); + setDefault(2, lowerTextBox.label.text); + } + + function setDefault(index:Int, name:String) { + tape.loaded[index].splice(0, tape.loaded[index].length); + if (name.trim() == "") { + tape.defaults[index] = null; + return; + } + + if (tape.defaults[index] != null) { + tape.defaults[index].components[0].anim = name; + trace(tape.defaults[index].components[0].anim); + return; + } + + final newData:AlphabetLetterData = { + isDefault: true, + advance: 0.0, + advanceEmpty: true, + components: [{ + anim: name, + + x: 0.0, + y: 0.0, + scaleX: 1.0, + scaleY: 1.0, + + shouldRotate: false, + angle: 0.0, + cos: 1.0, + sin: 0.0, + + hasColorMode: false, + colorMode: 0 + }], + startIndex: 0 + }; + tape.defaults[index] = newData; + } +} \ No newline at end of file diff --git a/source/funkin/menus/ui/Alphabet.hx b/source/funkin/menus/ui/Alphabet.hx index e2fc2b65..9aa2ef0a 100644 --- a/source/funkin/menus/ui/Alphabet.hx +++ b/source/funkin/menus/ui/Alphabet.hx @@ -114,6 +114,7 @@ enum abstract AlphabetRenderMode(ByteUInt) from ByteUInt to ByteUInt { } @:allow(funkin.editors.alphabet.AlphabetEditor) +@:allow(funkin.editors.alphabet.AlphabetMainDataScreen) class Alphabet extends FlxSprite { public var effects:Array = []; var __renderData:AlphabetRenderData; @@ -128,7 +129,7 @@ class Alphabet extends FlxSprite { public var alignment:AlphabetAlignment = LEFT; public var forceCase:CaseMode = NONE; - var __laneWidths:Array = []; // TODO: add alignment. + var __laneWidths:Array = []; var __forceWidth:Float = 0.0; // TODO: implement functionality for this. var __queueResize:Bool = false; var __animTime:Float = 0.0; @@ -156,6 +157,7 @@ class Alphabet extends FlxSprite { v[2] = []; v; }; + var manualLetters:Array = []; var failedLetters:Array = []; var failedOutlines:Array = []; @@ -168,7 +170,8 @@ class Alphabet extends FlxSprite { 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 + if (font != "") + this.font = (font is String) ? font : ((font == true) ? "bold" : "normal"); // == true just in case this.__renderData = new AlphabetRenderData(this); } @@ -482,7 +485,7 @@ class Alphabet extends FlxSprite { var name = char + Std.string(index); var anim = (data.isDefault) ? - component.anim.replace("LETTER", char) : + component.anim.replace("LOWERLETTER", char.toLowerCase()).replace("UPPERLETTER", char.toUpperCase()).replace("LETTER", char) : component.anim; animation.addByPrefix(name, anim, fps); return animation.exists(name) ? animation.getByName(name) : null; @@ -494,7 +497,7 @@ class Alphabet extends FlxSprite { if (animation.exists(name)) return animation.getByName(name); var anim = (data.isDefault) ? - component.anim.replace("LETTER", char) : + component.anim.replace("LOWERLETTER", char.toLowerCase()).replace("UPPERLETTER", char.toUpperCase()).replace("LETTER", char) : component.anim; animation.addByPrefix(name, anim, fps); if (!animation.exists(name)) { @@ -634,6 +637,7 @@ class Alphabet extends FlxSprite { components: components, startIndex: startIndex }); + manualLetters.push(char); case "anim": if(!node.exists("char")) { Logs.error(" must have a char attribute", "Alphabet"); @@ -698,6 +702,7 @@ class Alphabet extends FlxSprite { components: components, startIndex: (node.get("hasOutline") == "true") ? 1 : 0 }); + manualLetters.push(char); 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"); @@ -756,8 +761,8 @@ class Alphabet extends FlxSprite { 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("forceCasing", ["none", "upper", "lower"][forceCase]); + xml.set("useColorOffsets", ["tint", "offsets", "none"][colorMode]); xml.set("antialiasing", antialiasing ? "true" : "false"); xml.attributeOrder = alphabetProperties; @@ -780,9 +785,37 @@ class Alphabet extends FlxSprite { return xml; } + public function copyData(from:Alphabet) { + if (from.font == this.font) return; + + __queueResize = true; + @:bypassAccessor @:bypassAccessor this.font = from.font; // double bypass in case haxe gets finicky + this.frames = from.frames; + this.antialiasing = from.antialiasing; + this.fps = from.fps; + this.defaultAdvance = from.defaultAdvance; + this.lineGap = from.lineGap; + this.forceCase = from.forceCase; + this.colorMode = from.colorMode; + this.letterData = from.letterData; + this.defaults = from.defaults; + this.loaded = from.loaded; + this.manualLetters = from.manualLetters; + this.failedLetters = from.failedLetters; + this.failedOutlines = from.failedOutlines; + } + override function destroy():Void { originOffset = FlxDestroyUtil.destroy(originOffset); - failedLetters = []; + __drawScale = FlxDestroyUtil.put(__drawScale); + __renderData = null; + __laneWidths = null; + effects = null; + letterData = null; + defaults = null; + manualLetters = null; + failedLetters = null; + failedOutlines = null; super.destroy(); }