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 "";
}
}
}