/
Landilf
/
2413_Ryumin_CourseWork
Обзор
Документация
Войти
/
Landilf
/
2413_Ryumin_CourseWork
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
CourseWork/ConsoleUI/ConsoleRenderer.cs
324 строки
10 KB
Landilf
add project
27 янв 2026, 13:57
27 янв 2026, 13:57
753b3e7
Код
Авторство
О чём код?
using GameCore; using GameCore.Engine; using GameCore.Entities; using GameCore.Resources; namespace ConsoleUI; /// <summary> /// Рендерер игры в консоли. /// </summary> public class ConsoleRenderer { // ReSharper disable all InconsistentNaming #region Поля /// <summary> /// Ширина области просмотра. /// </summary> private const int VIEWPORT_WIDTH = GameConfig.VIEWPORT_WIDTH; /// <summary> /// Высота области просмотра. /// </summary> private const int VIEWPORT_HEIGHT = GameConfig.VIEWPORT_HEIGHT; /// <summary>Символ игрока.</summary> private const string CHAR_PLAYER = "[]"; /// <summary>Символ хвоста.</summary> private const string CHAR_TAIL = "::"; /// <summary>Пустой символ.</summary> private const string CHAR_EMPTY = " "; /// <summary>Символ бонуса скорости.</summary> private const string CHAR_BONUS_SPEED = ">>"; /// <summary>Символ бонуса неуязвимости.</summary> private const string CHAR_BONUS_INVULNERABILITY = "🛡"; #endregion #region Методы /// <summary> /// Инициализирует параметры консоли. /// </summary> public void Init() { try { Console.CursorVisible = false; if (OperatingSystem.IsWindows()) { if (Console.WindowWidth < 100) { Console.WindowWidth = 100; } if (Console.WindowHeight < 40) { Console.WindowHeight = 40; } if (Console.BufferHeight < 40) { Console.BufferHeight = 40; } } } catch { // Игнорируем ошибки настройки терминала, если они не поддерживаются окружением. } } /// <summary> /// Отрисовывает текущий кадр игры. /// </summary> /// <param name="parEngine">Игровой движок.</param> /// <param name="parHumanPlayer">Игрок-человек.</param> public void DrawGame(GameEngine? parEngine, Player parHumanPlayer) { var target = parHumanPlayer; if (!target.IsAlive) { var fallback = parEngine?.Players.FirstOrDefault(p => p is { IsBot: false, IsAlive: true }) ?? parEngine?.Players.FirstOrDefault(p => !p.IsBot); if (fallback != null) { target = fallback; } } var camX = Math.Clamp((int)Math.Round(target.X) - VIEWPORT_WIDTH / 2, -5, (parEngine?.Width - VIEWPORT_WIDTH + 5) ?? 0); var camY = Math.Clamp((int)Math.Round(target.Y) - VIEWPORT_HEIGHT / 2, -5, (parEngine?.Height - VIEWPORT_HEIGHT + 5) ?? 0); var winWidth = Console.WindowWidth; var winHeight = Console.WindowHeight; ConsoleColor currentFg = ConsoleColor.Gray; ConsoleColor currentBg = ConsoleColor.Black; Console.ForegroundColor = currentFg; Console.BackgroundColor = currentBg; var sb = new System.Text.StringBuilder(); var topPlayers = parEngine?.Players.OrderByDescending(x => x.PercentCaptured).Take(5).ToList(); for (int vy = 0; vy < VIEWPORT_HEIGHT; vy++) { if (vy >= winHeight) { break; } Console.SetCursorPosition(0, vy); int gy = camY + vy; for (int vx = 0; vx < VIEWPORT_WIDTH; vx++) { int gx = camX + vx; string text = CHAR_EMPTY; ConsoleColor fg = ConsoleColor.White; ConsoleColor bg = ConsoleColor.Black; if (gx >= 0 && gx < parEngine?.Width && gy >= 0 && gy < parEngine.Height) { var cell = parEngine.Map[gx, gy]; bg = ConsoleColor.Gray; if (gx == 0 || gx == parEngine.Width - 1 || gy == 0 || gy == parEngine.Height - 1) bg = ConsoleColor.DarkGray; if (cell.OwnerId != null) { var owner = parEngine.Players.FirstOrDefault(p => p.Id == cell.OwnerId); if (owner != null) { bg = GetDarkColor(owner.Color); } } if (cell.TailOwnerId != null) { var owner = parEngine.Players.FirstOrDefault(p => p.Id == cell.TailOwnerId); if (owner != null) { bg = GetColor(owner.Color); text = CHAR_TAIL; fg = ConsoleColor.White; } } var bonus = parEngine.Bonuses.FirstOrDefault(b => b.Position.X == gx && b.Position.Y == gy); if (bonus != null) { fg = bonus.Type == BonusType.Speed ? ConsoleColor.Yellow : ConsoleColor.White; text = bonus.Type == BonusType.Speed ? CHAR_BONUS_SPEED : CHAR_BONUS_INVULNERABILITY; } var pHere = parEngine.Players.FirstOrDefault(p => p.IsAlive && (int)Math.Round(p.X) == gx && (int)Math.Round(p.Y) == gy); if (pHere != null) { bg = GetColor(pHere.Color); fg = ConsoleColor.White; text = CHAR_PLAYER; if (pHere.InvulnerabilityTicks > 0) { fg = ConsoleColor.Cyan; } } } if (fg != currentFg || bg != currentBg) { if (sb.Length > 0) { Console.Write(sb.ToString()); sb.Clear(); } if (fg != currentFg) { Console.ForegroundColor = fg; currentFg = fg; } if (bg != currentBg) { Console.BackgroundColor = bg; currentBg = bg; } } sb.Append(text); } if (sb.Length > 0) { Console.Write(sb.ToString()); sb.Clear(); } // Сброс цвета для отступа if (currentBg != ConsoleColor.Black) { Console.BackgroundColor = ConsoleColor.Black; currentBg = ConsoleColor.Black; } // Рисуем HUD справа if (currentFg != ConsoleColor.White) { Console.ForegroundColor = ConsoleColor.White; currentFg = ConsoleColor.White; } Console.Write(@" "); // Отступ от поля if (vy == 1) // Статус игрока { Console.Write($@"{Strings.HudPlayer} "); var pColor = GetColor(parHumanPlayer.Color); Console.ForegroundColor = pColor; currentFg = pColor; Console.Write(parHumanPlayer.Name); Console.ForegroundColor = ConsoleColor.White; currentFg = ConsoleColor.White; Console.Write($@" | {parHumanPlayer.PercentCaptured:F1}%"); } else if (vy == 3) // Заголовок таблицы { Console.Write(Strings.HudScores); } else if (vy >= 4 && vy < 4 + 5) // Игроки в таблице { int index = vy - 4; if (topPlayers != null && index < topPlayers.Count) { var p = topPlayers[index]; var pColor = GetColor(p.Color); Console.ForegroundColor = pColor; currentFg = pColor; string pNameDisplay = p.Name.Length > 12 ? p.Name.Substring(0, 12) : p.Name.PadRight(12); string scoreDisplay = p.IsAlive ? $"{p.PercentCaptured,5:F1}%" : " DEAD"; Console.Write($@"{pNameDisplay} {scoreDisplay}"); } } // Очистка остатка строки int currentX = Console.CursorLeft; if (winWidth - currentX > 0) { int remaining = winWidth - currentX; if (vy == winHeight - 1) { remaining--; // Безопасность последней строки } if (remaining > 0) { Console.Write(new string(' ', remaining)); } } } // Очистка старых строк HUD под полем (если были) for (int r = VIEWPORT_HEIGHT; r < winHeight; r++) { Console.SetCursorPosition(0, r); Console.Write(new string(' ', winWidth - (r == winHeight - 1 ? 1 : 0))); } } /// <summary> /// Получает цвет консоли из hex-кода. /// </summary> /// <param name="parHexCode">Hex-код цвета.</param> private ConsoleColor GetColor(string parHexCode) { if (parHexCode.Equals(GameConfig.COLOR_PLAYER, StringComparison.OrdinalIgnoreCase)) { return ConsoleColor.Red; } if (parHexCode.Equals(GameConfig.COLOR_BOTS[0], StringComparison.OrdinalIgnoreCase)) { return ConsoleColor.Blue; } if (parHexCode.Equals(GameConfig.COLOR_BOTS[1], StringComparison.OrdinalIgnoreCase)) { return ConsoleColor.Green; } if (parHexCode.Equals(GameConfig.COLOR_BOTS[2], StringComparison.OrdinalIgnoreCase)) { return ConsoleColor.Yellow; } return ConsoleColor.White; } /// <summary> /// Получает темный цвет консоли из hex-кода (для фона). /// </summary> /// <param name="parHexCode">Hex-код цвета.</param> private ConsoleColor GetDarkColor(string parHexCode) { if (parHexCode.Equals(GameConfig.COLOR_PLAYER, StringComparison.OrdinalIgnoreCase)) { return ConsoleColor.DarkRed; } if (parHexCode.Equals(GameConfig.COLOR_BOTS[0], StringComparison.OrdinalIgnoreCase)) { return ConsoleColor.DarkBlue; } if (parHexCode.Equals(GameConfig.COLOR_BOTS[1], StringComparison.OrdinalIgnoreCase)) { return ConsoleColor.DarkGreen; } if (parHexCode.Equals(GameConfig.COLOR_BOTS[2], StringComparison.OrdinalIgnoreCase)) { return ConsoleColor.DarkYellow; } return ConsoleColor.DarkGray; } #endregion }