using System.Linq; using System.Reflection; using RimWorld; using UnityEngine; using Verse; namespace AIImages.Services { /// /// Сервис для умного определения цветов (вместо RGB значений) /// public static class ColorDescriptionService { /// /// Получает текстовое описание цвета волос /// Сначала проверяет гены цвета волос, затем использует вычисленный 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); } /// /// Проверяет, является ли ген специальным геном цвета кожи (зелёная, синяя, красная и т.д.) /// Исключает гены меланина (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_") && ( 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) { 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: // Пытаемся определить по названию (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) { // Проверяем через GeneUtility, если доступен // Или проверяем свойство suppresses через reflection, если оно есть var suppressesField = otherGene.def.GetType().GetField("suppresses"); if (suppressesField != null) { var suppresses = suppressesField.GetValue(otherGene.def); if ( suppresses is System.Collections.Generic.List suppressesList ) { if (suppressesList.Contains(gene.def)) return true; } else if ( suppresses is System.Collections.Generic.IEnumerable suppressesEnum ) { if (suppressesEnum.Contains(gene.def)) return true; } } } } } catch { // Если reflection не работает, возвращаем false // В RimWorld свойство Active уже учитывает подавление return false; } 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; // Специфичные описания для известных генов цвета волос 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: // Пытаемся определить по названию (defName всегда на английском) // Добавляем поддержку конкретных генов из RimWorld 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"; if (geneDefName.Contains("Copper")) return "copper-brown hair"; if (geneDefName.Contains("Orange")) return "orange-red hair"; if (geneDefName.Contains("Light")) return "light hair"; if (geneDefName.Contains("Dark")) return "dark 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; } } /// /// Получает текстовое описание цвета кожи по 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"; } /// /// Получает описание цвета одежды /// 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 ""; } } }