Add log entry functionality to AIImages. Introduce event description handling in PawnAppearanceData and update prompt generation logic to include event context. Enhance Window_AIImage to display log entries and allow image generation from log events, improving user interaction and character representation.
All checks were successful
SonarQube Analysis / Build and analyze (push) Successful in 1m45s

This commit is contained in:
Leonid Pershin
2025-11-01 14:27:14 +03:00
parent 5c9887c669
commit 59abcb11b8
8 changed files with 468 additions and 9 deletions

View File

@@ -3,6 +3,7 @@ using System.Linq;
using System.Threading;
using AIImages.Helpers;
using AIImages.Models;
using AIImages.Patches;
using AIImages.Services;
using RimWorld;
using UnityEngine;
@@ -72,11 +73,13 @@ namespace AIImages
private Vector2 mainScrollPosition = Vector2.zero;
private Vector2 promptScrollPosition = Vector2.zero;
private Vector2 negativePromptScrollPosition = Vector2.zero;
private Vector2 logEntriesScrollPosition = Vector2.zero;
private float copiedMessageTime = 0f;
// Состояние сворачиваемых секций промптов
private bool showPositivePrompt = false;
private bool showNegativePrompt = false;
private bool showLogEntries = true; // Развёрнута по умолчанию
/// <summary>
/// Обновляет данные персонажа
@@ -190,6 +193,24 @@ namespace AIImages
/// </summary>
public Pawn CurrentPawn => pawn;
/// <summary>
/// Генерирует изображение для пешки с описанием события
/// </summary>
public void GenerateImageForEvent(string eventDescription)
{
if (pawn == null || isGenerating)
return;
// Устанавливаем описание события
if (appearanceData != null)
{
appearanceData.EventDescription = eventDescription;
}
// Запускаем генерацию
StartGeneration();
}
/// <summary>
/// Отладочный метод для проверки состояния всех пешек
/// </summary>
@@ -307,11 +328,24 @@ namespace AIImages
try
{
// Генерируем промпты
string positivePrompt = promptGeneratorService.GeneratePositivePrompt(
appearanceData,
generationSettings
);
// Генерируем промпты - если есть описание события, используем его
string positivePrompt;
if (!string.IsNullOrEmpty(appearanceData?.EventDescription))
{
positivePrompt = promptGeneratorService.GeneratePositivePromptWithEvent(
appearanceData,
generationSettings,
appearanceData.EventDescription
);
}
else
{
positivePrompt = promptGeneratorService.GeneratePositivePrompt(
appearanceData,
generationSettings
);
}
string negativePrompt = promptGeneratorService.GenerateNegativePrompt(
generationSettings
);
@@ -575,7 +609,8 @@ namespace AIImages
contentY = DrawTraits(parentRect, contentY, lineHeight);
contentY = DrawGenes(parentRect, contentY, lineHeight);
contentY = DrawHediffs(parentRect, contentY, lineHeight);
DrawApparel(parentRect, contentY, lineHeight);
contentY = DrawApparel(parentRect, contentY, lineHeight);
contentY = DrawLogEntries(parentRect, contentY, lineHeight);
}
/// <summary>
@@ -757,13 +792,13 @@ namespace AIImages
/// <summary>
/// Отрисовывает одежду персонажа
/// </summary>
private void DrawApparel(Rect parentRect, float startY, float lineHeight)
private float DrawApparel(Rect parentRect, float startY, float lineHeight)
{
float contentY = startY;
var apparel = pawn.apparel?.WornApparel;
if (apparel == null || !apparel.Any())
return;
return contentY;
contentY += 15f;
Text.Font = GameFont.Small;
@@ -785,6 +820,141 @@ namespace AIImages
);
contentY += apparelHeight;
}
return contentY;
}
/// <summary>
/// Отрисовывает список записей лога пешки
/// </summary>
private float DrawLogEntries(Rect parentRect, float startY, float lineHeight)
{
float contentY = startY;
// Получаем записи лога
var entries = PawnLogPatch.GetAllLogEntries(pawn).ToList();
int entriesCount = entries?.Count ?? 0;
// Отладочная информация (временно)
if (entriesCount == 0 && pawn != null)
{
// Пробуем проверить, есть ли вообще доступ к логам
try
{
var logsProp = HarmonyLib.AccessTools.Property(typeof(Pawn), "logs");
var hasLogs = logsProp != null;
// Выводим в консоль для отладки
DebugLogger.Log(
$"[AI Images] Log entries count: {entriesCount}, Has logs property: {hasLogs}, Pawn: {pawn?.Name}"
);
}
catch (System.Exception ex)
{
DebugLogger.Warning($"[AI Images] Debug error: {ex.Message}");
}
}
contentY += 15f;
// Заголовок секции - всегда показываем, даже если записей нет
Text.Font = GameFont.Small;
Rect headerRect = new Rect(
parentRect.x + 5f,
contentY,
parentRect.width - 10f,
lineHeight + 5f
);
// Кнопка сворачивания/разворачивания
string headerText = "AIImages.Log.Entries".Translate() + $" ({entriesCount})";
if (Widgets.ButtonText(headerRect, headerText))
{
showLogEntries = !showLogEntries;
}
contentY += lineHeight + 10f;
// Разделитель
Widgets.DrawLineHorizontal(parentRect.x, contentY, parentRect.width);
contentY += 10f;
// Список записей (если развёрнуто и есть записи)
if (showLogEntries && entries != null && entries.Any())
{
float entryWidth = parentRect.width - 25f;
float scrollViewHeight = Mathf.Min(entries.Count * 30f + 20f, 300f);
Rect scrollViewRect = new Rect(
parentRect.x + 5f,
contentY,
entryWidth + 15f,
scrollViewHeight
);
Rect viewRect = new Rect(0f, 0f, entryWidth, entries.Count * 30f);
Widgets.BeginScrollView(scrollViewRect, ref logEntriesScrollPosition, viewRect);
Text.Font = GameFont.Tiny;
float entryY = 0f;
const float entryHeight = 28f;
const float buttonSize = 20f;
foreach (var entry in entries.Take(50)) // Ограничиваем для производительности
{
string entryText = entry.ToGameStringFromPOV(pawn);
if (string.IsNullOrEmpty(entryText))
continue;
// Обрезаем длинный текст
string shortText =
entryText.Length > 60 ? entryText.Substring(0, 57) + "..." : entryText;
Rect entryRect = new Rect(
0f,
entryY,
entryWidth - buttonSize - 5f,
entryHeight
);
Rect buttonRect = new Rect(
entryWidth - buttonSize,
entryY + (entryHeight - buttonSize) / 2f,
buttonSize,
buttonSize
);
// Фон записи
Widgets.DrawBoxSolid(entryRect, new Color(0.15f, 0.15f, 0.15f, 0.3f));
// Текст записи
Widgets.Label(entryRect.ContractedBy(3f), shortText);
// Кнопка генерации изображения
Texture2D iconTexture = ContentFinder<Texture2D>.Get(
"UI/Commands/AIImage",
true
);
if (iconTexture != null)
{
if (Widgets.ButtonImage(buttonRect, iconTexture))
{
GenerateImageForEvent(entryText);
}
TooltipHandler.TipRegion(
buttonRect,
"AIImages.Log.GenerateImage".Translate()
);
}
entryY += entryHeight;
}
Widgets.EndScrollView();
contentY += scrollViewHeight + 10f;
}
return contentY;
}
private void DrawRightColumn(Rect rect)
@@ -1311,7 +1481,12 @@ namespace AIImages
galleryLabel += " " + "AIImages.Gallery.ImagesCount".Translate(imageCount);
}
if (Widgets.ButtonText(new Rect(galleryButtonX, curY, galleryButtonWidth, 35f), galleryLabel))
if (
Widgets.ButtonText(
new Rect(galleryButtonX, curY, galleryButtonWidth, 35f),
galleryLabel
)
)
{
var galleryWindow = new Window_AIGallery(pawn);
Find.WindowStack.Add(galleryWindow);