package game.ui.console; import game.ui.text.TextFormats; import assets.Fonts; import openfl.Lib; import engine.ui.UIPane.PaneLayout; import openfl.events.Event; import openfl.text.TextFieldAutoSize; import openfl.text.TextField; import openfl.display.Sprite; import openfl.Assets; import openfl.text.TextFormat; import openfl.text.TextFieldType; import engine.ConVar; import engine.enums.console.CVarFlag; import engine.typedefs.console.CVar; import engine.typedefs.console.CCmd; import game.ui.console.elements.ConsoleInput; import engine.ui.UIPane; class Console extends Sprite { public var textFormat:TextFormat; public var cOut:TextField; public var cIn:TextField; public var cInput:ConsoleInput; public var cAutoComp:TextField; public static var consoleInstance:Console; @:convar({ name: "mat_consolebg", type: "CString", value: "0x222222", help: "console background color", callback: () -> { if (Console.consoleInstance != null){ Console.consoleInstance.cOut.backgroundColor = Std.parseInt(Console.consoleInstance.cvar_mat_consolebg.value); Console.consoleInstance.cIn.backgroundColor = Std.parseInt(Console.consoleInstance.cvar_mat_consolebg.value); } } }) public static var scvar_mat_consolebg:CVar; public var cvar_mat_consolebg:CVar; // public function cCmdToggleConsole(args:Array) { // toggle(); // } @:concmd("toggleconsole") public function ccmd_toggleconsole(cArgs:Array) { toggle(); }; public function new() { super(); cvar_mat_consolebg = scvar_mat_consolebg; consoleInstance = this; var consolePane = new UIPane("yeeto", {width: 800, height: 600, minWidth: 400, minHeight: 300, maxWidth: 1200, maxHeight: 800}); consolePane.layout = VERTICAL; consolePane.layout = VERTICAL; // graphics.beginFill(0x555555); // graphics.drawRect(0,0,800,600); cInput = new ConsoleInput(); cIn = cInput.tf; // cIn.setTextFormat(textFormat); cInput.y = 600 - cInput.height - 12; cInput.x = 12; cOut = new TextField(); cAutoComp = new TextField(); drawFields(cOut, cAutoComp); cOut.addEventListener(Event.CHANGE, onOutputTextChange); cIn.addEventListener(Event.CHANGE, onInputTextChange); this.addChild(cOut); this.addChild(cInput); this.addChild(cAutoComp); ConVar.registerCCmd("echo", (args:Array) -> { Console.devMsg(args.join(" ").split('"').join("")); }); ConVar.registerCCmd("quit", (args:Array) -> { Lib.application.window.close(); }); } public function drawFields(cOut:TextField, cAutoComp:TextField) { cOut.text = "hConsole Initialized\n"; cOut.defaultTextFormat = TextFormats.getFormats().cOutputFmt; cOut.wordWrap = true; // cOut.autoSize = TextFieldAutoSize.LEFT; cOut.multiline = true; cOut.background = true; trace(cvar_mat_consolebg); cOut.backgroundColor = Std.parseInt(cvar_mat_consolebg.value); cOut.width = 800 - 24; cOut.height = 600 - cIn.height - 38; cOut.y = 12; cOut.x = 12; cAutoComp.text = ""; cAutoComp.defaultTextFormat = TextFormats.getFormats().cInputFmt; cAutoComp.wordWrap = false; cAutoComp.multiline = true; cAutoComp.background = true; cAutoComp.backgroundColor = 0x11111100; cAutoComp.border = true; cAutoComp.borderColor = 0x55555500; cAutoComp.selectable = false; cAutoComp.width = 400; cAutoComp.height = 32 * 5; cAutoComp.visible = false; cAutoComp.x = 0; cAutoComp.y = 600; } public function parseCmd(cmd:String, bNoHist:Bool = false) { if (!bNoHist) history.push(cmd); cmd = cmd.split(";").join(" ; "); var subStrings = []; var startQuoteIndex:Int = cmd.indexOf('"'); var endQuoteIndex:Int; if (startQuoteIndex != -1) { while ((startQuoteIndex = cmd.indexOf('"')) > -1) { // push start of the cmd up until the quotes start subStrings.push(cmd.substring(0, startQuoteIndex)); // find next quote endQuoteIndex = cmd.indexOf('"', startQuoteIndex + 1) + 1; if (endQuoteIndex == 0) { cmd += '"'; endQuoteIndex = cmd.length; } // push quote content subStrings.push(cmd.substring(startQuoteIndex, endQuoteIndex)); cmd = cmd.substr(endQuoteIndex); } } subStrings.push(cmd); // Split args var newSubStrings = []; for (subString in subStrings) { if (subString.indexOf('"') == -1) { // split spaced args for (str in subString.split(" ")) { // we want to discard empty strings if (str != "") newSubStrings.push(str); } } else { newSubStrings.push(subString); } } var commands = []; // split off additional commands while (newSubStrings.length > 0) { for (i in 0...newSubStrings.length) { if (newSubStrings[i] == ";") { commands.push(newSubStrings.splice(0, i)); newSubStrings.shift(); break; } if (newSubStrings.length - 1 == i) { commands.push(newSubStrings); newSubStrings = []; } } } trace(commands); execCommands(commands); } public function execCommands(commands:Array>) { for (command in commands) { // Store command name, value and args var cName:String = command[0]; var cValue:String = command[1]; var cArgs:Array = command.slice(1); // Check if convar with that name actually exists if (ConVar.isCVar(cName)) { // Get reference to convar object var cv:CVar = ConVar.getCVar(command[0]); // Check number of params if (command.length == 1) { // No params after the cmd, print help string devMsg(cv.name + " - " + cv.helpString); devMsg(cv.name + " = " + cv.value); } else { // Check for convar type and set value accordingly switch (cv.type) { case CInt: ConVar.setCVar(cName, Std.parseInt(cValue)); trace(cValue); break; case CFloat: ConVar.setCVar(cName, Std.parseFloat(cValue)); break; case CBool: cValue = cValue.toLowerCase(); if (cValue == "1" || cValue == "true") { ConVar.setCVar(cName, true); } else if (cValue == "0" || cValue == "false") { ConVar.setCVar(cName, false); } break; case CString: ConVar.setCVar(cName, cValue); break; } } } // convar is actually a command, run it. else if (ConVar.isCmd(cName)) { ConVar.runCmd(cName, cArgs); } // convar doesn't exist else { devMsg("unkown command: " + command[0]); } } } public function submitInput() { cOut.appendText(">" + cInput.getText() + "\n"); parseCmd(cInput.getText()); cInput.setText(""); cAutoComp.visible = false; cOut.scrollV = cOut.maxScrollV; histSelect = -1; } public function onOutputTextChange(e:Event) { cOut.scrollV = cOut.maxScrollV; } public function onInputTextChange(e:Event) { if (cInput.getText() != "") { cAutoComp.text = ""; for (string in (autocompleteList = getCompList())) { cAutoComp.text += string + "\n"; trace(string); } cAutoComp.visible = true; } else { cAutoComp.visible = false; autocompleteList = history; } } public static function toggle():Void { consoleInstance.visible = !consoleInstance.visible; if (!consoleInstance.visible) { if (Lib.current.stage.focus == consoleInstance.cIn) { Lib.current.stage.focus = null; } } else { Lib.current.stage.focus = consoleInstance.cIn; } } public static function devMsg(msg:String):Void { consoleInstance.cOut.appendText(msg + "\n"); consoleInstance.cOut.scrollV = consoleInstance.cOut.maxScrollV; } public static var history:Array = []; public static var histSelect:Int = -1; public static var tmpInput:String = ""; public static var autocompleteList:Array = []; public static var compSelect:Int = -1; public static function getCompList():Array { // Split words var inp:Array = consoleInstance.cInput.getText().split(" "); var inpStripped:Array = [ for (word in inp) { if (word != "" && word != " ") { word; } } ]; // Stop if input is empty if (inp.length == 0) return []; var cVars:Array = ConVar.getCVarNames(); var cVarFiltered:Array = []; // Loop through convars for (cVar in cVars) { // if there's one word just check if the convar starts with the input string if (inp.length == 1) { // Check if the cvar starts with the input if (cVar.indexOf(inp[0]) == 0) { cVarFiltered.push(cVar); } } // User pressed space at least once but entered no other words. Check if first word occurs at any position else if (inpStripped.length == 1 && inp.length > 1) { if (cVar.indexOf(inp[0]) > -1) { cVarFiltered.push(cVar); } } // Multiple words, check for cvars that contain all of them else if (inpStripped.length > 1) { var bWordNotPresent:Bool = false; for (word in inpStripped) { if (cVar.indexOf(word) == -1) { bWordNotPresent = true; } } if (!bWordNotPresent) cVarFiltered.push(cVar); } } return cVarFiltered; } public static function histPrev():Void { // Only complete if input field is empty or scrolling through hist if (consoleInstance.cInput.getText() == "" || histSelect != -1) { // Store current input in tmpInput if (histSelect == -1) tmpInput = consoleInstance.cInput.getText(); // Only go through history if history is not empty if (history.length != 0) { // Check if currently selecting a history entry if (histSelect >= 0) { histSelect--; } // Not currently selecting a history entry else { // Put selector to the newest histSelect = history.length - 1; } if (histSelect != -1) { // Put correct history entry in the input field consoleInstance.cInput.setText(history[histSelect]); } else { // Restore tmp input consoleInstance.cInput.setText(tmpInput); } } // History is empty else { // Do nothing return; } } // Now we need to descend through the autocomplete list else { if (autocompleteList.length == 0) { return; } else { // Check if currently selecting an autocomplete entry if (compSelect > -1) {} // Not currently selecting an autocomplete entry else {} } } } public static function histNext():Void { // Only complete if input field is empty or scrolling through hist if (consoleInstance.cIn.text == "" || histSelect != -1) { // Store current input in tmpInput if (histSelect == -1) tmpInput = consoleInstance.cIn.text; // Only go through history if history is not empty if (history.length != 0) { // Check if currently selecting a history entry if (histSelect < history.length - 1) { histSelect++; } // Otherwise wrap around else { // Put selector to no selection histSelect = -1; } if (histSelect != -1) { // Put correct history entry in the input field consoleInstance.cIn.text = history[histSelect]; } else { // Restore tmp input consoleInstance.cIn.text = tmpInput; } } // History is empty else { // Do nothing return; } } } }