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
All checks were successful
SonarQube Analysis / Build and analyze (push) Successful in 1m45s
This commit is contained in:
Binary file not shown.
@@ -118,4 +118,7 @@
|
|||||||
<AIImages.Gallery.AllDeleted>Successfully deleted {0} images</AIImages.Gallery.AllDeleted>
|
<AIImages.Gallery.AllDeleted>Successfully deleted {0} images</AIImages.Gallery.AllDeleted>
|
||||||
<AIImages.Gallery.OpenGallery>Open gallery</AIImages.Gallery.OpenGallery>
|
<AIImages.Gallery.OpenGallery>Open gallery</AIImages.Gallery.OpenGallery>
|
||||||
<AIImages.Gallery.ImagesCount>({0})</AIImages.Gallery.ImagesCount>
|
<AIImages.Gallery.ImagesCount>({0})</AIImages.Gallery.ImagesCount>
|
||||||
|
<!-- Log -->
|
||||||
|
<AIImages.Log.Entries>Log Events</AIImages.Log.Entries>
|
||||||
|
<AIImages.Log.GenerateImage>Generate AI Image</AIImages.Log.GenerateImage>
|
||||||
</LanguageData>
|
</LanguageData>
|
||||||
|
|||||||
@@ -118,4 +118,7 @@
|
|||||||
<AIImages.Gallery.AllDeleted>Успешно удалено {0} изображений</AIImages.Gallery.AllDeleted>
|
<AIImages.Gallery.AllDeleted>Успешно удалено {0} изображений</AIImages.Gallery.AllDeleted>
|
||||||
<AIImages.Gallery.OpenGallery>Открыть галерею</AIImages.Gallery.OpenGallery>
|
<AIImages.Gallery.OpenGallery>Открыть галерею</AIImages.Gallery.OpenGallery>
|
||||||
<AIImages.Gallery.ImagesCount>({0})</AIImages.Gallery.ImagesCount>
|
<AIImages.Gallery.ImagesCount>({0})</AIImages.Gallery.ImagesCount>
|
||||||
|
<!-- Log -->
|
||||||
|
<AIImages.Log.Entries>События из журнала</AIImages.Log.Entries>
|
||||||
|
<AIImages.Log.GenerateImage>Сгенерировать AI изображение</AIImages.Log.GenerateImage>
|
||||||
</LanguageData>
|
</LanguageData>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace AIImages.Models
|
|||||||
public Color HairColor { get; set; }
|
public Color HairColor { get; set; }
|
||||||
public List<Trait> Traits { get; set; }
|
public List<Trait> Traits { get; set; }
|
||||||
public List<ApparelData> Apparel { get; set; }
|
public List<ApparelData> Apparel { get; set; }
|
||||||
|
public string EventDescription { get; set; }
|
||||||
|
|
||||||
public PawnAppearanceData()
|
public PawnAppearanceData()
|
||||||
{
|
{
|
||||||
|
|||||||
241
Source/AIImages/Patches/PawnLogPatch.cs
Normal file
241
Source/AIImages/Patches/PawnLogPatch.cs
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
using RimWorld;
|
||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace AIImages.Patches
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Патч для получения записей лога пешки через ITab_Pawn_Log
|
||||||
|
/// </summary>
|
||||||
|
[HarmonyPatch(typeof(ITab_Pawn_Log), "FillTab")]
|
||||||
|
public static class ITab_Pawn_Log_Patch
|
||||||
|
{
|
||||||
|
private static Pawn lastPawn = null;
|
||||||
|
private static List<LogEntry> cachedEntries = new List<LogEntry>();
|
||||||
|
|
||||||
|
[HarmonyPrefix]
|
||||||
|
public static void Prefix(ITab_Pawn_Log __instance)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Получаем пешку через рефлексию
|
||||||
|
var selPawnProp = AccessTools.Property(typeof(ITab), "SelPawn");
|
||||||
|
if (selPawnProp != null)
|
||||||
|
{
|
||||||
|
var pawn = selPawnProp.GetValue(__instance) as Pawn;
|
||||||
|
if (pawn != null)
|
||||||
|
{
|
||||||
|
// Пробуем получить записи напрямую через Pawn.logs
|
||||||
|
var logsProperty = AccessTools.Property(typeof(Pawn), "logs");
|
||||||
|
if (logsProperty != null)
|
||||||
|
{
|
||||||
|
var logs = logsProperty.GetValue(pawn);
|
||||||
|
if (logs != null)
|
||||||
|
{
|
||||||
|
// Пробуем AllEntries
|
||||||
|
var allEntriesProperty = AccessTools.Property(
|
||||||
|
logs.GetType(),
|
||||||
|
"AllEntries"
|
||||||
|
);
|
||||||
|
if (allEntriesProperty != null)
|
||||||
|
{
|
||||||
|
var entries = allEntriesProperty.GetValue(logs);
|
||||||
|
if (entries is IEnumerable<LogEntry> entriesList)
|
||||||
|
{
|
||||||
|
cachedEntries = entriesList.ToList();
|
||||||
|
lastPawn = pawn;
|
||||||
|
UnityEngine.Debug.Log(
|
||||||
|
$"[AI Images] Cached {cachedEntries.Count} log entries for {pawn.Name}"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogWarning(
|
||||||
|
$"[AI Images] ITab_Pawn_Log patch error: {ex.Message}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<LogEntry> GetCachedEntries(Pawn pawn)
|
||||||
|
{
|
||||||
|
if (pawn == lastPawn && cachedEntries != null && cachedEntries.Any())
|
||||||
|
{
|
||||||
|
return cachedEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если кэш не подходит, пытаемся получить напрямую
|
||||||
|
if (pawn != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var logsProperty = AccessTools.Property(typeof(Pawn), "logs");
|
||||||
|
if (logsProperty != null)
|
||||||
|
{
|
||||||
|
var logs = logsProperty.GetValue(pawn);
|
||||||
|
if (logs != null)
|
||||||
|
{
|
||||||
|
var allEntriesProperty = AccessTools.Property(
|
||||||
|
logs.GetType(),
|
||||||
|
"AllEntries"
|
||||||
|
);
|
||||||
|
if (allEntriesProperty != null)
|
||||||
|
{
|
||||||
|
var entries = allEntriesProperty.GetValue(logs);
|
||||||
|
if (entries is IEnumerable<LogEntry> entriesList)
|
||||||
|
{
|
||||||
|
var list = entriesList.ToList();
|
||||||
|
if (list.Any())
|
||||||
|
{
|
||||||
|
cachedEntries = list;
|
||||||
|
lastPawn = pawn;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogWarning(
|
||||||
|
$"[AI Images] Error getting entries directly: {ex.Message}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new List<LogEntry>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Патч для получения записей лога пешки
|
||||||
|
/// Упрощённая версия - только для получения данных
|
||||||
|
/// </summary>
|
||||||
|
public static class PawnLogPatch
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Получает все записи лога пешки
|
||||||
|
/// </summary>
|
||||||
|
public static IEnumerable<LogEntry> GetAllLogEntries(Pawn pawn)
|
||||||
|
{
|
||||||
|
// Сначала пробуем получить из кэша ITab_Pawn_Log
|
||||||
|
var cachedEntries = ITab_Pawn_Log_Patch.GetCachedEntries(pawn);
|
||||||
|
if (cachedEntries != null && cachedEntries.Any())
|
||||||
|
{
|
||||||
|
return cachedEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Затем пробуем другие способы
|
||||||
|
return GetAllLogEntriesInternal(pawn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Внутренний метод получения записей лога
|
||||||
|
/// </summary>
|
||||||
|
private static IEnumerable<LogEntry> GetAllLogEntriesInternal(Pawn pawn)
|
||||||
|
{
|
||||||
|
if (pawn == null)
|
||||||
|
return Enumerable.Empty<LogEntry>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Способ 1: Через property через reflection
|
||||||
|
var logsProperty = AccessTools.Property(typeof(Pawn), "logs");
|
||||||
|
if (logsProperty != null)
|
||||||
|
{
|
||||||
|
var logs = logsProperty.GetValue(pawn);
|
||||||
|
if (logs != null)
|
||||||
|
{
|
||||||
|
var allEntriesProperty = AccessTools.Property(logs.GetType(), "AllEntries");
|
||||||
|
if (allEntriesProperty != null)
|
||||||
|
{
|
||||||
|
var entries = allEntriesProperty.GetValue(logs);
|
||||||
|
if (entries is IEnumerable<LogEntry> entriesList)
|
||||||
|
{
|
||||||
|
return entriesList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пробуем метод GetEntries
|
||||||
|
var getEntriesMethod = AccessTools.Method(logs.GetType(), "GetEntries");
|
||||||
|
if (getEntriesMethod != null)
|
||||||
|
{
|
||||||
|
var entries = getEntriesMethod.Invoke(logs, null);
|
||||||
|
if (entries is IEnumerable<LogEntry> entriesList)
|
||||||
|
{
|
||||||
|
return entriesList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пробуем поле entries
|
||||||
|
var entriesField = AccessTools.Field(logs.GetType(), "entries");
|
||||||
|
if (entriesField != null)
|
||||||
|
{
|
||||||
|
var entries = entriesField.GetValue(logs);
|
||||||
|
if (entries is IEnumerable<LogEntry> entriesList)
|
||||||
|
{
|
||||||
|
return entriesList;
|
||||||
|
}
|
||||||
|
if (entries is System.Collections.IList entriesCollection)
|
||||||
|
{
|
||||||
|
return entriesCollection.Cast<LogEntry>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Способ 3: Через story.logs
|
||||||
|
if (pawn.story != null)
|
||||||
|
{
|
||||||
|
var logsField = AccessTools.Field(pawn.story.GetType(), "logs");
|
||||||
|
if (logsField != null)
|
||||||
|
{
|
||||||
|
var logs = logsField.GetValue(pawn.story);
|
||||||
|
if (logs != null)
|
||||||
|
{
|
||||||
|
var allEntriesProperty = AccessTools.Property(
|
||||||
|
logs.GetType(),
|
||||||
|
"AllEntries"
|
||||||
|
);
|
||||||
|
if (allEntriesProperty != null)
|
||||||
|
{
|
||||||
|
var entries = allEntriesProperty.GetValue(logs);
|
||||||
|
if (entries is IEnumerable<LogEntry> entriesList)
|
||||||
|
{
|
||||||
|
return entriesList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
// Логируем ошибку для отладки
|
||||||
|
UnityEngine.Debug.LogWarning(
|
||||||
|
$"[AI Images] Error getting log entries for {pawn?.Name}: {ex.Message}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если все способы не сработали, логируем информацию
|
||||||
|
UnityEngine.Debug.Log(
|
||||||
|
$"[AI Images] Could not get log entries for {pawn?.Name}. "
|
||||||
|
+ $"logs property exists: {AccessTools.Property(typeof(Pawn), "logs") != null}, "
|
||||||
|
+ $"story property exists: {AccessTools.Property(typeof(Pawn), "story") != null}"
|
||||||
|
);
|
||||||
|
|
||||||
|
return Enumerable.Empty<LogEntry>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -119,6 +119,33 @@ namespace AIImages.Services
|
|||||||
return prompt.ToString().Trim().TrimEnd(',');
|
return prompt.ToString().Trim().TrimEnd(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Генерирует позитивный промпт на основе данных о персонаже и события
|
||||||
|
/// </summary>
|
||||||
|
public string GeneratePositivePromptWithEvent(
|
||||||
|
PawnAppearanceData appearanceData,
|
||||||
|
StableDiffusionSettings settings,
|
||||||
|
string eventDescription
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (appearanceData == null)
|
||||||
|
return "portrait of a person";
|
||||||
|
|
||||||
|
// Генерируем базовый промпт
|
||||||
|
string basePrompt = GeneratePositivePrompt(appearanceData, settings);
|
||||||
|
|
||||||
|
// Добавляем описание события, если оно есть
|
||||||
|
if (!string.IsNullOrEmpty(eventDescription))
|
||||||
|
{
|
||||||
|
StringBuilder prompt = new StringBuilder(basePrompt);
|
||||||
|
prompt.Append(", ");
|
||||||
|
prompt.Append(eventDescription.ToLower());
|
||||||
|
return prompt.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return basePrompt;
|
||||||
|
}
|
||||||
|
|
||||||
public string GenerateNegativePrompt(StableDiffusionSettings settings)
|
public string GenerateNegativePrompt(StableDiffusionSettings settings)
|
||||||
{
|
{
|
||||||
StringBuilder negativePrompt = new StringBuilder();
|
StringBuilder negativePrompt = new StringBuilder();
|
||||||
|
|||||||
@@ -15,6 +15,15 @@ namespace AIImages.Services
|
|||||||
StableDiffusionSettings settings
|
StableDiffusionSettings settings
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Генерирует позитивный промпт на основе данных о персонаже и события
|
||||||
|
/// </summary>
|
||||||
|
string GeneratePositivePromptWithEvent(
|
||||||
|
PawnAppearanceData appearanceData,
|
||||||
|
StableDiffusionSettings settings,
|
||||||
|
string eventDescription
|
||||||
|
);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Генерирует негативный промпт на основе настроек
|
/// Генерирует негативный промпт на основе настроек
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using AIImages.Helpers;
|
using AIImages.Helpers;
|
||||||
using AIImages.Models;
|
using AIImages.Models;
|
||||||
|
using AIImages.Patches;
|
||||||
using AIImages.Services;
|
using AIImages.Services;
|
||||||
using RimWorld;
|
using RimWorld;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -72,11 +73,13 @@ namespace AIImages
|
|||||||
private Vector2 mainScrollPosition = Vector2.zero;
|
private Vector2 mainScrollPosition = Vector2.zero;
|
||||||
private Vector2 promptScrollPosition = Vector2.zero;
|
private Vector2 promptScrollPosition = Vector2.zero;
|
||||||
private Vector2 negativePromptScrollPosition = Vector2.zero;
|
private Vector2 negativePromptScrollPosition = Vector2.zero;
|
||||||
|
private Vector2 logEntriesScrollPosition = Vector2.zero;
|
||||||
private float copiedMessageTime = 0f;
|
private float copiedMessageTime = 0f;
|
||||||
|
|
||||||
// Состояние сворачиваемых секций промптов
|
// Состояние сворачиваемых секций промптов
|
||||||
private bool showPositivePrompt = false;
|
private bool showPositivePrompt = false;
|
||||||
private bool showNegativePrompt = false;
|
private bool showNegativePrompt = false;
|
||||||
|
private bool showLogEntries = true; // Развёрнута по умолчанию
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Обновляет данные персонажа
|
/// Обновляет данные персонажа
|
||||||
@@ -190,6 +193,24 @@ namespace AIImages
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Pawn CurrentPawn => pawn;
|
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>
|
||||||
/// Отладочный метод для проверки состояния всех пешек
|
/// Отладочный метод для проверки состояния всех пешек
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -307,11 +328,24 @@ namespace AIImages
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Генерируем промпты
|
// Генерируем промпты - если есть описание события, используем его
|
||||||
string positivePrompt = promptGeneratorService.GeneratePositivePrompt(
|
string positivePrompt;
|
||||||
appearanceData,
|
if (!string.IsNullOrEmpty(appearanceData?.EventDescription))
|
||||||
generationSettings
|
{
|
||||||
);
|
positivePrompt = promptGeneratorService.GeneratePositivePromptWithEvent(
|
||||||
|
appearanceData,
|
||||||
|
generationSettings,
|
||||||
|
appearanceData.EventDescription
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
positivePrompt = promptGeneratorService.GeneratePositivePrompt(
|
||||||
|
appearanceData,
|
||||||
|
generationSettings
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
string negativePrompt = promptGeneratorService.GenerateNegativePrompt(
|
string negativePrompt = promptGeneratorService.GenerateNegativePrompt(
|
||||||
generationSettings
|
generationSettings
|
||||||
);
|
);
|
||||||
@@ -575,7 +609,8 @@ namespace AIImages
|
|||||||
contentY = DrawTraits(parentRect, contentY, lineHeight);
|
contentY = DrawTraits(parentRect, contentY, lineHeight);
|
||||||
contentY = DrawGenes(parentRect, contentY, lineHeight);
|
contentY = DrawGenes(parentRect, contentY, lineHeight);
|
||||||
contentY = DrawHediffs(parentRect, contentY, lineHeight);
|
contentY = DrawHediffs(parentRect, contentY, lineHeight);
|
||||||
DrawApparel(parentRect, contentY, lineHeight);
|
contentY = DrawApparel(parentRect, contentY, lineHeight);
|
||||||
|
contentY = DrawLogEntries(parentRect, contentY, lineHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -757,13 +792,13 @@ namespace AIImages
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Отрисовывает одежду персонажа
|
/// Отрисовывает одежду персонажа
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void DrawApparel(Rect parentRect, float startY, float lineHeight)
|
private float DrawApparel(Rect parentRect, float startY, float lineHeight)
|
||||||
{
|
{
|
||||||
float contentY = startY;
|
float contentY = startY;
|
||||||
var apparel = pawn.apparel?.WornApparel;
|
var apparel = pawn.apparel?.WornApparel;
|
||||||
|
|
||||||
if (apparel == null || !apparel.Any())
|
if (apparel == null || !apparel.Any())
|
||||||
return;
|
return contentY;
|
||||||
|
|
||||||
contentY += 15f;
|
contentY += 15f;
|
||||||
Text.Font = GameFont.Small;
|
Text.Font = GameFont.Small;
|
||||||
@@ -785,6 +820,141 @@ namespace AIImages
|
|||||||
);
|
);
|
||||||
contentY += apparelHeight;
|
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)
|
private void DrawRightColumn(Rect rect)
|
||||||
@@ -1311,7 +1481,12 @@ namespace AIImages
|
|||||||
galleryLabel += " " + "AIImages.Gallery.ImagesCount".Translate(imageCount);
|
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);
|
var galleryWindow = new Window_AIGallery(pawn);
|
||||||
Find.WindowStack.Add(galleryWindow);
|
Find.WindowStack.Add(galleryWindow);
|
||||||
|
|||||||
Reference in New Issue
Block a user