diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..f815e81
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,17 @@
+# EditorConfig for RimWorld Mod
+
+root = true
+
+[*]
+charset = utf-8
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.cs]
+# Allow underscores in type names (common in RimWorld modding for Harmony patches)
+dotnet_diagnostic.IDE1006.severity = none
+
+# C# formatting
+indent_style = space
+indent_size = 4
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..56326f7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+# Build results
+Assemblies/*.dll
+Assemblies/*.pdb
+
+# C# build artifacts
+Source/**/bin/
+Source/**/obj/
+Source/**/.vs/
+
+# Rider
+.idea/
+
+# Visual Studio
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+.vs/
+
+# ReSharper
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Backup files
+*.bak
+*~
+
diff --git a/AIImages.sln b/AIImages.sln
new file mode 100644
index 0000000..a283d0d
--- /dev/null
+++ b/AIImages.sln
@@ -0,0 +1,29 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{B8EFCA5F-814F-285C-A8CB-F00F14650265}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AIImages", "Source\AIImages\AIImages.csproj", "{4034C9F8-9E91-BB9A-74AF-03434F728EA7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4034C9F8-9E91-BB9A-74AF-03434F728EA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4034C9F8-9E91-BB9A-74AF-03434F728EA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4034C9F8-9E91-BB9A-74AF-03434F728EA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4034C9F8-9E91-BB9A-74AF-03434F728EA7}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {4034C9F8-9E91-BB9A-74AF-03434F728EA7} = {B8EFCA5F-814F-285C-A8CB-F00F14650265}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {EDE3FCE6-7C4C-44E5-B099-32381852D4AF}
+ EndGlobalSection
+EndGlobal
diff --git a/About/About.xml b/About/About.xml
new file mode 100644
index 0000000..8fdffcf
--- /dev/null
+++ b/About/About.xml
@@ -0,0 +1,33 @@
+
+
+ AI Images
+ mrleo1nid
+ Mrleo1nid.aiimages
+
+ 1.6
+
+ Adds a button to pawns that opens a custom window.
+
+
+ rim.job.world
+ RimJobWorld
+ https://www.loverslab.com/topic/110270-mod-rimjobworld/
+
+
+ brrainz.harmony
+ Harmony
+ steam://url/CommunityFilePage/2009463077
+ https://github.com/pardeike/HarmonyRimWorld/releases/latest
+
+
+
+ Ludeon.RimWorld
+ Ludeon.RimWorld.Royalty
+ Ludeon.RimWorld.Ideology
+ Ludeon.RimWorld.Biotech
+ Ludeon.RimWorld.Anomaly
+ UnlimitedHugs.HugsLib
+ brrainz.harmony
+ rim.job.world
+
+
diff --git a/About/Preview.png b/About/Preview.png
new file mode 100644
index 0000000..0f20323
--- /dev/null
+++ b/About/Preview.png
@@ -0,0 +1 @@
+iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==
diff --git a/DEBUGGING_QUICKSTART_RU.txt b/DEBUGGING_QUICKSTART_RU.txt
new file mode 100644
index 0000000..44b6e82
--- /dev/null
+++ b/DEBUGGING_QUICKSTART_RU.txt
@@ -0,0 +1,170 @@
+╔══════════════════════════════════════════════════════════════════════════╗
+║ 🐛 БЫСТРЫЙ СТАРТ - ОТЛАДКА МОДА ║
+╚══════════════════════════════════════════════════════════════════════════╝
+
+
+═══════════════════════════════════════════════════════════════════════════
+ СПОСОБ 1: ATTACH TO PROCESS (С BREAKPOINTS) ⭐
+═══════════════════════════════════════════════════════════════════════════
+
+ ✅ Проект уже настроен для отладки!
+ ✅ .pdb файл создаётся автоматически при сборке
+
+ 📋 ШАГИ:
+
+ 1️⃣ Откройте проект в Visual Studio или Rider:
+ Source/AIImages/AIImages.csproj
+
+ 2️⃣ Соберите в Debug режиме (уже собрано):
+ cd Source/AIImages
+ dotnet build -c Debug
+
+ 3️⃣ Запустите RimWorld (через Steam или напрямую)
+
+ 4️⃣ В Visual Studio:
+ • Debug → Attach to Process... (Ctrl+Alt+P)
+ • Найдите "RimWorldWin64.exe"
+ • Нажмите "Attach"
+
+ В Rider:
+ • Run → Attach to Process...
+ • Найдите "RimWorldWin64"
+ • Нажмите OK
+
+ 5️⃣ Откройте файл с кодом (например PawnGizmoPatch.cs)
+
+ 6️⃣ Поставьте breakpoint:
+ • Кликните слева от номера строки
+ • Появится красная точка 🔴
+
+ 7️⃣ В игре выберите пешку
+ → Код остановится на breakpoint!
+ → Можно смотреть значения переменных
+
+
+═══════════════════════════════════════════════════════════════════════════
+ СПОСОБ 2: ЛОГИРОВАНИЕ (ПРОСТОЙ) 📝
+═══════════════════════════════════════════════════════════════════════════
+
+ 1️⃣ Добавьте в код Log.Message():
+
+ Log.Message("[AI Images] Pawn selected: " + __instance.Name);
+
+ 2️⃣ Соберите проект:
+ dotnet build
+
+ 3️⃣ Запустите RimWorld
+
+ 4️⃣ В игре нажмите Ctrl+F12 → окно логов
+
+ 5️⃣ Выберите пешку → увидите сообщение в логе!
+
+
+═══════════════════════════════════════════════════════════════════════════
+ ГОТОВЫЕ ПРИМЕРЫ ДЛЯ ВСТАВКИ
+═══════════════════════════════════════════════════════════════════════════
+
+ 📍 В PawnGizmoPatch.cs (строка 16):
+
+ Log.Message($"[AI Images] GetGizmos for: {__instance.Name}");
+
+ 📍 В Window_AIImage.cs (в DoWindowContents, строка 25):
+
+ Log.Message($"[AI Images] Drawing window for: {pawn.Name}");
+
+ 📍 При нажатии кнопки (PawnGizmoPatch.cs, строка 34):
+
+ Log.Message("[AI Images] Button clicked!");
+
+
+═══════════════════════════════════════════════════════════════════════════
+ ГДЕ НАХОДЯТСЯ ЛОГИ
+═══════════════════════════════════════════════════════════════════════════
+
+ В игре:
+ Ctrl+F12 → окно с логами в реальном времени
+
+ Файл лога:
+ C:\Users\mrleo1nid\AppData\LocalLow\Ludeon Studios\
+ RimWorld by Ludeon Studios\Player.log
+
+
+═══════════════════════════════════════════════════════════════════════════
+ ПОЛЕЗНЫЕ КОМАНДЫ
+═══════════════════════════════════════════════════════════════════════════
+
+ Сборка в Debug (с .pdb):
+ cd Source/AIImages
+ dotnet build -c Debug
+
+ Сборка в Release (без .pdb, для публикации):
+ dotnet build -c Release
+
+ Очистка и пересборка:
+ dotnet clean
+ dotnet build -c Debug
+
+
+═══════════════════════════════════════════════════════════════════════════
+ СОВЕТЫ
+═══════════════════════════════════════════════════════════════════════════
+
+ ✅ Используйте префикс [AI Images] в логах - легче искать
+
+ ✅ Dev Mode в игре (Options → Settings → Development mode)
+ Даёт доступ к Debug Actions и Debug Inspector
+
+ ✅ Логируйте важные моменты:
+ • Когда метод вызывается
+ • Значения переменных
+ • Успех/неудачу операций
+
+ ✅ Try-Catch для отлова ошибок:
+
+ try {
+ // Ваш код
+ } catch (Exception ex) {
+ Log.Error($"[AI Images] Error: {ex}");
+ }
+
+
+═══════════════════════════════════════════════════════════════════════════
+ TROUBLESHOOTING
+═══════════════════════════════════════════════════════════════════════════
+
+ ❓ Breakpoint не срабатывает?
+ → Убедитесь, что собрано в Debug (не Release)
+ → Проверьте, что .pdb файл есть в Assemblies/
+ → Код действительно выполняется? (добавьте Log.Message)
+
+ ❓ Не видите свой мод в Process List?
+ → Убедитесь, что мод загружен в игре
+ → Попробуйте перезапустить игру
+
+ ❓ "Symbols not loaded"?
+ → Удалите все из Assemblies/
+ → Пересоберите: dotnet clean && dotnet build -c Debug
+
+
+═══════════════════════════════════════════════════════════════════════════
+ ДОПОЛНИТЕЛЬНАЯ ДОКУМЕНТАЦИЯ
+═══════════════════════════════════════════════════════════════════════════
+
+ 📖 Полная инструкция: DEBUGGING_RU.md
+ 📖 Примеры кода: DEV_CHEATSHEET.txt
+
+
+╔══════════════════════════════════════════════════════════════════════════╗
+║ ✅ ВСЁ ГОТОВО ДЛЯ ОТЛАДКИ! ║
+║ ║
+║ Файлы в Assemblies/: ║
+║ • AIImages.dll ✅ ║
+║ • AIImages.pdb ✅ (отладочные символы) ║
+║ • 0Harmony.dll ✅ ║
+║ ║
+║ Запустите RimWorld и подключите отладчик! ║
+╚══════════════════════════════════════════════════════════════════════════╝
+
+
+ Удачной отладки! 🐛→✨
+
diff --git a/DEBUGGING_RU.md b/DEBUGGING_RU.md
new file mode 100644
index 0000000..3d846c5
--- /dev/null
+++ b/DEBUGGING_RU.md
@@ -0,0 +1,444 @@
+# 🐛 Отладка мода RimWorld
+
+## Способы отладки
+
+### 1. 🔴 Attach to Process (Рекомендуется)
+
+Это самый удобный способ для отладки модов RimWorld.
+
+#### Visual Studio
+
+1. **Измените .csproj для включения отладочных символов:**
+
+```xml
+
+ net472
+ latest
+ ..\..\Assemblies
+ false
+ portable
+ true
+
+```
+
+2. **Соберите проект в Debug режиме:**
+```bash
+cd Source/AIImages
+dotnet build -c Debug
+```
+
+3. **Запустите RimWorld**
+
+4. **В Visual Studio:**
+ - Меню: `Debug` → `Attach to Process...` (или `Ctrl+Alt+P`)
+ - Найдите процесс `RimWorldWin64.exe`
+ - Нажмите `Attach`
+
+5. **Установите breakpoint:**
+ - Откройте файл с кодом (например, `PawnGizmoPatch.cs`)
+ - Кликните слева от номера строки (появится красная точка)
+
+6. **Триггерьте код в игре:**
+ - Выберите пешку → код остановится на breakpoint!
+
+#### JetBrains Rider
+
+1. **Настройте Run Configuration:**
+ - `Run` → `Edit Configurations...`
+ - `+` → `.NET Executable`
+ - Имя: `RimWorld Debug`
+ - Exe path: `D:\SteamLibrary\steamapps\common\RimWorld\RimWorldWin64.exe`
+ - Working directory: `D:\SteamLibrary\steamapps\common\RimWorld`
+
+2. **Соберите проект в Debug:**
+```bash
+dotnet build -c Debug
+```
+
+3. **Запустите через Rider:**
+ - `Run` → `Debug 'RimWorld Debug'` (или `Shift+F9`)
+ - Игра запустится с подключенным отладчиком
+
+4. **Breakpoints:**
+ - Работают так же, как в Visual Studio
+
+---
+
+### 2. 📝 Логирование (Простой способ)
+
+Если не нужна полная отладка, используйте логирование.
+
+#### Добавьте в код:
+
+```csharp
+using Verse;
+
+// Информационное сообщение
+Log.Message("[AI Images] Pawn selected: " + __instance.Name);
+
+// Предупреждение
+Log.Warning("[AI Images] Something suspicious");
+
+// Ошибка
+Log.Error("[AI Images] Something went wrong!");
+```
+
+#### Просмотр логов:
+
+**В игре:**
+- Нажмите `Ctrl+F12` → откроется окно логов
+- Ищите строки с `[AI Images]`
+
+**Файл лога:**
+- Windows: `C:\Users\YourName\AppData\LocalLow\Ludeon Studios\RimWorld by Ludeon Studios\Player.log`
+- Или в папке RimWorld: `RimWorld\LogOutput\`
+
+---
+
+### 3. 🎮 Dev Mode в игре
+
+Включите режим разработчика для дополнительных инструментов.
+
+#### Как включить:
+
+1. Запустите RimWorld
+2. `Options` → `Settings` → включите `Development mode`
+3. Перезапустите игру
+
+#### Что даёт Dev Mode:
+
+**Debug Actions Menu** (верхняя панель):
+- Можно создавать своих debug actions
+- Быстрое тестирование функций
+
+**Debug Inspector** (`Top bar` → `Tools` → `Debug inspector`):
+- Кликните на любой объект
+- Просмотр всех полей и свойств в реальном времени
+
+**Debug Log** (`Ctrl+F12`):
+- Расширенная информация
+- Трассировка стека
+
+#### Создание Debug Action:
+
+```csharp
+using Verse;
+using RimWorld;
+
+namespace AIImages
+{
+ public static class DebugActions
+ {
+ [DebugAction(
+ "AI Images",
+ "Test Window",
+ actionType = DebugActionType.ToolMapForPawns,
+ allowedGameStates = AllowedGameStates.PlayingOnMap
+ )]
+ public static void TestWindow(Pawn pawn)
+ {
+ Find.WindowStack.Add(new Window_AIImage(pawn));
+ Log.Message($"[AI Images] Opened window for {pawn.Name}");
+ }
+ }
+}
+```
+
+Теперь в игре:
+- Верхняя панель → `Debug Actions`
+- Категория `AI Images` → `Test Window`
+- Кликните на пешку → окно откроется
+
+---
+
+### 4. 🔍 Продвинутая отладка
+
+#### dnSpy - декомпилятор с отладчиком
+
+**Установка:**
+1. Скачайте dnSpy: https://github.com/dnSpy/dnSpy/releases
+2. Распакуйте и запустите `dnSpy.exe`
+
+**Использование:**
+1. `File` → `Open` → выберите `RimWorld\RimWorldWin64_Data\Managed\Assembly-CSharp.dll`
+2. Изучайте код RimWorld!
+3. Можно ставить breakpoints прямо в коде игры
+
+**Attach to Process:**
+1. Запустите RimWorld
+2. В dnSpy: `Debug` → `Attach to Process`
+3. Выберите `RimWorldWin64.exe`
+4. Ставьте breakpoints в коде игры или вашего мода
+
+---
+
+## ⚙️ Настройка проекта для отладки
+
+### Обновите AIImages.csproj:
+
+```xml
+
+
+ net472
+ latest
+ ..\..\Assemblies
+ false
+
+
+ portable
+ true
+ false
+
+
+
+
+ None
+ false
+ true
+
+
+
+
+
+
+
+```
+
+### Создайте .pdb файлы:
+
+После сборки в Debug режиме в папке `Assemblies/` появится:
+- `AIImages.dll` - ваш мод
+- `AIImages.pdb` - отладочные символы ✨
+
+---
+
+## 🎯 Практические примеры
+
+### Пример 1: Отладка Harmony патча
+
+**PawnGizmoPatch.cs:**
+
+```csharp
+[HarmonyPostfix]
+public static IEnumerable Postfix(IEnumerable __result, Pawn __instance)
+{
+ Log.Message($"[AI Images] GetGizmos called for: {__instance.Name}"); // 👈 Лог
+
+ foreach (Gizmo gizmo in __result)
+ {
+ yield return gizmo;
+ }
+
+ if (__instance.IsColonist && __instance.Spawned && __instance.Faction == Faction.OfPlayer)
+ {
+ Log.Message($"[AI Images] Adding button for: {__instance.Name}"); // 👈 Лог
+
+ yield return new Command_Action
+ {
+ defaultLabel = "AI Image",
+ defaultDesc = "Open AI Image window",
+ icon = ContentFinder.Get("UI/Commands/AttackMelee", true),
+ action = delegate()
+ {
+ Log.Message($"[AI Images] Button clicked!"); // 👈 Лог
+ Find.WindowStack.Add(new Window_AIImage(__instance));
+ }
+ };
+ }
+}
+```
+
+### Пример 2: Отладка окна
+
+**Window_AIImage.cs:**
+
+```csharp
+public override void DoWindowContents(Rect inRect)
+{
+ try
+ {
+ Log.Message($"[AI Images] Drawing window for: {pawn.Name}"); // 👈 Лог
+
+ Text.Font = GameFont.Medium;
+ Widgets.Label(new Rect(0f, 0f, inRect.width, 40f), "AI Image Window");
+
+ Text.Font = GameFont.Small;
+ Widgets.Label(new Rect(0f, 50f, inRect.width, 30f), "Pawn: " + pawn.NameShortColored.Resolve());
+
+ Widgets.DrawBoxSolid(
+ new Rect(10f, 100f, inRect.width - 20f, inRect.height - 150f),
+ new Color(0.2f, 0.2f, 0.2f, 0.5f)
+ );
+
+ Log.Message("[AI Images] Window drawn successfully"); // 👈 Лог
+ }
+ catch (System.Exception ex)
+ {
+ Log.Error($"[AI Images] Error drawing window: {ex}"); // 👈 Отлов ошибок
+ }
+}
+```
+
+### Пример 3: Try-Catch для отлова ошибок
+
+```csharp
+public static IEnumerable Postfix(IEnumerable __result, Pawn __instance)
+{
+ foreach (Gizmo gizmo in __result)
+ {
+ yield return gizmo;
+ }
+
+ if (__instance.IsColonist && __instance.Spawned)
+ {
+ Command_Action button = null;
+
+ try
+ {
+ button = new Command_Action
+ {
+ defaultLabel = "AI Image",
+ defaultDesc = "Open AI Image window",
+ icon = ContentFinder.Get("UI/Commands/AttackMelee", true),
+ action = delegate()
+ {
+ try
+ {
+ Find.WindowStack.Add(new Window_AIImage(__instance));
+ }
+ catch (System.Exception ex)
+ {
+ Log.Error($"[AI Images] Failed to open window: {ex}");
+ }
+ }
+ };
+ }
+ catch (System.Exception ex)
+ {
+ Log.Error($"[AI Images] Failed to create button: {ex}");
+ }
+
+ if (button != null)
+ {
+ yield return button;
+ }
+ }
+}
+```
+
+---
+
+## 🚀 Быстрая отладка - Workflow
+
+### Вариант 1: С Breakpoints (Visual Studio/Rider)
+
+```bash
+# 1. Сборка в Debug
+cd Source/AIImages
+dotnet build -c Debug
+
+# 2. Запустите RimWorld
+
+# 3. Attach отладчик (Ctrl+Alt+P в VS)
+
+# 4. Поставьте breakpoint в коде
+
+# 5. Триггерьте код в игре → останавливается на breakpoint
+```
+
+### Вариант 2: С логированием (быстрый)
+
+```bash
+# 1. Добавьте Log.Message() в код
+
+# 2. Соберите
+dotnet build
+
+# 3. Запустите RimWorld
+
+# 4. Ctrl+F12 → смотрите логи в реальном времени
+```
+
+---
+
+## 📊 Полезные советы
+
+### 1. Используйте префикс в логах
+
+```csharp
+Log.Message("[AI Images] Your message");
+```
+
+Так проще искать в логе: `Ctrl+F` → `[AI Images]`
+
+### 2. Логируйте важные переменные
+
+```csharp
+Log.Message($"[AI Images] Pawn: {pawn.Name}, Age: {pawn.ageTracker.AgeBiologicalYears}");
+```
+
+### 3. Conditional compilation
+
+```csharp
+#if DEBUG
+ Log.Message("[AI Images] Debug info");
+#endif
+```
+
+Эти строки будут работать только в Debug сборке!
+
+### 4. Проверяйте null
+
+```csharp
+if (pawn?.health?.hediffSet != null)
+{
+ // Безопасно использовать
+}
+```
+
+### 5. Используйте Debug Actions для тестирования
+
+Создавайте тестовые функции с `[DebugAction]` - это быстрее, чем играть вручную.
+
+---
+
+## ⚠️ Частые проблемы
+
+### Breakpoint не срабатывает
+
+✅ Убедитесь, что:
+- Проект собран в Debug (`dotnet build -c Debug`)
+- Файл `.pdb` существует рядом с `.dll`
+- Отладчик подключен к правильному процессу
+- Код действительно выполняется (добавьте Log.Message для проверки)
+
+### "Symbols not loaded"
+
+✅ Решение:
+- Удалите `Assemblies/*.dll` и `Assemblies/*.pdb`
+- Пересоберите: `dotnet clean && dotnet build -c Debug`
+
+### Игра тормозит с отладчиком
+
+✅ Это нормально. Отладчик замедляет выполнение.
+
+---
+
+## 🎓 Дополнительные материалы
+
+- **Visual Studio Debugging Guide**: https://docs.microsoft.com/en-us/visualstudio/debugger/
+- **Rider Debugging**: https://www.jetbrains.com/help/rider/Debugging_Code.html
+- **dnSpy**: https://github.com/dnSpy/dnSpy
+
+---
+
+## ✅ Готово!
+
+Теперь вы можете:
+- ✅ Подключать отладчик к RimWorld
+- ✅ Ставить breakpoints и смотреть переменные
+- ✅ Использовать логирование
+- ✅ Создавать Debug Actions для тестирования
+
+**Удачной отладки! 🐛→✨**
+
diff --git a/LoadFolders.xml b/LoadFolders.xml
new file mode 100644
index 0000000..6e7f5e8
--- /dev/null
+++ b/LoadFolders.xml
@@ -0,0 +1,6 @@
+
+
+
+ /
+
+
diff --git a/Source/AIImages/AIImages.csproj b/Source/AIImages/AIImages.csproj
new file mode 100644
index 0000000..1a9f816
--- /dev/null
+++ b/Source/AIImages/AIImages.csproj
@@ -0,0 +1,22 @@
+
+
+ net472
+ latest
+ ..\..\Assemblies
+ false
+
+ portable
+ true
+ false
+
+
+
+ None
+ false
+ true
+
+
+
+
+
+
diff --git a/Source/AIImages/AIImagesMod.cs b/Source/AIImages/AIImagesMod.cs
new file mode 100644
index 0000000..4aac809
--- /dev/null
+++ b/Source/AIImages/AIImagesMod.cs
@@ -0,0 +1,19 @@
+using HarmonyLib;
+using Verse;
+
+namespace AIImages
+{
+ ///
+ /// Main mod class that initializes Harmony patches
+ ///
+ [StaticConstructorOnStartup]
+ public static class AIImagesMod
+ {
+ static AIImagesMod()
+ {
+ var harmony = new Harmony("Mrleo1nid.aiimages");
+ harmony.PatchAll();
+ Log.Message("[AI Images] Mod initialized successfully");
+ }
+ }
+}
diff --git a/Source/AIImages/PawnGizmoPatch.cs b/Source/AIImages/PawnGizmoPatch.cs
new file mode 100644
index 0000000..f4e0850
--- /dev/null
+++ b/Source/AIImages/PawnGizmoPatch.cs
@@ -0,0 +1,71 @@
+using System.Collections.Generic;
+using System.Linq;
+using HarmonyLib;
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+#pragma warning disable IDE1006 // Naming Styles
+
+namespace AIImages
+{
+ ///
+ /// Harmony patch to add a gizmo (button) to all colonist pawns
+ ///
+ [HarmonyPatch(typeof(Pawn), "GetGizmos")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Style",
+ "IDE1006:Naming Styles",
+ Justification = "RimWorld Harmony patch naming convention"
+ )]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Minor Code Smell",
+ "S101:Types should be named in PascalCase",
+ Justification = "RimWorld Harmony patch naming convention"
+ )]
+ public static class Pawn_GetGizmos_Patch
+ {
+ [HarmonyPostfix]
+ public static IEnumerable Postfix(IEnumerable __result, Pawn __instance)
+ {
+ // First, return all original gizmos
+ foreach (Gizmo gizmo in __result)
+ {
+ yield return gizmo;
+ }
+
+ // Only add button to colonist pawns that are spawned
+ if (
+ __instance.IsColonist
+ && __instance.Spawned
+ && __instance.Faction == Faction.OfPlayer
+ )
+ {
+ yield return new Command_Action
+ {
+ defaultLabel = "AI Image",
+ defaultDesc = "Open AI Image window",
+ icon = ContentFinder.Get("UI/Commands/AttackMelee", true),
+ action = delegate()
+ {
+ // Проверяем, открыто ли уже окно AI Image
+ Window_AIImage existingWindow = Find
+ .WindowStack.Windows.OfType()
+ .FirstOrDefault();
+
+ if (existingWindow != null)
+ {
+ // Если окно открыто, обновляем пешку
+ existingWindow.UpdatePawn(__instance);
+ }
+ else
+ {
+ // Если окна нет, создаём новое
+ Find.WindowStack.Add(new Window_AIImage(__instance));
+ }
+ },
+ };
+ }
+ }
+ }
+}
diff --git a/Source/AIImages/Window_AIImage.cs b/Source/AIImages/Window_AIImage.cs
new file mode 100644
index 0000000..a309eaa
--- /dev/null
+++ b/Source/AIImages/Window_AIImage.cs
@@ -0,0 +1,289 @@
+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;
+ }
+ }
+}