using System.Linq; using System.Reflection; using RimWorld; using UnityEngine; using Verse; namespace AIImages.Services { /// /// Сервис для умного определения цветов (вместо RGB значений) /// public static class ColorDescriptionService { // Константы для повторяющихся строковых литералов private const string Green = "Green"; private const string Orange = "Orange"; private const string Black = "Black"; private const string White = "White"; /// /// Получает текстовое описание цвета волос /// Сначала проверяет гены цвета волос, затем использует вычисленный RimWorld цвет волос /// public static string GetHairColorDescription(Pawn pawn) { if (pawn?.genes?.GenesListForReading != null) { // Ищем активные и неподавленные гены цвета волос var hairColorGenes = pawn .genes.GenesListForReading.Where(gene => gene.Active && !IsGeneSuppressed(pawn, gene) && IsHairColorGene(gene.def) ) .ToList(); // Если есть гены цвета волос, используем их описание if (hairColorGenes.Any()) { var hairColorGene = hairColorGenes[0]; string geneDescription = GetHairColorGeneDescription(hairColorGene.def); if (!string.IsNullOrEmpty(geneDescription)) return geneDescription; } } // Иначе используем вычисленный RimWorld цвет волос return GetHairColorDescription(pawn?.story?.HairColor ?? Color.white); } /// /// Получает текстовое описание цвета волос по RGB цвету (fallback метод) /// public static string GetHairColorDescription(Color color) { float h, s, v; Color.RGBToHSV(color, out h, out s, out v); // Проверяем на оттенки серого if (s < 0.15f) { return GetGrayscaleDescription(v); } // Определяем оттенок string hueDescription = GetHueDescription(h); string brightnessModifier = GetBrightnessModifier(v, s); return $"{brightnessModifier}{hueDescription}".Trim(); } /// /// Получает текстовое описание цвета кожи /// Сначала проверяет специальные гены цвета кожи (зелёная, синяя и т.д.), /// затем использует вычисленный RimWorld цвет кожи /// public static string GetSkinToneDescription(Pawn pawn) { if (pawn?.genes?.GenesListForReading == null) return GetSkinToneDescription(pawn.story.SkinColor); // Ищем активные и неподавленные гены цвета кожи (особенно специальные: зелёная, синяя, красная и т.д.) var skinColorGenes = pawn .genes.GenesListForReading.Where(gene => gene.Active && !IsGeneSuppressed(pawn, gene) && IsSpecialSkinColorGene(gene.def) ) .ToList(); // Если есть специальные гены цвета кожи, используем их описание if (skinColorGenes.Any()) { var specialGene = skinColorGenes[0]; string geneDescription = GetSpecialGeneSkinTone(specialGene.def.defName); if (!string.IsNullOrEmpty(geneDescription)) return geneDescription; } // Иначе RimWorld уже определил цвет текстуры пешки на основе генов (включая уровень мелатонина) // Используем этот вычисленный цвет return GetSkinToneDescription(pawn.story.SkinColor); } /// /// Получает текстовое описание цвета кожи по RGB цвету (fallback метод) /// public static string GetSkinToneDescription(Color color) { // Вычисляем яркость float brightness = (color.r + color.g + color.b) / 3f; // Определяем оттенок кожи if (brightness >= 0.85f) return "very fair skin"; else if (brightness >= 0.75f) return "fair skin"; else if (brightness >= 0.65f) return "light skin"; else if (brightness >= 0.55f) return "medium skin"; else if (brightness >= 0.45f) return "olive skin"; else if (brightness >= 0.35f) return "tan skin"; else if (brightness >= 0.25f) return "brown skin"; else if (brightness >= 0.15f) return "dark brown skin"; else return "very dark skin"; } /// /// Проверяет, является ли ген специальным геном цвета кожи (зелёная, синяя, красная и т.д.) /// Исключает гены меланина (Melanin1-9), так как они определяют обычный цвет кожи /// private static bool IsSpecialSkinColorGene(GeneDef geneDef) { if (geneDef == null) return false; string geneDefName = geneDef.defName; // Исключаем гены меланина - они не дают специальный цвет if (geneDefName.StartsWith("Skin_Melanin")) return false; // Специальные цвета: зелёная, синяя, красная, жёлтая, фиолетовая, оранжевая, чёрная, белая, серая return geneDefName.StartsWith("Skin_") && HasSpecialSkinColorKeyword(geneDefName); } /// /// Проверяет наличие ключевых слов специальных цветов кожи /// private static bool HasSpecialSkinColorKeyword(string geneDefName) { return geneDefName.Contains(Green) || geneDefName.Contains("Blue") || geneDefName.Contains("Red") || geneDefName.Contains("Yellow") || geneDefName.Contains("Purple") || geneDefName.Contains(Orange) || geneDefName.Contains(Black) || geneDefName.Contains(White) || geneDefName.Contains("Gray") || geneDefName.Contains("Grey") || geneDefName.Contains("Ink") || geneDefName.Contains("Slate") || geneDefName.Contains("Sheer") || geneDefName.Contains("Pale") || geneDefName.Contains("Deep"); } /// /// Получает описание для специальных генов цвета кожи /// private static string GetSpecialGeneSkinTone(string geneDefName) { // Сначала проверяем точные совпадения string exactMatch = GetExactSkinColorMatch(geneDefName); if (exactMatch != null) return exactMatch; // Затем пытаемся определить по ключевым словам return GetSkinColorByKeyword(geneDefName); } /// /// Получает точное совпадение для известных генов цвета кожи /// private static string GetExactSkinColorMatch(string geneDefName) { switch (geneDefName) { case "Skin_InkBlack": return "ink black skin"; case "Skin_SlateGray": return "slate gray skin"; case "Skin_LightGray": return "light gray skin"; case "Skin_SheerWhite": return "sheer white skin"; case "Skin_Blue": return "blue skin"; case "Skin_Purple": return "purple skin"; case "Skin_PaleRed": return "pale red skin"; case "Skin_DeepRed": return "deep red skin"; case "Skin_PaleYellow": return "pale yellow skin"; case "Skin_DeepYellow": return "deep yellow skin"; case "Skin_Orange": return "orange skin"; case "Skin_Green": return "green skin"; default: return null; } } /// /// Определяет цвет кожи по ключевым словам в названии гена /// private static string GetSkinColorByKeyword(string geneDefName) { // Пытаемся определить по названию (defName всегда на английском) if (geneDefName.Contains(Green)) return "green skin"; if (geneDefName.Contains("Blue")) return "blue skin"; if (geneDefName.Contains("Red")) return "red skin"; if (geneDefName.Contains("Yellow")) return "yellow skin"; if (geneDefName.Contains("Purple")) return "purple skin"; if (geneDefName.Contains(Orange)) return "orange skin"; if (geneDefName.Contains(Black)) return "black skin"; if (geneDefName.Contains(White)) return "white skin"; if (geneDefName.Contains("Gray") || geneDefName.Contains("Grey")) return "gray skin"; return null; } /// /// Проверяет, подавлен ли ген другим активным геном /// private static bool IsGeneSuppressed(Pawn pawn, Gene gene) { if (pawn?.genes == null || gene?.def == null) return false; // В RimWorld, если ген активен, он уже учтён в системе // Но мы можем проверить, подавляет ли другой активный ген данный ген // Используем GeneUtility для проверки подавления try { // Проверяем все активные гены на предмет подавления данного гена foreach (var otherGene in pawn.genes.GenesListForReading) { if ( otherGene.Active && otherGene != gene && otherGene.def != null && CheckGeneSuppression(otherGene.def, gene.def) ) { return true; } } } catch { // Если reflection не работает, возвращаем false // В RimWorld свойство Active уже учитывает подавление return false; } return false; } /// /// Проверяет, подавляет ли один ген другой через reflection /// private static bool CheckGeneSuppression(GeneDef suppressor, GeneDef suppressed) { var suppressesField = suppressor.GetType().GetField("suppresses"); if (suppressesField == null) return false; var suppresses = suppressesField.GetValue(suppressor); if (suppresses is System.Collections.Generic.List suppressesList) { return suppressesList.Contains(suppressed); } if (suppresses is System.Collections.Generic.IEnumerable suppressesEnum) { return suppressesEnum.Contains(suppressed); } return false; } /// /// Проверяет, является ли ген геном цвета волос /// private static bool IsHairColorGene(GeneDef geneDef) { if (geneDef == null) return false; string geneDefName = geneDef.defName; // Проверяем стандартные паттерны для генов цвета волос return geneDefName.StartsWith("Hair_") || geneDefName.Contains("HairColor") || geneDefName.Contains("HairColour") || geneDefName.Contains("HairTone") || ( geneDefName.Contains("Hair") && ( geneDefName.Contains("Color") || geneDefName.Contains("Colour") || geneDefName.Contains("Red") || geneDefName.Contains("Blue") || geneDefName.Contains(Green) || geneDefName.Contains("Blond") || geneDefName.Contains("Brown") || geneDefName.Contains(Black) || geneDefName.Contains(White) || geneDefName.Contains("Light") || geneDefName.Contains("Dark") || geneDefName.Contains("Ginger") || geneDefName.Contains("Auburn") || geneDefName.Contains("Copper") || geneDefName.Contains(Orange) ) ); } /// /// Получает описание цвета волос из гена /// private static string GetHairColorGeneDescription(GeneDef geneDef) { if (geneDef == null) return null; string geneDefName = geneDef.defName; // Сначала проверяем точные совпадения string exactMatch = GetExactHairColorMatch(geneDefName); if (exactMatch != null) return exactMatch; // Затем пытаемся определить по ключевым словам return GetHairColorByKeyword(geneDefName); } /// /// Получает точное совпадение для известных генов цвета волос /// private static string GetExactHairColorMatch(string geneDefName) { switch (geneDefName) { case "Hair_Blond": case "Hair_Blonde": return "blond hair"; case "Hair_Brown": return "brown hair"; case "Hair_Black": return "black hair"; case "Hair_White": return "white hair"; case "Hair_Red": return "red hair"; case "Hair_Ginger": return "ginger hair"; case "Hair_Auburn": return "auburn hair"; case "Hair_Copper": case "Hair_CopperBrown": return "copper-brown hair"; case "Hair_Light": return "light hair"; case "Hair_Dark": return "dark hair"; default: return null; } } /// /// Определяет цвет волос по ключевым словам в названии гена /// private static string GetHairColorByKeyword(string geneDefName) { // Проверяем основные цвета волос string basicColor = GetBasicHairColor(geneDefName); if (basicColor != null) return basicColor; // Проверяем специальные цвета string specialColor = GetSpecialHairColor(geneDefName); if (specialColor != null) return specialColor; // Проверяем оттенки return GetHairColorShade(geneDefName); } /// /// Получает основной цвет волос /// private static string GetBasicHairColor(string geneDefName) { if (geneDefName.Contains("Blond")) return "blond hair"; if (geneDefName.Contains("Brown")) return "brown hair"; if (geneDefName.Contains(Black)) return "black hair"; if (geneDefName.Contains(White)) return "white hair"; if (geneDefName.Contains("Red")) return "red hair"; if (geneDefName.Contains("Ginger")) return "ginger hair"; if (geneDefName.Contains("Auburn")) return "auburn hair"; return null; } /// /// Получает специальный цвет волос /// private static string GetSpecialHairColor(string geneDefName) { if (geneDefName.Contains("Copper")) return "copper-brown hair"; if (geneDefName.Contains(Orange)) return "orange-red hair"; if (geneDefName.Contains("Blue")) return "blue hair"; if (geneDefName.Contains(Green)) return "green hair"; if (geneDefName.Contains("Purple")) return "purple hair"; if (geneDefName.Contains("Pink")) return "pink hair"; if (geneDefName.Contains("Gray") || geneDefName.Contains("Grey")) return "gray hair"; return null; } /// /// Получает оттенок волос /// private static string GetHairColorShade(string geneDefName) { if (geneDefName.Contains("Light")) return "light hair"; if (geneDefName.Contains("Dark")) return "dark hair"; return null; } /// /// Получает описание цвета одежды /// public static string GetApparelColorDescription(Color color) { float h, s, v; Color.RGBToHSV(color, out h, out s, out v); // Проверяем на оттенки серого if (s < 0.1f) { return GetGrayscaleDescription(v); } // Определяем оттенок string hueDescription = GetHueDescription(h); // Модификаторы насыщенности и яркости string saturationModifier = ""; if (s < 0.3f) saturationModifier = "pale "; else if (s > 0.8f) saturationModifier = "vivid "; string brightnessModifier = ""; if (v < 0.3f) brightnessModifier = "dark "; else if (v > 0.8f) brightnessModifier = "bright "; return $"{brightnessModifier}{saturationModifier}{hueDescription}".Trim(); } private static string GetGrayscaleDescription(float value) { if (value >= 0.95f) return "white"; else if (value >= 0.8f) return "light gray"; else if (value >= 0.6f) return "gray"; else if (value >= 0.4f) return "dark gray"; else if (value >= 0.2f) return "charcoal"; else return "black"; } private static string GetHueDescription(float hue) { // Hue from 0 to 1 if (hue < 0.05f || hue >= 0.95f) return "red"; if (hue < 0.083f) return "orange-red"; if (hue < 0.15f) return "orange"; if (hue < 0.19f) return "golden"; if (hue < 0.22f) return "yellow"; if (hue < 0.35f) return "yellow-green"; if (hue < 0.45f) return "green"; if (hue < 0.52f) return "cyan"; if (hue < 0.58f) return "light blue"; if (hue < 0.65f) return "blue"; if (hue < 0.72f) return "deep blue"; if (hue < 0.78f) return "purple"; if (hue < 0.85f) return "violet"; if (hue < 0.92f) return "magenta"; return "pink"; } private static string GetBrightnessModifier(float value, float saturation) { // Для волос используем специальные термины if (value < 0.2f) return "jet black "; else if (value < 0.3f) return "black "; else if (value < 0.45f) return "dark "; else if (value > 0.85f && saturation < 0.3f) return "platinum "; else if (value > 0.75f) return "light "; else return ""; } } }