diff --git a/Assemblies/AIImages.dll b/Assemblies/AIImages.dll index ee3bb26..3ae3f55 100644 Binary files a/Assemblies/AIImages.dll and b/Assemblies/AIImages.dll differ diff --git a/Assemblies/Microsoft.Bcl.AsyncInterfaces.dll b/Assemblies/Microsoft.Bcl.AsyncInterfaces.dll new file mode 100644 index 0000000..476f1b1 Binary files /dev/null and b/Assemblies/Microsoft.Bcl.AsyncInterfaces.dll differ diff --git a/Assemblies/Microsoft.Extensions.DependencyInjection.Abstractions.dll b/Assemblies/Microsoft.Extensions.DependencyInjection.Abstractions.dll new file mode 100644 index 0000000..edda9e2 Binary files /dev/null and b/Assemblies/Microsoft.Extensions.DependencyInjection.Abstractions.dll differ diff --git a/Assemblies/Microsoft.Extensions.DependencyInjection.dll b/Assemblies/Microsoft.Extensions.DependencyInjection.dll new file mode 100644 index 0000000..d749915 Binary files /dev/null and b/Assemblies/Microsoft.Extensions.DependencyInjection.dll differ diff --git a/Assemblies/Microsoft.Extensions.Http.Polly.dll b/Assemblies/Microsoft.Extensions.Http.Polly.dll new file mode 100644 index 0000000..99d38f3 Binary files /dev/null and b/Assemblies/Microsoft.Extensions.Http.Polly.dll differ diff --git a/Assemblies/Microsoft.Extensions.Http.dll b/Assemblies/Microsoft.Extensions.Http.dll new file mode 100644 index 0000000..c9bb6b1 Binary files /dev/null and b/Assemblies/Microsoft.Extensions.Http.dll differ diff --git a/Assemblies/Microsoft.Extensions.Logging.Abstractions.dll b/Assemblies/Microsoft.Extensions.Logging.Abstractions.dll new file mode 100644 index 0000000..5e6a10f Binary files /dev/null and b/Assemblies/Microsoft.Extensions.Logging.Abstractions.dll differ diff --git a/Assemblies/Microsoft.Extensions.Logging.dll b/Assemblies/Microsoft.Extensions.Logging.dll new file mode 100644 index 0000000..1af1104 Binary files /dev/null and b/Assemblies/Microsoft.Extensions.Logging.dll differ diff --git a/Assemblies/Microsoft.Extensions.Options.dll b/Assemblies/Microsoft.Extensions.Options.dll new file mode 100644 index 0000000..1310caa Binary files /dev/null and b/Assemblies/Microsoft.Extensions.Options.dll differ diff --git a/Assemblies/Microsoft.Extensions.Primitives.dll b/Assemblies/Microsoft.Extensions.Primitives.dll new file mode 100644 index 0000000..f6daaab Binary files /dev/null and b/Assemblies/Microsoft.Extensions.Primitives.dll differ diff --git a/Assemblies/Newtonsoft.Json.dll b/Assemblies/Newtonsoft.Json.dll new file mode 100644 index 0000000..3f6541a Binary files /dev/null and b/Assemblies/Newtonsoft.Json.dll differ diff --git a/Assemblies/Polly.Extensions.Http.dll b/Assemblies/Polly.Extensions.Http.dll new file mode 100644 index 0000000..ef47032 Binary files /dev/null and b/Assemblies/Polly.Extensions.Http.dll differ diff --git a/Assemblies/Polly.dll b/Assemblies/Polly.dll new file mode 100644 index 0000000..a252caf Binary files /dev/null and b/Assemblies/Polly.dll differ diff --git a/Assemblies/StableDiffusionNet.dll b/Assemblies/StableDiffusionNet.dll new file mode 100644 index 0000000..c3dc3e4 Binary files /dev/null and b/Assemblies/StableDiffusionNet.dll differ diff --git a/Assemblies/System.Buffers.dll b/Assemblies/System.Buffers.dll new file mode 100644 index 0000000..f2d83c5 Binary files /dev/null and b/Assemblies/System.Buffers.dll differ diff --git a/Assemblies/System.Diagnostics.DiagnosticSource.dll b/Assemblies/System.Diagnostics.DiagnosticSource.dll new file mode 100644 index 0000000..0774d46 Binary files /dev/null and b/Assemblies/System.Diagnostics.DiagnosticSource.dll differ diff --git a/Assemblies/System.Memory.dll b/Assemblies/System.Memory.dll new file mode 100644 index 0000000..5d19470 Binary files /dev/null and b/Assemblies/System.Memory.dll differ diff --git a/Assemblies/System.Numerics.Vectors.dll b/Assemblies/System.Numerics.Vectors.dll new file mode 100644 index 0000000..0865972 Binary files /dev/null and b/Assemblies/System.Numerics.Vectors.dll differ diff --git a/Assemblies/System.Runtime.CompilerServices.Unsafe.dll b/Assemblies/System.Runtime.CompilerServices.Unsafe.dll new file mode 100644 index 0000000..c5ba4e4 Binary files /dev/null and b/Assemblies/System.Runtime.CompilerServices.Unsafe.dll differ diff --git a/Assemblies/System.Threading.Tasks.Extensions.dll b/Assemblies/System.Threading.Tasks.Extensions.dll new file mode 100644 index 0000000..eeec928 Binary files /dev/null and b/Assemblies/System.Threading.Tasks.Extensions.dll differ diff --git a/Assemblies/System.ValueTuple.dll b/Assemblies/System.ValueTuple.dll new file mode 100644 index 0000000..4ce28fd Binary files /dev/null and b/Assemblies/System.ValueTuple.dll differ diff --git a/Languages/English/Keyed/AIImages.xml b/Languages/English/Keyed/AIImages.xml index 24da3ec..7091f44 100644 --- a/Languages/English/Keyed/AIImages.xml +++ b/Languages/English/Keyed/AIImages.xml @@ -25,4 +25,8 @@ Material: {0} Durability: {0}/{1} ({2}%) Color: RGB({0}, {1}, {2}) + + Stable Diffusion Prompt + Copy Prompt + Copied! diff --git a/Languages/Russian/Keyed/AIImages.xml b/Languages/Russian/Keyed/AIImages.xml index 3a260f5..dc4375d 100644 --- a/Languages/Russian/Keyed/AIImages.xml +++ b/Languages/Russian/Keyed/AIImages.xml @@ -25,4 +25,8 @@ Материал: {0} Прочность: {0}/{1} ({2}%) Цвет: RGB({0}, {1}, {2}) + + Промпт для Stable Diffusion + Копировать промпт + Скопировано! diff --git a/Source/AIImages/AIImages.csproj b/Source/AIImages/AIImages.csproj index 1a9f816..881a33e 100644 --- a/Source/AIImages/AIImages.csproj +++ b/Source/AIImages/AIImages.csproj @@ -18,5 +18,6 @@ + diff --git a/Source/AIImages/Window_AIImage.cs b/Source/AIImages/Window_AIImage.cs index 544fd82..20a9576 100644 --- a/Source/AIImages/Window_AIImage.cs +++ b/Source/AIImages/Window_AIImage.cs @@ -37,9 +37,10 @@ namespace AIImages this.preventCameraMotion = false; // Не блокируем управление камерой } - public override Vector2 InitialSize => new Vector2(700f, 600f); + public override Vector2 InitialSize => new Vector2(700f, 700f); private Vector2 scrollPosition = Vector2.zero; + private float copiedMessageTime = 0f; /// /// Обновляет текущую пешку в окне @@ -71,6 +72,12 @@ namespace AIImages { pawn = selectedPawn; } + + // Уменьшаем таймер сообщения о копировании + if (copiedMessageTime > 0f) + { + copiedMessageTime -= Time.deltaTime; + } } /// @@ -209,6 +216,142 @@ namespace AIImages sb.AppendLine(); } + /// + /// Генерирует промпт для Stable Diffusion на основе внешности персонажа + /// + private string GenerateStableDiffusionPrompt() + { + if (pawn?.story == null) + return "portrait of a person"; + + StringBuilder prompt = new StringBuilder("portrait of a "); + + prompt.Append(GetAgeAndGenderDescription()); + prompt.Append(GetBodyTypeDescription()); + prompt.Append(GetSkinToneDescription()); + prompt.Append(GetHairDescription()); + prompt.Append(GetApparelPromptDescription()); + prompt.Append( + "realistic, detailed, high quality, professional lighting, 8k, photorealistic" + ); + + return prompt.ToString(); + } + + private string GetAgeAndGenderDescription() + { + string ageGroup = pawn.ageTracker.AgeBiologicalYears switch + { + < 18 => "young", + < 30 => "young adult", + < 50 => "middle-aged", + _ => "mature", + }; + return $"{ageGroup} {pawn.gender.GetLabel()}, "; + } + + private string GetBodyTypeDescription() + { + if (pawn.story.bodyType == null) + return ""; + + string bodyDesc = pawn.story.bodyType.defName.ToLower() switch + { + "thin" => "slender build", + "hulk" => "muscular build", + "fat" => "heavyset build", + _ => "average build", + }; + return $"{bodyDesc}, "; + } + + private string GetSkinToneDescription() + { + if (pawn.story.SkinColor == null) + return ""; + + float brightness = + (pawn.story.SkinColor.r + pawn.story.SkinColor.g + pawn.story.SkinColor.b) / 3f; + string skinTone = brightness switch + { + >= 0.8f => "fair skin", + >= 0.6f => "light skin", + >= 0.4f => "olive skin", + >= 0.2f => "brown skin", + _ => "dark skin", + }; + return $"{skinTone}, "; + } + + private string GetHairDescription() + { + if (pawn.story.hairDef == null) + return ""; + + string result = $"{pawn.story.hairDef.label.ToLower()} hair"; + if (pawn.story.HairColor != null) + { + result += $", {GetColorDescription(pawn.story.HairColor)} hair color"; + } + return result + ", "; + } + + private string GetApparelPromptDescription() + { + if (pawn.apparel?.WornApparel == null || !pawn.apparel.WornApparel.Any()) + return ""; + + List items = pawn + .apparel.WornApparel.Take(3) + .Select(a => + a.Stuff != null + ? $"{a.Stuff.label.ToLower()} {a.def.label.ToLower()}" + : a.def.label.ToLower() + ) + .ToList(); + + return $"wearing {string.Join(", ", items)}, "; + } + + /// + /// Получает текстовое описание цвета + /// + private string GetColorDescription(Color color) + { + // Определяем доминирующий цвет + float max = Mathf.Max(color.r, color.g, color.b); + float min = Mathf.Min(color.r, color.g, color.b); + float diff = max - min; + + if (diff < 0.1f) + { + // Оттенки серого + return max switch + { + > 0.8f => "white", + > 0.6f => "light gray", + > 0.4f => "gray", + > 0.2f => "dark gray", + _ => "black", + }; + } + + // Цветные + const float epsilon = 0.001f; + if (Mathf.Abs(color.r - max) < epsilon) + { + return color.g > color.b ? "orange" : "red"; + } + else if (Mathf.Abs(color.g - max) < epsilon) + { + return color.r > color.b ? "yellow" : "green"; + } + else + { + return color.r > color.g ? "purple" : "blue"; + } + } + public override void DoWindowContents(Rect inRect) { float curY = 0f; @@ -282,6 +425,47 @@ namespace AIImages new Rect(20f, contentY, scrollViewRect.width - 30f, apparelHeight), apparelText ); + contentY += apparelHeight + 20f; + + // Разделитель + Widgets.DrawLineHorizontal(10f, contentY, scrollViewRect.width - 20f); + contentY += 15f; + + // Секция "Stable Diffusion Промпт" + Text.Font = GameFont.Medium; + Widgets.Label( + new Rect(10f, contentY, scrollViewRect.width - 20f, 30f), + "AIImages.Prompt.SectionTitle".Translate() + ); + contentY += 35f; + + // Промпт текст + Text.Font = GameFont.Small; + string promptText = GenerateStableDiffusionPrompt(); + float promptHeight = Text.CalcHeight(promptText, scrollViewRect.width - 30f); + Widgets.Label( + new Rect(20f, contentY, scrollViewRect.width - 30f, promptHeight), + promptText + ); + contentY += promptHeight + 10f; + + // Кнопка копирования + Rect copyButtonRect = new Rect(20f, contentY, 150f, 30f); + + if (Widgets.ButtonText(copyButtonRect, "AIImages.Prompt.CopyButton".Translate())) + { + GUIUtility.systemCopyBuffer = promptText; + copiedMessageTime = 2f; // Показываем сообщение на 2 секунды + } + + // Сообщение о копировании + if (copiedMessageTime > 0f) + { + Rect copiedRect = new Rect(copyButtonRect.xMax + 10f, contentY, 100f, 30f); + GUI.color = new Color(0f, 1f, 0f, copiedMessageTime / 2f); // Затухающий зеленый + Widgets.Label(copiedRect, "AIImages.Prompt.Copied".Translate()); + GUI.color = Color.white; + } Widgets.EndScrollView(); } @@ -310,6 +494,19 @@ namespace AIImages string apparelText = GetApparelDescription(); height += Text.CalcHeight(apparelText, 640f) + 20f; + // Разделитель + height += 15f; + + // Заголовок "Промпт" + height += 35f; + + // Текст промпта + string promptText = GenerateStableDiffusionPrompt(); + height += Text.CalcHeight(promptText, 640f) + 10f; + + // Кнопка и отступ + height += 30f + 20f; + return height; } }