using System.Collections.Generic; using System.Linq; using System.Text; using RimWorld; using UnityEngine; using Verse; #pragma warning disable IDE1006 // Naming Styles namespace AIImages { /// /// Empty window that opens when clicking the pawn button /// [System.Diagnostics.CodeAnalysis.SuppressMessage( "Style", "IDE1006:Naming Styles", Justification = "RimWorld Window naming convention" )] [System.Diagnostics.CodeAnalysis.SuppressMessage( "Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "RimWorld Window naming convention" )] public class Window_AIImage : Window { private Pawn pawn; public Window_AIImage(Pawn pawn) { this.pawn = pawn; this.doCloseX = true; this.doCloseButton = true; this.forcePause = false; // Не ставим игру на паузу this.absorbInputAroundWindow = false; // Не блокируем клики вне окна this.draggable = true; // Делаем окно перемещаемым this.preventCameraMotion = false; // Не блокируем управление камерой } public override Vector2 InitialSize => new Vector2(700f, 600f); private Vector2 scrollPosition = Vector2.zero; /// /// Обновляет текущую пешку в окне /// public void UpdatePawn(Pawn newPawn) { this.pawn = newPawn; } /// /// Получить текущую пешку /// public Pawn CurrentPawn => pawn; /// /// Вызывается каждый кадр для обновления окна /// public override void WindowUpdate() { base.WindowUpdate(); // Проверяем, изменилась ли выбранная пешка Pawn selectedPawn = Find.Selector.SelectedPawns.FirstOrDefault(p => p.IsColonist && p.Spawned && p.Faction == Faction.OfPlayer ); // Если выбрана новая колонистская пешка, обновляем окно if (selectedPawn != null && selectedPawn != pawn) { pawn = selectedPawn; } } /// /// Получает описание внешности персонажа /// private string GetAppearanceDescription() { if (pawn?.story == null) return "Информация о внешности недоступна"; StringBuilder sb = new StringBuilder(); // Пол sb.AppendLine($"Пол: {pawn.gender.GetLabel()}"); // Возраст sb.AppendLine($"Возраст: {pawn.ageTracker.AgeBiologicalYears} лет"); // Тип тела if (pawn.story.bodyType != null) { sb.AppendLine($"Тип тела: {pawn.story.bodyType.defName}"); } // Цвет кожи if (pawn.story.SkinColor != null) { Color skinColor = pawn.story.SkinColor; sb.AppendLine( $"Цвет кожи: RGB({skinColor.r:F2}, {skinColor.g:F2}, {skinColor.b:F2})" ); } // Волосы if (pawn.story.hairDef != null) { sb.AppendLine($"Прическа: {pawn.story.hairDef.label}"); if (pawn.story.HairColor != null) { sb.AppendLine( $"Цвет волос: RGB({pawn.story.HairColor.r:F2}, {pawn.story.HairColor.g:F2}, {pawn.story.HairColor.b:F2})" ); } } // Черты характера if (pawn.story.traits?.allTraits != null && pawn.story.traits.allTraits.Any()) { sb.AppendLine("\nЧерты характера:"); foreach (var trait in pawn.story.traits.allTraits) { sb.AppendLine($" • {trait.LabelCap}"); } } return sb.ToString(); } /// /// Получает описание одежды персонажа /// private string GetApparelDescription() { if (pawn?.apparel == null) return "Информация об одежде недоступна"; StringBuilder sb = new StringBuilder(); List wornApparel = pawn.apparel.WornApparel; if (wornApparel == null || !wornApparel.Any()) { sb.AppendLine("Персонаж ничего не носит"); } else { sb.AppendLine($"Одежда ({wornApparel.Count} предметов):\n"); foreach (Apparel apparel in wornApparel) { FormatApparelItem(sb, apparel); } } return sb.ToString(); } /// /// Форматирует информацию об одном предмете одежды /// private void FormatApparelItem(StringBuilder sb, Apparel apparel) { sb.AppendLine($"• {apparel.LabelCap}"); if (apparel.TryGetQuality(out QualityCategory quality)) { sb.AppendLine($" Качество: {quality.GetLabel()}"); } if (apparel.Stuff != null) { sb.AppendLine($" Материал: {apparel.Stuff.LabelCap}"); } if (apparel.HitPoints < apparel.MaxHitPoints) { int percentage = (int)((float)apparel.HitPoints / apparel.MaxHitPoints * 100); sb.AppendLine( $" Прочность: {apparel.HitPoints}/{apparel.MaxHitPoints} ({percentage}%)" ); } if (apparel.DrawColor != Color.white) { sb.AppendLine( $" Цвет: RGB({apparel.DrawColor.r:F2}, {apparel.DrawColor.g:F2}, {apparel.DrawColor.b:F2})" ); } sb.AppendLine(); } public override void DoWindowContents(Rect inRect) { float curY = 0f; // Заголовок Text.Font = GameFont.Medium; Widgets.Label(new Rect(0f, curY, inRect.width, 40f), "AI Image Window"); curY += 45f; // Имя пешки Text.Font = GameFont.Small; Widgets.Label( new Rect(0f, curY, inRect.width, 30f), "Персонаж: " + pawn.NameShortColored.Resolve() ); curY += 40f; // Разделитель Widgets.DrawLineHorizontal(0f, curY, inRect.width); curY += 10f; // Область для прокрутки контента Rect scrollRect = new Rect(0f, curY, inRect.width, inRect.height - curY); Rect scrollViewRect = new Rect( 0f, 0f, scrollRect.width - 20f, CalculateContentHeight() ); Widgets.BeginScrollView(scrollRect, ref scrollPosition, scrollViewRect); float contentY = 0f; // Секция "Внешность" Text.Font = GameFont.Medium; Widgets.Label(new Rect(10f, contentY, scrollViewRect.width - 20f, 30f), "Внешность"); contentY += 35f; Text.Font = GameFont.Small; string appearanceText = GetAppearanceDescription(); float appearanceHeight = Text.CalcHeight(appearanceText, scrollViewRect.width - 30f); Widgets.Label( new Rect(20f, contentY, scrollViewRect.width - 30f, appearanceHeight), appearanceText ); contentY += appearanceHeight + 20f; // Разделитель Widgets.DrawLineHorizontal(10f, contentY, scrollViewRect.width - 20f); contentY += 15f; // Секция "Одежда" Text.Font = GameFont.Medium; Widgets.Label(new Rect(10f, contentY, scrollViewRect.width - 20f, 30f), "Одежда"); contentY += 35f; Text.Font = GameFont.Small; string apparelText = GetApparelDescription(); float apparelHeight = Text.CalcHeight(apparelText, scrollViewRect.width - 30f); Widgets.Label( new Rect(20f, contentY, scrollViewRect.width - 30f, apparelHeight), apparelText ); Widgets.EndScrollView(); } /// /// Вычисляет высоту всего контента для прокрутки /// private float CalculateContentHeight() { float height = 0f; // Заголовок "Внешность" height += 35f; // Текст внешности string appearanceText = GetAppearanceDescription(); height += Text.CalcHeight(appearanceText, 640f) + 20f; // Разделитель height += 15f; // Заголовок "Одежда" height += 35f; // Текст одежды string apparelText = GetApparelDescription(); height += Text.CalcHeight(apparelText, 640f) + 20f; return height; } } }