Enhance hair color handling in AIImages by adding support for hair color genes in PawnAppearanceData. Update ColorDescriptionService to prioritize gene-based hair color descriptions, improving character appearance representation. Refactor related services to utilize new hair color logic.
All checks were successful
SonarQube Analysis / Build and analyze (push) Successful in 1m40s

This commit is contained in:
Leonid Pershin
2025-11-01 09:15:29 +03:00
parent 60dcb279ae
commit 30010078b4
5 changed files with 431 additions and 34 deletions

View File

@@ -1,4 +1,5 @@
using System.Linq;
using System.Reflection;
using RimWorld;
using UnityEngine;
using Verse;
@@ -12,6 +13,35 @@ namespace AIImages.Services
{
/// <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)
{
@@ -43,10 +73,10 @@ namespace AIImages.Services
if (pawn?.genes?.GenesListForReading == null)
return GetSkinToneDescription(pawn.story.SkinColor);
// Ищем активные гены цвета кожи (особенно специальные: зелёная, синяя, красная и т.д.)
// Ищем активные и неподавленные гены цвета кожи (особенно специальные: зелёная, синяя, красная и т.д.)
var skinColorGenes = pawn
.genes.GenesListForReading.Where(gene =>
gene.Active && IsSpecialSkinColorGene(gene.def)
gene.Active && !IsGeneSuppressed(pawn, gene) && IsSpecialSkinColorGene(gene.def)
)
.ToList();
@@ -66,6 +96,7 @@ namespace AIImages.Services
/// <summary>
/// Проверяет, является ли ген специальным геном цвета кожи (зелёная, синяя, красная и т.д.)
/// Исключает гены меланина (Melanin1-9), так как они определяют обычный цвет кожи
/// </summary>
private static bool IsSpecialSkinColorGene(GeneDef geneDef)
{
@@ -74,6 +105,10 @@ namespace AIImages.Services
string geneDefName = geneDef.defName;
// Исключаем гены меланина - они не дают специальный цвет
if (geneDefName.StartsWith("Skin_Melanin"))
return false;
// Специальные цвета: зелёная, синяя, красная, жёлтая, фиолетовая, оранжевая, чёрная, белая, серая
return geneDefName.StartsWith("Skin_")
&& (
@@ -127,33 +162,194 @@ namespace AIImages.Services
case "Skin_Green":
return "green skin";
default:
// Пытаемся определить по названию
if (geneDefName.Contains("Green") || geneDefName.Contains("зел"))
// Пытаемся определить по названию (defName всегда на английском)
if (geneDefName.Contains("Green"))
return "green skin";
if (geneDefName.Contains("Blue") || geneDefName.Contains("син"))
if (geneDefName.Contains("Blue"))
return "blue skin";
if (geneDefName.Contains("Red") || geneDefName.Contains("красн"))
if (geneDefName.Contains("Red"))
return "red skin";
if (geneDefName.Contains("Yellow") || geneDefName.Contains("жёлт"))
if (geneDefName.Contains("Yellow"))
return "yellow skin";
if (geneDefName.Contains("Purple") || geneDefName.Contains("фиолет"))
if (geneDefName.Contains("Purple"))
return "purple skin";
if (geneDefName.Contains("Orange") || geneDefName.Contains("оранж"))
if (geneDefName.Contains("Orange"))
return "orange skin";
if (geneDefName.Contains("Black") || geneDefName.Contains("чёрн"))
if (geneDefName.Contains("Black"))
return "black skin";
if (geneDefName.Contains("White") || geneDefName.Contains("бел"))
if (geneDefName.Contains("White"))
return "white skin";
if (
geneDefName.Contains("Gray")
|| geneDefName.Contains("Grey")
|| geneDefName.Contains("сер")
)
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)
{
// Проверяем через 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<GeneDef> suppressesList
)
{
if (suppressesList.Contains(gene.def))
return true;
}
else if (
suppresses
is System.Collections.Generic.IEnumerable<GeneDef> suppressesEnum
)
{
if (suppressesEnum.Contains(gene.def))
return true;
}
}
}
}
}
catch
{
// Если reflection не работает, возвращаем false
// В RimWorld свойство Active уже учитывает подавление
return false;
}
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;
// Специфичные описания для известных генов цвета волос
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;
}
}
/// <summary>
/// Получает текстовое описание цвета кожи по RGB цвету (fallback метод)
/// </summary>