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,7 +1,23 @@
|
||||
namespace Game {
|
||||
using Game.UI;
|
||||
|
||||
namespace Game {
|
||||
internal class Program {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
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