using static Game.I18n; using System.Runtime.InteropServices; using System.Text; namespace Game.UI { public class TextDisplay { private const string RGB_FG_SEQ_START = "\e[38;2;"; private const string RGB_BG_SEQ_START = "\e[48;2;"; private const string ESC_SEQ_START = "\e["; private const string SETCURSOR_END = "H"; private const string GRAPHICS_SEQ_END = "m"; private const string HIDECURSOR_SEQ = "\e[?25l"; private const string SHOWCURSOR_SEQ = "\e[?25h"; private int currentFgColor = 0xffffff, currentBgColor = 0; private int cursorRow = 0, cursorCol = 0; private int windowWidth = 0, windowHeight = 0; public bool InitWindow() { if (Console.IsOutputRedirected) { Console.WriteLine(GetMessage(Message.OUTPUT_IS_NOT_TERMINAL)); return false; } Console.InputEncoding = Encoding.UTF8; Console.OutputEncoding = Encoding.UTF8; IntPtr consoleInHandle = Win32Imports.GetStdHandle(Win32Imports.STD_INPUT_HANDLE); IntPtr consoleOutHandle = Win32Imports.GetStdHandle(Win32Imports.STD_OUTPUT_HANDLE); uint inConsoleMode, outConsoleMode; if (!Win32Imports.GetConsoleMode(consoleInHandle, out inConsoleMode)) { Console.WriteLine(GetMessage(Message.ERROR_GETCONSOLEMODE)); return false; } if (!Win32Imports.GetConsoleMode(consoleOutHandle, out outConsoleMode)) { Console.WriteLine(GetMessage(Message.ERROR_GETCONSOLEMODE)); return false; } inConsoleMode |= Win32Imports.ENABLE_VIRTUAL_TERMINAL_INPUT; //inConsoleMode &= ~Win32Imports.ENABLE_LINE_INPUT; outConsoleMode |= Win32Imports.ENABLE_VIRTUAL_TERMINAL_PROCESSING; if (!Win32Imports.SetConsoleMode(consoleInHandle, inConsoleMode)) { Console.WriteLine(GetMessage(Message.ERROR_SETCONSOLEMODE)); return false; } if (!Win32Imports.SetConsoleMode(consoleOutHandle, outConsoleMode)) { Console.WriteLine(GetMessage(Message.ERROR_SETCONSOLEMODE)); return false; } SetColors(currentFgColor, currentBgColor); HideCursor(); SetCursor(cursorRow, cursorCol); windowWidth = Console.BufferWidth; windowHeight = Console.BufferHeight; return true; } public void ResetWindow() { SetColors(0xffffff, 0); SetCursor(1, 1); ShowCursor(); } public void PrintSimpleWelcome() { WriteColored("Welcome to КТО ХОЧЕТ СТАТЬ СЕКУНДОМЕРОМ", 0x5e61ed); } public void ShowSplash() { ClearWithColor(0x5e61ed); DrawTitle(windowHeight / 2 + 1 - 3, 0x5e61ed); DrawCopyright(0x5e61ed); } void DrawTitle(int y, int bgColor) { string text1 = "КТО ХОЧЕТ СТАТЬ"; string text2 = "СЕКУНДОМЕРОМ"; SetColors(0xffffff, 0); int text1Start = (windowWidth - text1.Length) / 2 + 1; SetCursor(y, text1Start - 6); for (int i = 0; i < 6; ++i) { WriteColored(" ", -1, ColorBlend(0, bgColor, (float)i / 6)); } WriteColored(text1, 0xffffff, 0); for (int i = 6; i >= 0; --i) { WriteColored(" ", -1, ColorBlend(0, bgColor, (float)i / 6)); } int text2Start = (windowWidth - text2.Length) / 2 + 1; SetCursor(y + 2, text2Start - 6); for (int i = 0; i < 6; ++i) { WriteColored(" ", -1, ColorBlend(0, bgColor, (float)i / 6)); } WriteColored(text2, 0xffffff, 0); for (int i = 6; i >= 0; --i) { WriteColored(" ", -1, ColorBlend(0, bgColor, (float)i / 6)); } } void DrawCopyright(int bgColor) { string bottomText = "(c) subvia, 2025"; SetCursor(windowHeight, (windowWidth - bottomText.Length) / 2 + 1); WriteColored(bottomText, 0x4d4dc0, bgColor); } public void WriteColored(string s, int color, int bgColor = -1) { if (color == -1) color = currentFgColor; (int r, int g, int b) = ((color >> 16) & 255, (color >> 8) & 255, color & 255); currentFgColor = color; if (bgColor == -1) { Console.Write(RGB_FG_SEQ_START + r + ';' + g + ';' + b + GRAPHICS_SEQ_END + s); } else { (int br, int bg, int bb) = ((bgColor >> 16) & 255, (bgColor >> 8) & 255, bgColor & 255); Console.Write(RGB_FG_SEQ_START + r + ';' + g + ';' + b + GRAPHICS_SEQ_END + RGB_BG_SEQ_START + br + ';' + bg + ';' + bb + GRAPHICS_SEQ_END + s); currentBgColor = bgColor; } } public void SetColors(int fg, int bg) { StringBuilder command = new StringBuilder(); if (fg != -1) { (int r, int g, int b) = ((fg >> 16) & 255, (fg >> 8) & 255, fg & 255); command.Append(RGB_FG_SEQ_START + r + ';' + g + ';' + b + GRAPHICS_SEQ_END); } if (bg != -1) { (int r, int g, int b) = ((bg >> 16) & 255, (bg >> 8) & 255, bg & 255); command.Append(RGB_BG_SEQ_START + r + ';' + g + ';' + b + GRAPHICS_SEQ_END); } Console.Write(command.ToString()); } public void ClearWithColor(int color) { (int r, int g, int b) = ((color >> 16) & 255, (color >> 8) & 255, color & 255); SetColors(-1, color); Console.Write("\e[2J"); } public void SetCursor(int row, int column) { Console.Write(ESC_SEQ_START + row + ';' + column + SETCURSOR_END); } public void HideCursor() { Console.Write(HIDECURSOR_SEQ); } public void ShowCursor() { Console.Write(SHOWCURSOR_SEQ); } public int ColorBlend(int fg, int bg, float alpha) { //return (int)(fg * alpha + bg * (1f - alpha)); return ((int)(((fg >> 16) & 255) * alpha + ((bg >> 16) & 255) * (1 - alpha)) << 16) + ((int)(((fg >> 8) & 255) * alpha + ((bg >> 8) & 255) * (1 - alpha)) << 8) + (int)((fg & 255) * alpha + (bg & 255) * (1 - alpha)); } } static class Win32Imports { public const int STD_INPUT_HANDLE = -10; public const int STD_OUTPUT_HANDLE = -11; public const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4; public const uint ENABLE_LINE_INPUT = 2; public const uint ENABLE_VIRTUAL_TERMINAL_INPUT = 512; [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr GetStdHandle(int stdHandle); [DllImport("kernel32.dll")] public static extern bool SetConsoleMode(IntPtr consoleHandle, uint mode); [DllImport("kernel32.dll")] public static extern bool GetConsoleMode(IntPtr consoleHandle, out uint mode); } }