All checks were successful
SonarQube Analysis / Build and analyze (push) Successful in 1m51s
579 lines
23 KiB
C#
579 lines
23 KiB
C#
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 "";
|
||
}
|
||
}
|
||
}
|