Files
ai-images/Source/AIImages/Services/ColorDescriptionService.cs

579 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Linq;
using System.Reflection;
using RimWorld;
using UnityEngine;
using Verse;
namespace AIImages.Services
{
/// <summary>
/// Сервис для умного определения цветов (вместо RGB значений)
/// </summary>
public static class ColorDescriptionService
{
// Константы для повторяющихся строковых литералов
private const string Green = "Green";
private const string Orange = "Orange";
private const string Black = "Black";
private const string White = "White";
/// <summary>
/// Получает текстовое описание цвета волос
/// Сначала проверяет гены цвета волос, затем использует вычисленный RimWorld цвет волос
/// </summary>
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);
}
/// <summary>
/// Получает текстовое описание цвета волос по RGB цвету (fallback метод)
/// </summary>
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();
}
/// <summary>
/// Получает текстовое описание цвета кожи
/// Сначала проверяет специальные гены цвета кожи (зелёная, синяя и т.д.),
/// затем использует вычисленный RimWorld цвет кожи
/// </summary>
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);
}
/// <summary>
/// Получает текстовое описание цвета кожи по RGB цвету (fallback метод)
/// </summary>
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";
}
/// <summary>
/// Проверяет, является ли ген специальным геном цвета кожи (зелёная, синяя, красная и т.д.)
/// Исключает гены меланина (Melanin1-9), так как они определяют обычный цвет кожи
/// </summary>
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);
}
/// <summary>
/// Проверяет наличие ключевых слов специальных цветов кожи
/// </summary>
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");
}
/// <summary>
/// Получает описание для специальных генов цвета кожи
/// </summary>
private static string GetSpecialGeneSkinTone(string geneDefName)
{
// Сначала проверяем точные совпадения
string exactMatch = GetExactSkinColorMatch(geneDefName);
if (exactMatch != null)
return exactMatch;
// Затем пытаемся определить по ключевым словам
return GetSkinColorByKeyword(geneDefName);
}
/// <summary>
/// Получает точное совпадение для известных генов цвета кожи
/// </summary>
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;
}
}
/// <summary>
/// Определяет цвет кожи по ключевым словам в названии гена
/// </summary>
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;
}
/// <summary>
/// Проверяет, подавлен ли ген другим активным геном
/// </summary>
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;
}
/// <summary>
/// Проверяет, подавляет ли один ген другой через reflection
/// </summary>
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<GeneDef> suppressesList)
{
return suppressesList.Contains(suppressed);
}
if (suppresses is System.Collections.Generic.IEnumerable<GeneDef> suppressesEnum)
{
return suppressesEnum.Contains(suppressed);
}
return false;
}
/// <summary>
/// Проверяет, является ли ген геном цвета волос
/// </summary>
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)
)
);
}
/// <summary>
/// Получает описание цвета волос из гена
/// </summary>
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);
}
/// <summary>
/// Получает точное совпадение для известных генов цвета волос
/// </summary>
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;
}
}
/// <summary>
/// Определяет цвет волос по ключевым словам в названии гена
/// </summary>
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);
}
/// <summary>
/// Получает основной цвет волос
/// </summary>
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;
}
/// <summary>
/// Получает специальный цвет волос
/// </summary>
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;
}
/// <summary>
/// Получает оттенок волос
/// </summary>
private static string GetHairColorShade(string geneDefName)
{
if (geneDefName.Contains("Light"))
return "light hair";
if (geneDefName.Contains("Dark"))
return "dark hair";
return null;
}
/// <summary>
/// Получает описание цвета одежды
/// </summary>
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 "";
}
}
}