add support for translations; basic terminal functions and splash screen
- create I18n class for translations - create TextDisplay class to draw on console, and to initialize and reset console configuration - create stub for future command-line option handling
This commit is contained in:
parent
609de00254
commit
f2653e66d4
7
App/CommandLineOptions.cs
Normal file
7
App/CommandLineOptions.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Game {
|
||||||
|
public struct CommandLineOptions {
|
||||||
|
public static CommandLineOptions Parse(string[] args) {
|
||||||
|
return new CommandLineOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
App/I18n.cs
Normal file
46
App/I18n.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
namespace Game {
|
||||||
|
public class I18n {
|
||||||
|
public static readonly string LANGUAGE = "ru-RU";
|
||||||
|
|
||||||
|
static readonly Dictionary<string, string[]> LANGUAGES = new Dictionary<string, string[]>() {
|
||||||
|
{"en-US", new string[] {
|
||||||
|
"No answers added for question",
|
||||||
|
"Expected `=>`",
|
||||||
|
"Expected `True` or `False`",
|
||||||
|
"Expected `||` between answers",
|
||||||
|
"The output is not a terminal",
|
||||||
|
"Syntax error in the question database: {0} ({1}:{2}:{3})",
|
||||||
|
"Syntax error in the question database: {0} (line {1} column {2})",
|
||||||
|
"Cannot get console mode",
|
||||||
|
"Cannot set console mode",
|
||||||
|
}},
|
||||||
|
{"ru-RU", new string[] {
|
||||||
|
"Не указано ни одного варианта ответа на вопрос",
|
||||||
|
"Ожидалось `=>`",
|
||||||
|
"Ожидалось `True` или `False`",
|
||||||
|
"Ожидалось `||` между вариантами ответа",
|
||||||
|
"Поток вывода не идёт в терминал",
|
||||||
|
"Ошибка синтаксиса в банке вопросов: {0} ({1}:{2}:{3})",
|
||||||
|
"Ошибка синтаксиса в банке вопросов: {0} (строка {1} символ {2})",
|
||||||
|
"Не удалось узнать режим консоли",
|
||||||
|
"Не удалось установить режим консоли",
|
||||||
|
}}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string? GetMessage(Message id) {
|
||||||
|
return LANGUAGES[LANGUAGE][(int)id];
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Message {
|
||||||
|
ERROR_NO_ANSWER_OPTIONS = 0,
|
||||||
|
ERROR_EXPECTED_ARROW_TOKEN,
|
||||||
|
ERROR_EXPECTED_TRUE_OR_FALSE,
|
||||||
|
ERROR_EXPECTED_ANSWER_SEPARATOR,
|
||||||
|
OUTPUT_IS_NOT_TERMINAL,
|
||||||
|
SYNTAX_ERROR_WITH_FILENAME,
|
||||||
|
SYNTAX_ERROR,
|
||||||
|
ERROR_GETCONSOLEMODE,
|
||||||
|
ERROR_SETCONSOLEMODE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,22 @@
|
|||||||
namespace Game {
|
using Game.UI;
|
||||||
|
|
||||||
|
namespace Game {
|
||||||
internal class Program {
|
internal class Program {
|
||||||
static int Main(string[] args) {
|
static int Main(string[] args) {
|
||||||
|
CommandLineOptions opts = CommandLineOptions.Parse(args);
|
||||||
|
|
||||||
|
TextDisplay display = new TextDisplay();
|
||||||
|
if (!display.InitWindow()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
display.PrintSimpleWelcome();
|
||||||
|
Thread.Sleep(1200);
|
||||||
|
display.ShowSplash();
|
||||||
|
Thread.Sleep(2500);
|
||||||
|
|
||||||
|
|
||||||
|
display.ResetWindow();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
App/UI/Colors.cs
Normal file
4
App/UI/Colors.cs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
namespace App.UI {
|
||||||
|
internal class Colors {
|
||||||
|
}
|
||||||
|
}
|
172
App/UI/TextDisplay.cs
Normal file
172
App/UI/TextDisplay.cs
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user