Добавьте файлы проекта.
This commit is contained in:
17
.editorconfig
Normal file
17
.editorconfig
Normal file
@@ -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
|
||||||
|
|
||||||
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
@@ -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
|
||||||
|
*~
|
||||||
|
|
||||||
29
AIImages.sln
Normal file
29
AIImages.sln
Normal file
@@ -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
|
||||||
33
About/About.xml
Normal file
33
About/About.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ModMetaData>
|
||||||
|
<name>AI Images</name>
|
||||||
|
<author>mrleo1nid</author>
|
||||||
|
<packageId>Mrleo1nid.aiimages</packageId>
|
||||||
|
<supportedVersions>
|
||||||
|
<li>1.6</li>
|
||||||
|
</supportedVersions>
|
||||||
|
<description>Adds a button to pawns that opens a custom window.</description>
|
||||||
|
<modDependencies>
|
||||||
|
<li>
|
||||||
|
<packageId>rim.job.world</packageId>
|
||||||
|
<displayName>RimJobWorld</displayName>
|
||||||
|
<downloadUrl>https://www.loverslab.com/topic/110270-mod-rimjobworld/</downloadUrl>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<packageId>brrainz.harmony</packageId>
|
||||||
|
<displayName>Harmony</displayName>
|
||||||
|
<steamWorkshopUrl>steam://url/CommunityFilePage/2009463077</steamWorkshopUrl>
|
||||||
|
<downloadUrl>https://github.com/pardeike/HarmonyRimWorld/releases/latest</downloadUrl>
|
||||||
|
</li>
|
||||||
|
</modDependencies>
|
||||||
|
<loadAfter>
|
||||||
|
<li>Ludeon.RimWorld</li>
|
||||||
|
<li>Ludeon.RimWorld.Royalty</li>
|
||||||
|
<li>Ludeon.RimWorld.Ideology</li>
|
||||||
|
<li>Ludeon.RimWorld.Biotech</li>
|
||||||
|
<li>Ludeon.RimWorld.Anomaly</li>
|
||||||
|
<li>UnlimitedHugs.HugsLib</li>
|
||||||
|
<li>brrainz.harmony</li>
|
||||||
|
<li>rim.job.world</li>
|
||||||
|
</loadAfter>
|
||||||
|
</ModMetaData>
|
||||||
1
About/Preview.png
Normal file
1
About/Preview.png
Normal file
@@ -0,0 +1 @@
|
|||||||
|
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==
|
||||||
170
DEBUGGING_QUICKSTART_RU.txt
Normal file
170
DEBUGGING_QUICKSTART_RU.txt
Normal file
@@ -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 и подключите отладчик! ║
|
||||||
|
╚══════════════════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
|
||||||
|
Удачной отладки! 🐛→✨
|
||||||
|
|
||||||
444
DEBUGGING_RU.md
Normal file
444
DEBUGGING_RU.md
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
# 🐛 Отладка мода RimWorld
|
||||||
|
|
||||||
|
## Способы отладки
|
||||||
|
|
||||||
|
### 1. 🔴 Attach to Process (Рекомендуется)
|
||||||
|
|
||||||
|
Это самый удобный способ для отладки модов RimWorld.
|
||||||
|
|
||||||
|
#### Visual Studio
|
||||||
|
|
||||||
|
1. **Измените .csproj для включения отладочных символов:**
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net472</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<OutputPath>..\..\Assemblies</OutputPath>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
</PropertyGroup>
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net472</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<OutputPath>..\..\Assemblies</OutputPath>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
|
||||||
|
<!-- Настройки отладки -->
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Release конфигурация (без отладочных символов) -->
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||||
|
<DebugType>None</DebugType>
|
||||||
|
<DebugSymbols>false</DebugSymbols>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.5.4104" />
|
||||||
|
<PackageReference Include="Lib.Harmony" Version="2.3.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Создайте .pdb файлы:
|
||||||
|
|
||||||
|
После сборки в Debug режиме в папке `Assemblies/` появится:
|
||||||
|
- `AIImages.dll` - ваш мод
|
||||||
|
- `AIImages.pdb` - отладочные символы ✨
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Практические примеры
|
||||||
|
|
||||||
|
### Пример 1: Отладка Harmony патча
|
||||||
|
|
||||||
|
**PawnGizmoPatch.cs:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
[HarmonyPostfix]
|
||||||
|
public static IEnumerable<Gizmo> Postfix(IEnumerable<Gizmo> __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<Texture2D>.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<Gizmo> Postfix(IEnumerable<Gizmo> __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<Texture2D>.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 для тестирования
|
||||||
|
|
||||||
|
**Удачной отладки! 🐛→✨**
|
||||||
|
|
||||||
6
LoadFolders.xml
Normal file
6
LoadFolders.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<loadFolders>
|
||||||
|
<v1.6>
|
||||||
|
<li>/</li>
|
||||||
|
</v1.6>
|
||||||
|
</loadFolders>
|
||||||
22
Source/AIImages/AIImages.csproj
Normal file
22
Source/AIImages/AIImages.csproj
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net472</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<OutputPath>..\..\Assemblies</OutputPath>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<!-- Debug configuration - with symbols for debugging -->
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
<!-- Release configuration - no debug symbols -->
|
||||||
|
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||||
|
<DebugType>None</DebugType>
|
||||||
|
<DebugSymbols>false</DebugSymbols>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.6.4566" />
|
||||||
|
<PackageReference Include="Lib.Harmony" Version="2.4.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
19
Source/AIImages/AIImagesMod.cs
Normal file
19
Source/AIImages/AIImagesMod.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace AIImages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Main mod class that initializes Harmony patches
|
||||||
|
/// </summary>
|
||||||
|
[StaticConstructorOnStartup]
|
||||||
|
public static class AIImagesMod
|
||||||
|
{
|
||||||
|
static AIImagesMod()
|
||||||
|
{
|
||||||
|
var harmony = new Harmony("Mrleo1nid.aiimages");
|
||||||
|
harmony.PatchAll();
|
||||||
|
Log.Message("[AI Images] Mod initialized successfully");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
Source/AIImages/PawnGizmoPatch.cs
Normal file
71
Source/AIImages/PawnGizmoPatch.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Harmony patch to add a gizmo (button) to all colonist pawns
|
||||||
|
/// </summary>
|
||||||
|
[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<Gizmo> Postfix(IEnumerable<Gizmo> __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<Texture2D>.Get("UI/Commands/AttackMelee", true),
|
||||||
|
action = delegate()
|
||||||
|
{
|
||||||
|
// Проверяем, открыто ли уже окно AI Image
|
||||||
|
Window_AIImage existingWindow = Find
|
||||||
|
.WindowStack.Windows.OfType<Window_AIImage>()
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (existingWindow != null)
|
||||||
|
{
|
||||||
|
// Если окно открыто, обновляем пешку
|
||||||
|
existingWindow.UpdatePawn(__instance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Если окна нет, создаём новое
|
||||||
|
Find.WindowStack.Add(new Window_AIImage(__instance));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
289
Source/AIImages/Window_AIImage.cs
Normal file
289
Source/AIImages/Window_AIImage.cs
Normal file
@@ -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
|
||||||
|
{
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user