Files
ai-images/Source/AIImages/Window_AIImage.cs
2025-10-26 17:02:04 +03:00

290 lines
9.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RimWorld;
using UnityEngine;
using Verse;
#pragma warning disable IDE1006 // Naming Styles
namespace AIImages
{
/// <summary>
/// Empty window that opens when clicking the pawn button
/// </summary>
[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;
/// <summary>
/// Обновляет текущую пешку в окне
/// </summary>
public void UpdatePawn(Pawn newPawn)
{
this.pawn = newPawn;
}
/// <summary>
/// Получить текущую пешку
/// </summary>
public Pawn CurrentPawn => pawn;
/// <summary>
/// Вызывается каждый кадр для обновления окна
/// </summary>
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;
}
}
/// <summary>
/// Получает описание внешности персонажа
/// </summary>
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();
}
/// <summary>
/// Получает описание одежды персонажа
/// </summary>
private string GetApparelDescription()
{
if (pawn?.apparel == null)
return "Информация об одежде недоступна";
StringBuilder sb = new StringBuilder();
List<Apparel> 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();
}
/// <summary>
/// Форматирует информацию об одном предмете одежды
/// </summary>
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();
}
/// <summary>
/// Вычисляет высоту всего контента для прокрутки
/// </summary>
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;
}
}
}